Add Kubernetes auth providers (#2147)
* Import auth providers for K8s * Vendor updates for K8s auth providers * Remove Azure since it is not compiling * Update vendor to remove Azure dependencies
This commit is contained in:
parent
fb971ffff3
commit
1018a8267a
140 changed files with 27108 additions and 0 deletions
66
Gopkg.lock
generated
66
Gopkg.lock
generated
|
@ -1,6 +1,14 @@
|
||||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:1acadbbc24182315b628f727b2e9ac653266d1644ca4007e0766c28110afc072"
|
||||||
|
name = "cloud.google.com/go"
|
||||||
|
packages = ["compute/metadata"]
|
||||||
|
pruneopts = ""
|
||||||
|
revision = "97efc2c9ffd9fe8ef47f7f3203dc60bbca547374"
|
||||||
|
version = "v0.28.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:6aa683f91e93784ec7b76fe1568492f419aa3aa0b72f2087912c8c2cada7a375"
|
digest = "1:6aa683f91e93784ec7b76fe1568492f419aa3aa0b72f2087912c8c2cada7a375"
|
||||||
name = "github.com/DataDog/dd-trace-go"
|
name = "github.com/DataDog/dd-trace-go"
|
||||||
|
@ -224,6 +232,22 @@
|
||||||
revision = "7c663266750e7d82587642f65e60bc4083f1f84e"
|
revision = "7c663266750e7d82587642f65e60bc4083f1f84e"
|
||||||
version = "v0.2.0"
|
version = "v0.2.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:c29d304a0d86a51588fe4a90e7e891a11877ebd31f6f263304f8d463bbeeed96"
|
||||||
|
name = "github.com/gophercloud/gophercloud"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"openstack",
|
||||||
|
"openstack/identity/v2/tenants",
|
||||||
|
"openstack/identity/v2/tokens",
|
||||||
|
"openstack/identity/v3/tokens",
|
||||||
|
"openstack/utils",
|
||||||
|
"pagination",
|
||||||
|
]
|
||||||
|
pruneopts = ""
|
||||||
|
revision = "bfc006765209a570e9a783bd7e4372e24fb72781"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:009a1928b8c096338b68b5822d838a72b4d8520715c1463614476359f3282ec8"
|
digest = "1:009a1928b8c096338b68b5822d838a72b4d8520715c1463614476359f3282ec8"
|
||||||
|
@ -428,6 +452,7 @@
|
||||||
name = "golang.org/x/net"
|
name = "golang.org/x/net"
|
||||||
packages = [
|
packages = [
|
||||||
"context",
|
"context",
|
||||||
|
"context/ctxhttp",
|
||||||
"http/httpguts",
|
"http/httpguts",
|
||||||
"http2",
|
"http2",
|
||||||
"http2/hpack",
|
"http2/hpack",
|
||||||
|
@ -438,6 +463,20 @@
|
||||||
pruneopts = ""
|
pruneopts = ""
|
||||||
revision = "4cb1c02c05b0e749b0365f61ae859a8e0cfceed9"
|
revision = "4cb1c02c05b0e749b0365f61ae859a8e0cfceed9"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:b697592485cb412be4188c08ca0beed9aab87f36b86418e21acc4a3998f63734"
|
||||||
|
name = "golang.org/x/oauth2"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"google",
|
||||||
|
"internal",
|
||||||
|
"jws",
|
||||||
|
"jwt",
|
||||||
|
]
|
||||||
|
pruneopts = ""
|
||||||
|
revision = "d2e6202438beef2727060aa7cabdd924d92ebfd9"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:274e6fab68b7f298bf3f70bd60d4ba0c55284d1d2034175fb3324924268ccd9e"
|
digest = "1:274e6fab68b7f298bf3f70bd60d4ba0c55284d1d2034175fb3324924268ccd9e"
|
||||||
|
@ -480,6 +519,25 @@
|
||||||
pruneopts = ""
|
pruneopts = ""
|
||||||
revision = "fbb02b2291d28baffd63558aa44b4b56f178d650"
|
revision = "fbb02b2291d28baffd63558aa44b4b56f178d650"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:8c432632a230496c35a15cfdf441436f04c90e724ad99c8463ef0c82bbe93edb"
|
||||||
|
name = "google.golang.org/appengine"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"internal",
|
||||||
|
"internal/app_identity",
|
||||||
|
"internal/base",
|
||||||
|
"internal/datastore",
|
||||||
|
"internal/log",
|
||||||
|
"internal/modules",
|
||||||
|
"internal/remote_api",
|
||||||
|
"internal/urlfetch",
|
||||||
|
"urlfetch",
|
||||||
|
]
|
||||||
|
pruneopts = ""
|
||||||
|
revision = "ae0ab99deb4dc413a2b4bd6c8bdd0eb67f1e4d06"
|
||||||
|
version = "v1.2.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:a5959f4640612317b0d3122569b7c02565ba6277aa0374cff2ed610c81ef8d74"
|
digest = "1:a5959f4640612317b0d3122569b7c02565ba6277aa0374cff2ed610c81ef8d74"
|
||||||
|
@ -697,9 +755,13 @@
|
||||||
"pkg/apis/clientauthentication/v1beta1",
|
"pkg/apis/clientauthentication/v1beta1",
|
||||||
"pkg/version",
|
"pkg/version",
|
||||||
"plugin/pkg/client/auth/exec",
|
"plugin/pkg/client/auth/exec",
|
||||||
|
"plugin/pkg/client/auth/gcp",
|
||||||
|
"plugin/pkg/client/auth/oidc",
|
||||||
|
"plugin/pkg/client/auth/openstack",
|
||||||
"rest",
|
"rest",
|
||||||
"rest/watch",
|
"rest/watch",
|
||||||
"testing",
|
"testing",
|
||||||
|
"third_party/forked/golang/template",
|
||||||
"tools/auth",
|
"tools/auth",
|
||||||
"tools/cache",
|
"tools/cache",
|
||||||
"tools/clientcmd",
|
"tools/clientcmd",
|
||||||
|
@ -716,6 +778,7 @@
|
||||||
"util/flowcontrol",
|
"util/flowcontrol",
|
||||||
"util/homedir",
|
"util/homedir",
|
||||||
"util/integer",
|
"util/integer",
|
||||||
|
"util/jsonpath",
|
||||||
"util/retry",
|
"util/retry",
|
||||||
]
|
]
|
||||||
pruneopts = ""
|
pruneopts = ""
|
||||||
|
@ -763,6 +826,9 @@
|
||||||
"k8s.io/apimachinery/pkg/watch",
|
"k8s.io/apimachinery/pkg/watch",
|
||||||
"k8s.io/client-go/kubernetes",
|
"k8s.io/client-go/kubernetes",
|
||||||
"k8s.io/client-go/kubernetes/fake",
|
"k8s.io/client-go/kubernetes/fake",
|
||||||
|
"k8s.io/client-go/plugin/pkg/client/auth/gcp",
|
||||||
|
"k8s.io/client-go/plugin/pkg/client/auth/oidc",
|
||||||
|
"k8s.io/client-go/plugin/pkg/client/auth/openstack",
|
||||||
"k8s.io/client-go/rest",
|
"k8s.io/client-go/rest",
|
||||||
"k8s.io/client-go/tools/cache",
|
"k8s.io/client-go/tools/cache",
|
||||||
"k8s.io/client-go/tools/clientcmd",
|
"k8s.io/client-go/tools/clientcmd",
|
||||||
|
|
|
@ -19,6 +19,14 @@ import (
|
||||||
"github.com/mholt/caddy"
|
"github.com/mholt/caddy"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
// Excluding azure because it is failing to compile
|
||||||
|
// pull this in here, because we want it excluded if plugin.cfg doesn't have k8s
|
||||||
|
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||||
|
// pull this in here, because we want it excluded if plugin.cfg doesn't have k8s
|
||||||
|
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
|
||||||
|
// pull this in here, because we want it excluded if plugin.cfg doesn't have k8s
|
||||||
|
_ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
25
vendor/cloud.google.com/go/.travis.yml
generated
vendored
Normal file
25
vendor/cloud.google.com/go/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
sudo: false
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.6.x
|
||||||
|
- 1.7.x
|
||||||
|
- 1.8.x
|
||||||
|
- 1.9.x
|
||||||
|
- 1.10.x
|
||||||
|
- 1.11.x
|
||||||
|
install:
|
||||||
|
- go get -v cloud.google.com/go/...
|
||||||
|
script:
|
||||||
|
- openssl aes-256-cbc -K $encrypted_a8b3f4fc85f4_key -iv $encrypted_a8b3f4fc85f4_iv -in keys.tar.enc -out keys.tar -d
|
||||||
|
- tar xvf keys.tar
|
||||||
|
- GCLOUD_TESTS_GOLANG_PROJECT_ID="dulcet-port-762"
|
||||||
|
GCLOUD_TESTS_GOLANG_KEY="$(pwd)/dulcet-port-762-key.json"
|
||||||
|
GCLOUD_TESTS_GOLANG_FIRESTORE_PROJECT_ID="gcloud-golang-firestore-tests"
|
||||||
|
GCLOUD_TESTS_GOLANG_FIRESTORE_KEY="$(pwd)/gcloud-golang-firestore-tests-key.json"
|
||||||
|
GCLOUD_TESTS_GOLANG_KEYRING="projects/dulcet-port-762/locations/us/keyRings/go-integration-test"
|
||||||
|
GCLOUD_TESTS_GOLANG_ENABLE_REPLAY=yes
|
||||||
|
travis_wait ./run-tests.sh $TRAVIS_COMMIT
|
||||||
|
env:
|
||||||
|
matrix:
|
||||||
|
# The GCLOUD_TESTS_API_KEY environment variable.
|
||||||
|
secure: VdldogUOoubQ60LhuHJ+g/aJoBiujkSkWEWl79Zb8cvQorcQbxISS+JsOOp4QkUOU4WwaHAm8/3pIH1QMWOR6O78DaLmDKi5Q4RpkVdCpUXy+OAfQaZIcBsispMrjxLXnqFjo9ELnrArfjoeCTzaX0QTCfwQwVmigC8rR30JBKI=
|
15
vendor/cloud.google.com/go/AUTHORS
generated
vendored
Normal file
15
vendor/cloud.google.com/go/AUTHORS
generated
vendored
Normal file
|
@ -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 <email address>
|
||||||
|
# The email address is not required for organizations.
|
||||||
|
|
||||||
|
Filippo Valsorda <hi@filippo.io>
|
||||||
|
Google Inc.
|
||||||
|
Ingo Oeser <nightlyone@googlemail.com>
|
||||||
|
Palm Stone Games, Inc.
|
||||||
|
Paweł Knap <pawelknap88@gmail.com>
|
||||||
|
Péter Szilágyi <peterke@gmail.com>
|
||||||
|
Tyler Treat <ttreat31@gmail.com>
|
915
vendor/cloud.google.com/go/CHANGES.md
generated
vendored
Normal file
915
vendor/cloud.google.com/go/CHANGES.md
generated
vendored
Normal file
|
@ -0,0 +1,915 @@
|
||||||
|
# Changes
|
||||||
|
|
||||||
|
## v0.28.0
|
||||||
|
|
||||||
|
- bigtable:
|
||||||
|
- Emulator returns Unimplemented for snapshot RPCs.
|
||||||
|
- bigquery:
|
||||||
|
- Support zero-length repeated, nested fields.
|
||||||
|
- cloud assets:
|
||||||
|
- Add v1beta client.
|
||||||
|
- datastore:
|
||||||
|
- Don't nil out transaction ID on retry.
|
||||||
|
- firestore:
|
||||||
|
- BREAKING CHANGE: When watching a query with Query.Snapshots, QuerySnapshotIterator.Next
|
||||||
|
returns a QuerySnapshot which contains read time, result size, change list and the DocumentIterator
|
||||||
|
(previously, QuerySnapshotIterator.Next returned just the DocumentIterator). See: https://godoc.org/cloud.google.com/go/firestore#Query.Snapshots.
|
||||||
|
- Add array-contains operator.
|
||||||
|
- IAM:
|
||||||
|
- Add iam/credentials/apiv1 client.
|
||||||
|
- pubsub:
|
||||||
|
- Canceling the context passed to Subscription.Receive causes Receive to return when
|
||||||
|
processing finishes on all messages currently in progress, even if new messages are arriving.
|
||||||
|
- redis:
|
||||||
|
- Add redis/apiv1 client.
|
||||||
|
- storage:
|
||||||
|
- Add Reader.Attrs.
|
||||||
|
- Deprecate several Reader getter methods: please use Reader.Attrs for these instead.
|
||||||
|
- Add ObjectHandle.Bucket and ObjectHandle.Object methods.
|
||||||
|
|
||||||
|
## v0.27.0
|
||||||
|
|
||||||
|
- bigquery:
|
||||||
|
- Allow modification of encryption configuration and partitioning options to a table via the Update call.
|
||||||
|
- Add a SchemaFromJSON function that converts a JSON table schema.
|
||||||
|
- bigtable:
|
||||||
|
- Restore cbt count functionality.
|
||||||
|
- containeranalysis:
|
||||||
|
- Add v1beta client.
|
||||||
|
- spanner:
|
||||||
|
- Fix a case where an iterator might not be closed correctly.
|
||||||
|
- storage:
|
||||||
|
- Add ServiceAccount method https://godoc.org/cloud.google.com/go/storage#Client.ServiceAccount.
|
||||||
|
- Add a method to Reader that returns the parsed value of the Last-Modified header.
|
||||||
|
|
||||||
|
## v0.26.0
|
||||||
|
|
||||||
|
- bigquery:
|
||||||
|
- Support filtering listed jobs by min/max creation time.
|
||||||
|
- Support data clustering (https://godoc.org/cloud.google.com/go/bigquery#Clustering).
|
||||||
|
- Include job creator email in Job struct.
|
||||||
|
- bigtable:
|
||||||
|
- Add `RowSampleFilter`.
|
||||||
|
- emulator: BREAKING BEHAVIOR CHANGE: Regexps in row, family, column and value filters
|
||||||
|
must match the entire target string to succeed. Previously, the emulator was
|
||||||
|
succeeding on partial matches.
|
||||||
|
NOTE: As of this release, this change only affects the emulator when run
|
||||||
|
from this repo (bigtable/cmd/emulator/cbtemulator.go). The version launched
|
||||||
|
from `gcloud` will be updated in a subsequent `gcloud` release.
|
||||||
|
- dataproc: Add apiv1beta2 client.
|
||||||
|
- datastore: Save non-nil pointer fields on omitempty.
|
||||||
|
- logging: populate Entry.Trace from the HTTP X-Cloud-Trace-Context header.
|
||||||
|
- logging/logadmin: Support writer_identity and include_children.
|
||||||
|
- pubsub:
|
||||||
|
- Support labels on topics and subscriptions.
|
||||||
|
- Support message storage policy for topics.
|
||||||
|
- Use the distribution of ack times to determine when to extend ack deadlines.
|
||||||
|
The only user-visible effect of this change should be that programs that
|
||||||
|
call only `Subscription.Receive` need no IAM permissions other than `Pub/Sub
|
||||||
|
Subscriber`.
|
||||||
|
- storage:
|
||||||
|
- Support predefined ACLs.
|
||||||
|
- Support additional ACL fields other than Entity and Role.
|
||||||
|
- Support bucket websites.
|
||||||
|
- Support bucket logging.
|
||||||
|
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## 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).
|
||||||
|
|
||||||
|
|
||||||
|
## 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".
|
||||||
|
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
- 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 {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## v0.9.0
|
||||||
|
|
||||||
|
- Breaking changes to some autogenerated clients.
|
||||||
|
- rpcreplay package added.
|
||||||
|
|
||||||
|
## v0.8.0
|
||||||
|
|
||||||
|
- profiler package added.
|
||||||
|
- storage:
|
||||||
|
- Retry Objects.Insert call.
|
||||||
|
- Add ProgressFunc to WRiter.
|
||||||
|
- pubsub: breaking 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.
|
||||||
|
|
||||||
|
## v0.7.0
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
## v0.6.0
|
||||||
|
|
||||||
|
- Beta release of BigQuery, DataStore, Logging and Storage. See the
|
||||||
|
[blog post](https://cloudplatform.googleblog.com/2016/12/announcing-new-google-cloud-client.html).
|
||||||
|
|
||||||
|
- bigquery:
|
||||||
|
- struct support. 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.
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
## v0.5.0
|
||||||
|
|
||||||
|
- bigquery:
|
||||||
|
- 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.
|
||||||
|
- Support for query parameters.
|
||||||
|
- Support deleting a dataset.
|
||||||
|
- Values from INTEGER columns will now be returned as int64, not int. This
|
||||||
|
will avoid errors arising from large values on 32-bit systems.
|
||||||
|
- datastore:
|
||||||
|
- Nested Go structs encoded 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.
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
## v0.4.0
|
||||||
|
|
||||||
|
- bigquery:
|
||||||
|
-`NewGCSReference` is now a function, not a method on `Client`.
|
||||||
|
- `Table.LoaderFrom` now accepts a `ReaderSource`, enabling
|
||||||
|
loading data into a table from a file or any `io.Reader`.
|
||||||
|
* 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
|
||||||
|
```
|
||||||
|
|
||||||
|
- pubsub: remove `pubsub.Done`. Use `iterator.Done` instead, where `iterator` is the package
|
||||||
|
`google.golang.org/api/iterator`.
|
||||||
|
|
||||||
|
## v0.3.0
|
||||||
|
|
||||||
|
- 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`).
|
||||||
|
|
||||||
|
- Package preview/logging deleted. Use logging instead.
|
||||||
|
|
||||||
|
## v0.2.0
|
||||||
|
|
||||||
|
- Logging client replaced with preview version (see below).
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
|
44
vendor/cloud.google.com/go/CODE_OF_CONDUCT.md
generated
vendored
Normal file
44
vendor/cloud.google.com/go/CODE_OF_CONDUCT.md
generated
vendored
Normal file
|
@ -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/)
|
||||||
|
|
174
vendor/cloud.google.com/go/CONTRIBUTING.md
generated
vendored
Normal file
174
vendor/cloud.google.com/go/CONTRIBUTING.md
generated
vendored
Normal file
|
@ -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 <name>`,
|
||||||
|
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
|
40
vendor/cloud.google.com/go/CONTRIBUTORS
generated
vendored
Normal file
40
vendor/cloud.google.com/go/CONTRIBUTORS
generated
vendored
Normal file
|
@ -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 <email address>
|
||||||
|
|
||||||
|
# Keep the list alphabetically sorted.
|
||||||
|
|
||||||
|
Alexis Hunt <lexer@google.com>
|
||||||
|
Andreas Litt <andreas.litt@gmail.com>
|
||||||
|
Andrew Gerrand <adg@golang.org>
|
||||||
|
Brad Fitzpatrick <bradfitz@golang.org>
|
||||||
|
Burcu Dogan <jbd@google.com>
|
||||||
|
Dave Day <djd@golang.org>
|
||||||
|
David Sansome <me@davidsansome.com>
|
||||||
|
David Symonds <dsymonds@golang.org>
|
||||||
|
Filippo Valsorda <hi@filippo.io>
|
||||||
|
Glenn Lewis <gmlewis@google.com>
|
||||||
|
Ingo Oeser <nightlyone@googlemail.com>
|
||||||
|
James Hall <james.hall@shopify.com>
|
||||||
|
Johan Euphrosine <proppy@google.com>
|
||||||
|
Jonathan Amsterdam <jba@google.com>
|
||||||
|
Kunpei Sakai <namusyaka@gmail.com>
|
||||||
|
Luna Duclos <luna.duclos@palmstonegames.com>
|
||||||
|
Magnus Hiie <magnus.hiie@gmail.com>
|
||||||
|
Mario Castro <mariocaster@gmail.com>
|
||||||
|
Michael McGreevy <mcgreevy@golang.org>
|
||||||
|
Omar Jarjur <ojarjur@google.com>
|
||||||
|
Paweł Knap <pawelknap88@gmail.com>
|
||||||
|
Péter Szilágyi <peterke@gmail.com>
|
||||||
|
Sarah Adams <shadams@google.com>
|
||||||
|
Thanatat Tamtan <acoshift@gmail.com>
|
||||||
|
Toby Burress <kurin@google.com>
|
||||||
|
Tuo Shan <shantuo@google.com>
|
||||||
|
Tyler Treat <ttreat31@gmail.com>
|
202
vendor/cloud.google.com/go/LICENSE
generated
vendored
Normal file
202
vendor/cloud.google.com/go/LICENSE
generated
vendored
Normal file
|
@ -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.
|
503
vendor/cloud.google.com/go/README.md
generated
vendored
Normal file
503
vendor/cloud.google.com/go/README.md
generated
vendored
Normal file
|
@ -0,0 +1,503 @@
|
||||||
|
# Google Cloud Client Libraries for Go
|
||||||
|
|
||||||
|
[](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-)
|
||||||
|
* [BigQuery](#cloud-bigquery-)
|
||||||
|
* [Stackdriver Logging](#stackdriver-logging-)
|
||||||
|
* [Cloud Spanner](#cloud-spanner-)
|
||||||
|
|
||||||
|
|
||||||
|
## News
|
||||||
|
|
||||||
|
_7 August 2018_
|
||||||
|
|
||||||
|
As of November 1, the code in the repo will no longer support Go versions 1.8
|
||||||
|
and earlier. No one other than AppEngine users should be on those old versions,
|
||||||
|
and AppEngine
|
||||||
|
[Standard](https://groups.google.com/forum/#!topic/google-appengine-go/e7oPNomd7ak)
|
||||||
|
and
|
||||||
|
[Flex](https://groups.google.com/forum/#!topic/google-appengine-go/wHsYtxvEbXI)
|
||||||
|
will stop supporting new deployments with those versions on that date.
|
||||||
|
|
||||||
|
|
||||||
|
Changes have been moved to [CHANGES](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/CHANGES.md).
|
||||||
|
|
||||||
|
|
||||||
|
## Supported APIs
|
||||||
|
|
||||||
|
Google API | Status | Package
|
||||||
|
---------------------------------------------|--------------|-----------------------------------------------------------
|
||||||
|
[Asset][cloud-asset] | alpha | [`godoc.org/cloud.google.com/go/asset/v1beta`][cloud-asset-ref]
|
||||||
|
[BigQuery][cloud-bigquery] | stable | [`godoc.org/cloud.google.com/go/bigquery`][cloud-bigquery-ref]
|
||||||
|
[Bigtable][cloud-bigtable] | stable | [`godoc.org/cloud.google.com/go/bigtable`][cloud-bigtable-ref]
|
||||||
|
[Container][cloud-container] | stable | [`godoc.org/cloud.google.com/go/container/apiv1`][cloud-container-ref]
|
||||||
|
[ContainerAnalysis][cloud-containeranalysis] | beta | [`godoc.org/cloud.google.com/go/containeranalysis/apiv1beta1`][cloud-containeranalysis-ref]
|
||||||
|
[Dataproc][cloud-dataproc] | stable | [`godoc.org/cloud.google.com/go/dataproc/apiv1`][cloud-dataproc-ref]
|
||||||
|
[Datastore][cloud-datastore] | stable | [`godoc.org/cloud.google.com/go/datastore`][cloud-datastore-ref]
|
||||||
|
[Debugger][cloud-debugger] | alpha | [`godoc.org/cloud.google.com/go/debugger/apiv2`][cloud-debugger-ref]
|
||||||
|
[Dialogflow][cloud-dialogflow] | alpha | [`godoc.org/cloud.google.com/go/dialogflow/apiv2`][cloud-dialogflow-ref]
|
||||||
|
[Data Loss Prevention][cloud-dlp] | alpha | [`godoc.org/cloud.google.com/go/dlp/apiv2`][cloud-dlp-ref]
|
||||||
|
[ErrorReporting][cloud-errors] | alpha | [`godoc.org/cloud.google.com/go/errorreporting`][cloud-errors-ref]
|
||||||
|
[Firestore][cloud-firestore] | beta | [`godoc.org/cloud.google.com/go/firestore`][cloud-firestore-ref]
|
||||||
|
[IAM][cloud-iam] | stable | [`godoc.org/cloud.google.com/go/iam`][cloud-iam-ref]
|
||||||
|
[KMS][cloud-kms] | stable | [`godoc.org/cloud.google.com/go/kms`][cloud-kms-ref]
|
||||||
|
[Natural Language][cloud-natural-language] | stable | [`godoc.org/cloud.google.com/go/language/apiv1`][cloud-natural-language-ref]
|
||||||
|
[Logging][cloud-logging] | stable | [`godoc.org/cloud.google.com/go/logging`][cloud-logging-ref]
|
||||||
|
[Monitoring][cloud-monitoring] | alpha | [`godoc.org/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 | [`godoc.org/cloud.google.com/go/pubsub`][cloud-pubsub-ref]
|
||||||
|
[Memorystore][cloud-memorystore] | stable | [`godoc.org/cloud.google.com/go/redis/apiv1beta1`][cloud-memorystore-ref]
|
||||||
|
[Spanner][cloud-spanner] | stable | [`godoc.org/cloud.google.com/go/spanner`][cloud-spanner-ref]
|
||||||
|
[Speech][cloud-speech] | stable | [`godoc.org/cloud.google.com/go/speech/apiv1`][cloud-speech-ref]
|
||||||
|
[Storage][cloud-storage] | stable | [`godoc.org/cloud.google.com/go/storage`][cloud-storage-ref]
|
||||||
|
[Text To Speech][cloud-texttospeech] | alpha | [`godoc.org/cloud.google.com/go/texttospeech/apiv1`][cloud-storage-ref]
|
||||||
|
[Trace][cloud-trace] | alpha | [`godoc.org/cloud.google.com/go/trace/apiv2`][cloud-translation-ref]
|
||||||
|
[Translation][cloud-translation] | stable | [`godoc.org/cloud.google.com/go/translate`][cloud-translation-ref]
|
||||||
|
[Video Intelligence][cloud-video] | alpha | [`godoc.org/cloud.google.com/go/videointelligence/apiv1beta1`][cloud-video-ref]
|
||||||
|
[Vision][cloud-vision] | stable | [`godoc.org/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.WithCredentialsFile`](https://godoc.org/google.golang.org/api/option#WithCredentialsFile)
|
||||||
|
to the `NewClient` function of the desired package. For example:
|
||||||
|
|
||||||
|
[snip]:# (auth-JSON)
|
||||||
|
```go
|
||||||
|
client, err := storage.NewClient(ctx, option.WithCredentialsFile("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 [](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 [](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 [](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)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## BigQuery [](https://godoc.org/cloud.google.com/go/bigquery)
|
||||||
|
|
||||||
|
- [About 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 [](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 [](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
|
||||||
|
|
||||||
|
[cloud-dataproc]: https://cloud.google.com/dataproc/
|
||||||
|
[cloud-dataproc-docs]: https://cloud.google.com/dataproc/docs
|
||||||
|
[cloud-dataproc-ref]: https://godoc.org/cloud.google.com/go/dataproc/apiv1
|
||||||
|
|
||||||
|
[cloud-iam]: https://cloud.google.com/iam/
|
||||||
|
[cloud-iam-docs]: https://cloud.google.com/iam/docs
|
||||||
|
[cloud-iam-ref]: https://godoc.org/cloud.google.com/go/iam
|
||||||
|
|
||||||
|
[cloud-kms]: https://cloud.google.com/kms/
|
||||||
|
[cloud-kms-docs]: https://cloud.google.com/kms/docs
|
||||||
|
[cloud-kms-ref]: https://godoc.org/cloud.google.com/go/kms/apiv1
|
||||||
|
|
||||||
|
[cloud-natural-language]: https://cloud.google.com/natural-language/
|
||||||
|
[cloud-natural-language-docs]: https://cloud.google.com/natural-language/docs
|
||||||
|
[cloud-natural-language-ref]: https://godoc.org/cloud.google.com/go/language/apiv1
|
||||||
|
|
||||||
|
[cloud-memorystore]: https://cloud.google.com/memorystore/
|
||||||
|
[cloud-memorystore-docs]: https://cloud.google.com/memorystore/docs
|
||||||
|
[cloud-memorystore-ref]: https://godoc.org/cloud.google.com/go/redis/apiv1beta1
|
||||||
|
|
||||||
|
[cloud-texttospeech]: https://cloud.google.com/texttospeech/
|
||||||
|
[cloud-texttospeech-docs]: https://cloud.google.com/texttospeech/docs
|
||||||
|
[cloud-texttospeech-ref]: https://godoc.org/cloud.google.com/go/texttospeech/apiv1
|
||||||
|
|
||||||
|
[cloud-trace]: https://cloud.google.com/trace/
|
||||||
|
[cloud-trace-docs]: https://cloud.google.com/trace/docs
|
||||||
|
[cloud-trace-ref]: https://godoc.org/cloud.google.com/go/trace/apiv1
|
||||||
|
|
||||||
|
[cloud-dialogflow]: https://cloud.google.com/dialogflow-enterprise/
|
||||||
|
[cloud-dialogflow-docs]: https://cloud.google.com/dialogflow-enterprise/docs/
|
||||||
|
[cloud-dialogflow-ref]: https://godoc.org/cloud.google.com/go/dialogflow/apiv2
|
||||||
|
|
||||||
|
[cloud-containeranalysis]: https://cloud.google.com/container-registry/docs/container-analysis
|
||||||
|
[cloud-containeranalysis-docs]: https://cloud.google.com/container-analysis/api/reference/rest/
|
||||||
|
[cloud-containeranalysis-ref]: https://godoc.org/cloud.google.com/go/devtools/containeranalysis/apiv1beta1
|
||||||
|
|
||||||
|
[cloud-asset]: https://cloud.google.com/security-command-center/docs/how-to-asset-inventory
|
||||||
|
[cloud-asset-docs]: https://cloud.google.com/security-command-center/docs/how-to-asset-inventory
|
||||||
|
[cloud-asset-ref]: https://godoc.org/cloud.google.com/go/asset/apiv1
|
13
vendor/cloud.google.com/go/RELEASING.md
generated
vendored
Normal file
13
vendor/cloud.google.com/go/RELEASING.md
generated
vendored
Normal file
|
@ -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 `CHANGES.md` to include a summary of the changes.
|
||||||
|
1. Mail the CL containing the `CHANGES.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`.
|
79
vendor/cloud.google.com/go/cloud.go
generated
vendored
Normal file
79
vendor/cloud.google.com/go/cloud.go
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
// Copyright 2014 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package cloud is the root of the packages used to access Google Cloud
|
||||||
|
Services. See https://godoc.org/cloud.google.com/go for a full list
|
||||||
|
of sub-packages.
|
||||||
|
|
||||||
|
|
||||||
|
Client Options
|
||||||
|
|
||||||
|
All clients in sub-packages are configurable via client options. These options are
|
||||||
|
described here: https://godoc.org/google.golang.org/api/option.
|
||||||
|
|
||||||
|
|
||||||
|
Authentication and Authorization
|
||||||
|
|
||||||
|
All the clients in sub-packages support authentication via Google Application Default
|
||||||
|
Credentials (see https://cloud.google.com/docs/authentication/production), or
|
||||||
|
by providing a JSON key file for a Service Account. See the authentication examples
|
||||||
|
in this package for details.
|
||||||
|
|
||||||
|
|
||||||
|
Timeouts and Cancellation
|
||||||
|
|
||||||
|
By default, all requests in sub-packages will run indefinitely, retrying on transient
|
||||||
|
errors when correctness allows. To set timeouts or arrange for cancellation, use
|
||||||
|
contexts. See the examples for details.
|
||||||
|
|
||||||
|
Do not attempt to control the initial connection (dialing) of a service by setting a
|
||||||
|
timeout on the context passed to NewClient. Dialing is non-blocking, so timeouts
|
||||||
|
would be ineffective and would only interfere with credential refreshing, which uses
|
||||||
|
the same context.
|
||||||
|
|
||||||
|
|
||||||
|
Connection Pooling
|
||||||
|
|
||||||
|
Connection pooling differs in clients based on their transport. Cloud
|
||||||
|
clients either rely on HTTP or gRPC transports to communicate
|
||||||
|
with Google Cloud.
|
||||||
|
|
||||||
|
Cloud clients that use HTTP (bigquery, compute, storage, and translate) rely on the
|
||||||
|
underlying HTTP transport to cache connections for later re-use. These are cached to
|
||||||
|
the default http.MaxIdleConns and http.MaxIdleConnsPerHost settings in
|
||||||
|
http.DefaultTransport.
|
||||||
|
|
||||||
|
For gRPC clients (all others in this repo), connection pooling is configurable. Users
|
||||||
|
of cloud client libraries may specify option.WithGRPCConnectionPool(n) as a client
|
||||||
|
option to NewClient calls. This configures the underlying gRPC connections to be
|
||||||
|
pooled and addressed in a round robin fashion.
|
||||||
|
|
||||||
|
|
||||||
|
Using the Libraries with Docker
|
||||||
|
|
||||||
|
Minimal docker images like Alpine lack CA certificates. This causes RPCs to appear to
|
||||||
|
hang, because gRPC retries indefinitely. See https://github.com/GoogleCloudPlatform/google-cloud-go/issues/928
|
||||||
|
for more information.
|
||||||
|
|
||||||
|
|
||||||
|
Debugging
|
||||||
|
|
||||||
|
To see gRPC logs, set the environment variable GRPC_GO_LOG_SEVERITY_LEVEL. See
|
||||||
|
https://godoc.org/google.golang.org/grpc/grpclog for more information.
|
||||||
|
|
||||||
|
For HTTP logging, set the GODEBUG environment variable to "http2debug=1" or "http2debug=2".
|
||||||
|
|
||||||
|
*/
|
||||||
|
package cloud // import "cloud.google.com/go"
|
503
vendor/cloud.google.com/go/compute/metadata/metadata.go
generated
vendored
Normal file
503
vendor/cloud.google.com/go/compute/metadata/metadata.go
generated
vendored
Normal file
|
@ -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
|
||||||
|
// "<instanceID>.c.<projID>.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
|
||||||
|
// "<instanceID>.c.<projID>.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/<projNum>/zones/<zoneName>".
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
vendor/cloud.google.com/go/issue_template.md
generated
vendored
Normal file
17
vendor/cloud.google.com/go/issue_template.md
generated
vendored
Normal file
|
@ -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.
|
BIN
vendor/cloud.google.com/go/keys.tar.enc
generated
vendored
Normal file
BIN
vendor/cloud.google.com/go/keys.tar.enc
generated
vendored
Normal file
Binary file not shown.
698
vendor/cloud.google.com/go/old-news.md
generated
vendored
Normal file
698
vendor/cloud.google.com/go/old-news.md
generated
vendored
Normal file
|
@ -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.
|
||||||
|
|
77
vendor/cloud.google.com/go/regen-gapic.sh
generated
vendored
Executable file
77
vendor/cloud.google.com/go/regen-gapic.sh
generated
vendored
Executable file
|
@ -0,0 +1,77 @@
|
||||||
|
#!/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/asset/artman_cloudasset_v1beta1.yaml
|
||||||
|
google/iam/credentials/artman_iamcredentials_v1.yaml
|
||||||
|
google/cloud/bigquery/datatransfer/artman_bigquerydatatransfer.yaml
|
||||||
|
google/cloud/dataproc/artman_dataproc_v1.yaml
|
||||||
|
google/cloud/dataproc/artman_dataproc_v1beta2.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/redis/artman_redis_v1.yaml
|
||||||
|
google/cloud/speech/artman_speech_v1.yaml
|
||||||
|
google/cloud/speech/artman_speech_v1p1beta1.yaml
|
||||||
|
google/cloud/tasks/artman_cloudtasks_v2beta2.yaml
|
||||||
|
google/cloud/tasks/artman_cloudtasks_v2beta3.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/devtools/containeranalysis/artman_containeranalysis_v1beta1.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)"
|
88
vendor/cloud.google.com/go/run-tests.sh
generated
vendored
Executable file
88
vendor/cloud.google.com/go/run-tests.sh
generated
vendored
Executable file
|
@ -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
|
3
vendor/github.com/gophercloud/gophercloud/.gitignore
generated
vendored
Normal file
3
vendor/github.com/gophercloud/gophercloud/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
**/*.swp
|
||||||
|
.idea
|
||||||
|
.vscode
|
21
vendor/github.com/gophercloud/gophercloud/.travis.yml
generated
vendored
Normal file
21
vendor/github.com/gophercloud/gophercloud/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
language: go
|
||||||
|
sudo: false
|
||||||
|
install:
|
||||||
|
- go get golang.org/x/crypto/ssh
|
||||||
|
- go get -v -tags 'fixtures acceptance' ./...
|
||||||
|
- go get github.com/wadey/gocovmerge
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- go get golang.org/x/tools/cmd/goimports
|
||||||
|
go:
|
||||||
|
- "1.10"
|
||||||
|
- "tip"
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- secure: "xSQsAG5wlL9emjbCdxzz/hYQsSpJ/bABO1kkbwMSISVcJ3Nk0u4ywF+LS4bgeOnwPfmFvNTOqVDu3RwEvMeWXSI76t1piCPcObutb2faKLVD/hLoAS76gYX+Z8yGWGHrSB7Do5vTPj1ERe2UljdrnsSeOXzoDwFxYRaZLX4bBOB4AyoGvRniil5QXPATiA1tsWX1VMicj8a4F8X+xeESzjt1Q5Iy31e7vkptu71bhvXCaoo5QhYwT+pLR9dN0S1b7Ro0KVvkRefmr1lUOSYd2e74h6Lc34tC1h3uYZCS4h47t7v5cOXvMNxinEj2C51RvbjvZI1RLVdkuAEJD1Iz4+Ote46nXbZ//6XRZMZz/YxQ13l7ux1PFjgEB6HAapmF5Xd8PRsgeTU9LRJxpiTJ3P5QJ3leS1va8qnziM5kYipj/Rn+V8g2ad/rgkRox9LSiR9VYZD2Pe45YCb1mTKSl2aIJnV7nkOqsShY5LNB4JZSg7xIffA+9YVDktw8dJlATjZqt7WvJJ49g6A61mIUV4C15q2JPGKTkZzDiG81NtmS7hFa7k0yaE2ELgYocbcuyUcAahhxntYTC0i23nJmEHVNiZmBO3u7EgpWe4KGVfumU+lt12tIn5b3dZRBBUk3QakKKozSK1QPHGpk/AZGrhu7H6l8to6IICKWtDcyMPQ="
|
||||||
|
before_script:
|
||||||
|
- go vet ./...
|
||||||
|
script:
|
||||||
|
- ./script/coverage
|
||||||
|
- ./script/format
|
||||||
|
after_success:
|
||||||
|
- $HOME/gopath/bin/goveralls -service=travis-ci -coverprofile=cover.out
|
86
vendor/github.com/gophercloud/gophercloud/.zuul.yaml
generated
vendored
Normal file
86
vendor/github.com/gophercloud/gophercloud/.zuul.yaml
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
- job:
|
||||||
|
name: gophercloud-unittest
|
||||||
|
parent: golang-test
|
||||||
|
description: |
|
||||||
|
Run gophercloud unit test
|
||||||
|
run: .zuul/playbooks/gophercloud-unittest/run.yaml
|
||||||
|
nodeset: ubuntu-xenial-ut
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: gophercloud-acceptance-test
|
||||||
|
parent: golang-test
|
||||||
|
description: |
|
||||||
|
Run gophercloud acceptance test on master branch
|
||||||
|
run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: gophercloud-acceptance-test-queens
|
||||||
|
parent: gophercloud-acceptance-test
|
||||||
|
description: |
|
||||||
|
Run gophercloud acceptance test on queens branch
|
||||||
|
vars:
|
||||||
|
global_env:
|
||||||
|
OS_BRANCH: stable/queens
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: gophercloud-acceptance-test-pike
|
||||||
|
parent: gophercloud-acceptance-test
|
||||||
|
description: |
|
||||||
|
Run gophercloud acceptance test on pike branch
|
||||||
|
vars:
|
||||||
|
global_env:
|
||||||
|
OS_BRANCH: stable/pike
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: gophercloud-acceptance-test-ocata
|
||||||
|
parent: gophercloud-acceptance-test
|
||||||
|
description: |
|
||||||
|
Run gophercloud acceptance test on ocata branch
|
||||||
|
vars:
|
||||||
|
global_env:
|
||||||
|
OS_BRANCH: stable/ocata
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: gophercloud-acceptance-test-newton
|
||||||
|
parent: gophercloud-acceptance-test
|
||||||
|
description: |
|
||||||
|
Run gophercloud acceptance test on newton branch
|
||||||
|
vars:
|
||||||
|
global_env:
|
||||||
|
OS_BRANCH: stable/newton
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: gophercloud-acceptance-test-mitaka
|
||||||
|
parent: gophercloud-acceptance-test
|
||||||
|
description: |
|
||||||
|
Run gophercloud acceptance test on mitaka branch
|
||||||
|
vars:
|
||||||
|
global_env:
|
||||||
|
OS_BRANCH: stable/mitaka
|
||||||
|
nodeset: ubuntu-trusty
|
||||||
|
|
||||||
|
- project:
|
||||||
|
name: gophercloud/gophercloud
|
||||||
|
check:
|
||||||
|
jobs:
|
||||||
|
- gophercloud-unittest
|
||||||
|
- gophercloud-acceptance-test
|
||||||
|
recheck-mitaka:
|
||||||
|
jobs:
|
||||||
|
- gophercloud-acceptance-test-mitaka
|
||||||
|
recheck-newton:
|
||||||
|
jobs:
|
||||||
|
- gophercloud-acceptance-test-newton
|
||||||
|
recheck-ocata:
|
||||||
|
jobs:
|
||||||
|
- gophercloud-acceptance-test-ocata
|
||||||
|
recheck-pike:
|
||||||
|
jobs:
|
||||||
|
- gophercloud-acceptance-test-pike
|
||||||
|
recheck-queens:
|
||||||
|
jobs:
|
||||||
|
- gophercloud-acceptance-test-queens
|
||||||
|
periodic:
|
||||||
|
jobs:
|
||||||
|
- gophercloud-unittest
|
||||||
|
- gophercloud-acceptance-test
|
0
vendor/github.com/gophercloud/gophercloud/CHANGELOG.md
generated
vendored
Normal file
0
vendor/github.com/gophercloud/gophercloud/CHANGELOG.md
generated
vendored
Normal file
191
vendor/github.com/gophercloud/gophercloud/LICENSE
generated
vendored
Normal file
191
vendor/github.com/gophercloud/gophercloud/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
Copyright 2012-2013 Rackspace, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
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
|
159
vendor/github.com/gophercloud/gophercloud/README.md
generated
vendored
Normal file
159
vendor/github.com/gophercloud/gophercloud/README.md
generated
vendored
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
# Gophercloud: an OpenStack SDK for Go
|
||||||
|
[](https://travis-ci.org/gophercloud/gophercloud)
|
||||||
|
[](https://coveralls.io/github/gophercloud/gophercloud?branch=master)
|
||||||
|
|
||||||
|
Gophercloud is an OpenStack Go SDK.
|
||||||
|
|
||||||
|
## Useful links
|
||||||
|
|
||||||
|
* [Reference documentation](http://godoc.org/github.com/gophercloud/gophercloud)
|
||||||
|
* [Effective Go](https://golang.org/doc/effective_go.html)
|
||||||
|
|
||||||
|
## How to install
|
||||||
|
|
||||||
|
Before installing, you need to ensure that your [GOPATH environment variable](https://golang.org/doc/code.html#GOPATH)
|
||||||
|
is pointing to an appropriate directory where you want to install Gophercloud:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir $HOME/go
|
||||||
|
export GOPATH=$HOME/go
|
||||||
|
```
|
||||||
|
|
||||||
|
To protect yourself against changes in your dependencies, we highly recommend choosing a
|
||||||
|
[dependency management solution](https://github.com/golang/go/wiki/PackageManagementTools) for
|
||||||
|
your projects, such as [godep](https://github.com/tools/godep). Once this is set up, you can install
|
||||||
|
Gophercloud as a dependency like so:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get github.com/gophercloud/gophercloud
|
||||||
|
|
||||||
|
# Edit your code to import relevant packages from "github.com/gophercloud/gophercloud"
|
||||||
|
|
||||||
|
godep save ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
This will install all the source files you need into a `Godeps/_workspace` directory, which is
|
||||||
|
referenceable from your own source files when you use the `godep go` command.
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
### Credentials
|
||||||
|
|
||||||
|
Because you'll be hitting an API, you will need to retrieve your OpenStack
|
||||||
|
credentials and either store them as environment variables or in your local Go
|
||||||
|
files. The first method is recommended because it decouples credential
|
||||||
|
information from source code, allowing you to push the latter to your version
|
||||||
|
control system without any security risk.
|
||||||
|
|
||||||
|
You will need to retrieve the following:
|
||||||
|
|
||||||
|
* username
|
||||||
|
* password
|
||||||
|
* a valid Keystone identity URL
|
||||||
|
|
||||||
|
For users that have the OpenStack dashboard installed, there's a shortcut. If
|
||||||
|
you visit the `project/access_and_security` path in Horizon and click on the
|
||||||
|
"Download OpenStack RC File" button at the top right hand corner, you will
|
||||||
|
download a bash file that exports all of your access details to environment
|
||||||
|
variables. To execute the file, run `source admin-openrc.sh` and you will be
|
||||||
|
prompted for your password.
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
Once you have access to your credentials, you can begin plugging them into
|
||||||
|
Gophercloud. The next step is authentication, and this is handled by a base
|
||||||
|
"Provider" struct. To get one, you can either pass in your credentials
|
||||||
|
explicitly, or tell Gophercloud to use environment variables:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Option 1: Pass in the values yourself
|
||||||
|
opts := gophercloud.AuthOptions{
|
||||||
|
IdentityEndpoint: "https://openstack.example.com:5000/v2.0",
|
||||||
|
Username: "{username}",
|
||||||
|
Password: "{password}",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option 2: Use a utility function to retrieve all your environment variables
|
||||||
|
opts, err := openstack.AuthOptionsFromEnv()
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you have the `opts` variable, you can pass it in and get back a
|
||||||
|
`ProviderClient` struct:
|
||||||
|
|
||||||
|
```go
|
||||||
|
provider, err := openstack.AuthenticatedClient(opts)
|
||||||
|
```
|
||||||
|
|
||||||
|
The `ProviderClient` is the top-level client that all of your OpenStack services
|
||||||
|
derive from. The provider contains all of the authentication details that allow
|
||||||
|
your Go code to access the API - such as the base URL and token ID.
|
||||||
|
|
||||||
|
### Provision a server
|
||||||
|
|
||||||
|
Once we have a base Provider, we inject it as a dependency into each OpenStack
|
||||||
|
service. In order to work with the Compute API, we need a Compute service
|
||||||
|
client; which can be created like so:
|
||||||
|
|
||||||
|
```go
|
||||||
|
client, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{
|
||||||
|
Region: os.Getenv("OS_REGION_NAME"),
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
We then use this `client` for any Compute API operation we want. In our case,
|
||||||
|
we want to provision a new server - so we invoke the `Create` method and pass
|
||||||
|
in the flavor ID (hardware specification) and image ID (operating system) we're
|
||||||
|
interested in:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||||
|
|
||||||
|
server, err := servers.Create(client, servers.CreateOpts{
|
||||||
|
Name: "My new server!",
|
||||||
|
FlavorRef: "flavor_id",
|
||||||
|
ImageRef: "image_id",
|
||||||
|
}).Extract()
|
||||||
|
```
|
||||||
|
|
||||||
|
The above code sample creates a new server with the parameters, and embodies the
|
||||||
|
new resource in the `server` variable (a
|
||||||
|
[`servers.Server`](http://godoc.org/github.com/gophercloud/gophercloud) struct).
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
Have a look at the [FAQ](./docs/FAQ.md) for some tips on customizing the way Gophercloud works.
|
||||||
|
|
||||||
|
## Backwards-Compatibility Guarantees
|
||||||
|
|
||||||
|
None. Vendor it and write tests covering the parts you use.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
See the [contributing guide](./.github/CONTRIBUTING.md).
|
||||||
|
|
||||||
|
## Help and feedback
|
||||||
|
|
||||||
|
If you're struggling with something or have spotted a potential bug, feel free
|
||||||
|
to submit an issue to our [bug tracker](https://github.com/gophercloud/gophercloud/issues).
|
||||||
|
|
||||||
|
## Thank You
|
||||||
|
|
||||||
|
We'd like to extend special thanks and appreciation to the following:
|
||||||
|
|
||||||
|
### OpenLab
|
||||||
|
|
||||||
|
<a href="http://openlabtesting.org/"><img src="./docs/assets/openlab.png" width="600px"></a>
|
||||||
|
|
||||||
|
OpenLab is providing a full CI environment to test each PR and merge for a variety of OpenStack releases.
|
||||||
|
|
||||||
|
### VEXXHOST
|
||||||
|
|
||||||
|
<a href="https://vexxhost.com/"><img src="./docs/assets/vexxhost.png" width="600px"></a>
|
||||||
|
|
||||||
|
VEXXHOST is providing their services to assist with the development and testing of Gophercloud.
|
419
vendor/github.com/gophercloud/gophercloud/auth_options.go
generated
vendored
Normal file
419
vendor/github.com/gophercloud/gophercloud/auth_options.go
generated
vendored
Normal file
|
@ -0,0 +1,419 @@
|
||||||
|
package gophercloud
|
||||||
|
|
||||||
|
/*
|
||||||
|
AuthOptions stores information needed to authenticate to an OpenStack Cloud.
|
||||||
|
You can populate one manually, or use a provider's AuthOptionsFromEnv() function
|
||||||
|
to read relevant information from the standard environment variables. Pass one
|
||||||
|
to a provider's AuthenticatedClient function to authenticate and obtain a
|
||||||
|
ProviderClient representing an active session on that provider.
|
||||||
|
|
||||||
|
Its fields are the union of those recognized by each identity implementation and
|
||||||
|
provider.
|
||||||
|
|
||||||
|
An example of manually providing authentication information:
|
||||||
|
|
||||||
|
opts := gophercloud.AuthOptions{
|
||||||
|
IdentityEndpoint: "https://openstack.example.com:5000/v2.0",
|
||||||
|
Username: "{username}",
|
||||||
|
Password: "{password}",
|
||||||
|
TenantID: "{tenant_id}",
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := openstack.AuthenticatedClient(opts)
|
||||||
|
|
||||||
|
An example of using AuthOptionsFromEnv(), where the environment variables can
|
||||||
|
be read from a file, such as a standard openrc file:
|
||||||
|
|
||||||
|
opts, err := openstack.AuthOptionsFromEnv()
|
||||||
|
provider, err := openstack.AuthenticatedClient(opts)
|
||||||
|
*/
|
||||||
|
type AuthOptions struct {
|
||||||
|
// IdentityEndpoint specifies the HTTP endpoint that is required to work with
|
||||||
|
// the Identity API of the appropriate version. While it's ultimately needed by
|
||||||
|
// all of the identity services, it will often be populated by a provider-level
|
||||||
|
// function.
|
||||||
|
//
|
||||||
|
// The IdentityEndpoint is typically referred to as the "auth_url" or
|
||||||
|
// "OS_AUTH_URL" in the information provided by the cloud operator.
|
||||||
|
IdentityEndpoint string `json:"-"`
|
||||||
|
|
||||||
|
// Username is required if using Identity V2 API. Consult with your provider's
|
||||||
|
// control panel to discover your account's username. In Identity V3, either
|
||||||
|
// UserID or a combination of Username and DomainID or DomainName are needed.
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
UserID string `json:"-"`
|
||||||
|
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
|
||||||
|
// At most one of DomainID and DomainName must be provided if using Username
|
||||||
|
// with Identity V3. Otherwise, either are optional.
|
||||||
|
DomainID string `json:"-"`
|
||||||
|
DomainName string `json:"name,omitempty"`
|
||||||
|
|
||||||
|
// The TenantID and TenantName fields are optional for the Identity V2 API.
|
||||||
|
// The same fields are known as project_id and project_name in the Identity
|
||||||
|
// V3 API, but are collected as TenantID and TenantName here in both cases.
|
||||||
|
// Some providers allow you to specify a TenantName instead of the TenantId.
|
||||||
|
// Some require both. Your provider's authentication policies will determine
|
||||||
|
// how these fields influence authentication.
|
||||||
|
// If DomainID or DomainName are provided, they will also apply to TenantName.
|
||||||
|
// It is not currently possible to authenticate with Username and a Domain
|
||||||
|
// and scope to a Project in a different Domain by using TenantName. To
|
||||||
|
// accomplish that, the ProjectID will need to be provided as the TenantID
|
||||||
|
// option.
|
||||||
|
TenantID string `json:"tenantId,omitempty"`
|
||||||
|
TenantName string `json:"tenantName,omitempty"`
|
||||||
|
|
||||||
|
// AllowReauth should be set to true if you grant permission for Gophercloud to
|
||||||
|
// cache your credentials in memory, and to allow Gophercloud to attempt to
|
||||||
|
// re-authenticate automatically if/when your token expires. If you set it to
|
||||||
|
// false, it will not cache these settings, but re-authentication will not be
|
||||||
|
// possible. This setting defaults to false.
|
||||||
|
//
|
||||||
|
// NOTE: The reauth function will try to re-authenticate endlessly if left
|
||||||
|
// unchecked. The way to limit the number of attempts is to provide a custom
|
||||||
|
// HTTP client to the provider client and provide a transport that implements
|
||||||
|
// the RoundTripper interface and stores the number of failed retries. For an
|
||||||
|
// example of this, see here:
|
||||||
|
// https://github.com/rackspace/rack/blob/1.0.0/auth/clients.go#L311
|
||||||
|
AllowReauth bool `json:"-"`
|
||||||
|
|
||||||
|
// TokenID allows users to authenticate (possibly as another user) with an
|
||||||
|
// authentication token ID.
|
||||||
|
TokenID string `json:"-"`
|
||||||
|
|
||||||
|
// Scope determines the scoping of the authentication request.
|
||||||
|
Scope *AuthScope `json:"-"`
|
||||||
|
|
||||||
|
// Authentication through Application Credentials requires supplying name, project and secret
|
||||||
|
// For project we can use TenantID
|
||||||
|
ApplicationCredentialID string `json:"-"`
|
||||||
|
ApplicationCredentialName string `json:"-"`
|
||||||
|
ApplicationCredentialSecret string `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthScope allows a created token to be limited to a specific domain or project.
|
||||||
|
type AuthScope struct {
|
||||||
|
ProjectID string
|
||||||
|
ProjectName string
|
||||||
|
DomainID string
|
||||||
|
DomainName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder
|
||||||
|
// interface in the v2 tokens package
|
||||||
|
func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) {
|
||||||
|
// Populate the request map.
|
||||||
|
authMap := make(map[string]interface{})
|
||||||
|
|
||||||
|
if opts.Username != "" {
|
||||||
|
if opts.Password != "" {
|
||||||
|
authMap["passwordCredentials"] = map[string]interface{}{
|
||||||
|
"username": opts.Username,
|
||||||
|
"password": opts.Password,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, ErrMissingInput{Argument: "Password"}
|
||||||
|
}
|
||||||
|
} else if opts.TokenID != "" {
|
||||||
|
authMap["token"] = map[string]interface{}{
|
||||||
|
"id": opts.TokenID,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, ErrMissingInput{Argument: "Username"}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.TenantID != "" {
|
||||||
|
authMap["tenantId"] = opts.TenantID
|
||||||
|
}
|
||||||
|
if opts.TenantName != "" {
|
||||||
|
authMap["tenantName"] = opts.TenantName
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]interface{}{"auth": authMap}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) {
|
||||||
|
type domainReq struct {
|
||||||
|
ID *string `json:"id,omitempty"`
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type projectReq struct {
|
||||||
|
Domain *domainReq `json:"domain,omitempty"`
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
ID *string `json:"id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type userReq struct {
|
||||||
|
ID *string `json:"id,omitempty"`
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
Domain *domainReq `json:"domain,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type passwordReq struct {
|
||||||
|
User userReq `json:"user"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type tokenReq struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type applicationCredentialReq struct {
|
||||||
|
ID *string `json:"id,omitempty"`
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
User *userReq `json:"user,omitempty"`
|
||||||
|
Secret *string `json:"secret,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type identityReq struct {
|
||||||
|
Methods []string `json:"methods"`
|
||||||
|
Password *passwordReq `json:"password,omitempty"`
|
||||||
|
Token *tokenReq `json:"token,omitempty"`
|
||||||
|
ApplicationCredential *applicationCredentialReq `json:"application_credential,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type authReq struct {
|
||||||
|
Identity identityReq `json:"identity"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type request struct {
|
||||||
|
Auth authReq `json:"auth"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate the request structure based on the provided arguments. Create and return an error
|
||||||
|
// if insufficient or incompatible information is present.
|
||||||
|
var req request
|
||||||
|
var userRequest userReq
|
||||||
|
|
||||||
|
if opts.Password == "" {
|
||||||
|
if opts.TokenID != "" {
|
||||||
|
// Because we aren't using password authentication, it's an error to also provide any of the user-based authentication
|
||||||
|
// parameters.
|
||||||
|
if opts.Username != "" {
|
||||||
|
return nil, ErrUsernameWithToken{}
|
||||||
|
}
|
||||||
|
if opts.UserID != "" {
|
||||||
|
return nil, ErrUserIDWithToken{}
|
||||||
|
}
|
||||||
|
if opts.DomainID != "" {
|
||||||
|
return nil, ErrDomainIDWithToken{}
|
||||||
|
}
|
||||||
|
if opts.DomainName != "" {
|
||||||
|
return nil, ErrDomainNameWithToken{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the request for Token authentication.
|
||||||
|
req.Auth.Identity.Methods = []string{"token"}
|
||||||
|
req.Auth.Identity.Token = &tokenReq{
|
||||||
|
ID: opts.TokenID,
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if opts.ApplicationCredentialID != "" {
|
||||||
|
// Configure the request for ApplicationCredentialID authentication.
|
||||||
|
// https://github.com/openstack/keystoneauth/blob/stable/rocky/keystoneauth1/identity/v3/application_credential.py#L48-L67
|
||||||
|
// There are three kinds of possible application_credential requests
|
||||||
|
// 1. application_credential id + secret
|
||||||
|
// 2. application_credential name + secret + user_id
|
||||||
|
// 3. application_credential name + secret + username + domain_id / domain_name
|
||||||
|
if opts.ApplicationCredentialSecret == "" {
|
||||||
|
return nil, ErrAppCredMissingSecret{}
|
||||||
|
}
|
||||||
|
req.Auth.Identity.Methods = []string{"application_credential"}
|
||||||
|
req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{
|
||||||
|
ID: &opts.ApplicationCredentialID,
|
||||||
|
Secret: &opts.ApplicationCredentialSecret,
|
||||||
|
}
|
||||||
|
} else if opts.ApplicationCredentialName != "" {
|
||||||
|
if opts.ApplicationCredentialSecret == "" {
|
||||||
|
return nil, ErrAppCredMissingSecret{}
|
||||||
|
}
|
||||||
|
// make sure that only one of DomainName or DomainID were provided
|
||||||
|
if opts.DomainID == "" && opts.DomainName == "" {
|
||||||
|
return nil, ErrDomainIDOrDomainName{}
|
||||||
|
}
|
||||||
|
req.Auth.Identity.Methods = []string{"application_credential"}
|
||||||
|
if opts.DomainID != "" {
|
||||||
|
userRequest = userReq{
|
||||||
|
Name: &opts.Username,
|
||||||
|
Domain: &domainReq{ID: &opts.DomainID},
|
||||||
|
}
|
||||||
|
} else if opts.DomainName != "" {
|
||||||
|
userRequest = userReq{
|
||||||
|
Name: &opts.Username,
|
||||||
|
Domain: &domainReq{Name: &opts.DomainName},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{
|
||||||
|
Name: &opts.ApplicationCredentialName,
|
||||||
|
User: &userRequest,
|
||||||
|
Secret: &opts.ApplicationCredentialSecret,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If no password or token ID or ApplicationCredential are available, authentication can't continue.
|
||||||
|
return nil, ErrMissingPassword{}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Password authentication.
|
||||||
|
req.Auth.Identity.Methods = []string{"password"}
|
||||||
|
|
||||||
|
// At least one of Username and UserID must be specified.
|
||||||
|
if opts.Username == "" && opts.UserID == "" {
|
||||||
|
return nil, ErrUsernameOrUserID{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Username != "" {
|
||||||
|
// If Username is provided, UserID may not be provided.
|
||||||
|
if opts.UserID != "" {
|
||||||
|
return nil, ErrUsernameOrUserID{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Either DomainID or DomainName must also be specified.
|
||||||
|
if opts.DomainID == "" && opts.DomainName == "" {
|
||||||
|
return nil, ErrDomainIDOrDomainName{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.DomainID != "" {
|
||||||
|
if opts.DomainName != "" {
|
||||||
|
return nil, ErrDomainIDOrDomainName{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the request for Username and Password authentication with a DomainID.
|
||||||
|
req.Auth.Identity.Password = &passwordReq{
|
||||||
|
User: userReq{
|
||||||
|
Name: &opts.Username,
|
||||||
|
Password: opts.Password,
|
||||||
|
Domain: &domainReq{ID: &opts.DomainID},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.DomainName != "" {
|
||||||
|
// Configure the request for Username and Password authentication with a DomainName.
|
||||||
|
req.Auth.Identity.Password = &passwordReq{
|
||||||
|
User: userReq{
|
||||||
|
Name: &opts.Username,
|
||||||
|
Password: opts.Password,
|
||||||
|
Domain: &domainReq{Name: &opts.DomainName},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.UserID != "" {
|
||||||
|
// If UserID is specified, neither DomainID nor DomainName may be.
|
||||||
|
if opts.DomainID != "" {
|
||||||
|
return nil, ErrDomainIDWithUserID{}
|
||||||
|
}
|
||||||
|
if opts.DomainName != "" {
|
||||||
|
return nil, ErrDomainNameWithUserID{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the request for UserID and Password authentication.
|
||||||
|
req.Auth.Identity.Password = &passwordReq{
|
||||||
|
User: userReq{ID: &opts.UserID, Password: opts.Password},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := BuildRequestBody(req, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(scope) != 0 {
|
||||||
|
b["auth"].(map[string]interface{})["scope"] = scope
|
||||||
|
}
|
||||||
|
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
|
||||||
|
// For backwards compatibility.
|
||||||
|
// If AuthOptions.Scope was not set, try to determine it.
|
||||||
|
// This works well for common scenarios.
|
||||||
|
if opts.Scope == nil {
|
||||||
|
opts.Scope = new(AuthScope)
|
||||||
|
if opts.TenantID != "" {
|
||||||
|
opts.Scope.ProjectID = opts.TenantID
|
||||||
|
} else {
|
||||||
|
if opts.TenantName != "" {
|
||||||
|
opts.Scope.ProjectName = opts.TenantName
|
||||||
|
opts.Scope.DomainID = opts.DomainID
|
||||||
|
opts.Scope.DomainName = opts.DomainName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Scope.ProjectName != "" {
|
||||||
|
// ProjectName provided: either DomainID or DomainName must also be supplied.
|
||||||
|
// ProjectID may not be supplied.
|
||||||
|
if opts.Scope.DomainID == "" && opts.Scope.DomainName == "" {
|
||||||
|
return nil, ErrScopeDomainIDOrDomainName{}
|
||||||
|
}
|
||||||
|
if opts.Scope.ProjectID != "" {
|
||||||
|
return nil, ErrScopeProjectIDOrProjectName{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Scope.DomainID != "" {
|
||||||
|
// ProjectName + DomainID
|
||||||
|
return map[string]interface{}{
|
||||||
|
"project": map[string]interface{}{
|
||||||
|
"name": &opts.Scope.ProjectName,
|
||||||
|
"domain": map[string]interface{}{"id": &opts.Scope.DomainID},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Scope.DomainName != "" {
|
||||||
|
// ProjectName + DomainName
|
||||||
|
return map[string]interface{}{
|
||||||
|
"project": map[string]interface{}{
|
||||||
|
"name": &opts.Scope.ProjectName,
|
||||||
|
"domain": map[string]interface{}{"name": &opts.Scope.DomainName},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
} else if opts.Scope.ProjectID != "" {
|
||||||
|
// ProjectID provided. ProjectName, DomainID, and DomainName may not be provided.
|
||||||
|
if opts.Scope.DomainID != "" {
|
||||||
|
return nil, ErrScopeProjectIDAlone{}
|
||||||
|
}
|
||||||
|
if opts.Scope.DomainName != "" {
|
||||||
|
return nil, ErrScopeProjectIDAlone{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectID
|
||||||
|
return map[string]interface{}{
|
||||||
|
"project": map[string]interface{}{
|
||||||
|
"id": &opts.Scope.ProjectID,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
} else if opts.Scope.DomainID != "" {
|
||||||
|
// DomainID provided. ProjectID, ProjectName, and DomainName may not be provided.
|
||||||
|
if opts.Scope.DomainName != "" {
|
||||||
|
return nil, ErrScopeDomainIDOrDomainName{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainID
|
||||||
|
return map[string]interface{}{
|
||||||
|
"domain": map[string]interface{}{
|
||||||
|
"id": &opts.Scope.DomainID,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
} else if opts.Scope.DomainName != "" {
|
||||||
|
// DomainName
|
||||||
|
return map[string]interface{}{
|
||||||
|
"domain": map[string]interface{}{
|
||||||
|
"name": &opts.Scope.DomainName,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts AuthOptions) CanReauth() bool {
|
||||||
|
return opts.AllowReauth
|
||||||
|
}
|
93
vendor/github.com/gophercloud/gophercloud/doc.go
generated
vendored
Normal file
93
vendor/github.com/gophercloud/gophercloud/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
Package gophercloud provides a multi-vendor interface to OpenStack-compatible
|
||||||
|
clouds. The library has a three-level hierarchy: providers, services, and
|
||||||
|
resources.
|
||||||
|
|
||||||
|
Authenticating with Providers
|
||||||
|
|
||||||
|
Provider structs represent the cloud providers that offer and manage a
|
||||||
|
collection of services. You will generally want to create one Provider
|
||||||
|
client per OpenStack cloud.
|
||||||
|
|
||||||
|
Use your OpenStack credentials to create a Provider client. The
|
||||||
|
IdentityEndpoint is typically refered to as "auth_url" or "OS_AUTH_URL" in
|
||||||
|
information provided by the cloud operator. Additionally, the cloud may refer to
|
||||||
|
TenantID or TenantName as project_id and project_name. Credentials are
|
||||||
|
specified like so:
|
||||||
|
|
||||||
|
opts := gophercloud.AuthOptions{
|
||||||
|
IdentityEndpoint: "https://openstack.example.com:5000/v2.0",
|
||||||
|
Username: "{username}",
|
||||||
|
Password: "{password}",
|
||||||
|
TenantID: "{tenant_id}",
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := openstack.AuthenticatedClient(opts)
|
||||||
|
|
||||||
|
You may also use the openstack.AuthOptionsFromEnv() helper function. This
|
||||||
|
function reads in standard environment variables frequently found in an
|
||||||
|
OpenStack `openrc` file. Again note that Gophercloud currently uses "tenant"
|
||||||
|
instead of "project".
|
||||||
|
|
||||||
|
opts, err := openstack.AuthOptionsFromEnv()
|
||||||
|
provider, err := openstack.AuthenticatedClient(opts)
|
||||||
|
|
||||||
|
Service Clients
|
||||||
|
|
||||||
|
Service structs are specific to a provider and handle all of the logic and
|
||||||
|
operations for a particular OpenStack service. Examples of services include:
|
||||||
|
Compute, Object Storage, Block Storage. In order to define one, you need to
|
||||||
|
pass in the parent provider, like so:
|
||||||
|
|
||||||
|
opts := gophercloud.EndpointOpts{Region: "RegionOne"}
|
||||||
|
|
||||||
|
client := openstack.NewComputeV2(provider, opts)
|
||||||
|
|
||||||
|
Resources
|
||||||
|
|
||||||
|
Resource structs are the domain models that services make use of in order
|
||||||
|
to work with and represent the state of API resources:
|
||||||
|
|
||||||
|
server, err := servers.Get(client, "{serverId}").Extract()
|
||||||
|
|
||||||
|
Intermediate Result structs are returned for API operations, which allow
|
||||||
|
generic access to the HTTP headers, response body, and any errors associated
|
||||||
|
with the network transaction. To turn a result into a usable resource struct,
|
||||||
|
you must call the Extract method which is chained to the response, or an
|
||||||
|
Extract function from an applicable extension:
|
||||||
|
|
||||||
|
result := servers.Get(client, "{serverId}")
|
||||||
|
|
||||||
|
// Attempt to extract the disk configuration from the OS-DCF disk config
|
||||||
|
// extension:
|
||||||
|
config, err := diskconfig.ExtractGet(result)
|
||||||
|
|
||||||
|
All requests that enumerate a collection return a Pager struct that is used to
|
||||||
|
iterate through the results one page at a time. Use the EachPage method on that
|
||||||
|
Pager to handle each successive Page in a closure, then use the appropriate
|
||||||
|
extraction method from that request's package to interpret that Page as a slice
|
||||||
|
of results:
|
||||||
|
|
||||||
|
err := servers.List(client, nil).EachPage(func (page pagination.Page) (bool, error) {
|
||||||
|
s, err := servers.ExtractServers(page)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the []servers.Server slice.
|
||||||
|
|
||||||
|
// Return "false" or an error to prematurely stop fetching new pages.
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
If you want to obtain the entire collection of pages without doing any
|
||||||
|
intermediary processing on each page, you can use the AllPages method:
|
||||||
|
|
||||||
|
allPages, err := servers.List(client, nil).AllPages()
|
||||||
|
allServers, err := servers.ExtractServers(allPages)
|
||||||
|
|
||||||
|
This top-level package contains utility functions and data types that are used
|
||||||
|
throughout the provider and service packages. Of particular note for end users
|
||||||
|
are the AuthOptions and EndpointOpts structs.
|
||||||
|
*/
|
||||||
|
package gophercloud
|
76
vendor/github.com/gophercloud/gophercloud/endpoint_search.go
generated
vendored
Normal file
76
vendor/github.com/gophercloud/gophercloud/endpoint_search.go
generated
vendored
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
package gophercloud
|
||||||
|
|
||||||
|
// Availability indicates to whom a specific service endpoint is accessible:
|
||||||
|
// the internet at large, internal networks only, or only to administrators.
|
||||||
|
// Different identity services use different terminology for these. Identity v2
|
||||||
|
// lists them as different kinds of URLs within the service catalog ("adminURL",
|
||||||
|
// "internalURL", and "publicURL"), while v3 lists them as "Interfaces" in an
|
||||||
|
// endpoint's response.
|
||||||
|
type Availability string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AvailabilityAdmin indicates that an endpoint is only available to
|
||||||
|
// administrators.
|
||||||
|
AvailabilityAdmin Availability = "admin"
|
||||||
|
|
||||||
|
// AvailabilityPublic indicates that an endpoint is available to everyone on
|
||||||
|
// the internet.
|
||||||
|
AvailabilityPublic Availability = "public"
|
||||||
|
|
||||||
|
// AvailabilityInternal indicates that an endpoint is only available within
|
||||||
|
// the cluster's internal network.
|
||||||
|
AvailabilityInternal Availability = "internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EndpointOpts specifies search criteria used by queries against an
|
||||||
|
// OpenStack service catalog. The options must contain enough information to
|
||||||
|
// unambiguously identify one, and only one, endpoint within the catalog.
|
||||||
|
//
|
||||||
|
// Usually, these are passed to service client factory functions in a provider
|
||||||
|
// package, like "openstack.NewComputeV2()".
|
||||||
|
type EndpointOpts struct {
|
||||||
|
// Type [required] is the service type for the client (e.g., "compute",
|
||||||
|
// "object-store"). Generally, this will be supplied by the service client
|
||||||
|
// function, but a user-given value will be honored if provided.
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// Name [optional] is the service name for the client (e.g., "nova") as it
|
||||||
|
// appears in the service catalog. Services can have the same Type but a
|
||||||
|
// different Name, which is why both Type and Name are sometimes needed.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Region [required] is the geographic region in which the endpoint resides,
|
||||||
|
// generally specifying which datacenter should house your resources.
|
||||||
|
// Required only for services that span multiple regions.
|
||||||
|
Region string
|
||||||
|
|
||||||
|
// Availability [optional] is the visibility of the endpoint to be returned.
|
||||||
|
// Valid types include the constants AvailabilityPublic, AvailabilityInternal,
|
||||||
|
// or AvailabilityAdmin from this package.
|
||||||
|
//
|
||||||
|
// Availability is not required, and defaults to AvailabilityPublic. Not all
|
||||||
|
// providers or services offer all Availability options.
|
||||||
|
Availability Availability
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
EndpointLocator is an internal function to be used by provider implementations.
|
||||||
|
|
||||||
|
It provides an implementation that locates a single endpoint from a service
|
||||||
|
catalog for a specific ProviderClient based on user-provided EndpointOpts. The
|
||||||
|
provider then uses it to discover related ServiceClients.
|
||||||
|
*/
|
||||||
|
type EndpointLocator func(EndpointOpts) (string, error)
|
||||||
|
|
||||||
|
// ApplyDefaults is an internal method to be used by provider implementations.
|
||||||
|
//
|
||||||
|
// It sets EndpointOpts fields if not already set, including a default type.
|
||||||
|
// Currently, EndpointOpts.Availability defaults to the public endpoint.
|
||||||
|
func (eo *EndpointOpts) ApplyDefaults(t string) {
|
||||||
|
if eo.Type == "" {
|
||||||
|
eo.Type = t
|
||||||
|
}
|
||||||
|
if eo.Availability == "" {
|
||||||
|
eo.Availability = AvailabilityPublic
|
||||||
|
}
|
||||||
|
}
|
460
vendor/github.com/gophercloud/gophercloud/errors.go
generated
vendored
Normal file
460
vendor/github.com/gophercloud/gophercloud/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,460 @@
|
||||||
|
package gophercloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BaseError is an error type that all other error types embed.
|
||||||
|
type BaseError struct {
|
||||||
|
DefaultErrString string
|
||||||
|
Info string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e BaseError) Error() string {
|
||||||
|
e.DefaultErrString = "An error occurred while executing a Gophercloud request."
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e BaseError) choseErrString() string {
|
||||||
|
if e.Info != "" {
|
||||||
|
return e.Info
|
||||||
|
}
|
||||||
|
return e.DefaultErrString
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMissingInput is the error when input is required in a particular
|
||||||
|
// situation but not provided by the user
|
||||||
|
type ErrMissingInput struct {
|
||||||
|
BaseError
|
||||||
|
Argument string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrMissingInput) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf("Missing input for argument [%s]", e.Argument)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrInvalidInput is an error type used for most non-HTTP Gophercloud errors.
|
||||||
|
type ErrInvalidInput struct {
|
||||||
|
ErrMissingInput
|
||||||
|
Value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrInvalidInput) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf("Invalid input provided for argument [%s]: [%+v]", e.Argument, e.Value)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMissingEnvironmentVariable is the error when environment variable is required
|
||||||
|
// in a particular situation but not provided by the user
|
||||||
|
type ErrMissingEnvironmentVariable struct {
|
||||||
|
BaseError
|
||||||
|
EnvironmentVariable string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrMissingEnvironmentVariable) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf("Missing environment variable [%s]", e.EnvironmentVariable)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMissingAnyoneOfEnvironmentVariables is the error when anyone of the environment variables
|
||||||
|
// is required in a particular situation but not provided by the user
|
||||||
|
type ErrMissingAnyoneOfEnvironmentVariables struct {
|
||||||
|
BaseError
|
||||||
|
EnvironmentVariables []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrMissingAnyoneOfEnvironmentVariables) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf(
|
||||||
|
"Missing one of the following environment variables [%s]",
|
||||||
|
strings.Join(e.EnvironmentVariables, ", "),
|
||||||
|
)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrUnexpectedResponseCode is returned by the Request method when a response code other than
|
||||||
|
// those listed in OkCodes is encountered.
|
||||||
|
type ErrUnexpectedResponseCode struct {
|
||||||
|
BaseError
|
||||||
|
URL string
|
||||||
|
Method string
|
||||||
|
Expected []int
|
||||||
|
Actual int
|
||||||
|
Body []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrUnexpectedResponseCode) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf(
|
||||||
|
"Expected HTTP response code %v when accessing [%s %s], but got %d instead\n%s",
|
||||||
|
e.Expected, e.Method, e.URL, e.Actual, e.Body,
|
||||||
|
)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDefault400 is the default error type returned on a 400 HTTP response code.
|
||||||
|
type ErrDefault400 struct {
|
||||||
|
ErrUnexpectedResponseCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDefault401 is the default error type returned on a 401 HTTP response code.
|
||||||
|
type ErrDefault401 struct {
|
||||||
|
ErrUnexpectedResponseCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDefault403 is the default error type returned on a 403 HTTP response code.
|
||||||
|
type ErrDefault403 struct {
|
||||||
|
ErrUnexpectedResponseCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDefault404 is the default error type returned on a 404 HTTP response code.
|
||||||
|
type ErrDefault404 struct {
|
||||||
|
ErrUnexpectedResponseCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDefault405 is the default error type returned on a 405 HTTP response code.
|
||||||
|
type ErrDefault405 struct {
|
||||||
|
ErrUnexpectedResponseCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDefault408 is the default error type returned on a 408 HTTP response code.
|
||||||
|
type ErrDefault408 struct {
|
||||||
|
ErrUnexpectedResponseCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDefault429 is the default error type returned on a 429 HTTP response code.
|
||||||
|
type ErrDefault429 struct {
|
||||||
|
ErrUnexpectedResponseCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDefault500 is the default error type returned on a 500 HTTP response code.
|
||||||
|
type ErrDefault500 struct {
|
||||||
|
ErrUnexpectedResponseCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDefault503 is the default error type returned on a 503 HTTP response code.
|
||||||
|
type ErrDefault503 struct {
|
||||||
|
ErrUnexpectedResponseCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrDefault400) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf(
|
||||||
|
"Bad request with: [%s %s], error message: %s",
|
||||||
|
e.Method, e.URL, e.Body,
|
||||||
|
)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
func (e ErrDefault401) Error() string {
|
||||||
|
return "Authentication failed"
|
||||||
|
}
|
||||||
|
func (e ErrDefault403) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf(
|
||||||
|
"Request forbidden: [%s %s], error message: %s",
|
||||||
|
e.Method, e.URL, e.Body,
|
||||||
|
)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
func (e ErrDefault404) Error() string {
|
||||||
|
return "Resource not found"
|
||||||
|
}
|
||||||
|
func (e ErrDefault405) Error() string {
|
||||||
|
return "Method not allowed"
|
||||||
|
}
|
||||||
|
func (e ErrDefault408) Error() string {
|
||||||
|
return "The server timed out waiting for the request"
|
||||||
|
}
|
||||||
|
func (e ErrDefault429) Error() string {
|
||||||
|
return "Too many requests have been sent in a given amount of time. Pause" +
|
||||||
|
" requests, wait up to one minute, and try again."
|
||||||
|
}
|
||||||
|
func (e ErrDefault500) Error() string {
|
||||||
|
return "Internal Server Error"
|
||||||
|
}
|
||||||
|
func (e ErrDefault503) Error() string {
|
||||||
|
return "The service is currently unable to handle the request due to a temporary" +
|
||||||
|
" overloading or maintenance. This is a temporary condition. Try again later."
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err400er is the interface resource error types implement to override the error message
|
||||||
|
// from a 400 error.
|
||||||
|
type Err400er interface {
|
||||||
|
Error400(ErrUnexpectedResponseCode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err401er is the interface resource error types implement to override the error message
|
||||||
|
// from a 401 error.
|
||||||
|
type Err401er interface {
|
||||||
|
Error401(ErrUnexpectedResponseCode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err403er is the interface resource error types implement to override the error message
|
||||||
|
// from a 403 error.
|
||||||
|
type Err403er interface {
|
||||||
|
Error403(ErrUnexpectedResponseCode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err404er is the interface resource error types implement to override the error message
|
||||||
|
// from a 404 error.
|
||||||
|
type Err404er interface {
|
||||||
|
Error404(ErrUnexpectedResponseCode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err405er is the interface resource error types implement to override the error message
|
||||||
|
// from a 405 error.
|
||||||
|
type Err405er interface {
|
||||||
|
Error405(ErrUnexpectedResponseCode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err408er is the interface resource error types implement to override the error message
|
||||||
|
// from a 408 error.
|
||||||
|
type Err408er interface {
|
||||||
|
Error408(ErrUnexpectedResponseCode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err429er is the interface resource error types implement to override the error message
|
||||||
|
// from a 429 error.
|
||||||
|
type Err429er interface {
|
||||||
|
Error429(ErrUnexpectedResponseCode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err500er is the interface resource error types implement to override the error message
|
||||||
|
// from a 500 error.
|
||||||
|
type Err500er interface {
|
||||||
|
Error500(ErrUnexpectedResponseCode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err503er is the interface resource error types implement to override the error message
|
||||||
|
// from a 503 error.
|
||||||
|
type Err503er interface {
|
||||||
|
Error503(ErrUnexpectedResponseCode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrTimeOut is the error type returned when an operations times out.
|
||||||
|
type ErrTimeOut struct {
|
||||||
|
BaseError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrTimeOut) Error() string {
|
||||||
|
e.DefaultErrString = "A time out occurred"
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrUnableToReauthenticate is the error type returned when reauthentication fails.
|
||||||
|
type ErrUnableToReauthenticate struct {
|
||||||
|
BaseError
|
||||||
|
ErrOriginal error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrUnableToReauthenticate) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf("Unable to re-authenticate: %s", e.ErrOriginal)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrErrorAfterReauthentication is the error type returned when reauthentication
|
||||||
|
// succeeds, but an error occurs afterword (usually an HTTP error).
|
||||||
|
type ErrErrorAfterReauthentication struct {
|
||||||
|
BaseError
|
||||||
|
ErrOriginal error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrErrorAfterReauthentication) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf("Successfully re-authenticated, but got error executing request: %s", e.ErrOriginal)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrServiceNotFound is returned when no service in a service catalog matches
|
||||||
|
// the provided EndpointOpts. This is generally returned by provider service
|
||||||
|
// factory methods like "NewComputeV2()" and can mean that a service is not
|
||||||
|
// enabled for your account.
|
||||||
|
type ErrServiceNotFound struct {
|
||||||
|
BaseError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrServiceNotFound) Error() string {
|
||||||
|
e.DefaultErrString = "No suitable service could be found in the service catalog."
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrEndpointNotFound is returned when no available endpoints match the
|
||||||
|
// provided EndpointOpts. This is also generally returned by provider service
|
||||||
|
// factory methods, and usually indicates that a region was specified
|
||||||
|
// incorrectly.
|
||||||
|
type ErrEndpointNotFound struct {
|
||||||
|
BaseError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrEndpointNotFound) Error() string {
|
||||||
|
e.DefaultErrString = "No suitable endpoint could be found in the service catalog."
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrResourceNotFound is the error when trying to retrieve a resource's
|
||||||
|
// ID by name and the resource doesn't exist.
|
||||||
|
type ErrResourceNotFound struct {
|
||||||
|
BaseError
|
||||||
|
Name string
|
||||||
|
ResourceType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrResourceNotFound) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf("Unable to find %s with name %s", e.ResourceType, e.Name)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMultipleResourcesFound is the error when trying to retrieve a resource's
|
||||||
|
// ID by name and multiple resources have the user-provided name.
|
||||||
|
type ErrMultipleResourcesFound struct {
|
||||||
|
BaseError
|
||||||
|
Name string
|
||||||
|
Count int
|
||||||
|
ResourceType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrMultipleResourcesFound) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf("Found %d %ss matching %s", e.Count, e.ResourceType, e.Name)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrUnexpectedType is the error when an unexpected type is encountered
|
||||||
|
type ErrUnexpectedType struct {
|
||||||
|
BaseError
|
||||||
|
Expected string
|
||||||
|
Actual string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrUnexpectedType) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf("Expected %s but got %s", e.Expected, e.Actual)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
func unacceptedAttributeErr(attribute string) string {
|
||||||
|
return fmt.Sprintf("The base Identity V3 API does not accept authentication by %s", attribute)
|
||||||
|
}
|
||||||
|
|
||||||
|
func redundantWithTokenErr(attribute string) string {
|
||||||
|
return fmt.Sprintf("%s may not be provided when authenticating with a TokenID", attribute)
|
||||||
|
}
|
||||||
|
|
||||||
|
func redundantWithUserID(attribute string) string {
|
||||||
|
return fmt.Sprintf("%s may not be provided when authenticating with a UserID", attribute)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrAPIKeyProvided indicates that an APIKey was provided but can't be used.
|
||||||
|
type ErrAPIKeyProvided struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrAPIKeyProvided) Error() string {
|
||||||
|
return unacceptedAttributeErr("APIKey")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrTenantIDProvided indicates that a TenantID was provided but can't be used.
|
||||||
|
type ErrTenantIDProvided struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrTenantIDProvided) Error() string {
|
||||||
|
return unacceptedAttributeErr("TenantID")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrTenantNameProvided indicates that a TenantName was provided but can't be used.
|
||||||
|
type ErrTenantNameProvided struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrTenantNameProvided) Error() string {
|
||||||
|
return unacceptedAttributeErr("TenantName")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrUsernameWithToken indicates that a Username was provided, but token authentication is being used instead.
|
||||||
|
type ErrUsernameWithToken struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrUsernameWithToken) Error() string {
|
||||||
|
return redundantWithTokenErr("Username")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrUserIDWithToken indicates that a UserID was provided, but token authentication is being used instead.
|
||||||
|
type ErrUserIDWithToken struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrUserIDWithToken) Error() string {
|
||||||
|
return redundantWithTokenErr("UserID")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDomainIDWithToken indicates that a DomainID was provided, but token authentication is being used instead.
|
||||||
|
type ErrDomainIDWithToken struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrDomainIDWithToken) Error() string {
|
||||||
|
return redundantWithTokenErr("DomainID")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDomainNameWithToken indicates that a DomainName was provided, but token authentication is being used instead.s
|
||||||
|
type ErrDomainNameWithToken struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrDomainNameWithToken) Error() string {
|
||||||
|
return redundantWithTokenErr("DomainName")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrUsernameOrUserID indicates that neither username nor userID are specified, or both are at once.
|
||||||
|
type ErrUsernameOrUserID struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrUsernameOrUserID) Error() string {
|
||||||
|
return "Exactly one of Username and UserID must be provided for password authentication"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDomainIDWithUserID indicates that a DomainID was provided, but unnecessary because a UserID is being used.
|
||||||
|
type ErrDomainIDWithUserID struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrDomainIDWithUserID) Error() string {
|
||||||
|
return redundantWithUserID("DomainID")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDomainNameWithUserID indicates that a DomainName was provided, but unnecessary because a UserID is being used.
|
||||||
|
type ErrDomainNameWithUserID struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrDomainNameWithUserID) Error() string {
|
||||||
|
return redundantWithUserID("DomainName")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDomainIDOrDomainName indicates that a username was provided, but no domain to scope it.
|
||||||
|
// It may also indicate that both a DomainID and a DomainName were provided at once.
|
||||||
|
type ErrDomainIDOrDomainName struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrDomainIDOrDomainName) Error() string {
|
||||||
|
return "You must provide exactly one of DomainID or DomainName to authenticate by Username"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMissingPassword indicates that no password was provided and no token is available.
|
||||||
|
type ErrMissingPassword struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrMissingPassword) Error() string {
|
||||||
|
return "You must provide a password to authenticate"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrScopeDomainIDOrDomainName indicates that a domain ID or Name was required in a Scope, but not present.
|
||||||
|
type ErrScopeDomainIDOrDomainName struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrScopeDomainIDOrDomainName) Error() string {
|
||||||
|
return "You must provide exactly one of DomainID or DomainName in a Scope with ProjectName"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrScopeProjectIDOrProjectName indicates that both a ProjectID and a ProjectName were provided in a Scope.
|
||||||
|
type ErrScopeProjectIDOrProjectName struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrScopeProjectIDOrProjectName) Error() string {
|
||||||
|
return "You must provide at most one of ProjectID or ProjectName in a Scope"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrScopeProjectIDAlone indicates that a ProjectID was provided with other constraints in a Scope.
|
||||||
|
type ErrScopeProjectIDAlone struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrScopeProjectIDAlone) Error() string {
|
||||||
|
return "ProjectID must be supplied alone in a Scope"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrScopeEmpty indicates that no credentials were provided in a Scope.
|
||||||
|
type ErrScopeEmpty struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrScopeEmpty) Error() string {
|
||||||
|
return "You must provide either a Project or Domain in a Scope"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrAppCredMissingSecret indicates that no Application Credential Secret was provided with Application Credential ID or Name
|
||||||
|
type ErrAppCredMissingSecret struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrAppCredMissingSecret) Error() string {
|
||||||
|
return "You must provide an Application Credential Secret"
|
||||||
|
}
|
98
vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go
generated
vendored
Normal file
98
vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go
generated
vendored
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
package openstack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
var nilOptions = gophercloud.AuthOptions{}
|
||||||
|
|
||||||
|
/*
|
||||||
|
AuthOptionsFromEnv fills out an identity.AuthOptions structure with the
|
||||||
|
settings found on the various OpenStack OS_* environment variables.
|
||||||
|
|
||||||
|
The following variables provide sources of truth: OS_AUTH_URL, OS_USERNAME,
|
||||||
|
OS_PASSWORD, OS_TENANT_ID, and OS_TENANT_NAME.
|
||||||
|
|
||||||
|
Of these, OS_USERNAME, OS_PASSWORD, and OS_AUTH_URL must have settings,
|
||||||
|
or an error will result. OS_TENANT_ID, OS_TENANT_NAME, OS_PROJECT_ID, and
|
||||||
|
OS_PROJECT_NAME are optional.
|
||||||
|
|
||||||
|
OS_TENANT_ID and OS_TENANT_NAME are mutually exclusive to OS_PROJECT_ID and
|
||||||
|
OS_PROJECT_NAME. If OS_PROJECT_ID and OS_PROJECT_NAME are set, they will
|
||||||
|
still be referred as "tenant" in Gophercloud.
|
||||||
|
|
||||||
|
To use this function, first set the OS_* environment variables (for example,
|
||||||
|
by sourcing an `openrc` file), then:
|
||||||
|
|
||||||
|
opts, err := openstack.AuthOptionsFromEnv()
|
||||||
|
provider, err := openstack.AuthenticatedClient(opts)
|
||||||
|
*/
|
||||||
|
func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) {
|
||||||
|
authURL := os.Getenv("OS_AUTH_URL")
|
||||||
|
username := os.Getenv("OS_USERNAME")
|
||||||
|
userID := os.Getenv("OS_USERID")
|
||||||
|
password := os.Getenv("OS_PASSWORD")
|
||||||
|
tenantID := os.Getenv("OS_TENANT_ID")
|
||||||
|
tenantName := os.Getenv("OS_TENANT_NAME")
|
||||||
|
domainID := os.Getenv("OS_DOMAIN_ID")
|
||||||
|
domainName := os.Getenv("OS_DOMAIN_NAME")
|
||||||
|
applicationCredentialID := os.Getenv("OS_APPLICATION_CREDENTIAL_ID")
|
||||||
|
applicationCredentialName := os.Getenv("OS_APPLICATION_CREDENTIAL_NAME")
|
||||||
|
applicationCredentialSecret := os.Getenv("OS_APPLICATION_CREDENTIAL_SECRET")
|
||||||
|
|
||||||
|
// If OS_PROJECT_ID is set, overwrite tenantID with the value.
|
||||||
|
if v := os.Getenv("OS_PROJECT_ID"); v != "" {
|
||||||
|
tenantID = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// If OS_PROJECT_NAME is set, overwrite tenantName with the value.
|
||||||
|
if v := os.Getenv("OS_PROJECT_NAME"); v != "" {
|
||||||
|
tenantName = v
|
||||||
|
}
|
||||||
|
|
||||||
|
if authURL == "" {
|
||||||
|
err := gophercloud.ErrMissingEnvironmentVariable{
|
||||||
|
EnvironmentVariable: "OS_AUTH_URL",
|
||||||
|
}
|
||||||
|
return nilOptions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if username == "" && userID == "" {
|
||||||
|
err := gophercloud.ErrMissingAnyoneOfEnvironmentVariables{
|
||||||
|
EnvironmentVariables: []string{"OS_USERNAME", "OS_USERID"},
|
||||||
|
}
|
||||||
|
return nilOptions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if password == "" && applicationCredentialID == "" && applicationCredentialName == "" {
|
||||||
|
err := gophercloud.ErrMissingEnvironmentVariable{
|
||||||
|
EnvironmentVariable: "OS_PASSWORD",
|
||||||
|
}
|
||||||
|
return nilOptions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if (applicationCredentialID != "" || applicationCredentialName != "") && applicationCredentialSecret == "" {
|
||||||
|
err := gophercloud.ErrMissingEnvironmentVariable{
|
||||||
|
EnvironmentVariable: "OS_APPLICATION_CREDENTIAL_SECRET",
|
||||||
|
}
|
||||||
|
return nilOptions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ao := gophercloud.AuthOptions{
|
||||||
|
IdentityEndpoint: authURL,
|
||||||
|
UserID: userID,
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
TenantID: tenantID,
|
||||||
|
TenantName: tenantName,
|
||||||
|
DomainID: domainID,
|
||||||
|
DomainName: domainName,
|
||||||
|
ApplicationCredentialID: applicationCredentialID,
|
||||||
|
ApplicationCredentialName: applicationCredentialName,
|
||||||
|
ApplicationCredentialSecret: applicationCredentialSecret,
|
||||||
|
}
|
||||||
|
|
||||||
|
return ao, nil
|
||||||
|
}
|
427
vendor/github.com/gophercloud/gophercloud/openstack/client.go
generated
vendored
Normal file
427
vendor/github.com/gophercloud/gophercloud/openstack/client.go
generated
vendored
Normal file
|
@ -0,0 +1,427 @@
|
||||||
|
package openstack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
|
||||||
|
tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// v2 represents Keystone v2.
|
||||||
|
// It should never increase beyond 2.0.
|
||||||
|
v2 = "v2.0"
|
||||||
|
|
||||||
|
// v3 represents Keystone v3.
|
||||||
|
// The version can be anything from v3 to v3.x.
|
||||||
|
v3 = "v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
NewClient prepares an unauthenticated ProviderClient instance.
|
||||||
|
Most users will probably prefer using the AuthenticatedClient function
|
||||||
|
instead.
|
||||||
|
|
||||||
|
This is useful if you wish to explicitly control the version of the identity
|
||||||
|
service that's used for authentication explicitly, for example.
|
||||||
|
|
||||||
|
A basic example of using this would be:
|
||||||
|
|
||||||
|
ao, err := openstack.AuthOptionsFromEnv()
|
||||||
|
provider, err := openstack.NewClient(ao.IdentityEndpoint)
|
||||||
|
client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{})
|
||||||
|
*/
|
||||||
|
func NewClient(endpoint string) (*gophercloud.ProviderClient, error) {
|
||||||
|
base, err := utils.BaseEndpoint(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint = gophercloud.NormalizeURL(endpoint)
|
||||||
|
base = gophercloud.NormalizeURL(base)
|
||||||
|
|
||||||
|
p := new(gophercloud.ProviderClient)
|
||||||
|
p.IdentityBase = base
|
||||||
|
p.IdentityEndpoint = endpoint
|
||||||
|
p.UseTokenLock()
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint
|
||||||
|
specified by the options, acquires a token, and returns a Provider Client
|
||||||
|
instance that's ready to operate.
|
||||||
|
|
||||||
|
If the full path to a versioned identity endpoint was specified (example:
|
||||||
|
http://example.com:5000/v3), that path will be used as the endpoint to query.
|
||||||
|
|
||||||
|
If a versionless endpoint was specified (example: http://example.com:5000/),
|
||||||
|
the endpoint will be queried to determine which versions of the identity service
|
||||||
|
are available, then chooses the most recent or most supported version.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
ao, err := openstack.AuthOptionsFromEnv()
|
||||||
|
provider, err := openstack.AuthenticatedClient(ao)
|
||||||
|
client, err := openstack.NewNetworkV2(client, gophercloud.EndpointOpts{
|
||||||
|
Region: os.Getenv("OS_REGION_NAME"),
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) {
|
||||||
|
client, err := NewClient(options.IdentityEndpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = Authenticate(client, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authenticate or re-authenticate against the most recent identity service
|
||||||
|
// supported at the provided endpoint.
|
||||||
|
func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
|
||||||
|
versions := []*utils.Version{
|
||||||
|
{ID: v2, Priority: 20, Suffix: "/v2.0/"},
|
||||||
|
{ID: v3, Priority: 30, Suffix: "/v3/"},
|
||||||
|
}
|
||||||
|
|
||||||
|
chosen, endpoint, err := utils.ChooseVersion(client, versions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch chosen.ID {
|
||||||
|
case v2:
|
||||||
|
return v2auth(client, endpoint, options, gophercloud.EndpointOpts{})
|
||||||
|
case v3:
|
||||||
|
return v3auth(client, endpoint, &options, gophercloud.EndpointOpts{})
|
||||||
|
default:
|
||||||
|
// The switch statement must be out of date from the versions list.
|
||||||
|
return fmt.Errorf("Unrecognized identity version: %s", chosen.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthenticateV2 explicitly authenticates against the identity v2 endpoint.
|
||||||
|
func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
|
||||||
|
return v2auth(client, "", options, eo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
|
||||||
|
v2Client, err := NewIdentityV2(client, eo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if endpoint != "" {
|
||||||
|
v2Client.Endpoint = endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
v2Opts := tokens2.AuthOptions{
|
||||||
|
IdentityEndpoint: options.IdentityEndpoint,
|
||||||
|
Username: options.Username,
|
||||||
|
Password: options.Password,
|
||||||
|
TenantID: options.TenantID,
|
||||||
|
TenantName: options.TenantName,
|
||||||
|
AllowReauth: options.AllowReauth,
|
||||||
|
TokenID: options.TokenID,
|
||||||
|
}
|
||||||
|
|
||||||
|
result := tokens2.Create(v2Client, v2Opts)
|
||||||
|
|
||||||
|
token, err := result.ExtractToken()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
catalog, err := result.ExtractServiceCatalog()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.AllowReauth {
|
||||||
|
// here we're creating a throw-away client (tac). it's a copy of the user's provider client, but
|
||||||
|
// with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`,
|
||||||
|
// this should retry authentication only once
|
||||||
|
tac := *client
|
||||||
|
tac.ReauthFunc = nil
|
||||||
|
tac.TokenID = ""
|
||||||
|
tao := options
|
||||||
|
tao.AllowReauth = false
|
||||||
|
client.ReauthFunc = func() error {
|
||||||
|
err := v2auth(&tac, endpoint, tao, eo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
client.TokenID = tac.TokenID
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client.TokenID = token.ID
|
||||||
|
client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
|
||||||
|
return V2EndpointURL(catalog, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthenticateV3 explicitly authenticates against the identity v3 service.
|
||||||
|
func AuthenticateV3(client *gophercloud.ProviderClient, options tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error {
|
||||||
|
return v3auth(client, "", options, eo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error {
|
||||||
|
// Override the generated service endpoint with the one returned by the version endpoint.
|
||||||
|
v3Client, err := NewIdentityV3(client, eo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if endpoint != "" {
|
||||||
|
v3Client.Endpoint = endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
result := tokens3.Create(v3Client, opts)
|
||||||
|
|
||||||
|
token, err := result.ExtractToken()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
catalog, err := result.ExtractServiceCatalog()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client.TokenID = token.ID
|
||||||
|
|
||||||
|
if opts.CanReauth() {
|
||||||
|
// here we're creating a throw-away client (tac). it's a copy of the user's provider client, but
|
||||||
|
// with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`,
|
||||||
|
// this should retry authentication only once
|
||||||
|
tac := *client
|
||||||
|
tac.ReauthFunc = nil
|
||||||
|
tac.TokenID = ""
|
||||||
|
var tao tokens3.AuthOptionsBuilder
|
||||||
|
switch ot := opts.(type) {
|
||||||
|
case *gophercloud.AuthOptions:
|
||||||
|
o := *ot
|
||||||
|
o.AllowReauth = false
|
||||||
|
tao = &o
|
||||||
|
case *tokens3.AuthOptions:
|
||||||
|
o := *ot
|
||||||
|
o.AllowReauth = false
|
||||||
|
tao = &o
|
||||||
|
default:
|
||||||
|
tao = opts
|
||||||
|
}
|
||||||
|
client.ReauthFunc = func() error {
|
||||||
|
err := v3auth(&tac, endpoint, tao, eo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
client.TokenID = tac.TokenID
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
|
||||||
|
return V3EndpointURL(catalog, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIdentityV2 creates a ServiceClient that may be used to interact with the
|
||||||
|
// v2 identity service.
|
||||||
|
func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
endpoint := client.IdentityBase + "v2.0/"
|
||||||
|
clientType := "identity"
|
||||||
|
var err error
|
||||||
|
if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) {
|
||||||
|
eo.ApplyDefaults(clientType)
|
||||||
|
endpoint, err = client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &gophercloud.ServiceClient{
|
||||||
|
ProviderClient: client,
|
||||||
|
Endpoint: endpoint,
|
||||||
|
Type: clientType,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIdentityV3 creates a ServiceClient that may be used to access the v3
|
||||||
|
// identity service.
|
||||||
|
func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
endpoint := client.IdentityBase + "v3/"
|
||||||
|
clientType := "identity"
|
||||||
|
var err error
|
||||||
|
if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) {
|
||||||
|
eo.ApplyDefaults(clientType)
|
||||||
|
endpoint, err = client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure endpoint still has a suffix of v3.
|
||||||
|
// This is because EndpointLocator might have found a versionless
|
||||||
|
// endpoint or the published endpoint is still /v2.0. In both
|
||||||
|
// cases, we need to fix the endpoint to point to /v3.
|
||||||
|
base, err := utils.BaseEndpoint(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
base = gophercloud.NormalizeURL(base)
|
||||||
|
|
||||||
|
endpoint = base + "v3/"
|
||||||
|
|
||||||
|
return &gophercloud.ServiceClient{
|
||||||
|
ProviderClient: client,
|
||||||
|
Endpoint: endpoint,
|
||||||
|
Type: clientType,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts, clientType string) (*gophercloud.ServiceClient, error) {
|
||||||
|
sc := new(gophercloud.ServiceClient)
|
||||||
|
eo.ApplyDefaults(clientType)
|
||||||
|
url, err := client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return sc, err
|
||||||
|
}
|
||||||
|
sc.ProviderClient = client
|
||||||
|
sc.Endpoint = url
|
||||||
|
sc.Type = clientType
|
||||||
|
return sc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewObjectStorageV1 creates a ServiceClient that may be used with the v1
|
||||||
|
// object storage package.
|
||||||
|
func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
return initClientOpts(client, eo, "object-store")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewComputeV2 creates a ServiceClient that may be used with the v2 compute
|
||||||
|
// package.
|
||||||
|
func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
return initClientOpts(client, eo, "compute")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNetworkV2 creates a ServiceClient that may be used with the v2 network
|
||||||
|
// package.
|
||||||
|
func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
sc, err := initClientOpts(client, eo, "network")
|
||||||
|
sc.ResourceBase = sc.Endpoint + "v2.0/"
|
||||||
|
return sc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBlockStorageV1 creates a ServiceClient that may be used to access the v1
|
||||||
|
// block storage service.
|
||||||
|
func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
return initClientOpts(client, eo, "volume")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBlockStorageV2 creates a ServiceClient that may be used to access the v2
|
||||||
|
// block storage service.
|
||||||
|
func NewBlockStorageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
return initClientOpts(client, eo, "volumev2")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBlockStorageV3 creates a ServiceClient that may be used to access the v3 block storage service.
|
||||||
|
func NewBlockStorageV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
return initClientOpts(client, eo, "volumev3")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSharedFileSystemV2 creates a ServiceClient that may be used to access the v2 shared file system service.
|
||||||
|
func NewSharedFileSystemV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
return initClientOpts(client, eo, "sharev2")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1
|
||||||
|
// CDN service.
|
||||||
|
func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
return initClientOpts(client, eo, "cdn")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOrchestrationV1 creates a ServiceClient that may be used to access the v1
|
||||||
|
// orchestration service.
|
||||||
|
func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
return initClientOpts(client, eo, "orchestration")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDBV1 creates a ServiceClient that may be used to access the v1 DB service.
|
||||||
|
func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
return initClientOpts(client, eo, "database")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSV2 creates a ServiceClient that may be used to access the v2 DNS
|
||||||
|
// service.
|
||||||
|
func NewDNSV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
sc, err := initClientOpts(client, eo, "dns")
|
||||||
|
sc.ResourceBase = sc.Endpoint + "v2/"
|
||||||
|
return sc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImageServiceV2 creates a ServiceClient that may be used to access the v2
|
||||||
|
// image service.
|
||||||
|
func NewImageServiceV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
sc, err := initClientOpts(client, eo, "image")
|
||||||
|
sc.ResourceBase = sc.Endpoint + "v2/"
|
||||||
|
return sc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLoadBalancerV2 creates a ServiceClient that may be used to access the v2
|
||||||
|
// load balancer service.
|
||||||
|
func NewLoadBalancerV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
sc, err := initClientOpts(client, eo, "load-balancer")
|
||||||
|
sc.ResourceBase = sc.Endpoint + "v2.0/"
|
||||||
|
return sc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClusteringV1 creates a ServiceClient that may be used with the v1 clustering
|
||||||
|
// package.
|
||||||
|
func NewClusteringV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
return initClientOpts(client, eo, "clustering")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMessagingV2 creates a ServiceClient that may be used with the v2 messaging
|
||||||
|
// service.
|
||||||
|
func NewMessagingV2(client *gophercloud.ProviderClient, clientID string, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
sc, err := initClientOpts(client, eo, "messaging")
|
||||||
|
sc.MoreHeaders = map[string]string{"Client-ID": clientID}
|
||||||
|
return sc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContainerV1 creates a ServiceClient that may be used with v1 container package
|
||||||
|
func NewContainerV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
return initClientOpts(client, eo, "container")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewKeyManagerV1 creates a ServiceClient that may be used with the v1 key
|
||||||
|
// manager service.
|
||||||
|
func NewKeyManagerV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
sc, err := initClientOpts(client, eo, "key-manager")
|
||||||
|
sc.ResourceBase = sc.Endpoint + "v1/"
|
||||||
|
return sc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContainerInfraV1 creates a ServiceClient that may be used with the v1 container infra management
|
||||||
|
// package.
|
||||||
|
func NewContainerInfraV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
return initClientOpts(client, eo, "container-infra")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWorkflowV2 creates a ServiceClient that may be used with the v2 workflow management package.
|
||||||
|
func NewWorkflowV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
return initClientOpts(client, eo, "workflowv2")
|
||||||
|
}
|
14
vendor/github.com/gophercloud/gophercloud/openstack/doc.go
generated
vendored
Normal file
14
vendor/github.com/gophercloud/gophercloud/openstack/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
/*
|
||||||
|
Package openstack contains resources for the individual OpenStack projects
|
||||||
|
supported in Gophercloud. It also includes functions to authenticate to an
|
||||||
|
OpenStack cloud and for provisioning various service-level clients.
|
||||||
|
|
||||||
|
Example of Creating a Service Client
|
||||||
|
|
||||||
|
ao, err := openstack.AuthOptionsFromEnv()
|
||||||
|
provider, err := openstack.AuthenticatedClient(ao)
|
||||||
|
client, err := openstack.NewNetworkV2(client, gophercloud.EndpointOpts{
|
||||||
|
Region: os.Getenv("OS_REGION_NAME"),
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
package openstack
|
107
vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go
generated
vendored
Normal file
107
vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go
generated
vendored
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
package openstack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
|
||||||
|
tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
V2EndpointURL discovers the endpoint URL for a specific service from a
|
||||||
|
ServiceCatalog acquired during the v2 identity service.
|
||||||
|
|
||||||
|
The specified EndpointOpts are used to identify a unique, unambiguous endpoint
|
||||||
|
to return. It's an error both when multiple endpoints match the provided
|
||||||
|
criteria and when none do. The minimum that can be specified is a Type, but you
|
||||||
|
will also often need to specify a Name and/or a Region depending on what's
|
||||||
|
available on your OpenStack deployment.
|
||||||
|
*/
|
||||||
|
func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) {
|
||||||
|
// Extract Endpoints from the catalog entries that match the requested Type, Name if provided, and Region if provided.
|
||||||
|
var endpoints = make([]tokens2.Endpoint, 0, 1)
|
||||||
|
for _, entry := range catalog.Entries {
|
||||||
|
if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) {
|
||||||
|
for _, endpoint := range entry.Endpoints {
|
||||||
|
if opts.Region == "" || endpoint.Region == opts.Region {
|
||||||
|
endpoints = append(endpoints, endpoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report an error if the options were ambiguous.
|
||||||
|
if len(endpoints) > 1 {
|
||||||
|
err := &ErrMultipleMatchingEndpointsV2{}
|
||||||
|
err.Endpoints = endpoints
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the appropriate URL from the matching Endpoint.
|
||||||
|
for _, endpoint := range endpoints {
|
||||||
|
switch opts.Availability {
|
||||||
|
case gophercloud.AvailabilityPublic:
|
||||||
|
return gophercloud.NormalizeURL(endpoint.PublicURL), nil
|
||||||
|
case gophercloud.AvailabilityInternal:
|
||||||
|
return gophercloud.NormalizeURL(endpoint.InternalURL), nil
|
||||||
|
case gophercloud.AvailabilityAdmin:
|
||||||
|
return gophercloud.NormalizeURL(endpoint.AdminURL), nil
|
||||||
|
default:
|
||||||
|
err := &ErrInvalidAvailabilityProvided{}
|
||||||
|
err.Argument = "Availability"
|
||||||
|
err.Value = opts.Availability
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report an error if there were no matching endpoints.
|
||||||
|
err := &gophercloud.ErrEndpointNotFound{}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
V3EndpointURL discovers the endpoint URL for a specific service from a Catalog
|
||||||
|
acquired during the v3 identity service.
|
||||||
|
|
||||||
|
The specified EndpointOpts are used to identify a unique, unambiguous endpoint
|
||||||
|
to return. It's an error both when multiple endpoints match the provided
|
||||||
|
criteria and when none do. The minimum that can be specified is a Type, but you
|
||||||
|
will also often need to specify a Name and/or a Region depending on what's
|
||||||
|
available on your OpenStack deployment.
|
||||||
|
*/
|
||||||
|
func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) {
|
||||||
|
// Extract Endpoints from the catalog entries that match the requested Type, Interface,
|
||||||
|
// Name if provided, and Region if provided.
|
||||||
|
var endpoints = make([]tokens3.Endpoint, 0, 1)
|
||||||
|
for _, entry := range catalog.Entries {
|
||||||
|
if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) {
|
||||||
|
for _, endpoint := range entry.Endpoints {
|
||||||
|
if opts.Availability != gophercloud.AvailabilityAdmin &&
|
||||||
|
opts.Availability != gophercloud.AvailabilityPublic &&
|
||||||
|
opts.Availability != gophercloud.AvailabilityInternal {
|
||||||
|
err := &ErrInvalidAvailabilityProvided{}
|
||||||
|
err.Argument = "Availability"
|
||||||
|
err.Value = opts.Availability
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if (opts.Availability == gophercloud.Availability(endpoint.Interface)) &&
|
||||||
|
(opts.Region == "" || endpoint.Region == opts.Region || endpoint.RegionID == opts.Region) {
|
||||||
|
endpoints = append(endpoints, endpoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report an error if the options were ambiguous.
|
||||||
|
if len(endpoints) > 1 {
|
||||||
|
return "", ErrMultipleMatchingEndpointsV3{Endpoints: endpoints}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the URL from the matching Endpoint.
|
||||||
|
for _, endpoint := range endpoints {
|
||||||
|
return gophercloud.NormalizeURL(endpoint.URL), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report an error if there were no matching endpoints.
|
||||||
|
err := &gophercloud.ErrEndpointNotFound{}
|
||||||
|
return "", err
|
||||||
|
}
|
71
vendor/github.com/gophercloud/gophercloud/openstack/errors.go
generated
vendored
Normal file
71
vendor/github.com/gophercloud/gophercloud/openstack/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
package openstack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
|
||||||
|
tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrEndpointNotFound is the error when no suitable endpoint can be found
|
||||||
|
// in the user's catalog
|
||||||
|
type ErrEndpointNotFound struct{ gophercloud.BaseError }
|
||||||
|
|
||||||
|
func (e ErrEndpointNotFound) Error() string {
|
||||||
|
return "No suitable endpoint could be found in the service catalog."
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrInvalidAvailabilityProvided is the error when an invalid endpoint
|
||||||
|
// availability is provided
|
||||||
|
type ErrInvalidAvailabilityProvided struct{ gophercloud.ErrInvalidInput }
|
||||||
|
|
||||||
|
func (e ErrInvalidAvailabilityProvided) Error() string {
|
||||||
|
return fmt.Sprintf("Unexpected availability in endpoint query: %s", e.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMultipleMatchingEndpointsV2 is the error when more than one endpoint
|
||||||
|
// for the given options is found in the v2 catalog
|
||||||
|
type ErrMultipleMatchingEndpointsV2 struct {
|
||||||
|
gophercloud.BaseError
|
||||||
|
Endpoints []tokens2.Endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrMultipleMatchingEndpointsV2) Error() string {
|
||||||
|
return fmt.Sprintf("Discovered %d matching endpoints: %#v", len(e.Endpoints), e.Endpoints)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMultipleMatchingEndpointsV3 is the error when more than one endpoint
|
||||||
|
// for the given options is found in the v3 catalog
|
||||||
|
type ErrMultipleMatchingEndpointsV3 struct {
|
||||||
|
gophercloud.BaseError
|
||||||
|
Endpoints []tokens3.Endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrMultipleMatchingEndpointsV3) Error() string {
|
||||||
|
return fmt.Sprintf("Discovered %d matching endpoints: %#v", len(e.Endpoints), e.Endpoints)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrNoAuthURL is the error when the OS_AUTH_URL environment variable is not
|
||||||
|
// found
|
||||||
|
type ErrNoAuthURL struct{ gophercloud.ErrInvalidInput }
|
||||||
|
|
||||||
|
func (e ErrNoAuthURL) Error() string {
|
||||||
|
return "Environment variable OS_AUTH_URL needs to be set."
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrNoUsername is the error when the OS_USERNAME environment variable is not
|
||||||
|
// found
|
||||||
|
type ErrNoUsername struct{ gophercloud.ErrInvalidInput }
|
||||||
|
|
||||||
|
func (e ErrNoUsername) Error() string {
|
||||||
|
return "Environment variable OS_USERNAME needs to be set."
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrNoPassword is the error when the OS_PASSWORD environment variable is not
|
||||||
|
// found
|
||||||
|
type ErrNoPassword struct{ gophercloud.ErrInvalidInput }
|
||||||
|
|
||||||
|
func (e ErrNoPassword) Error() string {
|
||||||
|
return "Environment variable OS_PASSWORD needs to be set."
|
||||||
|
}
|
65
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/doc.go
generated
vendored
Normal file
65
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
Package tenants provides information and interaction with the
|
||||||
|
tenants API resource for the OpenStack Identity service.
|
||||||
|
|
||||||
|
See http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2
|
||||||
|
and http://developer.openstack.org/api-ref-identity-v2.html#admin-tenants
|
||||||
|
for more information.
|
||||||
|
|
||||||
|
Example to List Tenants
|
||||||
|
|
||||||
|
listOpts := tenants.ListOpts{
|
||||||
|
Limit: 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
allPages, err := tenants.List(identityClient, listOpts).AllPages()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
allTenants, err := tenants.ExtractTenants(allPages)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tenant := range allTenants {
|
||||||
|
fmt.Printf("%+v\n", tenant)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Create a Tenant
|
||||||
|
|
||||||
|
createOpts := tenants.CreateOpts{
|
||||||
|
Name: "tenant_name",
|
||||||
|
Description: "this is a tenant",
|
||||||
|
Enabled: gophercloud.Enabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
tenant, err := tenants.Create(identityClient, createOpts).Extract()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Update a Tenant
|
||||||
|
|
||||||
|
tenantID := "e6db6ed6277c461a853458589063b295"
|
||||||
|
|
||||||
|
updateOpts := tenants.UpdateOpts{
|
||||||
|
Description: "this is a new description",
|
||||||
|
Enabled: gophercloud.Disabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
tenant, err := tenants.Update(identityClient, tenantID, updateOpts).Extract()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Delete a Tenant
|
||||||
|
|
||||||
|
tenantID := "e6db6ed6277c461a853458589063b295"
|
||||||
|
|
||||||
|
err := tenants.Delete(identitYClient, tenantID).ExtractErr()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
package tenants
|
116
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go
generated
vendored
Normal file
116
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go
generated
vendored
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
package tenants
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListOpts filters the Tenants that are returned by the List call.
|
||||||
|
type ListOpts struct {
|
||||||
|
// Marker is the ID of the last Tenant on the previous page.
|
||||||
|
Marker string `q:"marker"`
|
||||||
|
|
||||||
|
// Limit specifies the page size.
|
||||||
|
Limit int `q:"limit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// List enumerates the Tenants to which the current token has access.
|
||||||
|
func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager {
|
||||||
|
url := listURL(client)
|
||||||
|
if opts != nil {
|
||||||
|
q, err := gophercloud.BuildQueryString(opts)
|
||||||
|
if err != nil {
|
||||||
|
return pagination.Pager{Err: err}
|
||||||
|
}
|
||||||
|
url += q.String()
|
||||||
|
}
|
||||||
|
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
||||||
|
return TenantPage{pagination.LinkedPageBase{PageResult: r}}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOpts represents the options needed when creating new tenant.
|
||||||
|
type CreateOpts struct {
|
||||||
|
// Name is the name of the tenant.
|
||||||
|
Name string `json:"name" required:"true"`
|
||||||
|
|
||||||
|
// Description is the description of the tenant.
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
|
||||||
|
// Enabled sets the tenant status to enabled or disabled.
|
||||||
|
Enabled *bool `json:"enabled,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOptsBuilder enables extensions to add additional parameters to the
|
||||||
|
// Create request.
|
||||||
|
type CreateOptsBuilder interface {
|
||||||
|
ToTenantCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToTenantCreateMap assembles a request body based on the contents of
|
||||||
|
// a CreateOpts.
|
||||||
|
func (opts CreateOpts) ToTenantCreateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "tenant")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create is the operation responsible for creating new tenant.
|
||||||
|
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
||||||
|
b, err := opts.ToTenantCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200, 201},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get requests details on a single tenant by ID.
|
||||||
|
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
||||||
|
_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// Update request.
|
||||||
|
type UpdateOptsBuilder interface {
|
||||||
|
ToTenantUpdateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOpts specifies the base attributes that may be updated on an existing
|
||||||
|
// tenant.
|
||||||
|
type UpdateOpts struct {
|
||||||
|
// Name is the name of the tenant.
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
|
||||||
|
// Description is the description of the tenant.
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
|
||||||
|
// Enabled sets the tenant status to enabled or disabled.
|
||||||
|
Enabled *bool `json:"enabled,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToTenantUpdateMap formats an UpdateOpts structure into a request body.
|
||||||
|
func (opts UpdateOpts) ToTenantUpdateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "tenant")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update is the operation responsible for updating exist tenants by their TenantID.
|
||||||
|
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
|
||||||
|
b, err := opts.ToTenantUpdateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Put(updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete is the operation responsible for permanently deleting a tenant.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
||||||
|
_, r.Err = client.Delete(deleteURL(client, id), nil)
|
||||||
|
return
|
||||||
|
}
|
91
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go
generated
vendored
Normal file
91
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go
generated
vendored
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package tenants
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tenant is a grouping of users in the identity service.
|
||||||
|
type Tenant struct {
|
||||||
|
// ID is a unique identifier for this tenant.
|
||||||
|
ID string `json:"id"`
|
||||||
|
|
||||||
|
// Name is a friendlier user-facing name for this tenant.
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Description is a human-readable explanation of this Tenant's purpose.
|
||||||
|
Description string `json:"description"`
|
||||||
|
|
||||||
|
// Enabled indicates whether or not a tenant is active.
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TenantPage is a single page of Tenant results.
|
||||||
|
type TenantPage struct {
|
||||||
|
pagination.LinkedPageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty determines whether or not a page of Tenants contains any results.
|
||||||
|
func (r TenantPage) IsEmpty() (bool, error) {
|
||||||
|
tenants, err := ExtractTenants(r)
|
||||||
|
return len(tenants) == 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextPageURL extracts the "next" link from the tenants_links section of the result.
|
||||||
|
func (r TenantPage) NextPageURL() (string, error) {
|
||||||
|
var s struct {
|
||||||
|
Links []gophercloud.Link `json:"tenants_links"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return gophercloud.ExtractNextURL(s.Links)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractTenants returns a slice of Tenants contained in a single page of
|
||||||
|
// results.
|
||||||
|
func ExtractTenants(r pagination.Page) ([]Tenant, error) {
|
||||||
|
var s struct {
|
||||||
|
Tenants []Tenant `json:"tenants"`
|
||||||
|
}
|
||||||
|
err := (r.(TenantPage)).ExtractInto(&s)
|
||||||
|
return s.Tenants, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type tenantResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract interprets any tenantResults as a Tenant.
|
||||||
|
func (r tenantResult) Extract() (*Tenant, error) {
|
||||||
|
var s struct {
|
||||||
|
Tenant *Tenant `json:"tenant"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.Tenant, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult is the response from a Get request. Call its Extract method to
|
||||||
|
// interpret it as a Tenant.
|
||||||
|
type GetResult struct {
|
||||||
|
tenantResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult is the response from a Create request. Call its Extract method
|
||||||
|
// to interpret it as a Tenant.
|
||||||
|
type CreateResult struct {
|
||||||
|
tenantResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResult is the response from a Get request. Call its ExtractErr method
|
||||||
|
// to determine if the call succeeded or failed.
|
||||||
|
type DeleteResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateResult is the response from a Update request. Call its Extract method
|
||||||
|
// to interpret it as a Tenant.
|
||||||
|
type UpdateResult struct {
|
||||||
|
tenantResult
|
||||||
|
}
|
23
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/urls.go
generated
vendored
Normal file
23
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/urls.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package tenants
|
||||||
|
|
||||||
|
import "github.com/gophercloud/gophercloud"
|
||||||
|
|
||||||
|
func listURL(client *gophercloud.ServiceClient) string {
|
||||||
|
return client.ServiceURL("tenants")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getURL(client *gophercloud.ServiceClient, tenantID string) string {
|
||||||
|
return client.ServiceURL("tenants", tenantID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createURL(client *gophercloud.ServiceClient) string {
|
||||||
|
return client.ServiceURL("tenants")
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteURL(client *gophercloud.ServiceClient, tenantID string) string {
|
||||||
|
return client.ServiceURL("tenants", tenantID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateURL(client *gophercloud.ServiceClient, tenantID string) string {
|
||||||
|
return client.ServiceURL("tenants", tenantID)
|
||||||
|
}
|
46
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/doc.go
generated
vendored
Normal file
46
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
Package tokens provides information and interaction with the token API
|
||||||
|
resource for the OpenStack Identity service.
|
||||||
|
|
||||||
|
For more information, see:
|
||||||
|
http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2
|
||||||
|
|
||||||
|
Example to Create an Unscoped Token from a Password
|
||||||
|
|
||||||
|
authOpts := gophercloud.AuthOptions{
|
||||||
|
Username: "user",
|
||||||
|
Password: "pass"
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := tokens.Create(identityClient, authOpts).ExtractToken()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Create a Token from a Tenant ID and Password
|
||||||
|
|
||||||
|
authOpts := gophercloud.AuthOptions{
|
||||||
|
Username: "user",
|
||||||
|
Password: "password",
|
||||||
|
TenantID: "fc394f2ab2df4114bde39905f800dc57"
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := tokens.Create(identityClient, authOpts).ExtractToken()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Create a Token from a Tenant Name and Password
|
||||||
|
|
||||||
|
authOpts := gophercloud.AuthOptions{
|
||||||
|
Username: "user",
|
||||||
|
Password: "password",
|
||||||
|
TenantName: "tenantname"
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := tokens.Create(identityClient, authOpts).ExtractToken()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
package tokens
|
103
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go
generated
vendored
Normal file
103
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go
generated
vendored
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package tokens
|
||||||
|
|
||||||
|
import "github.com/gophercloud/gophercloud"
|
||||||
|
|
||||||
|
// PasswordCredentialsV2 represents the required options to authenticate
|
||||||
|
// with a username and password.
|
||||||
|
type PasswordCredentialsV2 struct {
|
||||||
|
Username string `json:"username" required:"true"`
|
||||||
|
Password string `json:"password" required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokenCredentialsV2 represents the required options to authenticate
|
||||||
|
// with a token.
|
||||||
|
type TokenCredentialsV2 struct {
|
||||||
|
ID string `json:"id,omitempty" required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthOptionsV2 wraps a gophercloud AuthOptions in order to adhere to the
|
||||||
|
// AuthOptionsBuilder interface.
|
||||||
|
type AuthOptionsV2 struct {
|
||||||
|
PasswordCredentials *PasswordCredentialsV2 `json:"passwordCredentials,omitempty" xor:"TokenCredentials"`
|
||||||
|
|
||||||
|
// The TenantID and TenantName fields are optional for the Identity V2 API.
|
||||||
|
// Some providers allow you to specify a TenantName instead of the TenantId.
|
||||||
|
// Some require both. Your provider's authentication policies will determine
|
||||||
|
// how these fields influence authentication.
|
||||||
|
TenantID string `json:"tenantId,omitempty"`
|
||||||
|
TenantName string `json:"tenantName,omitempty"`
|
||||||
|
|
||||||
|
// TokenCredentials allows users to authenticate (possibly as another user)
|
||||||
|
// with an authentication token ID.
|
||||||
|
TokenCredentials *TokenCredentialsV2 `json:"token,omitempty" xor:"PasswordCredentials"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthOptionsBuilder allows extensions to add additional parameters to the
|
||||||
|
// token create request.
|
||||||
|
type AuthOptionsBuilder interface {
|
||||||
|
// ToTokenCreateMap assembles the Create request body, returning an error
|
||||||
|
// if parameters are missing or inconsistent.
|
||||||
|
ToTokenV2CreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthOptions are the valid options for Openstack Identity v2 authentication.
|
||||||
|
// For field descriptions, see gophercloud.AuthOptions.
|
||||||
|
type AuthOptions struct {
|
||||||
|
IdentityEndpoint string `json:"-"`
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
TenantID string `json:"tenantId,omitempty"`
|
||||||
|
TenantName string `json:"tenantName,omitempty"`
|
||||||
|
AllowReauth bool `json:"-"`
|
||||||
|
TokenID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToTokenV2CreateMap builds a token request body from the given AuthOptions.
|
||||||
|
func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) {
|
||||||
|
v2Opts := AuthOptionsV2{
|
||||||
|
TenantID: opts.TenantID,
|
||||||
|
TenantName: opts.TenantName,
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Password != "" {
|
||||||
|
v2Opts.PasswordCredentials = &PasswordCredentialsV2{
|
||||||
|
Username: opts.Username,
|
||||||
|
Password: opts.Password,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
v2Opts.TokenCredentials = &TokenCredentialsV2{
|
||||||
|
ID: opts.TokenID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := gophercloud.BuildRequestBody(v2Opts, "auth")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create authenticates to the identity service and attempts to acquire a Token.
|
||||||
|
// Generally, rather than interact with this call directly, end users should
|
||||||
|
// call openstack.AuthenticatedClient(), which abstracts all of the gory details
|
||||||
|
// about navigating service catalogs and such.
|
||||||
|
func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r CreateResult) {
|
||||||
|
b, err := auth.ToTokenV2CreateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200, 203},
|
||||||
|
MoreHeaders: map[string]string{"X-Auth-Token": ""},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get validates and retrieves information for user's token.
|
||||||
|
func Get(client *gophercloud.ServiceClient, token string) (r GetResult) {
|
||||||
|
_, r.Err = client.Get(GetURL(client, token), &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200, 203},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
159
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go
generated
vendored
Normal file
159
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go
generated
vendored
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
package tokens
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/identity/v2/tenants"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Token provides only the most basic information related to an authentication
|
||||||
|
// token.
|
||||||
|
type Token struct {
|
||||||
|
// ID provides the primary means of identifying a user to the OpenStack API.
|
||||||
|
// OpenStack defines this field as an opaque value, so do not depend on its
|
||||||
|
// content. It is safe, however, to compare for equality.
|
||||||
|
ID string
|
||||||
|
|
||||||
|
// ExpiresAt provides a timestamp in ISO 8601 format, indicating when the
|
||||||
|
// authentication token becomes invalid. After this point in time, future
|
||||||
|
// API requests made using this authentication token will respond with
|
||||||
|
// errors. Either the caller will need to reauthenticate manually, or more
|
||||||
|
// preferably, the caller should exploit automatic re-authentication.
|
||||||
|
// See the AuthOptions structure for more details.
|
||||||
|
ExpiresAt time.Time
|
||||||
|
|
||||||
|
// Tenant provides information about the tenant to which this token grants
|
||||||
|
// access.
|
||||||
|
Tenant tenants.Tenant
|
||||||
|
}
|
||||||
|
|
||||||
|
// Role is a role for a user.
|
||||||
|
type Role struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// User is an OpenStack user.
|
||||||
|
type User struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
UserName string `json:"username"`
|
||||||
|
Roles []Role `json:"roles"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Endpoint represents a single API endpoint offered by a service.
|
||||||
|
// It provides the public and internal URLs, if supported, along with a region
|
||||||
|
// specifier, again if provided.
|
||||||
|
//
|
||||||
|
// The significance of the Region field will depend upon your provider.
|
||||||
|
//
|
||||||
|
// In addition, the interface offered by the service will have version
|
||||||
|
// information associated with it through the VersionId, VersionInfo, and
|
||||||
|
// VersionList fields, if provided or supported.
|
||||||
|
//
|
||||||
|
// In all cases, fields which aren't supported by the provider and service
|
||||||
|
// combined will assume a zero-value ("").
|
||||||
|
type Endpoint struct {
|
||||||
|
TenantID string `json:"tenantId"`
|
||||||
|
PublicURL string `json:"publicURL"`
|
||||||
|
InternalURL string `json:"internalURL"`
|
||||||
|
AdminURL string `json:"adminURL"`
|
||||||
|
Region string `json:"region"`
|
||||||
|
VersionID string `json:"versionId"`
|
||||||
|
VersionInfo string `json:"versionInfo"`
|
||||||
|
VersionList string `json:"versionList"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CatalogEntry provides a type-safe interface to an Identity API V2 service
|
||||||
|
// catalog listing.
|
||||||
|
//
|
||||||
|
// Each class of service, such as cloud DNS or block storage services, will have
|
||||||
|
// a single CatalogEntry representing it.
|
||||||
|
//
|
||||||
|
// Note: when looking for the desired service, try, whenever possible, to key
|
||||||
|
// off the type field. Otherwise, you'll tie the representation of the service
|
||||||
|
// to a specific provider.
|
||||||
|
type CatalogEntry struct {
|
||||||
|
// Name will contain the provider-specified name for the service.
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Type will contain a type string if OpenStack defines a type for the
|
||||||
|
// service. Otherwise, for provider-specific services, the provider may assign
|
||||||
|
// their own type strings.
|
||||||
|
Type string `json:"type"`
|
||||||
|
|
||||||
|
// Endpoints will let the caller iterate over all the different endpoints that
|
||||||
|
// may exist for the service.
|
||||||
|
Endpoints []Endpoint `json:"endpoints"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceCatalog provides a view into the service catalog from a previous,
|
||||||
|
// successful authentication.
|
||||||
|
type ServiceCatalog struct {
|
||||||
|
Entries []CatalogEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult is the response from a Create request. Use ExtractToken() to
|
||||||
|
// interpret it as a Token, or ExtractServiceCatalog() to interpret it as a
|
||||||
|
// service catalog.
|
||||||
|
type CreateResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult is the deferred response from a Get call, which is the same with a
|
||||||
|
// Created token. Use ExtractUser() to interpret it as a User.
|
||||||
|
type GetResult struct {
|
||||||
|
CreateResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractToken returns the just-created Token from a CreateResult.
|
||||||
|
func (r CreateResult) ExtractToken() (*Token, error) {
|
||||||
|
var s struct {
|
||||||
|
Access struct {
|
||||||
|
Token struct {
|
||||||
|
Expires string `json:"expires"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Tenant tenants.Tenant `json:"tenant"`
|
||||||
|
} `json:"token"`
|
||||||
|
} `json:"access"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
expiresTs, err := time.Parse(gophercloud.RFC3339Milli, s.Access.Token.Expires)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Token{
|
||||||
|
ID: s.Access.Token.ID,
|
||||||
|
ExpiresAt: expiresTs,
|
||||||
|
Tenant: s.Access.Token.Tenant,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractServiceCatalog returns the ServiceCatalog that was generated along
|
||||||
|
// with the user's Token.
|
||||||
|
func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
|
||||||
|
var s struct {
|
||||||
|
Access struct {
|
||||||
|
Entries []CatalogEntry `json:"serviceCatalog"`
|
||||||
|
} `json:"access"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return &ServiceCatalog{Entries: s.Access.Entries}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractUser returns the User from a GetResult.
|
||||||
|
func (r GetResult) ExtractUser() (*User, error) {
|
||||||
|
var s struct {
|
||||||
|
Access struct {
|
||||||
|
User User `json:"user"`
|
||||||
|
} `json:"access"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return &s.Access.User, err
|
||||||
|
}
|
13
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/urls.go
generated
vendored
Normal file
13
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/urls.go
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package tokens
|
||||||
|
|
||||||
|
import "github.com/gophercloud/gophercloud"
|
||||||
|
|
||||||
|
// CreateURL generates the URL used to create new Tokens.
|
||||||
|
func CreateURL(client *gophercloud.ServiceClient) string {
|
||||||
|
return client.ServiceURL("tokens")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetURL generates the URL used to Validate Tokens.
|
||||||
|
func GetURL(client *gophercloud.ServiceClient, token string) string {
|
||||||
|
return client.ServiceURL("tokens", token)
|
||||||
|
}
|
108
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/doc.go
generated
vendored
Normal file
108
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
Package tokens provides information and interaction with the token API
|
||||||
|
resource for the OpenStack Identity service.
|
||||||
|
|
||||||
|
For more information, see:
|
||||||
|
http://developer.openstack.org/api-ref-identity-v3.html#tokens-v3
|
||||||
|
|
||||||
|
Example to Create a Token From a Username and Password
|
||||||
|
|
||||||
|
authOptions := tokens.AuthOptions{
|
||||||
|
UserID: "username",
|
||||||
|
Password: "password",
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := tokens.Create(identityClient, authOptions).ExtractToken()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Create a Token From a Username, Password, and Domain
|
||||||
|
|
||||||
|
authOptions := tokens.AuthOptions{
|
||||||
|
UserID: "username",
|
||||||
|
Password: "password",
|
||||||
|
DomainID: "default",
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := tokens.Create(identityClient, authOptions).ExtractToken()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
authOptions = tokens.AuthOptions{
|
||||||
|
UserID: "username",
|
||||||
|
Password: "password",
|
||||||
|
DomainName: "default",
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err = tokens.Create(identityClient, authOptions).ExtractToken()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Create a Token From a Token
|
||||||
|
|
||||||
|
authOptions := tokens.AuthOptions{
|
||||||
|
TokenID: "token_id",
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := tokens.Create(identityClient, authOptions).ExtractToken()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Create a Token from a Username and Password with Project ID Scope
|
||||||
|
|
||||||
|
scope := tokens.Scope{
|
||||||
|
ProjectID: "0fe36e73809d46aeae6705c39077b1b3",
|
||||||
|
}
|
||||||
|
|
||||||
|
authOptions := tokens.AuthOptions{
|
||||||
|
Scope: &scope,
|
||||||
|
UserID: "username",
|
||||||
|
Password: "password",
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err = tokens.Create(identityClient, authOptions).ExtractToken()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Create a Token from a Username and Password with Domain ID Scope
|
||||||
|
|
||||||
|
scope := tokens.Scope{
|
||||||
|
DomainID: "default",
|
||||||
|
}
|
||||||
|
|
||||||
|
authOptions := tokens.AuthOptions{
|
||||||
|
Scope: &scope,
|
||||||
|
UserID: "username",
|
||||||
|
Password: "password",
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err = tokens.Create(identityClient, authOptions).ExtractToken()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Create a Token from a Username and Password with Project Name Scope
|
||||||
|
|
||||||
|
scope := tokens.Scope{
|
||||||
|
ProjectName: "project_name",
|
||||||
|
DomainID: "default",
|
||||||
|
}
|
||||||
|
|
||||||
|
authOptions := tokens.AuthOptions{
|
||||||
|
Scope: &scope,
|
||||||
|
UserID: "username",
|
||||||
|
Password: "password",
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err = tokens.Create(identityClient, authOptions).ExtractToken()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
package tokens
|
162
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go
generated
vendored
Normal file
162
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go
generated
vendored
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
package tokens
|
||||||
|
|
||||||
|
import "github.com/gophercloud/gophercloud"
|
||||||
|
|
||||||
|
// Scope allows a created token to be limited to a specific domain or project.
|
||||||
|
type Scope struct {
|
||||||
|
ProjectID string
|
||||||
|
ProjectName string
|
||||||
|
DomainID string
|
||||||
|
DomainName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthOptionsBuilder provides the ability for extensions to add additional
|
||||||
|
// parameters to AuthOptions. Extensions must satisfy all required methods.
|
||||||
|
type AuthOptionsBuilder interface {
|
||||||
|
// ToTokenV3CreateMap assembles the Create request body, returning an error
|
||||||
|
// if parameters are missing or inconsistent.
|
||||||
|
ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error)
|
||||||
|
ToTokenV3ScopeMap() (map[string]interface{}, error)
|
||||||
|
CanReauth() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthOptions represents options for authenticating a user.
|
||||||
|
type AuthOptions struct {
|
||||||
|
// IdentityEndpoint specifies the HTTP endpoint that is required to work with
|
||||||
|
// the Identity API of the appropriate version. While it's ultimately needed
|
||||||
|
// by all of the identity services, it will often be populated by a
|
||||||
|
// provider-level function.
|
||||||
|
IdentityEndpoint string `json:"-"`
|
||||||
|
|
||||||
|
// Username is required if using Identity V2 API. Consult with your provider's
|
||||||
|
// control panel to discover your account's username. In Identity V3, either
|
||||||
|
// UserID or a combination of Username and DomainID or DomainName are needed.
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
UserID string `json:"id,omitempty"`
|
||||||
|
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
|
||||||
|
// At most one of DomainID and DomainName must be provided if using Username
|
||||||
|
// with Identity V3. Otherwise, either are optional.
|
||||||
|
DomainID string `json:"-"`
|
||||||
|
DomainName string `json:"name,omitempty"`
|
||||||
|
|
||||||
|
// AllowReauth should be set to true if you grant permission for Gophercloud
|
||||||
|
// to cache your credentials in memory, and to allow Gophercloud to attempt
|
||||||
|
// to re-authenticate automatically if/when your token expires. If you set
|
||||||
|
// it to false, it will not cache these settings, but re-authentication will
|
||||||
|
// not be possible. This setting defaults to false.
|
||||||
|
AllowReauth bool `json:"-"`
|
||||||
|
|
||||||
|
// TokenID allows users to authenticate (possibly as another user) with an
|
||||||
|
// authentication token ID.
|
||||||
|
TokenID string `json:"-"`
|
||||||
|
|
||||||
|
// Authentication through Application Credentials requires supplying name, project and secret
|
||||||
|
// For project we can use TenantID
|
||||||
|
ApplicationCredentialID string `json:"-"`
|
||||||
|
ApplicationCredentialName string `json:"-"`
|
||||||
|
ApplicationCredentialSecret string `json:"-"`
|
||||||
|
|
||||||
|
Scope Scope `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToTokenV3CreateMap builds a request body from AuthOptions.
|
||||||
|
func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) {
|
||||||
|
gophercloudAuthOpts := gophercloud.AuthOptions{
|
||||||
|
Username: opts.Username,
|
||||||
|
UserID: opts.UserID,
|
||||||
|
Password: opts.Password,
|
||||||
|
DomainID: opts.DomainID,
|
||||||
|
DomainName: opts.DomainName,
|
||||||
|
AllowReauth: opts.AllowReauth,
|
||||||
|
TokenID: opts.TokenID,
|
||||||
|
ApplicationCredentialID: opts.ApplicationCredentialID,
|
||||||
|
ApplicationCredentialName: opts.ApplicationCredentialName,
|
||||||
|
ApplicationCredentialSecret: opts.ApplicationCredentialSecret,
|
||||||
|
}
|
||||||
|
|
||||||
|
return gophercloudAuthOpts.ToTokenV3CreateMap(scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToTokenV3CreateMap builds a scope request body from AuthOptions.
|
||||||
|
func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
|
||||||
|
scope := gophercloud.AuthScope(opts.Scope)
|
||||||
|
|
||||||
|
gophercloudAuthOpts := gophercloud.AuthOptions{
|
||||||
|
Scope: &scope,
|
||||||
|
DomainID: opts.DomainID,
|
||||||
|
DomainName: opts.DomainName,
|
||||||
|
}
|
||||||
|
|
||||||
|
return gophercloudAuthOpts.ToTokenV3ScopeMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *AuthOptions) CanReauth() bool {
|
||||||
|
return opts.AllowReauth
|
||||||
|
}
|
||||||
|
|
||||||
|
func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"X-Subject-Token": subjectToken,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create authenticates and either generates a new token, or changes the Scope
|
||||||
|
// of an existing token.
|
||||||
|
func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) {
|
||||||
|
scope, err := opts.ToTokenV3ScopeMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := opts.ToTokenV3CreateMap(scope)
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.Post(tokenURL(c), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
MoreHeaders: map[string]string{"X-Auth-Token": ""},
|
||||||
|
})
|
||||||
|
r.Err = err
|
||||||
|
if resp != nil {
|
||||||
|
r.Header = resp.Header
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get validates and retrieves information about another token.
|
||||||
|
func Get(c *gophercloud.ServiceClient, token string) (r GetResult) {
|
||||||
|
resp, err := c.Get(tokenURL(c), &r.Body, &gophercloud.RequestOpts{
|
||||||
|
MoreHeaders: subjectTokenHeaders(c, token),
|
||||||
|
OkCodes: []int{200, 203},
|
||||||
|
})
|
||||||
|
if resp != nil {
|
||||||
|
r.Err = err
|
||||||
|
r.Header = resp.Header
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate determines if a specified token is valid or not.
|
||||||
|
func Validate(c *gophercloud.ServiceClient, token string) (bool, error) {
|
||||||
|
resp, err := c.Head(tokenURL(c), &gophercloud.RequestOpts{
|
||||||
|
MoreHeaders: subjectTokenHeaders(c, token),
|
||||||
|
OkCodes: []int{200, 204, 404},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.StatusCode == 200 || resp.StatusCode == 204, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revoke immediately makes specified token invalid.
|
||||||
|
func Revoke(c *gophercloud.ServiceClient, token string) (r RevokeResult) {
|
||||||
|
_, r.Err = c.Delete(tokenURL(c), &gophercloud.RequestOpts{
|
||||||
|
MoreHeaders: subjectTokenHeaders(c, token),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
171
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go
generated
vendored
Normal file
171
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go
generated
vendored
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
package tokens
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Endpoint represents a single API endpoint offered by a service.
|
||||||
|
// It matches either a public, internal or admin URL.
|
||||||
|
// If supported, it contains a region specifier, again if provided.
|
||||||
|
// The significance of the Region field will depend upon your provider.
|
||||||
|
type Endpoint struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Region string `json:"region"`
|
||||||
|
RegionID string `json:"region_id"`
|
||||||
|
Interface string `json:"interface"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CatalogEntry provides a type-safe interface to an Identity API V3 service
|
||||||
|
// catalog listing. Each class of service, such as cloud DNS or block storage
|
||||||
|
// services, could have multiple CatalogEntry representing it (one by interface
|
||||||
|
// type, e.g public, admin or internal).
|
||||||
|
//
|
||||||
|
// Note: when looking for the desired service, try, whenever possible, to key
|
||||||
|
// off the type field. Otherwise, you'll tie the representation of the service
|
||||||
|
// to a specific provider.
|
||||||
|
type CatalogEntry struct {
|
||||||
|
// Service ID
|
||||||
|
ID string `json:"id"`
|
||||||
|
|
||||||
|
// Name will contain the provider-specified name for the service.
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Type will contain a type string if OpenStack defines a type for the
|
||||||
|
// service. Otherwise, for provider-specific services, the provider may
|
||||||
|
// assign their own type strings.
|
||||||
|
Type string `json:"type"`
|
||||||
|
|
||||||
|
// Endpoints will let the caller iterate over all the different endpoints that
|
||||||
|
// may exist for the service.
|
||||||
|
Endpoints []Endpoint `json:"endpoints"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceCatalog provides a view into the service catalog from a previous,
|
||||||
|
// successful authentication.
|
||||||
|
type ServiceCatalog struct {
|
||||||
|
Entries []CatalogEntry `json:"catalog"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Domain provides information about the domain to which this token grants
|
||||||
|
// access.
|
||||||
|
type Domain struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// User represents a user resource that exists in the Identity Service.
|
||||||
|
type User struct {
|
||||||
|
Domain Domain `json:"domain"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Role provides information about roles to which User is authorized.
|
||||||
|
type Role struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Project provides information about project to which User is authorized.
|
||||||
|
type Project struct {
|
||||||
|
Domain Domain `json:"domain"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// commonResult is the response from a request. A commonResult has various
|
||||||
|
// methods which can be used to extract different details about the result.
|
||||||
|
type commonResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract is a shortcut for ExtractToken.
|
||||||
|
// This function is deprecated and still present for backward compatibility.
|
||||||
|
func (r commonResult) Extract() (*Token, error) {
|
||||||
|
return r.ExtractToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractToken interprets a commonResult as a Token.
|
||||||
|
func (r commonResult) ExtractToken() (*Token, error) {
|
||||||
|
var s Token
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the token itself from the stored headers.
|
||||||
|
s.ID = r.Header.Get("X-Subject-Token")
|
||||||
|
|
||||||
|
return &s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractServiceCatalog returns the ServiceCatalog that was generated along
|
||||||
|
// with the user's Token.
|
||||||
|
func (r commonResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
|
||||||
|
var s ServiceCatalog
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return &s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractUser returns the User that is the owner of the Token.
|
||||||
|
func (r commonResult) ExtractUser() (*User, error) {
|
||||||
|
var s struct {
|
||||||
|
User *User `json:"user"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.User, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractRoles returns Roles to which User is authorized.
|
||||||
|
func (r commonResult) ExtractRoles() ([]Role, error) {
|
||||||
|
var s struct {
|
||||||
|
Roles []Role `json:"roles"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.Roles, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractProject returns Project to which User is authorized.
|
||||||
|
func (r commonResult) ExtractProject() (*Project, error) {
|
||||||
|
var s struct {
|
||||||
|
Project *Project `json:"project"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.Project, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult is the response from a Create request. Use ExtractToken()
|
||||||
|
// to interpret it as a Token, or ExtractServiceCatalog() to interpret it
|
||||||
|
// as a service catalog.
|
||||||
|
type CreateResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult is the response from a Get request. Use ExtractToken()
|
||||||
|
// to interpret it as a Token, or ExtractServiceCatalog() to interpret it
|
||||||
|
// as a service catalog.
|
||||||
|
type GetResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// RevokeResult is response from a Revoke request.
|
||||||
|
type RevokeResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token is a string that grants a user access to a controlled set of services
|
||||||
|
// in an OpenStack provider. Each Token is valid for a set length of time.
|
||||||
|
type Token struct {
|
||||||
|
// ID is the issued token.
|
||||||
|
ID string `json:"id"`
|
||||||
|
|
||||||
|
// ExpiresAt is the timestamp at which this token will no longer be accepted.
|
||||||
|
ExpiresAt time.Time `json:"expires_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r commonResult) ExtractInto(v interface{}) error {
|
||||||
|
return r.ExtractIntoStructPtr(v, "token")
|
||||||
|
}
|
7
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/urls.go
generated
vendored
Normal file
7
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/urls.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package tokens
|
||||||
|
|
||||||
|
import "github.com/gophercloud/gophercloud"
|
||||||
|
|
||||||
|
func tokenURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return c.ServiceURL("auth", "tokens")
|
||||||
|
}
|
28
vendor/github.com/gophercloud/gophercloud/openstack/utils/base_endpoint.go
generated
vendored
Normal file
28
vendor/github.com/gophercloud/gophercloud/openstack/utils/base_endpoint.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BaseEndpoint will return a URL without the /vX.Y
|
||||||
|
// portion of the URL.
|
||||||
|
func BaseEndpoint(endpoint string) (string, error) {
|
||||||
|
u, err := url.Parse(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
u.RawQuery, u.Fragment = "", ""
|
||||||
|
|
||||||
|
path := u.Path
|
||||||
|
versionRe := regexp.MustCompile("v[0-9.]+/?")
|
||||||
|
|
||||||
|
if version := versionRe.FindString(path); version != "" {
|
||||||
|
versionIndex := strings.Index(path, version)
|
||||||
|
u.Path = path[:versionIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.String(), nil
|
||||||
|
}
|
111
vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go
generated
vendored
Normal file
111
vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Version is a supported API version, corresponding to a vN package within the appropriate service.
|
||||||
|
type Version struct {
|
||||||
|
ID string
|
||||||
|
Suffix string
|
||||||
|
Priority int
|
||||||
|
}
|
||||||
|
|
||||||
|
var goodStatus = map[string]bool{
|
||||||
|
"current": true,
|
||||||
|
"supported": true,
|
||||||
|
"stable": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChooseVersion queries the base endpoint of an API to choose the most recent non-experimental alternative from a service's
|
||||||
|
// published versions.
|
||||||
|
// It returns the highest-Priority Version among the alternatives that are provided, as well as its corresponding endpoint.
|
||||||
|
func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (*Version, string, error) {
|
||||||
|
type linkResp struct {
|
||||||
|
Href string `json:"href"`
|
||||||
|
Rel string `json:"rel"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type valueResp struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Links []linkResp `json:"links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type versionsResp struct {
|
||||||
|
Values []valueResp `json:"values"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type response struct {
|
||||||
|
Versions versionsResp `json:"versions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
normalize := func(endpoint string) string {
|
||||||
|
if !strings.HasSuffix(endpoint, "/") {
|
||||||
|
return endpoint + "/"
|
||||||
|
}
|
||||||
|
return endpoint
|
||||||
|
}
|
||||||
|
identityEndpoint := normalize(client.IdentityEndpoint)
|
||||||
|
|
||||||
|
// If a full endpoint is specified, check version suffixes for a match first.
|
||||||
|
for _, v := range recognized {
|
||||||
|
if strings.HasSuffix(identityEndpoint, v.Suffix) {
|
||||||
|
return v, identityEndpoint, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp response
|
||||||
|
_, err := client.Request("GET", client.IdentityBase, &gophercloud.RequestOpts{
|
||||||
|
JSONResponse: &resp,
|
||||||
|
OkCodes: []int{200, 300},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var highest *Version
|
||||||
|
var endpoint string
|
||||||
|
|
||||||
|
for _, value := range resp.Versions.Values {
|
||||||
|
href := ""
|
||||||
|
for _, link := range value.Links {
|
||||||
|
if link.Rel == "self" {
|
||||||
|
href = normalize(link.Href)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, version := range recognized {
|
||||||
|
if strings.Contains(value.ID, version.ID) {
|
||||||
|
// Prefer a version that exactly matches the provided endpoint.
|
||||||
|
if href == identityEndpoint {
|
||||||
|
if href == "" {
|
||||||
|
return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", value.ID, client.IdentityBase)
|
||||||
|
}
|
||||||
|
return version, href, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, find the highest-priority version with a whitelisted status.
|
||||||
|
if goodStatus[strings.ToLower(value.Status)] {
|
||||||
|
if highest == nil || version.Priority > highest.Priority {
|
||||||
|
highest = version
|
||||||
|
endpoint = href
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if highest == nil {
|
||||||
|
return nil, "", fmt.Errorf("No supported version available from endpoint %s", client.IdentityBase)
|
||||||
|
}
|
||||||
|
if endpoint == "" {
|
||||||
|
return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", highest.ID, client.IdentityBase)
|
||||||
|
}
|
||||||
|
|
||||||
|
return highest, endpoint, nil
|
||||||
|
}
|
60
vendor/github.com/gophercloud/gophercloud/pagination/http.go
generated
vendored
Normal file
60
vendor/github.com/gophercloud/gophercloud/pagination/http.go
generated
vendored
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package pagination
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PageResult stores the HTTP response that returned the current page of results.
|
||||||
|
type PageResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
url.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
// PageResultFrom parses an HTTP response as JSON and returns a PageResult containing the
|
||||||
|
// results, interpreting it as JSON if the content type indicates.
|
||||||
|
func PageResultFrom(resp *http.Response) (PageResult, error) {
|
||||||
|
var parsedBody interface{}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
rawBody, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return PageResult{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(resp.Header.Get("Content-Type"), "application/json") {
|
||||||
|
err = json.Unmarshal(rawBody, &parsedBody)
|
||||||
|
if err != nil {
|
||||||
|
return PageResult{}, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parsedBody = rawBody
|
||||||
|
}
|
||||||
|
|
||||||
|
return PageResultFromParsed(resp, parsedBody), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PageResultFromParsed constructs a PageResult from an HTTP response that has already had its
|
||||||
|
// body parsed as JSON (and closed).
|
||||||
|
func PageResultFromParsed(resp *http.Response, body interface{}) PageResult {
|
||||||
|
return PageResult{
|
||||||
|
Result: gophercloud.Result{
|
||||||
|
Body: body,
|
||||||
|
Header: resp.Header,
|
||||||
|
},
|
||||||
|
URL: *resp.Request.URL,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request performs an HTTP request and extracts the http.Response from the result.
|
||||||
|
func Request(client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) {
|
||||||
|
return client.Get(url, nil, &gophercloud.RequestOpts{
|
||||||
|
MoreHeaders: headers,
|
||||||
|
OkCodes: []int{200, 204, 300},
|
||||||
|
})
|
||||||
|
}
|
92
vendor/github.com/gophercloud/gophercloud/pagination/linked.go
generated
vendored
Normal file
92
vendor/github.com/gophercloud/gophercloud/pagination/linked.go
generated
vendored
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
package pagination
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LinkedPageBase may be embedded to implement a page that provides navigational "Next" and "Previous" links within its result.
|
||||||
|
type LinkedPageBase struct {
|
||||||
|
PageResult
|
||||||
|
|
||||||
|
// LinkPath lists the keys that should be traversed within a response to arrive at the "next" pointer.
|
||||||
|
// If any link along the path is missing, an empty URL will be returned.
|
||||||
|
// If any link results in an unexpected value type, an error will be returned.
|
||||||
|
// When left as "nil", []string{"links", "next"} will be used as a default.
|
||||||
|
LinkPath []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextPageURL extracts the pagination structure from a JSON response and returns the "next" link, if one is present.
|
||||||
|
// It assumes that the links are available in a "links" element of the top-level response object.
|
||||||
|
// If this is not the case, override NextPageURL on your result type.
|
||||||
|
func (current LinkedPageBase) NextPageURL() (string, error) {
|
||||||
|
var path []string
|
||||||
|
var key string
|
||||||
|
|
||||||
|
if current.LinkPath == nil {
|
||||||
|
path = []string{"links", "next"}
|
||||||
|
} else {
|
||||||
|
path = current.LinkPath
|
||||||
|
}
|
||||||
|
|
||||||
|
submap, ok := current.Body.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
err := gophercloud.ErrUnexpectedType{}
|
||||||
|
err.Expected = "map[string]interface{}"
|
||||||
|
err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body))
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
key, path = path[0], path[1:len(path)]
|
||||||
|
|
||||||
|
value, ok := submap[key]
|
||||||
|
if !ok {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(path) > 0 {
|
||||||
|
submap, ok = value.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
err := gophercloud.ErrUnexpectedType{}
|
||||||
|
err.Expected = "map[string]interface{}"
|
||||||
|
err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value))
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if value == nil {
|
||||||
|
// Actual null element.
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
url, ok := value.(string)
|
||||||
|
if !ok {
|
||||||
|
err := gophercloud.ErrUnexpectedType{}
|
||||||
|
err.Expected = "string"
|
||||||
|
err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value))
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return url, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty satisifies the IsEmpty method of the Page interface
|
||||||
|
func (current LinkedPageBase) IsEmpty() (bool, error) {
|
||||||
|
if b, ok := current.Body.([]interface{}); ok {
|
||||||
|
return len(b) == 0, nil
|
||||||
|
}
|
||||||
|
err := gophercloud.ErrUnexpectedType{}
|
||||||
|
err.Expected = "[]interface{}"
|
||||||
|
err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body))
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBody returns the linked page's body. This method is needed to satisfy the
|
||||||
|
// Page interface.
|
||||||
|
func (current LinkedPageBase) GetBody() interface{} {
|
||||||
|
return current.Body
|
||||||
|
}
|
58
vendor/github.com/gophercloud/gophercloud/pagination/marker.go
generated
vendored
Normal file
58
vendor/github.com/gophercloud/gophercloud/pagination/marker.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package pagination
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MarkerPage is a stricter Page interface that describes additional functionality required for use with NewMarkerPager.
|
||||||
|
// For convenience, embed the MarkedPageBase struct.
|
||||||
|
type MarkerPage interface {
|
||||||
|
Page
|
||||||
|
|
||||||
|
// LastMarker returns the last "marker" value on this page.
|
||||||
|
LastMarker() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkerPageBase is a page in a collection that's paginated by "limit" and "marker" query parameters.
|
||||||
|
type MarkerPageBase struct {
|
||||||
|
PageResult
|
||||||
|
|
||||||
|
// Owner is a reference to the embedding struct.
|
||||||
|
Owner MarkerPage
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextPageURL generates the URL for the page of results after this one.
|
||||||
|
func (current MarkerPageBase) NextPageURL() (string, error) {
|
||||||
|
currentURL := current.URL
|
||||||
|
|
||||||
|
mark, err := current.Owner.LastMarker()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
q := currentURL.Query()
|
||||||
|
q.Set("marker", mark)
|
||||||
|
currentURL.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
return currentURL.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty satisifies the IsEmpty method of the Page interface
|
||||||
|
func (current MarkerPageBase) IsEmpty() (bool, error) {
|
||||||
|
if b, ok := current.Body.([]interface{}); ok {
|
||||||
|
return len(b) == 0, nil
|
||||||
|
}
|
||||||
|
err := gophercloud.ErrUnexpectedType{}
|
||||||
|
err.Expected = "[]interface{}"
|
||||||
|
err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body))
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBody returns the linked page's body. This method is needed to satisfy the
|
||||||
|
// Page interface.
|
||||||
|
func (current MarkerPageBase) GetBody() interface{} {
|
||||||
|
return current.Body
|
||||||
|
}
|
251
vendor/github.com/gophercloud/gophercloud/pagination/pager.go
generated
vendored
Normal file
251
vendor/github.com/gophercloud/gophercloud/pagination/pager.go
generated
vendored
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
package pagination
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrPageNotAvailable is returned from a Pager when a next or previous page is requested, but does not exist.
|
||||||
|
ErrPageNotAvailable = errors.New("The requested page does not exist.")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Page must be satisfied by the result type of any resource collection.
|
||||||
|
// It allows clients to interact with the resource uniformly, regardless of whether or not or how it's paginated.
|
||||||
|
// Generally, rather than implementing this interface directly, implementors should embed one of the concrete PageBase structs,
|
||||||
|
// instead.
|
||||||
|
// Depending on the pagination strategy of a particular resource, there may be an additional subinterface that the result type
|
||||||
|
// will need to implement.
|
||||||
|
type Page interface {
|
||||||
|
// NextPageURL generates the URL for the page of data that follows this collection.
|
||||||
|
// Return "" if no such page exists.
|
||||||
|
NextPageURL() (string, error)
|
||||||
|
|
||||||
|
// IsEmpty returns true if this Page has no items in it.
|
||||||
|
IsEmpty() (bool, error)
|
||||||
|
|
||||||
|
// GetBody returns the Page Body. This is used in the `AllPages` method.
|
||||||
|
GetBody() interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pager knows how to advance through a specific resource collection, one page at a time.
|
||||||
|
type Pager struct {
|
||||||
|
client *gophercloud.ServiceClient
|
||||||
|
|
||||||
|
initialURL string
|
||||||
|
|
||||||
|
createPage func(r PageResult) Page
|
||||||
|
|
||||||
|
firstPage Page
|
||||||
|
|
||||||
|
Err error
|
||||||
|
|
||||||
|
// Headers supplies additional HTTP headers to populate on each paged request.
|
||||||
|
Headers map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPager constructs a manually-configured pager.
|
||||||
|
// Supply the URL for the first page, a function that requests a specific page given a URL, and a function that counts a page.
|
||||||
|
func NewPager(client *gophercloud.ServiceClient, initialURL string, createPage func(r PageResult) Page) Pager {
|
||||||
|
return Pager{
|
||||||
|
client: client,
|
||||||
|
initialURL: initialURL,
|
||||||
|
createPage: createPage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPageCreator returns a new Pager that substitutes a different page creation function. This is
|
||||||
|
// useful for overriding List functions in delegation.
|
||||||
|
func (p Pager) WithPageCreator(createPage func(r PageResult) Page) Pager {
|
||||||
|
return Pager{
|
||||||
|
client: p.client,
|
||||||
|
initialURL: p.initialURL,
|
||||||
|
createPage: createPage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Pager) fetchNextPage(url string) (Page, error) {
|
||||||
|
resp, err := Request(p.client, p.Headers, url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
remembered, err := PageResultFrom(resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.createPage(remembered), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EachPage iterates over each page returned by a Pager, yielding one at a time to a handler function.
|
||||||
|
// Return "false" from the handler to prematurely stop iterating.
|
||||||
|
func (p Pager) EachPage(handler func(Page) (bool, error)) error {
|
||||||
|
if p.Err != nil {
|
||||||
|
return p.Err
|
||||||
|
}
|
||||||
|
currentURL := p.initialURL
|
||||||
|
for {
|
||||||
|
var currentPage Page
|
||||||
|
|
||||||
|
// if first page has already been fetched, no need to fetch it again
|
||||||
|
if p.firstPage != nil {
|
||||||
|
currentPage = p.firstPage
|
||||||
|
p.firstPage = nil
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
currentPage, err = p.fetchNextPage(currentURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
empty, err := currentPage.IsEmpty()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if empty {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := handler(currentPage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
currentURL, err = currentPage.NextPageURL()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if currentURL == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllPages returns all the pages from a `List` operation in a single page,
|
||||||
|
// allowing the user to retrieve all the pages at once.
|
||||||
|
func (p Pager) AllPages() (Page, error) {
|
||||||
|
// pagesSlice holds all the pages until they get converted into as Page Body.
|
||||||
|
var pagesSlice []interface{}
|
||||||
|
// body will contain the final concatenated Page body.
|
||||||
|
var body reflect.Value
|
||||||
|
|
||||||
|
// Grab a first page to ascertain the page body type.
|
||||||
|
firstPage, err := p.fetchNextPage(p.initialURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Store the page type so we can use reflection to create a new mega-page of
|
||||||
|
// that type.
|
||||||
|
pageType := reflect.TypeOf(firstPage)
|
||||||
|
|
||||||
|
// if it's a single page, just return the firstPage (first page)
|
||||||
|
if _, found := pageType.FieldByName("SinglePageBase"); found {
|
||||||
|
return firstPage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the first page to avoid getting it twice
|
||||||
|
p.firstPage = firstPage
|
||||||
|
|
||||||
|
// Switch on the page body type. Recognized types are `map[string]interface{}`,
|
||||||
|
// `[]byte`, and `[]interface{}`.
|
||||||
|
switch pb := firstPage.GetBody().(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
// key is the map key for the page body if the body type is `map[string]interface{}`.
|
||||||
|
var key string
|
||||||
|
// Iterate over the pages to concatenate the bodies.
|
||||||
|
err = p.EachPage(func(page Page) (bool, error) {
|
||||||
|
b := page.GetBody().(map[string]interface{})
|
||||||
|
for k, v := range b {
|
||||||
|
// If it's a linked page, we don't want the `links`, we want the other one.
|
||||||
|
if !strings.HasSuffix(k, "links") {
|
||||||
|
// check the field's type. we only want []interface{} (which is really []map[string]interface{})
|
||||||
|
switch vt := v.(type) {
|
||||||
|
case []interface{}:
|
||||||
|
key = k
|
||||||
|
pagesSlice = append(pagesSlice, vt...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Set body to value of type `map[string]interface{}`
|
||||||
|
body = reflect.MakeMap(reflect.MapOf(reflect.TypeOf(key), reflect.TypeOf(pagesSlice)))
|
||||||
|
body.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(pagesSlice))
|
||||||
|
case []byte:
|
||||||
|
// Iterate over the pages to concatenate the bodies.
|
||||||
|
err = p.EachPage(func(page Page) (bool, error) {
|
||||||
|
b := page.GetBody().([]byte)
|
||||||
|
pagesSlice = append(pagesSlice, b)
|
||||||
|
// seperate pages with a comma
|
||||||
|
pagesSlice = append(pagesSlice, []byte{10})
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(pagesSlice) > 0 {
|
||||||
|
// Remove the trailing comma.
|
||||||
|
pagesSlice = pagesSlice[:len(pagesSlice)-1]
|
||||||
|
}
|
||||||
|
var b []byte
|
||||||
|
// Combine the slice of slices in to a single slice.
|
||||||
|
for _, slice := range pagesSlice {
|
||||||
|
b = append(b, slice.([]byte)...)
|
||||||
|
}
|
||||||
|
// Set body to value of type `bytes`.
|
||||||
|
body = reflect.New(reflect.TypeOf(b)).Elem()
|
||||||
|
body.SetBytes(b)
|
||||||
|
case []interface{}:
|
||||||
|
// Iterate over the pages to concatenate the bodies.
|
||||||
|
err = p.EachPage(func(page Page) (bool, error) {
|
||||||
|
b := page.GetBody().([]interface{})
|
||||||
|
pagesSlice = append(pagesSlice, b...)
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Set body to value of type `[]interface{}`
|
||||||
|
body = reflect.MakeSlice(reflect.TypeOf(pagesSlice), len(pagesSlice), len(pagesSlice))
|
||||||
|
for i, s := range pagesSlice {
|
||||||
|
body.Index(i).Set(reflect.ValueOf(s))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
err := gophercloud.ErrUnexpectedType{}
|
||||||
|
err.Expected = "map[string]interface{}/[]byte/[]interface{}"
|
||||||
|
err.Actual = fmt.Sprintf("%T", pb)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each `Extract*` function is expecting a specific type of page coming back,
|
||||||
|
// otherwise the type assertion in those functions will fail. pageType is needed
|
||||||
|
// to create a type in this method that has the same type that the `Extract*`
|
||||||
|
// function is expecting and set the Body of that object to the concatenated
|
||||||
|
// pages.
|
||||||
|
page := reflect.New(pageType)
|
||||||
|
// Set the page body to be the concatenated pages.
|
||||||
|
page.Elem().FieldByName("Body").Set(body)
|
||||||
|
// Set any additional headers that were pass along. The `objectstorage` pacakge,
|
||||||
|
// for example, passes a Content-Type header.
|
||||||
|
h := make(http.Header)
|
||||||
|
for k, v := range p.Headers {
|
||||||
|
h.Add(k, v)
|
||||||
|
}
|
||||||
|
page.Elem().FieldByName("Header").Set(reflect.ValueOf(h))
|
||||||
|
// Type assert the page to a Page interface so that the type assertion in the
|
||||||
|
// `Extract*` methods will work.
|
||||||
|
return page.Elem().Interface().(Page), err
|
||||||
|
}
|
4
vendor/github.com/gophercloud/gophercloud/pagination/pkg.go
generated
vendored
Normal file
4
vendor/github.com/gophercloud/gophercloud/pagination/pkg.go
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
/*
|
||||||
|
Package pagination contains utilities and convenience structs that implement common pagination idioms within OpenStack APIs.
|
||||||
|
*/
|
||||||
|
package pagination
|
33
vendor/github.com/gophercloud/gophercloud/pagination/single.go
generated
vendored
Normal file
33
vendor/github.com/gophercloud/gophercloud/pagination/single.go
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package pagination
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SinglePageBase may be embedded in a Page that contains all of the results from an operation at once.
|
||||||
|
type SinglePageBase PageResult
|
||||||
|
|
||||||
|
// NextPageURL always returns "" to indicate that there are no more pages to return.
|
||||||
|
func (current SinglePageBase) NextPageURL() (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty satisifies the IsEmpty method of the Page interface
|
||||||
|
func (current SinglePageBase) IsEmpty() (bool, error) {
|
||||||
|
if b, ok := current.Body.([]interface{}); ok {
|
||||||
|
return len(b) == 0, nil
|
||||||
|
}
|
||||||
|
err := gophercloud.ErrUnexpectedType{}
|
||||||
|
err.Expected = "[]interface{}"
|
||||||
|
err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body))
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBody returns the single page's body. This method is needed to satisfy the
|
||||||
|
// Page interface.
|
||||||
|
func (current SinglePageBase) GetBody() interface{} {
|
||||||
|
return current.Body
|
||||||
|
}
|
475
vendor/github.com/gophercloud/gophercloud/params.go
generated
vendored
Normal file
475
vendor/github.com/gophercloud/gophercloud/params.go
generated
vendored
Normal file
|
@ -0,0 +1,475 @@
|
||||||
|
package gophercloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
BuildRequestBody builds a map[string]interface from the given `struct`. If
|
||||||
|
parent is not an empty string, the final map[string]interface returned will
|
||||||
|
encapsulate the built one. For example:
|
||||||
|
|
||||||
|
disk := 1
|
||||||
|
createOpts := flavors.CreateOpts{
|
||||||
|
ID: "1",
|
||||||
|
Name: "m1.tiny",
|
||||||
|
Disk: &disk,
|
||||||
|
RAM: 512,
|
||||||
|
VCPUs: 1,
|
||||||
|
RxTxFactor: 1.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := gophercloud.BuildRequestBody(createOpts, "flavor")
|
||||||
|
|
||||||
|
The above example can be run as-is, however it is recommended to look at how
|
||||||
|
BuildRequestBody is used within Gophercloud to more fully understand how it
|
||||||
|
fits within the request process as a whole rather than use it directly as shown
|
||||||
|
above.
|
||||||
|
*/
|
||||||
|
func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, error) {
|
||||||
|
optsValue := reflect.ValueOf(opts)
|
||||||
|
if optsValue.Kind() == reflect.Ptr {
|
||||||
|
optsValue = optsValue.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
optsType := reflect.TypeOf(opts)
|
||||||
|
if optsType.Kind() == reflect.Ptr {
|
||||||
|
optsType = optsType.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
optsMap := make(map[string]interface{})
|
||||||
|
if optsValue.Kind() == reflect.Struct {
|
||||||
|
//fmt.Printf("optsValue.Kind() is a reflect.Struct: %+v\n", optsValue.Kind())
|
||||||
|
for i := 0; i < optsValue.NumField(); i++ {
|
||||||
|
v := optsValue.Field(i)
|
||||||
|
f := optsType.Field(i)
|
||||||
|
|
||||||
|
if f.Name != strings.Title(f.Name) {
|
||||||
|
//fmt.Printf("Skipping field: %s...\n", f.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
//fmt.Printf("Starting on field: %s...\n", f.Name)
|
||||||
|
|
||||||
|
zero := isZero(v)
|
||||||
|
//fmt.Printf("v is zero?: %v\n", zero)
|
||||||
|
|
||||||
|
// if the field has a required tag that's set to "true"
|
||||||
|
if requiredTag := f.Tag.Get("required"); requiredTag == "true" {
|
||||||
|
//fmt.Printf("Checking required field [%s]:\n\tv: %+v\n\tisZero:%v\n", f.Name, v.Interface(), zero)
|
||||||
|
// if the field's value is zero, return a missing-argument error
|
||||||
|
if zero {
|
||||||
|
// if the field has a 'required' tag, it can't have a zero-value
|
||||||
|
err := ErrMissingInput{}
|
||||||
|
err.Argument = f.Name
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if xorTag := f.Tag.Get("xor"); xorTag != "" {
|
||||||
|
//fmt.Printf("Checking `xor` tag for field [%s] with value %+v:\n\txorTag: %s\n", f.Name, v, xorTag)
|
||||||
|
xorField := optsValue.FieldByName(xorTag)
|
||||||
|
var xorFieldIsZero bool
|
||||||
|
if reflect.ValueOf(xorField.Interface()) == reflect.Zero(xorField.Type()) {
|
||||||
|
xorFieldIsZero = true
|
||||||
|
} else {
|
||||||
|
if xorField.Kind() == reflect.Ptr {
|
||||||
|
xorField = xorField.Elem()
|
||||||
|
}
|
||||||
|
xorFieldIsZero = isZero(xorField)
|
||||||
|
}
|
||||||
|
if !(zero != xorFieldIsZero) {
|
||||||
|
err := ErrMissingInput{}
|
||||||
|
err.Argument = fmt.Sprintf("%s/%s", f.Name, xorTag)
|
||||||
|
err.Info = fmt.Sprintf("Exactly one of %s and %s must be provided", f.Name, xorTag)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if orTag := f.Tag.Get("or"); orTag != "" {
|
||||||
|
//fmt.Printf("Checking `or` tag for field with:\n\tname: %+v\n\torTag:%s\n", f.Name, orTag)
|
||||||
|
//fmt.Printf("field is zero?: %v\n", zero)
|
||||||
|
if zero {
|
||||||
|
orField := optsValue.FieldByName(orTag)
|
||||||
|
var orFieldIsZero bool
|
||||||
|
if reflect.ValueOf(orField.Interface()) == reflect.Zero(orField.Type()) {
|
||||||
|
orFieldIsZero = true
|
||||||
|
} else {
|
||||||
|
if orField.Kind() == reflect.Ptr {
|
||||||
|
orField = orField.Elem()
|
||||||
|
}
|
||||||
|
orFieldIsZero = isZero(orField)
|
||||||
|
}
|
||||||
|
if orFieldIsZero {
|
||||||
|
err := ErrMissingInput{}
|
||||||
|
err.Argument = fmt.Sprintf("%s/%s", f.Name, orTag)
|
||||||
|
err.Info = fmt.Sprintf("At least one of %s and %s must be provided", f.Name, orTag)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonTag := f.Tag.Get("json")
|
||||||
|
if jsonTag == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Kind() == reflect.Struct || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) {
|
||||||
|
if zero {
|
||||||
|
//fmt.Printf("value before change: %+v\n", optsValue.Field(i))
|
||||||
|
if jsonTag != "" {
|
||||||
|
jsonTagPieces := strings.Split(jsonTag, ",")
|
||||||
|
if len(jsonTagPieces) > 1 && jsonTagPieces[1] == "omitempty" {
|
||||||
|
if v.CanSet() {
|
||||||
|
if !v.IsNil() {
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v.Set(reflect.Zero(v.Type()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//fmt.Printf("value after change: %+v\n", optsValue.Field(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
//fmt.Printf("Calling BuildRequestBody with:\n\tv: %+v\n\tf.Name:%s\n", v.Interface(), f.Name)
|
||||||
|
_, err := BuildRequestBody(v.Interface(), f.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//fmt.Printf("opts: %+v \n", opts)
|
||||||
|
|
||||||
|
b, err := json.Marshal(opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//fmt.Printf("string(b): %s\n", string(b))
|
||||||
|
|
||||||
|
err = json.Unmarshal(b, &optsMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//fmt.Printf("optsMap: %+v\n", optsMap)
|
||||||
|
|
||||||
|
if parent != "" {
|
||||||
|
optsMap = map[string]interface{}{parent: optsMap}
|
||||||
|
}
|
||||||
|
//fmt.Printf("optsMap after parent added: %+v\n", optsMap)
|
||||||
|
return optsMap, nil
|
||||||
|
}
|
||||||
|
// Return an error if the underlying type of 'opts' isn't a struct.
|
||||||
|
return nil, fmt.Errorf("Options type is not a struct.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnabledState is a convenience type, mostly used in Create and Update
|
||||||
|
// operations. Because the zero value of a bool is FALSE, we need to use a
|
||||||
|
// pointer instead to indicate zero-ness.
|
||||||
|
type EnabledState *bool
|
||||||
|
|
||||||
|
// Convenience vars for EnabledState values.
|
||||||
|
var (
|
||||||
|
iTrue = true
|
||||||
|
iFalse = false
|
||||||
|
|
||||||
|
Enabled EnabledState = &iTrue
|
||||||
|
Disabled EnabledState = &iFalse
|
||||||
|
)
|
||||||
|
|
||||||
|
// IPVersion is a type for the possible IP address versions. Valid instances
|
||||||
|
// are IPv4 and IPv6
|
||||||
|
type IPVersion int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// IPv4 is used for IP version 4 addresses
|
||||||
|
IPv4 IPVersion = 4
|
||||||
|
// IPv6 is used for IP version 6 addresses
|
||||||
|
IPv6 IPVersion = 6
|
||||||
|
)
|
||||||
|
|
||||||
|
// IntToPointer is a function for converting integers into integer pointers.
|
||||||
|
// This is useful when passing in options to operations.
|
||||||
|
func IntToPointer(i int) *int {
|
||||||
|
return &i
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
MaybeString is an internal function to be used by request methods in individual
|
||||||
|
resource packages.
|
||||||
|
|
||||||
|
It takes a string that might be a zero value and returns either a pointer to its
|
||||||
|
address or nil. This is useful for allowing users to conveniently omit values
|
||||||
|
from an options struct by leaving them zeroed, but still pass nil to the JSON
|
||||||
|
serializer so they'll be omitted from the request body.
|
||||||
|
*/
|
||||||
|
func MaybeString(original string) *string {
|
||||||
|
if original != "" {
|
||||||
|
return &original
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
MaybeInt is an internal function to be used by request methods in individual
|
||||||
|
resource packages.
|
||||||
|
|
||||||
|
Like MaybeString, it accepts an int that may or may not be a zero value, and
|
||||||
|
returns either a pointer to its address or nil. It's intended to hint that the
|
||||||
|
JSON serializer should omit its field.
|
||||||
|
*/
|
||||||
|
func MaybeInt(original int) *int {
|
||||||
|
if original != 0 {
|
||||||
|
return &original
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func isUnderlyingStructZero(v reflect.Value) bool {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
return isUnderlyingStructZero(v.Elem())
|
||||||
|
default:
|
||||||
|
return isZero(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
var t time.Time
|
||||||
|
|
||||||
|
func isZero(v reflect.Value) bool {
|
||||||
|
//fmt.Printf("\n\nchecking isZero for value: %+v\n", v)
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
if v.IsNil() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
case reflect.Func, reflect.Map, reflect.Slice:
|
||||||
|
return v.IsNil()
|
||||||
|
case reflect.Array:
|
||||||
|
z := true
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
z = z && isZero(v.Index(i))
|
||||||
|
}
|
||||||
|
return z
|
||||||
|
case reflect.Struct:
|
||||||
|
if v.Type() == reflect.TypeOf(t) {
|
||||||
|
if v.Interface().(time.Time).IsZero() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
z := true
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
z = z && isZero(v.Field(i))
|
||||||
|
}
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
// Compare other types directly:
|
||||||
|
z := reflect.Zero(v.Type())
|
||||||
|
//fmt.Printf("zero type for value: %+v\n\n\n", z)
|
||||||
|
return v.Interface() == z.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
BuildQueryString is an internal function to be used by request methods in
|
||||||
|
individual resource packages.
|
||||||
|
|
||||||
|
It accepts a tagged structure and expands it into a URL struct. Field names are
|
||||||
|
converted into query parameters based on a "q" tag. For example:
|
||||||
|
|
||||||
|
type struct Something {
|
||||||
|
Bar string `q:"x_bar"`
|
||||||
|
Baz int `q:"lorem_ipsum"`
|
||||||
|
}
|
||||||
|
|
||||||
|
instance := Something{
|
||||||
|
Bar: "AAA",
|
||||||
|
Baz: "BBB",
|
||||||
|
}
|
||||||
|
|
||||||
|
will be converted into "?x_bar=AAA&lorem_ipsum=BBB".
|
||||||
|
|
||||||
|
The struct's fields may be strings, integers, or boolean values. Fields left at
|
||||||
|
their type's zero value will be omitted from the query.
|
||||||
|
*/
|
||||||
|
func BuildQueryString(opts interface{}) (*url.URL, error) {
|
||||||
|
optsValue := reflect.ValueOf(opts)
|
||||||
|
if optsValue.Kind() == reflect.Ptr {
|
||||||
|
optsValue = optsValue.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
optsType := reflect.TypeOf(opts)
|
||||||
|
if optsType.Kind() == reflect.Ptr {
|
||||||
|
optsType = optsType.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
params := url.Values{}
|
||||||
|
|
||||||
|
if optsValue.Kind() == reflect.Struct {
|
||||||
|
for i := 0; i < optsValue.NumField(); i++ {
|
||||||
|
v := optsValue.Field(i)
|
||||||
|
f := optsType.Field(i)
|
||||||
|
qTag := f.Tag.Get("q")
|
||||||
|
|
||||||
|
// if the field has a 'q' tag, it goes in the query string
|
||||||
|
if qTag != "" {
|
||||||
|
tags := strings.Split(qTag, ",")
|
||||||
|
|
||||||
|
// if the field is set, add it to the slice of query pieces
|
||||||
|
if !isZero(v) {
|
||||||
|
loop:
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
v = v.Elem()
|
||||||
|
goto loop
|
||||||
|
case reflect.String:
|
||||||
|
params.Add(tags[0], v.String())
|
||||||
|
case reflect.Int:
|
||||||
|
params.Add(tags[0], strconv.FormatInt(v.Int(), 10))
|
||||||
|
case reflect.Bool:
|
||||||
|
params.Add(tags[0], strconv.FormatBool(v.Bool()))
|
||||||
|
case reflect.Slice:
|
||||||
|
switch v.Type().Elem() {
|
||||||
|
case reflect.TypeOf(0):
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
params.Add(tags[0], strconv.FormatInt(v.Index(i).Int(), 10))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
params.Add(tags[0], v.Index(i).String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
if v.Type().Key().Kind() == reflect.String && v.Type().Elem().Kind() == reflect.String {
|
||||||
|
var s []string
|
||||||
|
for _, k := range v.MapKeys() {
|
||||||
|
value := v.MapIndex(k).String()
|
||||||
|
s = append(s, fmt.Sprintf("'%s':'%s'", k.String(), value))
|
||||||
|
}
|
||||||
|
params.Add(tags[0], fmt.Sprintf("{%s}", strings.Join(s, ", ")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if the field has a 'required' tag, it can't have a zero-value
|
||||||
|
if requiredTag := f.Tag.Get("required"); requiredTag == "true" {
|
||||||
|
return &url.URL{}, fmt.Errorf("Required query parameter [%s] not set.", f.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &url.URL{RawQuery: params.Encode()}, nil
|
||||||
|
}
|
||||||
|
// Return an error if the underlying type of 'opts' isn't a struct.
|
||||||
|
return nil, fmt.Errorf("Options type is not a struct.")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
BuildHeaders is an internal function to be used by request methods in
|
||||||
|
individual resource packages.
|
||||||
|
|
||||||
|
It accepts an arbitrary tagged structure and produces a string map that's
|
||||||
|
suitable for use as the HTTP headers of an outgoing request. Field names are
|
||||||
|
mapped to header names based in "h" tags.
|
||||||
|
|
||||||
|
type struct Something {
|
||||||
|
Bar string `h:"x_bar"`
|
||||||
|
Baz int `h:"lorem_ipsum"`
|
||||||
|
}
|
||||||
|
|
||||||
|
instance := Something{
|
||||||
|
Bar: "AAA",
|
||||||
|
Baz: "BBB",
|
||||||
|
}
|
||||||
|
|
||||||
|
will be converted into:
|
||||||
|
|
||||||
|
map[string]string{
|
||||||
|
"x_bar": "AAA",
|
||||||
|
"lorem_ipsum": "BBB",
|
||||||
|
}
|
||||||
|
|
||||||
|
Untagged fields and fields left at their zero values are skipped. Integers,
|
||||||
|
booleans and string values are supported.
|
||||||
|
*/
|
||||||
|
func BuildHeaders(opts interface{}) (map[string]string, error) {
|
||||||
|
optsValue := reflect.ValueOf(opts)
|
||||||
|
if optsValue.Kind() == reflect.Ptr {
|
||||||
|
optsValue = optsValue.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
optsType := reflect.TypeOf(opts)
|
||||||
|
if optsType.Kind() == reflect.Ptr {
|
||||||
|
optsType = optsType.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
optsMap := make(map[string]string)
|
||||||
|
if optsValue.Kind() == reflect.Struct {
|
||||||
|
for i := 0; i < optsValue.NumField(); i++ {
|
||||||
|
v := optsValue.Field(i)
|
||||||
|
f := optsType.Field(i)
|
||||||
|
hTag := f.Tag.Get("h")
|
||||||
|
|
||||||
|
// if the field has a 'h' tag, it goes in the header
|
||||||
|
if hTag != "" {
|
||||||
|
tags := strings.Split(hTag, ",")
|
||||||
|
|
||||||
|
// if the field is set, add it to the slice of query pieces
|
||||||
|
if !isZero(v) {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
optsMap[tags[0]] = v.String()
|
||||||
|
case reflect.Int:
|
||||||
|
optsMap[tags[0]] = strconv.FormatInt(v.Int(), 10)
|
||||||
|
case reflect.Bool:
|
||||||
|
optsMap[tags[0]] = strconv.FormatBool(v.Bool())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if the field has a 'required' tag, it can't have a zero-value
|
||||||
|
if requiredTag := f.Tag.Get("required"); requiredTag == "true" {
|
||||||
|
return optsMap, fmt.Errorf("Required header [%s] not set.", f.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return optsMap, nil
|
||||||
|
}
|
||||||
|
// Return an error if the underlying type of 'opts' isn't a struct.
|
||||||
|
return optsMap, fmt.Errorf("Options type is not a struct.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDSliceToQueryString takes a slice of elements and converts them into a query
|
||||||
|
// string. For example, if name=foo and slice=[]int{20, 40, 60}, then the
|
||||||
|
// result would be `?name=20&name=40&name=60'
|
||||||
|
func IDSliceToQueryString(name string, ids []int) string {
|
||||||
|
str := ""
|
||||||
|
for k, v := range ids {
|
||||||
|
if k == 0 {
|
||||||
|
str += "?"
|
||||||
|
} else {
|
||||||
|
str += "&"
|
||||||
|
}
|
||||||
|
str += fmt.Sprintf("%s=%s", name, strconv.Itoa(v))
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntWithinRange returns TRUE if an integer falls within a defined range, and
|
||||||
|
// FALSE if not.
|
||||||
|
func IntWithinRange(val, min, max int) bool {
|
||||||
|
return val > min && val < max
|
||||||
|
}
|
391
vendor/github.com/gophercloud/gophercloud/provider_client.go
generated
vendored
Normal file
391
vendor/github.com/gophercloud/gophercloud/provider_client.go
generated
vendored
Normal file
|
@ -0,0 +1,391 @@
|
||||||
|
package gophercloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultUserAgent is the default User-Agent string set in the request header.
|
||||||
|
const DefaultUserAgent = "gophercloud/2.0.0"
|
||||||
|
|
||||||
|
// UserAgent represents a User-Agent header.
|
||||||
|
type UserAgent struct {
|
||||||
|
// prepend is the slice of User-Agent strings to prepend to DefaultUserAgent.
|
||||||
|
// All the strings to prepend are accumulated and prepended in the Join method.
|
||||||
|
prepend []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepend prepends a user-defined string to the default User-Agent string. Users
|
||||||
|
// may pass in one or more strings to prepend.
|
||||||
|
func (ua *UserAgent) Prepend(s ...string) {
|
||||||
|
ua.prepend = append(s, ua.prepend...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join concatenates all the user-defined User-Agend strings with the default
|
||||||
|
// Gophercloud User-Agent string.
|
||||||
|
func (ua *UserAgent) Join() string {
|
||||||
|
uaSlice := append(ua.prepend, DefaultUserAgent)
|
||||||
|
return strings.Join(uaSlice, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProviderClient stores details that are required to interact with any
|
||||||
|
// services within a specific provider's API.
|
||||||
|
//
|
||||||
|
// Generally, you acquire a ProviderClient by calling the NewClient method in
|
||||||
|
// the appropriate provider's child package, providing whatever authentication
|
||||||
|
// credentials are required.
|
||||||
|
type ProviderClient struct {
|
||||||
|
// IdentityBase is the base URL used for a particular provider's identity
|
||||||
|
// service - it will be used when issuing authenticatation requests. It
|
||||||
|
// should point to the root resource of the identity service, not a specific
|
||||||
|
// identity version.
|
||||||
|
IdentityBase string
|
||||||
|
|
||||||
|
// IdentityEndpoint is the identity endpoint. This may be a specific version
|
||||||
|
// of the identity service. If this is the case, this endpoint is used rather
|
||||||
|
// than querying versions first.
|
||||||
|
IdentityEndpoint string
|
||||||
|
|
||||||
|
// TokenID is the ID of the most recently issued valid token.
|
||||||
|
// NOTE: Aside from within a custom ReauthFunc, this field shouldn't be set by an application.
|
||||||
|
// To safely read or write this value, call `Token` or `SetToken`, respectively
|
||||||
|
TokenID string
|
||||||
|
|
||||||
|
// EndpointLocator describes how this provider discovers the endpoints for
|
||||||
|
// its constituent services.
|
||||||
|
EndpointLocator EndpointLocator
|
||||||
|
|
||||||
|
// HTTPClient allows users to interject arbitrary http, https, or other transit behaviors.
|
||||||
|
HTTPClient http.Client
|
||||||
|
|
||||||
|
// UserAgent represents the User-Agent header in the HTTP request.
|
||||||
|
UserAgent UserAgent
|
||||||
|
|
||||||
|
// ReauthFunc is the function used to re-authenticate the user if the request
|
||||||
|
// fails with a 401 HTTP response code. This a needed because there may be multiple
|
||||||
|
// authentication functions for different Identity service versions.
|
||||||
|
ReauthFunc func() error
|
||||||
|
|
||||||
|
mut *sync.RWMutex
|
||||||
|
|
||||||
|
reauthmut *reauthlock
|
||||||
|
}
|
||||||
|
|
||||||
|
type reauthlock struct {
|
||||||
|
sync.RWMutex
|
||||||
|
reauthing bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthenticatedHeaders returns a map of HTTP headers that are common for all
|
||||||
|
// authenticated service requests.
|
||||||
|
func (client *ProviderClient) AuthenticatedHeaders() (m map[string]string) {
|
||||||
|
if client.reauthmut != nil {
|
||||||
|
client.reauthmut.RLock()
|
||||||
|
if client.reauthmut.reauthing {
|
||||||
|
client.reauthmut.RUnlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client.reauthmut.RUnlock()
|
||||||
|
}
|
||||||
|
t := client.Token()
|
||||||
|
if t == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return map[string]string{"X-Auth-Token": t}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseTokenLock creates a mutex that is used to allow safe concurrent access to the auth token.
|
||||||
|
// If the application's ProviderClient is not used concurrently, this doesn't need to be called.
|
||||||
|
func (client *ProviderClient) UseTokenLock() {
|
||||||
|
client.mut = new(sync.RWMutex)
|
||||||
|
client.reauthmut = new(reauthlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token safely reads the value of the auth token from the ProviderClient. Applications should
|
||||||
|
// call this method to access the token instead of the TokenID field
|
||||||
|
func (client *ProviderClient) Token() string {
|
||||||
|
if client.mut != nil {
|
||||||
|
client.mut.RLock()
|
||||||
|
defer client.mut.RUnlock()
|
||||||
|
}
|
||||||
|
return client.TokenID
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetToken safely sets the value of the auth token in the ProviderClient. Applications may
|
||||||
|
// use this method in a custom ReauthFunc
|
||||||
|
func (client *ProviderClient) SetToken(t string) {
|
||||||
|
if client.mut != nil {
|
||||||
|
client.mut.Lock()
|
||||||
|
defer client.mut.Unlock()
|
||||||
|
}
|
||||||
|
client.TokenID = t
|
||||||
|
}
|
||||||
|
|
||||||
|
//Reauthenticate calls client.ReauthFunc in a thread-safe way. If this is
|
||||||
|
//called because of a 401 response, the caller may pass the previous token. In
|
||||||
|
//this case, the reauthentication can be skipped if another thread has already
|
||||||
|
//reauthenticated in the meantime. If no previous token is known, an empty
|
||||||
|
//string should be passed instead to force unconditional reauthentication.
|
||||||
|
func (client *ProviderClient) Reauthenticate(previousToken string) (err error) {
|
||||||
|
if client.ReauthFunc == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if client.mut == nil {
|
||||||
|
return client.ReauthFunc()
|
||||||
|
}
|
||||||
|
client.mut.Lock()
|
||||||
|
defer client.mut.Unlock()
|
||||||
|
|
||||||
|
client.reauthmut.Lock()
|
||||||
|
client.reauthmut.reauthing = true
|
||||||
|
client.reauthmut.Unlock()
|
||||||
|
|
||||||
|
if previousToken == "" || client.TokenID == previousToken {
|
||||||
|
err = client.ReauthFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
client.reauthmut.Lock()
|
||||||
|
client.reauthmut.reauthing = false
|
||||||
|
client.reauthmut.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestOpts customizes the behavior of the provider.Request() method.
|
||||||
|
type RequestOpts struct {
|
||||||
|
// JSONBody, if provided, will be encoded as JSON and used as the body of the HTTP request. The
|
||||||
|
// content type of the request will default to "application/json" unless overridden by MoreHeaders.
|
||||||
|
// It's an error to specify both a JSONBody and a RawBody.
|
||||||
|
JSONBody interface{}
|
||||||
|
// RawBody contains an io.Reader that will be consumed by the request directly. No content-type
|
||||||
|
// will be set unless one is provided explicitly by MoreHeaders.
|
||||||
|
RawBody io.Reader
|
||||||
|
// JSONResponse, if provided, will be populated with the contents of the response body parsed as
|
||||||
|
// JSON.
|
||||||
|
JSONResponse interface{}
|
||||||
|
// OkCodes contains a list of numeric HTTP status codes that should be interpreted as success. If
|
||||||
|
// the response has a different code, an error will be returned.
|
||||||
|
OkCodes []int
|
||||||
|
// MoreHeaders specifies additional HTTP headers to be provide on the request. If a header is
|
||||||
|
// provided with a blank value (""), that header will be *omitted* instead: use this to suppress
|
||||||
|
// the default Accept header or an inferred Content-Type, for example.
|
||||||
|
MoreHeaders map[string]string
|
||||||
|
// ErrorContext specifies the resource error type to return if an error is encountered.
|
||||||
|
// This lets resources override default error messages based on the response status code.
|
||||||
|
ErrorContext error
|
||||||
|
}
|
||||||
|
|
||||||
|
var applicationJSON = "application/json"
|
||||||
|
|
||||||
|
// Request performs an HTTP request using the ProviderClient's current HTTPClient. An authentication
|
||||||
|
// header will automatically be provided.
|
||||||
|
func (client *ProviderClient) Request(method, url string, options *RequestOpts) (*http.Response, error) {
|
||||||
|
var body io.Reader
|
||||||
|
var contentType *string
|
||||||
|
|
||||||
|
// Derive the content body by either encoding an arbitrary object as JSON, or by taking a provided
|
||||||
|
// io.ReadSeeker as-is. Default the content-type to application/json.
|
||||||
|
if options.JSONBody != nil {
|
||||||
|
if options.RawBody != nil {
|
||||||
|
panic("Please provide only one of JSONBody or RawBody to gophercloud.Request().")
|
||||||
|
}
|
||||||
|
|
||||||
|
rendered, err := json.Marshal(options.JSONBody)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
body = bytes.NewReader(rendered)
|
||||||
|
contentType = &applicationJSON
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.RawBody != nil {
|
||||||
|
body = options.RawBody
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the http.Request.
|
||||||
|
req, err := http.NewRequest(method, url, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate the request headers. Apply options.MoreHeaders last, to give the caller the chance to
|
||||||
|
// modify or omit any header.
|
||||||
|
if contentType != nil {
|
||||||
|
req.Header.Set("Content-Type", *contentType)
|
||||||
|
}
|
||||||
|
req.Header.Set("Accept", applicationJSON)
|
||||||
|
|
||||||
|
// Set the User-Agent header
|
||||||
|
req.Header.Set("User-Agent", client.UserAgent.Join())
|
||||||
|
|
||||||
|
if options.MoreHeaders != nil {
|
||||||
|
for k, v := range options.MoreHeaders {
|
||||||
|
if v != "" {
|
||||||
|
req.Header.Set(k, v)
|
||||||
|
} else {
|
||||||
|
req.Header.Del(k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get latest token from client
|
||||||
|
for k, v := range client.AuthenticatedHeaders() {
|
||||||
|
req.Header.Set(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set connection parameter to close the connection immediately when we've got the response
|
||||||
|
req.Close = true
|
||||||
|
|
||||||
|
prereqtok := req.Header.Get("X-Auth-Token")
|
||||||
|
|
||||||
|
// Issue the request.
|
||||||
|
resp, err := client.HTTPClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow default OkCodes if none explicitly set
|
||||||
|
if options.OkCodes == nil {
|
||||||
|
options.OkCodes = defaultOkCodes(method)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the HTTP response status.
|
||||||
|
var ok bool
|
||||||
|
for _, code := range options.OkCodes {
|
||||||
|
if resp.StatusCode == code {
|
||||||
|
ok = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
resp.Body.Close()
|
||||||
|
respErr := ErrUnexpectedResponseCode{
|
||||||
|
URL: url,
|
||||||
|
Method: method,
|
||||||
|
Expected: options.OkCodes,
|
||||||
|
Actual: resp.StatusCode,
|
||||||
|
Body: body,
|
||||||
|
}
|
||||||
|
|
||||||
|
errType := options.ErrorContext
|
||||||
|
switch resp.StatusCode {
|
||||||
|
case http.StatusBadRequest:
|
||||||
|
err = ErrDefault400{respErr}
|
||||||
|
if error400er, ok := errType.(Err400er); ok {
|
||||||
|
err = error400er.Error400(respErr)
|
||||||
|
}
|
||||||
|
case http.StatusUnauthorized:
|
||||||
|
if client.ReauthFunc != nil {
|
||||||
|
err = client.Reauthenticate(prereqtok)
|
||||||
|
if err != nil {
|
||||||
|
e := &ErrUnableToReauthenticate{}
|
||||||
|
e.ErrOriginal = respErr
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
if options.RawBody != nil {
|
||||||
|
if seeker, ok := options.RawBody.(io.Seeker); ok {
|
||||||
|
seeker.Seek(0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// make a new call to request with a nil reauth func in order to avoid infinite loop
|
||||||
|
reauthFunc := client.ReauthFunc
|
||||||
|
client.ReauthFunc = nil
|
||||||
|
resp, err = client.Request(method, url, options)
|
||||||
|
client.ReauthFunc = reauthFunc
|
||||||
|
if err != nil {
|
||||||
|
switch err.(type) {
|
||||||
|
case *ErrUnexpectedResponseCode:
|
||||||
|
e := &ErrErrorAfterReauthentication{}
|
||||||
|
e.ErrOriginal = err.(*ErrUnexpectedResponseCode)
|
||||||
|
return nil, e
|
||||||
|
default:
|
||||||
|
e := &ErrErrorAfterReauthentication{}
|
||||||
|
e.ErrOriginal = err
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
err = ErrDefault401{respErr}
|
||||||
|
if error401er, ok := errType.(Err401er); ok {
|
||||||
|
err = error401er.Error401(respErr)
|
||||||
|
}
|
||||||
|
case http.StatusForbidden:
|
||||||
|
err = ErrDefault403{respErr}
|
||||||
|
if error403er, ok := errType.(Err403er); ok {
|
||||||
|
err = error403er.Error403(respErr)
|
||||||
|
}
|
||||||
|
case http.StatusNotFound:
|
||||||
|
err = ErrDefault404{respErr}
|
||||||
|
if error404er, ok := errType.(Err404er); ok {
|
||||||
|
err = error404er.Error404(respErr)
|
||||||
|
}
|
||||||
|
case http.StatusMethodNotAllowed:
|
||||||
|
err = ErrDefault405{respErr}
|
||||||
|
if error405er, ok := errType.(Err405er); ok {
|
||||||
|
err = error405er.Error405(respErr)
|
||||||
|
}
|
||||||
|
case http.StatusRequestTimeout:
|
||||||
|
err = ErrDefault408{respErr}
|
||||||
|
if error408er, ok := errType.(Err408er); ok {
|
||||||
|
err = error408er.Error408(respErr)
|
||||||
|
}
|
||||||
|
case 429:
|
||||||
|
err = ErrDefault429{respErr}
|
||||||
|
if error429er, ok := errType.(Err429er); ok {
|
||||||
|
err = error429er.Error429(respErr)
|
||||||
|
}
|
||||||
|
case http.StatusInternalServerError:
|
||||||
|
err = ErrDefault500{respErr}
|
||||||
|
if error500er, ok := errType.(Err500er); ok {
|
||||||
|
err = error500er.Error500(respErr)
|
||||||
|
}
|
||||||
|
case http.StatusServiceUnavailable:
|
||||||
|
err = ErrDefault503{respErr}
|
||||||
|
if error503er, ok := errType.(Err503er); ok {
|
||||||
|
err = error503er.Error503(respErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
err = respErr
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the response body as JSON, if requested to do so.
|
||||||
|
if options.JSONResponse != nil {
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(options.JSONResponse); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultOkCodes(method string) []int {
|
||||||
|
switch {
|
||||||
|
case method == "GET":
|
||||||
|
return []int{200}
|
||||||
|
case method == "POST":
|
||||||
|
return []int{201, 202}
|
||||||
|
case method == "PUT":
|
||||||
|
return []int{201, 202}
|
||||||
|
case method == "PATCH":
|
||||||
|
return []int{200, 202, 204}
|
||||||
|
case method == "DELETE":
|
||||||
|
return []int{202, 204}
|
||||||
|
}
|
||||||
|
|
||||||
|
return []int{}
|
||||||
|
}
|
446
vendor/github.com/gophercloud/gophercloud/results.go
generated
vendored
Normal file
446
vendor/github.com/gophercloud/gophercloud/results.go
generated
vendored
Normal file
|
@ -0,0 +1,446 @@
|
||||||
|
package gophercloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Result is an internal type to be used by individual resource packages, but its
|
||||||
|
methods will be available on a wide variety of user-facing embedding types.
|
||||||
|
|
||||||
|
It acts as a base struct that other Result types, returned from request
|
||||||
|
functions, can embed for convenience. All Results capture basic information
|
||||||
|
from the HTTP transaction that was performed, including the response body,
|
||||||
|
HTTP headers, and any errors that happened.
|
||||||
|
|
||||||
|
Generally, each Result type will have an Extract method that can be used to
|
||||||
|
further interpret the result's payload in a specific context. Extensions or
|
||||||
|
providers can then provide additional extraction functions to pull out
|
||||||
|
provider- or extension-specific information as well.
|
||||||
|
*/
|
||||||
|
type Result struct {
|
||||||
|
// Body is the payload of the HTTP response from the server. In most cases,
|
||||||
|
// this will be the deserialized JSON structure.
|
||||||
|
Body interface{}
|
||||||
|
|
||||||
|
// Header contains the HTTP header structure from the original response.
|
||||||
|
Header http.Header
|
||||||
|
|
||||||
|
// Err is an error that occurred during the operation. It's deferred until
|
||||||
|
// extraction to make it easier to chain the Extract call.
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractInto allows users to provide an object into which `Extract` will extract
|
||||||
|
// the `Result.Body`. This would be useful for OpenStack providers that have
|
||||||
|
// different fields in the response object than OpenStack proper.
|
||||||
|
func (r Result) ExtractInto(to interface{}) error {
|
||||||
|
if r.Err != nil {
|
||||||
|
return r.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
if reader, ok := r.Body.(io.Reader); ok {
|
||||||
|
if readCloser, ok := reader.(io.Closer); ok {
|
||||||
|
defer readCloser.Close()
|
||||||
|
}
|
||||||
|
return json.NewDecoder(reader).Decode(to)
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(b, to)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Result) extractIntoPtr(to interface{}, label string) error {
|
||||||
|
if label == "" {
|
||||||
|
return r.ExtractInto(&to)
|
||||||
|
}
|
||||||
|
|
||||||
|
var m map[string]interface{}
|
||||||
|
err := r.ExtractInto(&m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(m[label])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
toValue := reflect.ValueOf(to)
|
||||||
|
if toValue.Kind() == reflect.Ptr {
|
||||||
|
toValue = toValue.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch toValue.Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
typeOfV := toValue.Type().Elem()
|
||||||
|
if typeOfV.Kind() == reflect.Struct {
|
||||||
|
if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous {
|
||||||
|
newSlice := reflect.MakeSlice(reflect.SliceOf(typeOfV), 0, 0)
|
||||||
|
|
||||||
|
for _, v := range m[label].([]interface{}) {
|
||||||
|
// For each iteration of the slice, we create a new struct.
|
||||||
|
// This is to work around a bug where elements of a slice
|
||||||
|
// are reused and not overwritten when the same copy of the
|
||||||
|
// struct is used:
|
||||||
|
//
|
||||||
|
// https://github.com/golang/go/issues/21092
|
||||||
|
// https://github.com/golang/go/issues/24155
|
||||||
|
// https://play.golang.org/p/NHo3ywlPZli
|
||||||
|
newType := reflect.New(typeOfV).Elem()
|
||||||
|
|
||||||
|
b, err := json.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is needed for structs with an UnmarshalJSON method.
|
||||||
|
// Technically this is just unmarshalling the response into
|
||||||
|
// a struct that is never used, but it's good enough to
|
||||||
|
// trigger the UnmarshalJSON method.
|
||||||
|
for i := 0; i < newType.NumField(); i++ {
|
||||||
|
s := newType.Field(i).Addr().Interface()
|
||||||
|
|
||||||
|
// Unmarshal is used rather than NewDecoder to also work
|
||||||
|
// around the above-mentioned bug.
|
||||||
|
err = json.Unmarshal(b, s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newSlice = reflect.Append(newSlice, newType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// "to" should now be properly modeled to receive the
|
||||||
|
// JSON response body and unmarshal into all the correct
|
||||||
|
// fields of the struct or composed extension struct
|
||||||
|
// at the end of this method.
|
||||||
|
toValue.Set(newSlice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
typeOfV := toValue.Type()
|
||||||
|
if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous {
|
||||||
|
for i := 0; i < toValue.NumField(); i++ {
|
||||||
|
toField := toValue.Field(i)
|
||||||
|
if toField.Kind() == reflect.Struct {
|
||||||
|
s := toField.Addr().Interface()
|
||||||
|
err = json.NewDecoder(bytes.NewReader(b)).Decode(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(b, &to)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractIntoStructPtr will unmarshal the Result (r) into the provided
|
||||||
|
// interface{} (to).
|
||||||
|
//
|
||||||
|
// NOTE: For internal use only
|
||||||
|
//
|
||||||
|
// `to` must be a pointer to an underlying struct type
|
||||||
|
//
|
||||||
|
// If provided, `label` will be filtered out of the response
|
||||||
|
// body prior to `r` being unmarshalled into `to`.
|
||||||
|
func (r Result) ExtractIntoStructPtr(to interface{}, label string) error {
|
||||||
|
if r.Err != nil {
|
||||||
|
return r.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
t := reflect.TypeOf(to)
|
||||||
|
if k := t.Kind(); k != reflect.Ptr {
|
||||||
|
return fmt.Errorf("Expected pointer, got %v", k)
|
||||||
|
}
|
||||||
|
switch t.Elem().Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
return r.extractIntoPtr(to, label)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Expected pointer to struct, got: %v", t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractIntoSlicePtr will unmarshal the Result (r) into the provided
|
||||||
|
// interface{} (to).
|
||||||
|
//
|
||||||
|
// NOTE: For internal use only
|
||||||
|
//
|
||||||
|
// `to` must be a pointer to an underlying slice type
|
||||||
|
//
|
||||||
|
// If provided, `label` will be filtered out of the response
|
||||||
|
// body prior to `r` being unmarshalled into `to`.
|
||||||
|
func (r Result) ExtractIntoSlicePtr(to interface{}, label string) error {
|
||||||
|
if r.Err != nil {
|
||||||
|
return r.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
t := reflect.TypeOf(to)
|
||||||
|
if k := t.Kind(); k != reflect.Ptr {
|
||||||
|
return fmt.Errorf("Expected pointer, got %v", k)
|
||||||
|
}
|
||||||
|
switch t.Elem().Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
return r.extractIntoPtr(to, label)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Expected pointer to slice, got: %v", t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrettyPrintJSON creates a string containing the full response body as
|
||||||
|
// pretty-printed JSON. It's useful for capturing test fixtures and for
|
||||||
|
// debugging extraction bugs. If you include its output in an issue related to
|
||||||
|
// a buggy extraction function, we will all love you forever.
|
||||||
|
func (r Result) PrettyPrintJSON() string {
|
||||||
|
pretty, err := json.MarshalIndent(r.Body, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
return string(pretty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrResult is an internal type to be used by individual resource packages, but
|
||||||
|
// its methods will be available on a wide variety of user-facing embedding
|
||||||
|
// types.
|
||||||
|
//
|
||||||
|
// It represents results that only contain a potential error and
|
||||||
|
// nothing else. Usually, if the operation executed successfully, the Err field
|
||||||
|
// will be nil; otherwise it will be stocked with a relevant error. Use the
|
||||||
|
// ExtractErr method
|
||||||
|
// to cleanly pull it out.
|
||||||
|
type ErrResult struct {
|
||||||
|
Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractErr is a function that extracts error information, or nil, from a result.
|
||||||
|
func (r ErrResult) ExtractErr() error {
|
||||||
|
return r.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
HeaderResult is an internal type to be used by individual resource packages, but
|
||||||
|
its methods will be available on a wide variety of user-facing embedding types.
|
||||||
|
|
||||||
|
It represents a result that only contains an error (possibly nil) and an
|
||||||
|
http.Header. This is used, for example, by the objectstorage packages in
|
||||||
|
openstack, because most of the operations don't return response bodies, but do
|
||||||
|
have relevant information in headers.
|
||||||
|
*/
|
||||||
|
type HeaderResult struct {
|
||||||
|
Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractInto allows users to provide an object into which `Extract` will
|
||||||
|
// extract the http.Header headers of the result.
|
||||||
|
func (r HeaderResult) ExtractInto(to interface{}) error {
|
||||||
|
if r.Err != nil {
|
||||||
|
return r.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpHeaderMap := map[string]string{}
|
||||||
|
for k, v := range r.Header {
|
||||||
|
if len(v) > 0 {
|
||||||
|
tmpHeaderMap[k] = v[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(tmpHeaderMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(b, to)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC3339Milli describes a common time format used by some API responses.
|
||||||
|
const RFC3339Milli = "2006-01-02T15:04:05.999999Z"
|
||||||
|
|
||||||
|
type JSONRFC3339Milli time.Time
|
||||||
|
|
||||||
|
func (jt *JSONRFC3339Milli) UnmarshalJSON(data []byte) error {
|
||||||
|
b := bytes.NewBuffer(data)
|
||||||
|
dec := json.NewDecoder(b)
|
||||||
|
var s string
|
||||||
|
if err := dec.Decode(&s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t, err := time.Parse(RFC3339Milli, s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*jt = JSONRFC3339Milli(t)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const RFC3339MilliNoZ = "2006-01-02T15:04:05.999999"
|
||||||
|
|
||||||
|
type JSONRFC3339MilliNoZ time.Time
|
||||||
|
|
||||||
|
func (jt *JSONRFC3339MilliNoZ) UnmarshalJSON(data []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(data, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t, err := time.Parse(RFC3339MilliNoZ, s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*jt = JSONRFC3339MilliNoZ(t)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type JSONRFC1123 time.Time
|
||||||
|
|
||||||
|
func (jt *JSONRFC1123) UnmarshalJSON(data []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(data, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t, err := time.Parse(time.RFC1123, s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*jt = JSONRFC1123(t)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type JSONUnix time.Time
|
||||||
|
|
||||||
|
func (jt *JSONUnix) UnmarshalJSON(data []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(data, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
unix, err := strconv.ParseInt(s, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t = time.Unix(unix, 0)
|
||||||
|
*jt = JSONUnix(t)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC3339NoZ is the time format used in Heat (Orchestration).
|
||||||
|
const RFC3339NoZ = "2006-01-02T15:04:05"
|
||||||
|
|
||||||
|
type JSONRFC3339NoZ time.Time
|
||||||
|
|
||||||
|
func (jt *JSONRFC3339NoZ) UnmarshalJSON(data []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(data, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t, err := time.Parse(RFC3339NoZ, s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*jt = JSONRFC3339NoZ(t)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC3339ZNoT is the time format used in Zun (Containers Service).
|
||||||
|
const RFC3339ZNoT = "2006-01-02 15:04:05-07:00"
|
||||||
|
|
||||||
|
type JSONRFC3339ZNoT time.Time
|
||||||
|
|
||||||
|
func (jt *JSONRFC3339ZNoT) UnmarshalJSON(data []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(data, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t, err := time.Parse(RFC3339ZNoT, s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*jt = JSONRFC3339ZNoT(t)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC3339ZNoTNoZ is another time format used in Zun (Containers Service).
|
||||||
|
const RFC3339ZNoTNoZ = "2006-01-02 15:04:05"
|
||||||
|
|
||||||
|
type JSONRFC3339ZNoTNoZ time.Time
|
||||||
|
|
||||||
|
func (jt *JSONRFC3339ZNoTNoZ) UnmarshalJSON(data []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(data, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t, err := time.Parse(RFC3339ZNoTNoZ, s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*jt = JSONRFC3339ZNoTNoZ(t)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Link is an internal type to be used in packages of collection resources that are
|
||||||
|
paginated in a certain way.
|
||||||
|
|
||||||
|
It's a response substructure common to many paginated collection results that is
|
||||||
|
used to point to related pages. Usually, the one we care about is the one with
|
||||||
|
Rel field set to "next".
|
||||||
|
*/
|
||||||
|
type Link struct {
|
||||||
|
Href string `json:"href"`
|
||||||
|
Rel string `json:"rel"`
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
ExtractNextURL is an internal function useful for packages of collection
|
||||||
|
resources that are paginated in a certain way.
|
||||||
|
|
||||||
|
It attempts to extract the "next" URL from slice of Link structs, or
|
||||||
|
"" if no such URL is present.
|
||||||
|
*/
|
||||||
|
func ExtractNextURL(links []Link) (string, error) {
|
||||||
|
var url string
|
||||||
|
|
||||||
|
for _, l := range links {
|
||||||
|
if l.Rel == "next" {
|
||||||
|
url = l.Href
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if url == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return url, nil
|
||||||
|
}
|
150
vendor/github.com/gophercloud/gophercloud/service_client.go
generated
vendored
Normal file
150
vendor/github.com/gophercloud/gophercloud/service_client.go
generated
vendored
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
package gophercloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServiceClient stores details required to interact with a specific service API implemented by a provider.
|
||||||
|
// Generally, you'll acquire these by calling the appropriate `New` method on a ProviderClient.
|
||||||
|
type ServiceClient struct {
|
||||||
|
// ProviderClient is a reference to the provider that implements this service.
|
||||||
|
*ProviderClient
|
||||||
|
|
||||||
|
// Endpoint is the base URL of the service's API, acquired from a service catalog.
|
||||||
|
// It MUST end with a /.
|
||||||
|
Endpoint string
|
||||||
|
|
||||||
|
// ResourceBase is the base URL shared by the resources within a service's API. It should include
|
||||||
|
// the API version and, like Endpoint, MUST end with a / if set. If not set, the Endpoint is used
|
||||||
|
// as-is, instead.
|
||||||
|
ResourceBase string
|
||||||
|
|
||||||
|
// This is the service client type (e.g. compute, sharev2).
|
||||||
|
// NOTE: FOR INTERNAL USE ONLY. DO NOT SET. GOPHERCLOUD WILL SET THIS.
|
||||||
|
// It is only exported because it gets set in a different package.
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// The microversion of the service to use. Set this to use a particular microversion.
|
||||||
|
Microversion string
|
||||||
|
|
||||||
|
// MoreHeaders allows users (or Gophercloud) to set service-wide headers on requests. Put another way,
|
||||||
|
// values set in this field will be set on all the HTTP requests the service client sends.
|
||||||
|
MoreHeaders map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResourceBaseURL returns the base URL of any resources used by this service. It MUST end with a /.
|
||||||
|
func (client *ServiceClient) ResourceBaseURL() string {
|
||||||
|
if client.ResourceBase != "" {
|
||||||
|
return client.ResourceBase
|
||||||
|
}
|
||||||
|
return client.Endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceURL constructs a URL for a resource belonging to this provider.
|
||||||
|
func (client *ServiceClient) ServiceURL(parts ...string) string {
|
||||||
|
return client.ResourceBaseURL() + strings.Join(parts, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *ServiceClient) initReqOpts(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) {
|
||||||
|
if v, ok := (JSONBody).(io.Reader); ok {
|
||||||
|
opts.RawBody = v
|
||||||
|
} else if JSONBody != nil {
|
||||||
|
opts.JSONBody = JSONBody
|
||||||
|
}
|
||||||
|
|
||||||
|
if JSONResponse != nil {
|
||||||
|
opts.JSONResponse = JSONResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.MoreHeaders == nil {
|
||||||
|
opts.MoreHeaders = make(map[string]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if client.Microversion != "" {
|
||||||
|
client.setMicroversionHeader(opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get calls `Request` with the "GET" HTTP verb.
|
||||||
|
func (client *ServiceClient) Get(url string, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = new(RequestOpts)
|
||||||
|
}
|
||||||
|
client.initReqOpts(url, nil, JSONResponse, opts)
|
||||||
|
return client.Request("GET", url, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post calls `Request` with the "POST" HTTP verb.
|
||||||
|
func (client *ServiceClient) Post(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = new(RequestOpts)
|
||||||
|
}
|
||||||
|
client.initReqOpts(url, JSONBody, JSONResponse, opts)
|
||||||
|
return client.Request("POST", url, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put calls `Request` with the "PUT" HTTP verb.
|
||||||
|
func (client *ServiceClient) Put(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = new(RequestOpts)
|
||||||
|
}
|
||||||
|
client.initReqOpts(url, JSONBody, JSONResponse, opts)
|
||||||
|
return client.Request("PUT", url, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch calls `Request` with the "PATCH" HTTP verb.
|
||||||
|
func (client *ServiceClient) Patch(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = new(RequestOpts)
|
||||||
|
}
|
||||||
|
client.initReqOpts(url, JSONBody, JSONResponse, opts)
|
||||||
|
return client.Request("PATCH", url, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete calls `Request` with the "DELETE" HTTP verb.
|
||||||
|
func (client *ServiceClient) Delete(url string, opts *RequestOpts) (*http.Response, error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = new(RequestOpts)
|
||||||
|
}
|
||||||
|
client.initReqOpts(url, nil, nil, opts)
|
||||||
|
return client.Request("DELETE", url, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head calls `Request` with the "HEAD" HTTP verb.
|
||||||
|
func (client *ServiceClient) Head(url string, opts *RequestOpts) (*http.Response, error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = new(RequestOpts)
|
||||||
|
}
|
||||||
|
client.initReqOpts(url, nil, nil, opts)
|
||||||
|
return client.Request("HEAD", url, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) {
|
||||||
|
switch client.Type {
|
||||||
|
case "compute":
|
||||||
|
opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion
|
||||||
|
case "sharev2":
|
||||||
|
opts.MoreHeaders["X-OpenStack-Manila-API-Version"] = client.Microversion
|
||||||
|
case "volume":
|
||||||
|
opts.MoreHeaders["X-OpenStack-Volume-API-Version"] = client.Microversion
|
||||||
|
}
|
||||||
|
|
||||||
|
if client.Type != "" {
|
||||||
|
opts.MoreHeaders["OpenStack-API-Version"] = client.Type + " " + client.Microversion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request carries out the HTTP operation for the service client
|
||||||
|
func (client *ServiceClient) Request(method, url string, options *RequestOpts) (*http.Response, error) {
|
||||||
|
if len(client.MoreHeaders) > 0 {
|
||||||
|
if options == nil {
|
||||||
|
options = new(RequestOpts)
|
||||||
|
}
|
||||||
|
for k, v := range client.MoreHeaders {
|
||||||
|
options.MoreHeaders[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return client.ProviderClient.Request(method, url, options)
|
||||||
|
}
|
102
vendor/github.com/gophercloud/gophercloud/util.go
generated
vendored
Normal file
102
vendor/github.com/gophercloud/gophercloud/util.go
generated
vendored
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
package gophercloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WaitFor polls a predicate function, once per second, up to a timeout limit.
|
||||||
|
// This is useful to wait for a resource to transition to a certain state.
|
||||||
|
// To handle situations when the predicate might hang indefinitely, the
|
||||||
|
// predicate will be prematurely cancelled after the timeout.
|
||||||
|
// Resource packages will wrap this in a more convenient function that's
|
||||||
|
// specific to a certain resource, but it can also be useful on its own.
|
||||||
|
func WaitFor(timeout int, predicate func() (bool, error)) error {
|
||||||
|
type WaitForResult struct {
|
||||||
|
Success bool
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
start := time.Now().Unix()
|
||||||
|
|
||||||
|
for {
|
||||||
|
// If a timeout is set, and that's been exceeded, shut it down.
|
||||||
|
if timeout >= 0 && time.Now().Unix()-start >= int64(timeout) {
|
||||||
|
return fmt.Errorf("A timeout occurred")
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
var result WaitForResult
|
||||||
|
ch := make(chan bool, 1)
|
||||||
|
go func() {
|
||||||
|
defer close(ch)
|
||||||
|
satisfied, err := predicate()
|
||||||
|
result.Success = satisfied
|
||||||
|
result.Error = err
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
if result.Error != nil {
|
||||||
|
return result.Error
|
||||||
|
}
|
||||||
|
if result.Success {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// If the predicate has not finished by the timeout, cancel it.
|
||||||
|
case <-time.After(time.Duration(timeout) * time.Second):
|
||||||
|
return fmt.Errorf("A timeout occurred")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormalizeURL is an internal function to be used by provider clients.
|
||||||
|
//
|
||||||
|
// It ensures that each endpoint URL has a closing `/`, as expected by
|
||||||
|
// ServiceClient's methods.
|
||||||
|
func NormalizeURL(url string) string {
|
||||||
|
if !strings.HasSuffix(url, "/") {
|
||||||
|
return url + "/"
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormalizePathURL is used to convert rawPath to a fqdn, using basePath as
|
||||||
|
// a reference in the filesystem, if necessary. basePath is assumed to contain
|
||||||
|
// either '.' when first used, or the file:// type fqdn of the parent resource.
|
||||||
|
// e.g. myFavScript.yaml => file://opt/lib/myFavScript.yaml
|
||||||
|
func NormalizePathURL(basePath, rawPath string) (string, error) {
|
||||||
|
u, err := url.Parse(rawPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// if a scheme is defined, it must be a fqdn already
|
||||||
|
if u.Scheme != "" {
|
||||||
|
return u.String(), nil
|
||||||
|
}
|
||||||
|
// if basePath is a url, then child resources are assumed to be relative to it
|
||||||
|
bu, err := url.Parse(basePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var basePathSys, absPathSys string
|
||||||
|
if bu.Scheme != "" {
|
||||||
|
basePathSys = filepath.FromSlash(bu.Path)
|
||||||
|
absPathSys = filepath.Join(basePathSys, rawPath)
|
||||||
|
bu.Path = filepath.ToSlash(absPathSys)
|
||||||
|
return bu.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
absPathSys = filepath.Join(basePath, rawPath)
|
||||||
|
u.Path = filepath.ToSlash(absPathSys)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
u.Scheme = "file"
|
||||||
|
return u.String(), nil
|
||||||
|
|
||||||
|
}
|
74
vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go
generated
vendored
Normal file
74
vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build go1.7
|
||||||
|
|
||||||
|
// Package ctxhttp provides helper functions for performing context-aware HTTP requests.
|
||||||
|
package ctxhttp // import "golang.org/x/net/context/ctxhttp"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Do sends an HTTP request with the provided http.Client and returns
|
||||||
|
// an HTTP response.
|
||||||
|
//
|
||||||
|
// If the client is nil, http.DefaultClient is used.
|
||||||
|
//
|
||||||
|
// The provided ctx must be non-nil. If it is canceled or times out,
|
||||||
|
// ctx.Err() will be returned.
|
||||||
|
func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
|
||||||
|
if client == nil {
|
||||||
|
client = http.DefaultClient
|
||||||
|
}
|
||||||
|
resp, err := client.Do(req.WithContext(ctx))
|
||||||
|
// If we got an error, and the context has been canceled,
|
||||||
|
// the context's error is probably more useful.
|
||||||
|
if err != nil {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
err = ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get issues a GET request via the Do function.
|
||||||
|
func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return Do(ctx, client, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head issues a HEAD request via the Do function.
|
||||||
|
func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
|
||||||
|
req, err := http.NewRequest("HEAD", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return Do(ctx, client, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post issues a POST request via the Do function.
|
||||||
|
func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) {
|
||||||
|
req, err := http.NewRequest("POST", url, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", bodyType)
|
||||||
|
return Do(ctx, client, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostForm issues a POST request via the Do function.
|
||||||
|
func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) {
|
||||||
|
return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
|
||||||
|
}
|
147
vendor/golang.org/x/net/context/ctxhttp/ctxhttp_pre17.go
generated
vendored
Normal file
147
vendor/golang.org/x/net/context/ctxhttp/ctxhttp_pre17.go
generated
vendored
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
// 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 !go1.7
|
||||||
|
|
||||||
|
package ctxhttp // import "golang.org/x/net/context/ctxhttp"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func nop() {}
|
||||||
|
|
||||||
|
var (
|
||||||
|
testHookContextDoneBeforeHeaders = nop
|
||||||
|
testHookDoReturned = nop
|
||||||
|
testHookDidBodyClose = nop
|
||||||
|
)
|
||||||
|
|
||||||
|
// Do sends an HTTP request with the provided http.Client and returns an HTTP response.
|
||||||
|
// If the client is nil, http.DefaultClient is used.
|
||||||
|
// If the context is canceled or times out, ctx.Err() will be returned.
|
||||||
|
func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
|
||||||
|
if client == nil {
|
||||||
|
client = http.DefaultClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(djd): Respect any existing value of req.Cancel.
|
||||||
|
cancel := make(chan struct{})
|
||||||
|
req.Cancel = cancel
|
||||||
|
|
||||||
|
type responseAndError struct {
|
||||||
|
resp *http.Response
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
result := make(chan responseAndError, 1)
|
||||||
|
|
||||||
|
// Make local copies of test hooks closed over by goroutines below.
|
||||||
|
// Prevents data races in tests.
|
||||||
|
testHookDoReturned := testHookDoReturned
|
||||||
|
testHookDidBodyClose := testHookDidBodyClose
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
testHookDoReturned()
|
||||||
|
result <- responseAndError{resp, err}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var resp *http.Response
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
testHookContextDoneBeforeHeaders()
|
||||||
|
close(cancel)
|
||||||
|
// Clean up after the goroutine calling client.Do:
|
||||||
|
go func() {
|
||||||
|
if r := <-result; r.resp != nil {
|
||||||
|
testHookDidBodyClose()
|
||||||
|
r.resp.Body.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case r := <-result:
|
||||||
|
var err error
|
||||||
|
resp, err = r.resp, r.err
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
close(cancel)
|
||||||
|
case <-c:
|
||||||
|
// The response's Body is closed.
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
resp.Body = ¬ifyingReader{resp.Body, c}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get issues a GET request via the Do function.
|
||||||
|
func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return Do(ctx, client, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head issues a HEAD request via the Do function.
|
||||||
|
func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
|
||||||
|
req, err := http.NewRequest("HEAD", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return Do(ctx, client, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post issues a POST request via the Do function.
|
||||||
|
func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) {
|
||||||
|
req, err := http.NewRequest("POST", url, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", bodyType)
|
||||||
|
return Do(ctx, client, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostForm issues a POST request via the Do function.
|
||||||
|
func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) {
|
||||||
|
return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// notifyingReader is an io.ReadCloser that closes the notify channel after
|
||||||
|
// Close is called or a Read fails on the underlying ReadCloser.
|
||||||
|
type notifyingReader struct {
|
||||||
|
io.ReadCloser
|
||||||
|
notify chan<- struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *notifyingReader) Read(p []byte) (int, error) {
|
||||||
|
n, err := r.ReadCloser.Read(p)
|
||||||
|
if err != nil && r.notify != nil {
|
||||||
|
close(r.notify)
|
||||||
|
r.notify = nil
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *notifyingReader) Close() error {
|
||||||
|
err := r.ReadCloser.Close()
|
||||||
|
if r.notify != nil {
|
||||||
|
close(r.notify)
|
||||||
|
r.notify = nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
13
vendor/golang.org/x/oauth2/.travis.yml
generated
vendored
Normal file
13
vendor/golang.org/x/oauth2/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- tip
|
||||||
|
|
||||||
|
install:
|
||||||
|
- export GOPATH="$HOME/gopath"
|
||||||
|
- mkdir -p "$GOPATH/src/golang.org/x"
|
||||||
|
- mv "$TRAVIS_BUILD_DIR" "$GOPATH/src/golang.org/x/oauth2"
|
||||||
|
- go get -v -t -d golang.org/x/oauth2/...
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go test -v golang.org/x/oauth2/...
|
3
vendor/golang.org/x/oauth2/AUTHORS
generated
vendored
Normal file
3
vendor/golang.org/x/oauth2/AUTHORS
generated
vendored
Normal file
|
@ -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.
|
26
vendor/golang.org/x/oauth2/CONTRIBUTING.md
generated
vendored
Normal file
26
vendor/golang.org/x/oauth2/CONTRIBUTING.md
generated
vendored
Normal file
|
@ -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.
|
3
vendor/golang.org/x/oauth2/CONTRIBUTORS
generated
vendored
Normal file
3
vendor/golang.org/x/oauth2/CONTRIBUTORS
generated
vendored
Normal file
|
@ -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.
|
27
vendor/golang.org/x/oauth2/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/oauth2/LICENSE
generated
vendored
Normal file
|
@ -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.
|
77
vendor/golang.org/x/oauth2/README.md
generated
vendored
Normal file
77
vendor/golang.org/x/oauth2/README.md
generated
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
# OAuth2 for Go
|
||||||
|
|
||||||
|
[](https://travis-ci.org/golang/oauth2)
|
||||||
|
[](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.
|
89
vendor/golang.org/x/oauth2/google/appengine.go
generated
vendored
Normal file
89
vendor/golang.org/x/oauth2/google/appengine.go
generated
vendored
Normal file
|
@ -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
|
||||||
|
}
|
14
vendor/golang.org/x/oauth2/google/appengine_hook.go
generated
vendored
Normal file
14
vendor/golang.org/x/oauth2/google/appengine_hook.go
generated
vendored
Normal file
|
@ -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
|
||||||
|
}
|
11
vendor/golang.org/x/oauth2/google/appengineflex_hook.go
generated
vendored
Normal file
11
vendor/golang.org/x/oauth2/google/appengineflex_hook.go
generated
vendored
Normal file
|
@ -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.
|
||||||
|
}
|
115
vendor/golang.org/x/oauth2/google/default.go
generated
vendored
Normal file
115
vendor/golang.org/x/oauth2/google/default.go
generated
vendored
Normal file
|
@ -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...)
|
||||||
|
}
|
42
vendor/golang.org/x/oauth2/google/doc_go19.go
generated
vendored
Normal file
42
vendor/golang.org/x/oauth2/google/doc_go19.go
generated
vendored
Normal file
|
@ -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"
|
43
vendor/golang.org/x/oauth2/google/doc_not_go19.go
generated
vendored
Normal file
43
vendor/golang.org/x/oauth2/google/doc_not_go19.go
generated
vendored
Normal file
|
@ -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"
|
57
vendor/golang.org/x/oauth2/google/go19.go
generated
vendored
Normal file
57
vendor/golang.org/x/oauth2/google/go19.go
generated
vendored
Normal file
|
@ -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)
|
||||||
|
}
|
192
vendor/golang.org/x/oauth2/google/google.go
generated
vendored
Normal file
192
vendor/golang.org/x/oauth2/google/google.go
generated
vendored
Normal file
|
@ -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
|
||||||
|
}
|
74
vendor/golang.org/x/oauth2/google/jwt.go
generated
vendored
Normal file
74
vendor/golang.org/x/oauth2/google/jwt.go
generated
vendored
Normal file
|
@ -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
|
||||||
|
}
|
54
vendor/golang.org/x/oauth2/google/not_go19.go
generated
vendored
Normal file
54
vendor/golang.org/x/oauth2/google/not_go19.go
generated
vendored
Normal file
|
@ -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)
|
||||||
|
}
|
201
vendor/golang.org/x/oauth2/google/sdk.go
generated
vendored
Normal file
201
vendor/golang.org/x/oauth2/google/sdk.go
generated
vendored
Normal file
|
@ -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 ""
|
||||||
|
}
|
13
vendor/golang.org/x/oauth2/internal/client_appengine.go
generated
vendored
Normal file
13
vendor/golang.org/x/oauth2/internal/client_appengine.go
generated
vendored
Normal file
|
@ -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
|
||||||
|
}
|
6
vendor/golang.org/x/oauth2/internal/doc.go
generated
vendored
Normal file
6
vendor/golang.org/x/oauth2/internal/doc.go
generated
vendored
Normal file
|
@ -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
|
37
vendor/golang.org/x/oauth2/internal/oauth2.go
generated
vendored
Normal file
37
vendor/golang.org/x/oauth2/internal/oauth2.go
generated
vendored
Normal file
|
@ -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
|
||||||
|
}
|
272
vendor/golang.org/x/oauth2/internal/token.go
generated
vendored
Normal file
272
vendor/golang.org/x/oauth2/internal/token.go
generated
vendored
Normal file
|
@ -0,0 +1,272 @@
|
||||||
|
// 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://api.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",
|
||||||
|
"https://account.health.nokia.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
34
vendor/golang.org/x/oauth2/internal/transport.go
generated
vendored
Normal file
34
vendor/golang.org/x/oauth2/internal/transport.go
generated
vendored
Normal file
|
@ -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
|
||||||
|
}
|
182
vendor/golang.org/x/oauth2/jws/jws.go
generated
vendored
Normal file
182
vendor/golang.org/x/oauth2/jws/jws.go
generated
vendored
Normal file
|
@ -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))
|
||||||
|
}
|
162
vendor/golang.org/x/oauth2/jwt/jwt.go
generated
vendored
Normal file
162
vendor/golang.org/x/oauth2/jwt/jwt.go
generated
vendored
Normal file
|
@ -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
|
||||||
|
}
|
362
vendor/golang.org/x/oauth2/oauth2.go
generated
vendored
Normal file
362
vendor/golang.org/x/oauth2/oauth2.go
generated
vendored
Normal file
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
175
vendor/golang.org/x/oauth2/token.go
generated
vendored
Normal file
175
vendor/golang.org/x/oauth2/token.go
generated
vendored
Normal file
|
@ -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)
|
||||||
|
}
|
144
vendor/golang.org/x/oauth2/transport.go
generated
vendored
Normal file
144
vendor/golang.org/x/oauth2/transport.go
generated
vendored
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
24
vendor/google.golang.org/appengine/.travis.yml
generated
vendored
Normal file
24
vendor/google.golang.org/appengine/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.6.x
|
||||||
|
- 1.7.x
|
||||||
|
- 1.8.x
|
||||||
|
- 1.9.x
|
||||||
|
|
||||||
|
go_import_path: google.golang.org/appengine
|
||||||
|
|
||||||
|
install:
|
||||||
|
- go get -u -v $(go list -f '{{join .Imports "\n"}}{{"\n"}}{{join .TestImports "\n"}}' ./... | sort | uniq | grep -v appengine)
|
||||||
|
- mkdir /tmp/sdk
|
||||||
|
- curl -o /tmp/sdk.zip "https://storage.googleapis.com/appengine-sdks/featured/go_appengine_sdk_linux_amd64-1.9.40.zip"
|
||||||
|
- unzip -q /tmp/sdk.zip -d /tmp/sdk
|
||||||
|
- export PATH="$PATH:/tmp/sdk/go_appengine"
|
||||||
|
- export APPENGINE_DEV_APPSERVER=/tmp/sdk/go_appengine/dev_appserver.py
|
||||||
|
|
||||||
|
script:
|
||||||
|
- goapp version
|
||||||
|
- go version
|
||||||
|
- go test -v google.golang.org/appengine/...
|
||||||
|
- go test -v -race google.golang.org/appengine/...
|
||||||
|
- goapp test -v google.golang.org/appengine/...
|
90
vendor/google.golang.org/appengine/CONTRIBUTING.md
generated
vendored
Normal file
90
vendor/google.golang.org/appengine/CONTRIBUTING.md
generated
vendored
Normal file
|
@ -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
|
202
vendor/google.golang.org/appengine/LICENSE
generated
vendored
Normal file
202
vendor/google.golang.org/appengine/LICENSE
generated
vendored
Normal file
|
@ -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.
|
73
vendor/google.golang.org/appengine/README.md
generated
vendored
Normal file
73
vendor/google.golang.org/appengine/README.md
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
# Go App Engine packages
|
||||||
|
|
||||||
|
[](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.
|
113
vendor/google.golang.org/appengine/appengine.go
generated
vendored
Normal file
113
vendor/google.golang.org/appengine/appengine.go
generated
vendored
Normal file
|
@ -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)
|
||||||
|
}
|
20
vendor/google.golang.org/appengine/appengine_vm.go
generated
vendored
Normal file
20
vendor/google.golang.org/appengine/appengine_vm.go
generated
vendored
Normal file
|
@ -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()
|
||||||
|
}
|
46
vendor/google.golang.org/appengine/errors.go
generated
vendored
Normal file
46
vendor/google.golang.org/appengine/errors.go
generated
vendored
Normal file
|
@ -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)
|
||||||
|
}
|
7
vendor/google.golang.org/appengine/go.mod
generated
vendored
Normal file
7
vendor/google.golang.org/appengine/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
module google.golang.org/appengine
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/golang/protobuf v1.2.0
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225
|
||||||
|
golang.org/x/text v0.3.0
|
||||||
|
)
|
3
vendor/google.golang.org/appengine/go.sum
generated
vendored
Normal file
3
vendor/google.golang.org/appengine/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
142
vendor/google.golang.org/appengine/identity.go
generated
vendored
Normal file
142
vendor/google.golang.org/appengine/identity.go
generated
vendored
Normal file
|
@ -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)
|
||||||
|
}
|
660
vendor/google.golang.org/appengine/internal/api.go
generated
vendored
Normal file
660
vendor/google.golang.org/appengine/internal/api.go
generated
vendored
Normal file
|
@ -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})
|
||||||
|
}
|
169
vendor/google.golang.org/appengine/internal/api_classic.go
generated
vendored
Normal file
169
vendor/google.golang.org/appengine/internal/api_classic.go
generated
vendored
Normal file
|
@ -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...)
|
||||||
|
}
|
123
vendor/google.golang.org/appengine/internal/api_common.go
generated
vendored
Normal file
123
vendor/google.golang.org/appengine/internal/api_common.go
generated
vendored
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue