mirror of
https://github.com/containous/traefik.git
synced 2025-09-20 05:44:23 +03:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
443902a0f0 | ||
|
9eb02d9b03 | ||
|
2eb651645d | ||
|
630571fdc8 | ||
|
00fc43ebce | ||
|
6d906fa4c8 | ||
|
6a4c7796e3 | ||
|
67704e333d | ||
|
76c9cea856 | ||
|
0366fb9bc2 | ||
|
5fed947eaa | ||
|
c289279d24 |
18
CHANGELOG.md
18
CHANGELOG.md
@@ -1,5 +1,22 @@
|
||||
# Change Log
|
||||
|
||||
## [v1.7.9](https://github.com/containous/traefik/tree/v1.7.9) (2019-02-11)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.8...v1.7.9)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Updates of Lego. ([#4480](https://github.com/containous/traefik/pull/4480) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** app-root on non-explicit path include "/" in the redirect ([#4458](https://github.com/containous/traefik/pull/4458) by [doctori](https://github.com/doctori))
|
||||
- **[middleware]** Missing trailers with retry ([#4442](https://github.com/containous/traefik/pull/4442) by [juliens](https://github.com/juliens))
|
||||
- **[rancher]** Handle errors when working with rancher ([#4378](https://github.com/containous/traefik/pull/4378) by [apsifly](https://github.com/apsifly))
|
||||
- **[servicefabric]** Add support for specifying the name of the endpoint. ([#4479](https://github.com/containous/traefik/pull/4479) by [ldez](https://github.com/ldez))
|
||||
- **[tls]** insecureSkipVerify for the passTLSCert transport ([#4438](https://github.com/containous/traefik/pull/4438) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[tracing]** Add Tracing Header Context Name option for Jaeger ([#4459](https://github.com/containous/traefik/pull/4459) by [gadoor](https://github.com/gadoor))
|
||||
|
||||
**Documentation:**
|
||||
- **[metrics]** Update default value of buckets for Prometheus ([#4468](https://github.com/containous/traefik/pull/4468) by [adam-golab](https://github.com/adam-golab))
|
||||
- **[rules]** Fixes the display of the associativity rules. ([#4478](https://github.com/containous/traefik/pull/4478) by [ldez](https://github.com/ldez))
|
||||
- Fixed curl example ([#4471](https://github.com/containous/traefik/pull/4471) by [rgarrigue](https://github.com/rgarrigue))
|
||||
|
||||
## [v1.7.8](https://github.com/containous/traefik/tree/v1.7.8) (2019-01-29)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.7...v1.7.8)
|
||||
|
||||
@@ -11,6 +28,7 @@
|
||||
- **[ecs]** Cache exising task definitions to avoid rate limiting ([#4177](https://github.com/containous/traefik/pull/4177) by [hwhelan-CB](https://github.com/hwhelan-CB))
|
||||
- **[tls]** Check for dynamic tls updates on configuration preload ([#4022](https://github.com/containous/traefik/pull/4022) by [ffilippopoulos](https://github.com/ffilippopoulos))
|
||||
- **[tracing]** Support Datadog tracer priority sampling ([#4359](https://github.com/containous/traefik/pull/4359) by [jcassee](https://github.com/jcassee))
|
||||
- Update to Go 1.11.5 [CVE-2019-6486](https://nvd.nist.gov/vuln/detail/CVE-2019-6486)
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** More detailed info about Google Cloud DNS. ([#4395](https://github.com/containous/traefik/pull/4395) by [ldez](https://github.com/ldez))
|
||||
|
102
Gopkg.lock
generated
102
Gopkg.lock
generated
@@ -167,8 +167,8 @@
|
||||
"edgegrid",
|
||||
"jsonhooks-v1"
|
||||
]
|
||||
revision = "a494eba1efa1f38338393727dff63389a6a66534"
|
||||
version = "v0.6.0"
|
||||
revision = "1471ce9c14c6d8c007516e129262962a628fecdf"
|
||||
version = "v0.7.3"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/aliyun/alibaba-cloud-sdk-go"
|
||||
@@ -255,10 +255,16 @@
|
||||
revision = "4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/cenk/backoff"
|
||||
packages = ["."]
|
||||
revision = "2ea60e5f094469f9e65adb9cd103795b73ae743e"
|
||||
revision = "1e4cf3da559842a91afcb6ea6141451e6c30c618"
|
||||
version = "v2.1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/cenkalti/backoff"
|
||||
packages = ["."]
|
||||
revision = "1e4cf3da559842a91afcb6ea6141451e6c30c618"
|
||||
version = "v2.1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/cloudflare/cloudflare-go"
|
||||
@@ -302,8 +308,8 @@
|
||||
[[projects]]
|
||||
name = "github.com/containous/traefik-extra-service-fabric"
|
||||
packages = ["."]
|
||||
revision = "6e90a9eef2ac9d320e55d6e994d169673a8d8b0f"
|
||||
version = "v1.3.0"
|
||||
revision = "b9142ccc2205284b288c0b085444fedd42ea5126"
|
||||
version = "v1.4.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/coreos/bbolt"
|
||||
@@ -597,8 +603,8 @@
|
||||
[[projects]]
|
||||
name = "github.com/go-ini/ini"
|
||||
packages = ["."]
|
||||
revision = "32e4c1e6bc4e7d0d8451aa6b75200d19e37a536a"
|
||||
version = "v1.32.0"
|
||||
revision = "6ed8d5f64cd79a498d1f3fab5880cc376ce41bbe"
|
||||
version = "v1.41.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/go-kit/kit"
|
||||
@@ -711,6 +717,12 @@
|
||||
packages = ["."]
|
||||
revision = "bbcb9da2d746f8bdbd6a936686a0a6067ada0ec5"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/google/uuid"
|
||||
packages = ["."]
|
||||
revision = "064e2069ce9c359c118179501254f67d7d37ba24"
|
||||
version = "0.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/googleapis/gnostic"
|
||||
packages = [
|
||||
@@ -721,6 +733,22 @@
|
||||
revision = "ee43cbb60db7bd22502942cccbc39059117352ab"
|
||||
version = "v0.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/gophercloud/gophercloud"
|
||||
packages = [
|
||||
".",
|
||||
"openstack",
|
||||
"openstack/dns/v2/recordsets",
|
||||
"openstack/dns/v2/zones",
|
||||
"openstack/identity/v2/tenants",
|
||||
"openstack/identity/v2/tokens",
|
||||
"openstack/identity/v3/tokens",
|
||||
"openstack/utils",
|
||||
"pagination"
|
||||
]
|
||||
revision = "bc37892e196848d7d56ba0db974ba1d7d5c43b2a"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gorilla/context"
|
||||
packages = ["."]
|
||||
@@ -869,12 +897,6 @@
|
||||
packages = ["."]
|
||||
revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/ldez/go-auroradns"
|
||||
packages = ["."]
|
||||
revision = "b40dfcae7c417f8129579362695dc1f3cfe5928d"
|
||||
version = "v2.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/libkermit/compose"
|
||||
@@ -1023,6 +1045,18 @@
|
||||
packages = ["namecom"]
|
||||
revision = "08470befbe04613bd4b44cb6978b05d50294c4d4"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/nrdcg/auroradns"
|
||||
packages = ["."]
|
||||
revision = "750ca8603f9f2cca2457acb22ea6e44d3f05358c"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/nrdcg/goinwx"
|
||||
packages = ["."]
|
||||
revision = "d8152159450570012552f924a0ae6ab3d8c617e0"
|
||||
version = "v0.6.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/ogier/pflag"
|
||||
@@ -1212,12 +1246,6 @@
|
||||
revision = "a67f783a3814b8729bd2dac5780b5f78f8dbd64d"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/smueller18/goinwx"
|
||||
packages = ["."]
|
||||
revision = "5d138389109eca96463f44f692408f0d1c731278"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/pflag"
|
||||
packages = ["."]
|
||||
@@ -1275,12 +1303,6 @@
|
||||
revision = "1dc93a7db3567a5ccf865106afac88278ba940cf"
|
||||
version = "v5.8.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/tuvistavie/securerandom"
|
||||
packages = ["."]
|
||||
revision = "15512123a948d62f6361bd84818e11f2ad84059a"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/tv42/zbase32"
|
||||
packages = ["."]
|
||||
@@ -1291,23 +1313,30 @@
|
||||
packages = [
|
||||
".",
|
||||
"config",
|
||||
"internal/baggage",
|
||||
"internal/baggage/remote",
|
||||
"internal/spanlog",
|
||||
"internal/throttler",
|
||||
"internal/throttler/remote",
|
||||
"log",
|
||||
"rpcmetrics",
|
||||
"thrift",
|
||||
"thrift-gen/agent",
|
||||
"thrift-gen/baggage",
|
||||
"thrift-gen/jaeger",
|
||||
"thrift-gen/sampling",
|
||||
"thrift-gen/zipkincore",
|
||||
"transport",
|
||||
"utils"
|
||||
]
|
||||
revision = "3e3870040def0ebdaf65a003863fa64f5cb26139"
|
||||
version = "v2.9.0"
|
||||
revision = "1a782e2da844727691fef1757c72eb190c2909f0"
|
||||
version = "v2.15.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/uber/jaeger-lib"
|
||||
packages = ["metrics"]
|
||||
revision = "3b2a9ad2a045881ab7a0f81d465be54c8292ee4f"
|
||||
version = "v1.1.0"
|
||||
revision = "ed3a127ec5fef7ae9ea95b01b542c47fbd999ce5"
|
||||
version = "v1.5.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/ugorji/go"
|
||||
@@ -1405,6 +1434,7 @@
|
||||
"providers/dns/cloudxns/internal",
|
||||
"providers/dns/conoha",
|
||||
"providers/dns/conoha/internal",
|
||||
"providers/dns/designate",
|
||||
"providers/dns/digitalocean",
|
||||
"providers/dns/dnsimple",
|
||||
"providers/dns/dnsmadeeasy",
|
||||
@@ -1454,8 +1484,8 @@
|
||||
"providers/dns/zoneee",
|
||||
"registration"
|
||||
]
|
||||
revision = "00ad82dec10ad0af1e037ee652ad1f1cc7015178"
|
||||
version = "v2.1.0"
|
||||
revision = "52e43eb318b07ace590c2144804292c89aa74802"
|
||||
version = "v2.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -1642,12 +1672,6 @@
|
||||
revision = "5b3e00af70a9484542169a976dcab8d03e601a17"
|
||||
version = "v1.30.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "v1"
|
||||
name = "gopkg.in/mattes/go-expand-tilde.v1"
|
||||
packages = ["."]
|
||||
revision = "cb884138e64c9a8bf5c7d6106d74b0fca082df0c"
|
||||
|
||||
[[projects]]
|
||||
branch = "v2"
|
||||
name = "gopkg.in/ns1/ns1-go.v2"
|
||||
@@ -1880,6 +1904,6 @@
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "781857167b93ec784d40c7c093a46c4d817f2d47b0dbc1f08fe3ec8ffc562cf9"
|
||||
inputs-digest = "8e2d1952c8e853cc6514e5962809e746f0f20eda9dce49e5f17a1d35544fc79e"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
@@ -71,7 +71,7 @@
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/containous/traefik-extra-service-fabric"
|
||||
version = "1.3.0"
|
||||
version = "1.4.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/coreos/go-systemd"
|
||||
@@ -167,7 +167,7 @@
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/uber/jaeger-client-go"
|
||||
version = "2.9.0"
|
||||
version = "2.15.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/uber/jaeger-lib"
|
||||
|
@@ -224,10 +224,11 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
ServiceName: "traefik",
|
||||
SpanNameLimit: 0,
|
||||
Jaeger: &jaeger.Config{
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
TraceContextHeaderName: "uber-trace-id",
|
||||
},
|
||||
Zipkin: &zipkin.Config{
|
||||
HTTPEndpoint: "http://localhost:9411/api/v1/spans",
|
||||
|
@@ -34,6 +34,7 @@ import (
|
||||
"github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/pkg/errors"
|
||||
jaegercli "github.com/uber/jaeger-client-go"
|
||||
"github.com/xenolf/lego/challenge/dns01"
|
||||
)
|
||||
|
||||
@@ -335,10 +336,11 @@ func (gc *GlobalConfiguration) initTracing() {
|
||||
case jaeger.Name:
|
||||
if gc.Tracing.Jaeger == nil {
|
||||
gc.Tracing.Jaeger = &jaeger.Config{
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
TraceContextHeaderName: jaegercli.TraceContextHeaderName,
|
||||
}
|
||||
}
|
||||
if gc.Tracing.Zipkin != nil {
|
||||
|
@@ -142,10 +142,11 @@ func TestSetEffectiveConfigurationTracing(t *testing.T) {
|
||||
expected: &tracing.Tracing{
|
||||
Backend: "jaeger",
|
||||
Jaeger: &jaeger.Config{
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
TraceContextHeaderName: "uber-trace-id",
|
||||
},
|
||||
Zipkin: nil,
|
||||
},
|
||||
@@ -155,10 +156,11 @@ func TestSetEffectiveConfigurationTracing(t *testing.T) {
|
||||
tracing: &tracing.Tracing{
|
||||
Backend: "zipkin",
|
||||
Jaeger: &jaeger.Config{
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
TraceContextHeaderName: "uber-trace-id",
|
||||
},
|
||||
},
|
||||
expected: &tracing.Tracing{
|
||||
@@ -177,10 +179,11 @@ func TestSetEffectiveConfigurationTracing(t *testing.T) {
|
||||
tracing: &tracing.Tracing{
|
||||
Backend: "zipkin",
|
||||
Jaeger: &jaeger.Config{
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
TraceContextHeaderName: "uber-trace-id",
|
||||
},
|
||||
Zipkin: &zipkin.Config{
|
||||
HTTPEndpoint: "http://powpow:9411/api/v1/spans",
|
||||
|
@@ -95,6 +95,7 @@ Following is the list of existing modifier rules:
|
||||
Matcher rules determine if a particular request should be forwarded to a backend.
|
||||
|
||||
The associativity rule is the following:
|
||||
|
||||
- `,` is the `OR` operator (works **only inside a matcher**, ex: `Host:foo.com,bar.com`).
|
||||
- i.e., forward a request if any rule matches.
|
||||
- Does not work for `Headers` and `HeadersRegexp`.
|
||||
|
@@ -271,61 +271,63 @@ Useful if internal networks block external DNS queries.
|
||||
|
||||
##### `provider`
|
||||
|
||||
Here is a list of supported `provider`s, that can automate the DNS verification, along with the required environment variables and their [wildcard & root domain support](/configuration/acme/#wildcard-domains) for each. Do not hesitate to complete it.
|
||||
Here is a list of supported `provider`s, that can automate the DNS verification, along with the required environment variables and their [wildcard & root domain support](/configuration/acme/#wildcard-domains) for each.
|
||||
Do not hesitate to complete it.
|
||||
|
||||
| Provider Name | Provider Code | Environment Variables | Wildcard & Root Domain Support |
|
||||
|--------------------------------------------------------|----------------|-------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------|
|
||||
| [ACME DNS](https://github.com/joohoi/acme-dns) | `acme-dns` | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH` | Not tested yet |
|
||||
| [Alibaba Cloud](https://www.vultr.com) | `alidns` | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID` | Not tested yet |
|
||||
| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | Not tested yet |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]` | Not tested yet |
|
||||
| [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | Not tested yet |
|
||||
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CF_API_EMAIL`, `CF_API_KEY` - The `Global API Key` needs to be used, not the `Origin CA Key` | YES |
|
||||
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | Not tested yet |
|
||||
| [ConoHa](https://www.conoha.jp) | `conoha` | `CONOHA_TENANT_ID`, `CONOHA_API_USERNAME`, `CONOHA_API_PASSWORD` | YES |
|
||||
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | YES |
|
||||
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | YES |
|
||||
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | Not tested yet |
|
||||
| [DNSPod](https://www.dnspod.com/) | `dnspod` | `DNSPOD_API_KEY` | Not tested yet |
|
||||
| [DreamHost](https://www.dreamhost.com/) | `dreamhost` | `DREAMHOST_API_KEY` | YES |
|
||||
| [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | YES |
|
||||
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | Not tested yet |
|
||||
| External Program | `exec` | `EXEC_PATH` | YES |
|
||||
| [Exoscale](https://www.exoscale.com) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | YES |
|
||||
| [Fast DNS](https://www.akamai.com/) | `fastdns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | Not tested yet |
|
||||
| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | Not tested yet |
|
||||
| [Gandi v5](http://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` | YES |
|
||||
| [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | Not tested yet |
|
||||
| [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | Not tested yet |
|
||||
| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, Application Default Credentials (2) (3), [`GCE_SERVICE_ACCOUNT_FILE`] | YES |
|
||||
| [hosting.de](https://www.hosting.de) | `hostingde` | `HOSTINGDE_API_KEY`, `HOSTINGDE_ZONE_NAME` | Not tested yet |
|
||||
| HTTP request | `httpreq` | `HTTPREQ_ENDPOINT`, `HTTPREQ_MODE`, `HTTPREQ_USERNAME`, `HTTPREQ_PASSWORD` (1) | YES |
|
||||
| [IIJ](https://www.iij.ad.jp/) | `iij` | `IIJ_API_ACCESS_KEY`, `IIJ_API_SECRET_KEY`, `IIJ_DO_SERVICE_CODE` | Not tested yet |
|
||||
| [INWX](https://www.inwx.de/en) | `inwx` | `INWX_USERNAME`, `INWX_PASSWORD` | YES |
|
||||
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | Not tested yet |
|
||||
| [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` | Not tested yet |
|
||||
| [Linode v4](https://www.linode.com) | `linodev4` | `LINODE_TOKEN` | Not tested yet |
|
||||
| manual | - | none, but you need to run Traefik interactively, turn on `acmeLogging` to see instructions and press <kbd>Enter</kbd>. | YES |
|
||||
| [MyDNS.jp](https://www.mydns.jp/) | `mydnsjp` | `MYDNSJP_MASTER_ID`, `MYDNSJP_PASSWORD` | YES |
|
||||
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | YES |
|
||||
| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | Not tested yet |
|
||||
| [Netcup](https://www.netcup.eu/) | `netcup` | `NETCUP_CUSTOMER_NUMBER`, `NETCUP_API_KEY`, `NETCUP_API_PASSWORD` | Not tested yet |
|
||||
| [NIFCloud](https://cloud.nifty.com/service/dns.htm) | `nifcloud` | `NIFCLOUD_ACCESS_KEY_ID`, `NIFCLOUD_SECRET_ACCESS_KEY` | Not tested yet |
|
||||
| [Ns1](https://ns1.com/) | `ns1` | `NS1_API_KEY` | Not tested yet |
|
||||
| [Open Telekom Cloud](https://cloud.telekom.de) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` | Not tested yet |
|
||||
| [OVH](https://www.ovh.com) | `ovh` | `OVH_ENDPOINT`, `OVH_APPLICATION_KEY`, `OVH_APPLICATION_SECRET`, `OVH_CONSUMER_KEY` | YES |
|
||||
| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` | Not tested yet |
|
||||
| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` | Not tested yet |
|
||||
| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` | Not tested yet |
|
||||
| [Route 53](https://aws.amazon.com/route53/) | `route53` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `[AWS_REGION]`, `[AWS_HOSTED_ZONE_ID]` or a configured user/instance IAM profile. | YES |
|
||||
| [Sakura Cloud](https://cloud.sakura.ad.jp/) | `sakuracloud` | `SAKURACLOUD_ACCESS_TOKEN`, `SAKURACLOUD_ACCESS_TOKEN_SECRET` | Not tested yet |
|
||||
| [Selectel](https://selectel.ru/en/) | `selectel` | `SELECTEL_API_TOKEN` | YES |
|
||||
| [Stackpath](https://www.stackpath.com/) | `stackpath` | `STACKPATH_CLIENT_ID`, `STACKPATH_CLIENT_SECRET`, `STACKPATH_STACK_ID` | Not tested yet |
|
||||
| [TransIP](https://www.transip.nl/) | `transip` | `TRANSIP_ACCOUNT_NAME`, `TRANSIP_PRIVATE_KEY_PATH` | YES |
|
||||
| [VegaDNS](https://github.com/shupp/VegaDNS-API) | `vegadns` | `SECRET_VEGADNS_KEY`, `SECRET_VEGADNS_SECRET`, `VEGADNS_URL` | Not tested yet |
|
||||
| [Vscale](https://vscale.io/) | `vscale` | `VSCALE_API_TOKEN` | YES |
|
||||
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | Not tested yet |
|
||||
| [Zone.ee](https://www.zone.ee) | `zoneee` | `ZONEEE_API_USER`, `ZONEEE_API_KEY` | YES |
|
||||
| Provider Name | Provider Code | Environment Variables | Wildcard & Root Domain Support |
|
||||
|-------------------------------------------------------------|----------------|-------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------|
|
||||
| [ACME DNS](https://github.com/joohoi/acme-dns) | `acme-dns` | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH` | Not tested yet |
|
||||
| [Alibaba Cloud](https://www.vultr.com) | `alidns` | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID` | Not tested yet |
|
||||
| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | Not tested yet |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]` | Not tested yet |
|
||||
| [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | Not tested yet |
|
||||
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CF_API_EMAIL`, `CF_API_KEY` - The `Global API Key` needs to be used, not the `Origin CA Key` | YES |
|
||||
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | Not tested yet |
|
||||
| [ConoHa](https://www.conoha.jp) | `conoha` | `CONOHA_TENANT_ID`, `CONOHA_API_USERNAME`, `CONOHA_API_PASSWORD` | YES |
|
||||
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | YES |
|
||||
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | YES |
|
||||
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | Not tested yet |
|
||||
| [DNSPod](https://www.dnspod.com/) | `dnspod` | `DNSPOD_API_KEY` | Not tested yet |
|
||||
| [DreamHost](https://www.dreamhost.com/) | `dreamhost` | `DREAMHOST_API_KEY` | YES |
|
||||
| [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | YES |
|
||||
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | Not tested yet |
|
||||
| External Program | `exec` | `EXEC_PATH` | YES |
|
||||
| [Exoscale](https://www.exoscale.com) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | YES |
|
||||
| [Fast DNS](https://www.akamai.com/) | `fastdns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | YES |
|
||||
| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | Not tested yet |
|
||||
| [Gandi v5](http://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` | YES |
|
||||
| [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | Not tested yet |
|
||||
| [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | Not tested yet |
|
||||
| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, Application Default Credentials (2) (3), [`GCE_SERVICE_ACCOUNT_FILE`] | YES |
|
||||
| [hosting.de](https://www.hosting.de) | `hostingde` | `HOSTINGDE_API_KEY`, `HOSTINGDE_ZONE_NAME` | Not tested yet |
|
||||
| HTTP request | `httpreq` | `HTTPREQ_ENDPOINT`, `HTTPREQ_MODE`, `HTTPREQ_USERNAME`, `HTTPREQ_PASSWORD` (1) | YES |
|
||||
| [IIJ](https://www.iij.ad.jp/) | `iij` | `IIJ_API_ACCESS_KEY`, `IIJ_API_SECRET_KEY`, `IIJ_DO_SERVICE_CODE` | Not tested yet |
|
||||
| [INWX](https://www.inwx.de/en) | `inwx` | `INWX_USERNAME`, `INWX_PASSWORD` | YES |
|
||||
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | Not tested yet |
|
||||
| [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` | Not tested yet |
|
||||
| [Linode v4](https://www.linode.com) | `linodev4` | `LINODE_TOKEN` | Not tested yet |
|
||||
| manual | - | none, but you need to run Traefik interactively, turn on `acmeLogging` to see instructions and press <kbd>Enter</kbd>. | YES |
|
||||
| [MyDNS.jp](https://www.mydns.jp/) | `mydnsjp` | `MYDNSJP_MASTER_ID`, `MYDNSJP_PASSWORD` | YES |
|
||||
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | YES |
|
||||
| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | Not tested yet |
|
||||
| [Netcup](https://www.netcup.eu/) | `netcup` | `NETCUP_CUSTOMER_NUMBER`, `NETCUP_API_KEY`, `NETCUP_API_PASSWORD` | Not tested yet |
|
||||
| [NIFCloud](https://cloud.nifty.com/service/dns.htm) | `nifcloud` | `NIFCLOUD_ACCESS_KEY_ID`, `NIFCLOUD_SECRET_ACCESS_KEY` | Not tested yet |
|
||||
| [Ns1](https://ns1.com/) | `ns1` | `NS1_API_KEY` | Not tested yet |
|
||||
| [Open Telekom Cloud](https://cloud.telekom.de) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` | Not tested yet |
|
||||
| [OVH](https://www.ovh.com) | `ovh` | `OVH_ENDPOINT`, `OVH_APPLICATION_KEY`, `OVH_APPLICATION_SECRET`, `OVH_CONSUMER_KEY` | YES |
|
||||
| [Openstack Designate](https://docs.openstack.org/designate) | `designate` | `OS_AUTH_URL`, `OS_USERNAME`, `OS_PASSWORD`, `OS_TENANT_NAME`, `OS_REGION_NAME` | YES |
|
||||
| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` | Not tested yet |
|
||||
| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` | Not tested yet |
|
||||
| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` | Not tested yet |
|
||||
| [Route 53](https://aws.amazon.com/route53/) | `route53` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `[AWS_REGION]`, `[AWS_HOSTED_ZONE_ID]` or a configured user/instance IAM profile. | YES |
|
||||
| [Sakura Cloud](https://cloud.sakura.ad.jp/) | `sakuracloud` | `SAKURACLOUD_ACCESS_TOKEN`, `SAKURACLOUD_ACCESS_TOKEN_SECRET` | Not tested yet |
|
||||
| [Selectel](https://selectel.ru/en/) | `selectel` | `SELECTEL_API_TOKEN` | YES |
|
||||
| [Stackpath](https://www.stackpath.com/) | `stackpath` | `STACKPATH_CLIENT_ID`, `STACKPATH_CLIENT_SECRET`, `STACKPATH_STACK_ID` | Not tested yet |
|
||||
| [TransIP](https://www.transip.nl/) | `transip` | `TRANSIP_ACCOUNT_NAME`, `TRANSIP_PRIVATE_KEY_PATH` | YES |
|
||||
| [VegaDNS](https://github.com/shupp/VegaDNS-API) | `vegadns` | `SECRET_VEGADNS_KEY`, `SECRET_VEGADNS_SECRET`, `VEGADNS_URL` | Not tested yet |
|
||||
| [Vscale](https://vscale.io/) | `vscale` | `VSCALE_API_TOKEN` | YES |
|
||||
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | Not tested yet |
|
||||
| [Zone.ee](https://www.zone.ee) | `zoneee` | `ZONEEE_API_USER`, `ZONEEE_API_KEY` | YES |
|
||||
|
||||
- (1): more information about the HTTP message format can be found [here](https://github.com/xenolf/lego/blob/master/providers/dns/httpreq/readme.md)
|
||||
- (2): https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application
|
||||
|
@@ -29,7 +29,7 @@ Traefik can be configured:
|
||||
|
||||
|
||||
```shell
|
||||
curl -XPUT @file "http://localhost:8080/api/providers/rest"
|
||||
curl -XPUT -d @file "http://localhost:8080/api/providers/rest"
|
||||
```
|
||||
|
||||
with `@file`:
|
||||
|
@@ -96,11 +96,12 @@ Labels, set through extensions or the property manager, can be used on services
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.enable=false` | Disable this container in Traefik |
|
||||
| `traefik.enable=false` | Disable this container in Traefik |
|
||||
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.servicefabric.groupname` | Group all services with the same name into a single backend in Traefik |
|
||||
| `traefik.servicefabric.groupweight` | Set the weighting of the current services nodes in the backend group |
|
||||
| `traefik.servicefabric.enablelabeloverrides` | Toggle whether labels can be overridden using the Service Fabric Property Manager API |
|
||||
| `traefik.servicefabric.groupname` | Group all services with the same name into a single backend in Traefik |
|
||||
| `traefik.servicefabric.groupweight` | Set the weighting of the current services nodes in the backend group |
|
||||
| `traefik.servicefabric.enablelabeloverrides` | Toggle whether labels can be overridden using the Service Fabric Property Manager API |
|
||||
| `traefik.servicefabric.endpointname` | Specify the name of the endpoint |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. |
|
||||
|
@@ -20,7 +20,7 @@
|
||||
# Buckets for latency metrics
|
||||
#
|
||||
# Optional
|
||||
# Default: [0.1, 0.3, 1.2, 5]
|
||||
# Default: [0.1, 0.3, 1.2, 5.0]
|
||||
#
|
||||
buckets = [0.1,0.3,1.2,5.0]
|
||||
|
||||
|
@@ -58,6 +58,13 @@ Traefik supports three tracing backends: Jaeger, Zipkin and DataDog.
|
||||
# Default: "127.0.0.1:6831"
|
||||
#
|
||||
localAgentHostPort = "127.0.0.1:6831"
|
||||
|
||||
# Trace Context Header Name is the http header name used to propagate tracing context.
|
||||
# This must be in lower-case to avoid mismatches when decoding incoming headers.
|
||||
#
|
||||
# Default: "uber-trace-id"
|
||||
#
|
||||
traceContextHeaderName = "uber-trace-id"
|
||||
```
|
||||
|
||||
!!! warning
|
||||
|
31
integration/fixtures/grpc/config_retry.toml
Normal file
31
integration/fixtures/grpc/config_retry.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
defaultEntryPoints = ["https"]
|
||||
|
||||
rootCAs = [ """{{ .CertContent }}""" ]
|
||||
|
||||
[retry]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.https]
|
||||
address = ":4443"
|
||||
[entryPoints.https.tls]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
certFile = """{{ .CertContent }}"""
|
||||
keyFile = """{{ .KeyContent }}"""
|
||||
|
||||
|
||||
[api]
|
||||
|
||||
[file]
|
||||
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.servers.server1]
|
||||
url = "https://127.0.0.1:{{ .GRPCServerPort }}"
|
||||
weight = 1
|
||||
|
||||
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
backend = "backend1"
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "Host:127.0.0.1"
|
@@ -417,3 +417,45 @@ func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) {
|
||||
})
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
func (s *GRPCSuite) TestGRPCWithRetry(c *check.C) {
|
||||
lis, err := net.Listen("tcp", ":0")
|
||||
_, port, err := net.SplitHostPort(lis.Addr().String())
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
go func() {
|
||||
err := startGRPCServer(lis, &myserver{})
|
||||
c.Log(err)
|
||||
c.Assert(err, check.IsNil)
|
||||
}()
|
||||
|
||||
file := s.adaptFile(c, "fixtures/grpc/config_retry.toml", struct {
|
||||
CertContent string
|
||||
KeyContent string
|
||||
GRPCServerPort string
|
||||
}{
|
||||
CertContent: string(LocalhostCert),
|
||||
KeyContent: string(LocalhostKey),
|
||||
GRPCServerPort: port,
|
||||
})
|
||||
|
||||
defer os.Remove(file)
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
defer display(c)
|
||||
|
||||
err = cmd.Start()
|
||||
c.Assert(err, check.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 1*time.Second, try.BodyContains("Host:127.0.0.1"))
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
var response string
|
||||
err = try.Do(1*time.Second, func() error {
|
||||
response, err = callHelloClientGRPC("World", true)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(response, check.Equals, "Hello World")
|
||||
}
|
||||
|
@@ -110,6 +110,7 @@ type retryResponseWriterWithoutCloseNotify struct {
|
||||
responseWriter http.ResponseWriter
|
||||
headers http.Header
|
||||
shouldRetry bool
|
||||
written bool
|
||||
}
|
||||
|
||||
func (rr *retryResponseWriterWithoutCloseNotify) ShouldRetry() bool {
|
||||
@@ -121,6 +122,9 @@ func (rr *retryResponseWriterWithoutCloseNotify) DisableRetries() {
|
||||
}
|
||||
|
||||
func (rr *retryResponseWriterWithoutCloseNotify) Header() http.Header {
|
||||
if rr.written {
|
||||
return rr.responseWriter.Header()
|
||||
}
|
||||
return rr.headers
|
||||
}
|
||||
|
||||
@@ -155,6 +159,7 @@ func (rr *retryResponseWriterWithoutCloseNotify) WriteHeader(code int) {
|
||||
}
|
||||
|
||||
rr.responseWriter.WriteHeader(code)
|
||||
rr.written = true
|
||||
}
|
||||
|
||||
func (rr *retryResponseWriterWithoutCloseNotify) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/uber/jaeger-client-go"
|
||||
jaegercfg "github.com/uber/jaeger-client-go/config"
|
||||
jaegermet "github.com/uber/jaeger-lib/metrics"
|
||||
)
|
||||
@@ -14,10 +15,11 @@ const Name = "jaeger"
|
||||
|
||||
// Config provides configuration settings for a jaeger tracer
|
||||
type Config struct {
|
||||
SamplingServerURL string `description:"set the sampling server url." export:"false"`
|
||||
SamplingType string `description:"set the sampling type." export:"true"`
|
||||
SamplingParam float64 `description:"set the sampling parameter." export:"true"`
|
||||
LocalAgentHostPort string `description:"set jaeger-agent's host:port that the reporter will used." export:"false"`
|
||||
SamplingServerURL string `description:"set the sampling server url." export:"false"`
|
||||
SamplingType string `description:"set the sampling type." export:"true"`
|
||||
SamplingParam float64 `description:"set the sampling parameter." export:"true"`
|
||||
LocalAgentHostPort string `description:"set jaeger-agent's host:port that the reporter will used." export:"false"`
|
||||
TraceContextHeaderName string `description:"set the header to use for the trace-id." export:"true"`
|
||||
}
|
||||
|
||||
// Setup sets up the tracer
|
||||
@@ -32,6 +34,9 @@ func (c *Config) Setup(componentName string) (opentracing.Tracer, io.Closer, err
|
||||
LogSpans: true,
|
||||
LocalAgentHostPort: c.LocalAgentHostPort,
|
||||
},
|
||||
Headers: &jaeger.HeadersConfig{
|
||||
TraceContextHeaderName: c.TraceContextHeaderName,
|
||||
},
|
||||
}
|
||||
|
||||
jMetricsFactory := jaegermet.NullFactory
|
||||
|
@@ -945,8 +945,12 @@ func getFrontendRedirect(i *extensionsv1beta1.Ingress, baseName, path string) *t
|
||||
permanent := getBoolValue(i.Annotations, annotationKubernetesRedirectPermanent, false)
|
||||
|
||||
if appRoot := getStringValue(i.Annotations, annotationKubernetesAppRoot, ""); appRoot != "" && (path == "/" || path == "") {
|
||||
regex := fmt.Sprintf("%s$", baseName)
|
||||
if path == "" {
|
||||
regex = fmt.Sprintf("%s/$", baseName)
|
||||
}
|
||||
return &types.Redirect{
|
||||
Regex: fmt.Sprintf("%s$", baseName),
|
||||
Regex: regex,
|
||||
Replacement: fmt.Sprintf("%s/%s", strings.TrimRight(baseName, "/"), strings.TrimLeft(appRoot, "/")),
|
||||
Permanent: permanent,
|
||||
}
|
||||
|
@@ -1686,7 +1686,7 @@ rateset:
|
||||
),
|
||||
frontend("root3",
|
||||
passHostHeader(),
|
||||
redirectRegex("root3$", "root3/root"),
|
||||
redirectRegex("root3/$", "root3/root"),
|
||||
routes(
|
||||
route("root3", "Host:root3"),
|
||||
),
|
||||
|
@@ -71,9 +71,18 @@ func (p *Provider) apiProvide(configurationChan chan<- types.ConfigMessage, pool
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
var stacks = listRancherStacks(rancherClient)
|
||||
var services = listRancherServices(rancherClient)
|
||||
var container = listRancherContainer(rancherClient)
|
||||
stacks, err := listRancherStacks(rancherClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
services, err := listRancherServices(rancherClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
container, err := listRancherContainer(rancherClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var rancherData = parseAPISourcedRancherData(stacks, services, container)
|
||||
|
||||
@@ -94,20 +103,29 @@ func (p *Provider) apiProvide(configurationChan chan<- types.ConfigMessage, pool
|
||||
|
||||
if errAPI != nil {
|
||||
log.Errorf("Cannot establish connection: %+v, Rancher API return: %+v; Skipping refresh Data from Rancher API.", errAPI, checkAPI)
|
||||
} else {
|
||||
log.Debugf("Refreshing new Data from Rancher API")
|
||||
stacks := listRancherStacks(rancherClient)
|
||||
services := listRancherServices(rancherClient)
|
||||
container := listRancherContainer(rancherClient)
|
||||
continue
|
||||
}
|
||||
log.Debugf("Refreshing new Data from Rancher API")
|
||||
stacks, err = listRancherStacks(rancherClient)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
services, err = listRancherServices(rancherClient)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
container, err = listRancherContainer(rancherClient)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
rancherData := parseAPISourcedRancherData(stacks, services, container)
|
||||
rancherData := parseAPISourcedRancherData(stacks, services, container)
|
||||
|
||||
configuration := p.buildConfiguration(rancherData)
|
||||
if configuration != nil {
|
||||
configurationChan <- types.ConfigMessage{
|
||||
ProviderName: "rancher",
|
||||
Configuration: configuration,
|
||||
}
|
||||
configuration := p.buildConfiguration(rancherData)
|
||||
if configuration != nil {
|
||||
configurationChan <- types.ConfigMessage{
|
||||
ProviderName: "rancher",
|
||||
Configuration: configuration,
|
||||
}
|
||||
}
|
||||
case <-stop:
|
||||
@@ -133,7 +151,7 @@ func (p *Provider) apiProvide(configurationChan chan<- types.ConfigMessage, pool
|
||||
return nil
|
||||
}
|
||||
|
||||
func listRancherStacks(client *rancher.RancherClient) []*rancher.Stack {
|
||||
func listRancherStacks(client *rancher.RancherClient) ([]*rancher.Stack, error) {
|
||||
|
||||
var stackList []*rancher.Stack
|
||||
|
||||
@@ -147,10 +165,10 @@ func listRancherStacks(client *rancher.RancherClient) []*rancher.Stack {
|
||||
stackList = append(stackList, &stacks.Data[k])
|
||||
}
|
||||
|
||||
return stackList
|
||||
return stackList, err
|
||||
}
|
||||
|
||||
func listRancherServices(client *rancher.RancherClient) []*rancher.Service {
|
||||
func listRancherServices(client *rancher.RancherClient) ([]*rancher.Service, error) {
|
||||
|
||||
var servicesList []*rancher.Service
|
||||
|
||||
@@ -164,10 +182,10 @@ func listRancherServices(client *rancher.RancherClient) []*rancher.Service {
|
||||
servicesList = append(servicesList, &services.Data[k])
|
||||
}
|
||||
|
||||
return servicesList
|
||||
return servicesList, err
|
||||
}
|
||||
|
||||
func listRancherContainer(client *rancher.RancherClient) []*rancher.Container {
|
||||
func listRancherContainer(client *rancher.RancherClient) ([]*rancher.Container, error) {
|
||||
|
||||
var containerList []*rancher.Container
|
||||
|
||||
@@ -175,6 +193,7 @@ func listRancherContainer(client *rancher.RancherClient) []*rancher.Container {
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Cannot get Provider Services %+v", err)
|
||||
return containerList, err
|
||||
}
|
||||
|
||||
valid := true
|
||||
@@ -195,7 +214,7 @@ func listRancherContainer(client *rancher.RancherClient) []*rancher.Container {
|
||||
}
|
||||
}
|
||||
|
||||
return containerList
|
||||
return containerList, err
|
||||
}
|
||||
|
||||
func parseAPISourcedRancherData(stacks []*rancher.Stack, services []*rancher.Service, containers []*rancher.Container) []rancherData {
|
||||
|
@@ -211,6 +211,7 @@ func (s *Server) getRoundTripper(entryPointName string, passTLSCert bool, tls *t
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create TLSClientConfig: %v", err)
|
||||
}
|
||||
tlsConfig.InsecureSkipVerify = s.globalConfiguration.InsecureSkipVerify
|
||||
|
||||
transport, err := createHTTPTransport(s.globalConfiguration)
|
||||
if err != nil {
|
||||
|
53
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/client-v1/client.go
generated
vendored
53
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/client-v1/client.go
generated
vendored
@@ -6,8 +6,11 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
@@ -16,7 +19,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
libraryVersion = "0.6.0"
|
||||
libraryVersion = "0.6.2"
|
||||
// UserAgent is the User-Agent value sent for all requests
|
||||
UserAgent = "Akamai-Open-Edgegrid-golang/" + libraryVersion + " golang/" + strings.TrimPrefix(runtime.Version(), "go")
|
||||
// Client is the *http.Client to use
|
||||
@@ -61,13 +64,21 @@ func NewRequest(config edgegrid.Config, method, path string, body io.Reader) (*h
|
||||
// NewJSONRequest creates an HTTP request that can be sent to the Akamai APIs with a JSON body
|
||||
// The JSON body is encoded and the Content-Type/Accept headers are set automatically.
|
||||
func NewJSONRequest(config edgegrid.Config, method, path string, body interface{}) (*http.Request, error) {
|
||||
jsonBody, err := jsonhooks.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var req *http.Request
|
||||
var err error
|
||||
|
||||
if body != nil {
|
||||
jsonBody, err := jsonhooks.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := bytes.NewReader(jsonBody)
|
||||
req, err = NewRequest(config, method, path, buf)
|
||||
} else {
|
||||
req, err = NewRequest(config, method, path, nil)
|
||||
}
|
||||
|
||||
buf := bytes.NewReader(jsonBody)
|
||||
req, err := NewRequest(config, method, path, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -78,6 +89,36 @@ func NewJSONRequest(config edgegrid.Config, method, path string, body interface{
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// NewMultiPartFormDataRequest creates an HTTP request that uploads a file to the Akamai API
|
||||
func NewMultiPartFormDataRequest(config edgegrid.Config, uriPath, filePath string, otherFormParams map[string]string) (*http.Request, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
// TODO: make this field name configurable
|
||||
part, err := writer.CreateFormFile("importFile", filepath.Base(filePath))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = io.Copy(part, file)
|
||||
|
||||
for key, val := range otherFormParams {
|
||||
_ = writer.WriteField(key, val)
|
||||
}
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := NewRequest(config, "POST", uriPath, body)
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
return req, err
|
||||
}
|
||||
|
||||
// Do performs a given HTTP Request, signed with the Akamai OPEN Edgegrid
|
||||
// Authorization header. An edgegrid.Response or an error is returned.
|
||||
func Do(config edgegrid.Config, req *http.Request) (*http.Response, error) {
|
||||
|
47
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/client-v1/errors.go
generated
vendored
47
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/client-v1/errors.go
generated
vendored
@@ -12,22 +12,42 @@ import (
|
||||
// APIError exposes an Akamai OPEN Edgegrid Error
|
||||
type APIError struct {
|
||||
error
|
||||
Type string `json:"type"`
|
||||
Title string `json:"title"`
|
||||
Status int `json:"status"`
|
||||
Detail string `json:"detail"`
|
||||
Instance string `json:"instance"`
|
||||
Method string `json:"method"`
|
||||
ServerIP string `json:"serverIp"`
|
||||
ClientIP string `json:"clientIp"`
|
||||
RequestID string `json:"requestId"`
|
||||
RequestTime string `json:"requestTime"`
|
||||
Response *http.Response `json:"-"`
|
||||
RawBody string `json:"-"`
|
||||
Type string `json:"type"`
|
||||
Title string `json:"title"`
|
||||
Status int `json:"status"`
|
||||
Detail string `json:"detail"`
|
||||
Errors []APIErrorDetail `json:"errors"`
|
||||
Problems []APIErrorDetail `json:"problems"`
|
||||
Instance string `json:"instance"`
|
||||
Method string `json:"method"`
|
||||
ServerIP string `json:"serverIp"`
|
||||
ClientIP string `json:"clientIp"`
|
||||
RequestID string `json:"requestId"`
|
||||
RequestTime string `json:"requestTime"`
|
||||
Response *http.Response `json:"-"`
|
||||
RawBody string `json:"-"`
|
||||
}
|
||||
|
||||
type APIErrorDetail struct {
|
||||
Type string `json:"type"`
|
||||
Title string `json:"title"`
|
||||
Detail string `json:"detail"`
|
||||
RejectedValue string `json:"rejectedValue"`
|
||||
}
|
||||
|
||||
func (error APIError) Error() string {
|
||||
return strings.TrimSpace(fmt.Sprintf("API Error: %d %s %s More Info %s", error.Status, error.Title, error.Detail, error.Type))
|
||||
var errorDetails string
|
||||
if len(error.Errors) > 0 {
|
||||
for _, e := range error.Errors {
|
||||
errorDetails = fmt.Sprintf("%s \n %s", errorDetails, e)
|
||||
}
|
||||
}
|
||||
if len(error.Problems) > 0 {
|
||||
for _, e := range error.Problems {
|
||||
errorDetails = fmt.Sprintf("%s \n %s", errorDetails, e)
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(fmt.Sprintf("API Error: %d %s %s More Info %s\n %s", error.Status, error.Title, error.Detail, error.Type, errorDetails))
|
||||
}
|
||||
|
||||
// NewAPIError creates a new API error based on a Response,
|
||||
@@ -45,7 +65,6 @@ func NewAPIError(response *http.Response) APIError {
|
||||
// other purposes.
|
||||
func NewAPIErrorFromBody(response *http.Response, body []byte) APIError {
|
||||
error := APIError{}
|
||||
|
||||
if err := jsonhooks.Unmarshal(body, &error); err == nil {
|
||||
error.Status = response.StatusCode
|
||||
error.Title = response.Status
|
||||
|
19
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1/record.go
generated
vendored
19
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1/record.go
generated
vendored
@@ -1323,15 +1323,16 @@ func (record *RrsigRecord) ToMap() map[string]interface{} {
|
||||
}
|
||||
|
||||
type SoaRecord struct {
|
||||
fieldMap []string `json:"-"`
|
||||
TTL int `json:"ttl,omitempty"`
|
||||
Originserver string `json:"originserver,omitempty"`
|
||||
Contact string `json:"contact,omitempty"`
|
||||
Serial uint `json:"serial,omitempty"`
|
||||
Refresh int `json:"refresh,omitempty"`
|
||||
Retry int `json:"retry,omitempty"`
|
||||
Expire int `json:"expire,omitempty"`
|
||||
Minimum uint `json:"minimum,omitempty"`
|
||||
fieldMap []string `json:"-"`
|
||||
originalSerial uint `json:"-"`
|
||||
TTL int `json:"ttl,omitempty"`
|
||||
Originserver string `json:"originserver,omitempty"`
|
||||
Contact string `json:"contact,omitempty"`
|
||||
Serial uint `json:"serial,omitempty"`
|
||||
Refresh int `json:"refresh,omitempty"`
|
||||
Retry int `json:"retry,omitempty"`
|
||||
Expire int `json:"expire,omitempty"`
|
||||
Minimum uint `json:"minimum,omitempty"`
|
||||
}
|
||||
|
||||
func NewSoaRecord() *SoaRecord {
|
||||
|
34
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1/zone.go
generated
vendored
34
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1/zone.go
generated
vendored
@@ -82,7 +82,7 @@ func GetZone(hostname string) (*Zone, error) {
|
||||
} else if res.StatusCode == 404 {
|
||||
return nil, &ZoneError{zoneName: hostname}
|
||||
} else {
|
||||
err = client.BodyJSON(res, &zone)
|
||||
err = client.BodyJSON(res, zone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -762,11 +762,18 @@ func (zone *Zone) removeTxtRecord(record *TxtRecord) error {
|
||||
return errors.New("Txt Record not found")
|
||||
}
|
||||
|
||||
func (zone *Zone) PreMarshalJSON() error {
|
||||
func (zone *Zone) PostUnmarshalJSON() error {
|
||||
if zone.Zone.Soa.Serial > 0 {
|
||||
zone.Zone.Soa.Serial = zone.Zone.Soa.Serial + 1
|
||||
} else {
|
||||
zone.Zone.Soa.originalSerial = zone.Zone.Soa.Serial
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (zone *Zone) PreMarshalJSON() error {
|
||||
if zone.Zone.Soa.Serial == 0 {
|
||||
zone.Zone.Soa.Serial = uint(time.Now().Unix())
|
||||
} else if zone.Zone.Soa.Serial == zone.Zone.Soa.originalSerial {
|
||||
zone.Zone.Soa.Serial = zone.Zone.Soa.Serial + 1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -786,21 +793,24 @@ func (zone *Zone) validateCnames() (bool, []name) {
|
||||
}
|
||||
|
||||
func (zone *Zone) removeCnameName(host string) {
|
||||
for i, v := range cnameNames {
|
||||
if v.name == host {
|
||||
r := cnameNames[:i]
|
||||
cnameNames = append(r, cnameNames[i+1:]...)
|
||||
var ncn []name
|
||||
for _, v := range cnameNames {
|
||||
if v.name != host {
|
||||
ncn =append(ncn, v)
|
||||
}
|
||||
}
|
||||
cnameNames = ncn
|
||||
}
|
||||
|
||||
|
||||
func (zone *Zone) removeNonCnameName(host string) {
|
||||
for i, v := range nonCnameNames {
|
||||
if v.name == host {
|
||||
r := nonCnameNames[:i]
|
||||
nonCnameNames = append(r, nonCnameNames[i+1:]...)
|
||||
var ncn []name
|
||||
for _, v := range nonCnameNames {
|
||||
if v.name != host {
|
||||
ncn =append(ncn, v)
|
||||
}
|
||||
}
|
||||
nonCnameNames = ncn
|
||||
}
|
||||
|
||||
func (zone *Zone) FindRecords(recordType string, options map[string]interface{}) []DNSRecord {
|
||||
|
4
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid/config.go
generated
vendored
4
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid/config.go
generated
vendored
@@ -7,7 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-ini/ini"
|
||||
"gopkg.in/mattes/go-expand-tilde.v1"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
// Config struct provides all the necessary fields to
|
||||
@@ -86,7 +86,7 @@ func InitEdgeRc(filepath string, section string) (Config, error) {
|
||||
// Tilde seems to be not working when passing ~/.edgerc as file
|
||||
// Takes current user and use home dir instead
|
||||
|
||||
path, err := tilde.Expand(filepath)
|
||||
path, err := homedir.Expand(filepath)
|
||||
|
||||
if err != nil {
|
||||
return c, fmt.Errorf(errorMap[ErrHomeDirNotFound], err)
|
||||
|
6
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid/signer.go
generated
vendored
6
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid/signer.go
generated
vendored
@@ -14,8 +14,8 @@ import (
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/tuvistavie/securerandom"
|
||||
)
|
||||
|
||||
const defaultSection = "DEFAULT"
|
||||
@@ -49,12 +49,12 @@ func makeEdgeTimeStamp() string {
|
||||
// It is a random string used to detect replayed request messages.
|
||||
// A GUID is recommended.
|
||||
func createNonce() string {
|
||||
uuid, err := securerandom.Uuid()
|
||||
uuid, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
log.Errorf(errorMap[ErrUUIDGenerateFailed], err)
|
||||
return ""
|
||||
}
|
||||
return uuid
|
||||
return uuid.String()
|
||||
}
|
||||
|
||||
func stringMinifier(in string) (out string) {
|
||||
|
2
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/jsonhooks-v1/jsonhooks.go
generated
vendored
2
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/jsonhooks-v1/jsonhooks.go
generated
vendored
@@ -44,7 +44,7 @@ type PreJSONMarshaler interface {
|
||||
// ImplementsPreJSONMarshaler checks for support for the PreMarshalJSON pre-hook
|
||||
func ImplementsPreJSONMarshaler(v interface{}) bool {
|
||||
value := reflect.ValueOf(v)
|
||||
if value.Kind() == reflect.Ptr && value.IsNil() {
|
||||
if !value.IsValid() {
|
||||
return false
|
||||
}
|
||||
|
||||
|
11
vendor/github.com/cenk/backoff/context.go
generated
vendored
11
vendor/github.com/cenk/backoff/context.go
generated
vendored
@@ -1,9 +1,8 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// BackOffContext is a backoff policy that stops retrying after the context
|
||||
@@ -52,9 +51,13 @@ func (b *backOffContext) Context() context.Context {
|
||||
|
||||
func (b *backOffContext) NextBackOff() time.Duration {
|
||||
select {
|
||||
case <-b.Context().Done():
|
||||
case <-b.ctx.Done():
|
||||
return Stop
|
||||
default:
|
||||
return b.BackOff.NextBackOff()
|
||||
}
|
||||
next := b.BackOff.NextBackOff()
|
||||
if deadline, ok := b.ctx.Deadline(); ok && deadline.Sub(time.Now()) < next {
|
||||
return Stop
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
7
vendor/github.com/cenk/backoff/exponential.go
generated
vendored
7
vendor/github.com/cenk/backoff/exponential.go
generated
vendored
@@ -63,7 +63,6 @@ type ExponentialBackOff struct {
|
||||
|
||||
currentInterval time.Duration
|
||||
startTime time.Time
|
||||
random *rand.Rand
|
||||
}
|
||||
|
||||
// Clock is an interface that returns current time for BackOff.
|
||||
@@ -89,7 +88,6 @@ func NewExponentialBackOff() *ExponentialBackOff {
|
||||
MaxInterval: DefaultMaxInterval,
|
||||
MaxElapsedTime: DefaultMaxElapsedTime,
|
||||
Clock: SystemClock,
|
||||
random: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
}
|
||||
b.Reset()
|
||||
return b
|
||||
@@ -118,10 +116,7 @@ func (b *ExponentialBackOff) NextBackOff() time.Duration {
|
||||
return Stop
|
||||
}
|
||||
defer b.incrementCurrentInterval()
|
||||
if b.random == nil {
|
||||
b.random = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
}
|
||||
return getRandomValueFromInterval(b.RandomizationFactor, b.random.Float64(), b.currentInterval)
|
||||
return getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
|
||||
}
|
||||
|
||||
// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance
|
||||
|
12
vendor/github.com/cenk/backoff/retry.go
generated
vendored
12
vendor/github.com/cenk/backoff/retry.go
generated
vendored
@@ -15,7 +15,6 @@ type Notify func(error, time.Duration)
|
||||
|
||||
// Retry the operation o until it does not return error or BackOff stops.
|
||||
// o is guaranteed to be run at least once.
|
||||
// It is the caller's responsibility to reset b after Retry returns.
|
||||
//
|
||||
// If o returns a *PermanentError, the operation is not retried, and the
|
||||
// wrapped error is returned.
|
||||
@@ -29,6 +28,7 @@ func Retry(o Operation, b BackOff) error { return RetryNotify(o, b, nil) }
|
||||
func RetryNotify(operation Operation, b BackOff, notify Notify) error {
|
||||
var err error
|
||||
var next time.Duration
|
||||
var t *time.Timer
|
||||
|
||||
cb := ensureContext(b)
|
||||
|
||||
@@ -42,7 +42,7 @@ func RetryNotify(operation Operation, b BackOff, notify Notify) error {
|
||||
return permanent.Err
|
||||
}
|
||||
|
||||
if next = b.NextBackOff(); next == Stop {
|
||||
if next = cb.NextBackOff(); next == Stop {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -50,11 +50,15 @@ func RetryNotify(operation Operation, b BackOff, notify Notify) error {
|
||||
notify(err, next)
|
||||
}
|
||||
|
||||
t := time.NewTimer(next)
|
||||
if t == nil {
|
||||
t = time.NewTimer(next)
|
||||
defer t.Stop()
|
||||
} else {
|
||||
t.Reset(next)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-cb.Context().Done():
|
||||
t.Stop()
|
||||
return err
|
||||
case <-t.C:
|
||||
}
|
||||
|
2
vendor/github.com/cenk/backoff/ticker.go
generated
vendored
2
vendor/github.com/cenk/backoff/ticker.go
generated
vendored
@@ -1,7 +1,6 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -34,7 +33,6 @@ func NewTicker(b BackOff) *Ticker {
|
||||
}
|
||||
t.b.Reset()
|
||||
go t.run()
|
||||
runtime.SetFinalizer(t, (*Ticker).Stop)
|
||||
return t
|
||||
}
|
||||
|
||||
|
20
vendor/github.com/cenkalti/backoff/LICENSE
generated
vendored
Normal file
20
vendor/github.com/cenkalti/backoff/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Cenk Altı
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
66
vendor/github.com/cenkalti/backoff/backoff.go
generated
vendored
Normal file
66
vendor/github.com/cenkalti/backoff/backoff.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
// Package backoff implements backoff algorithms for retrying operations.
|
||||
//
|
||||
// Use Retry function for retrying operations that may fail.
|
||||
// If Retry does not meet your needs,
|
||||
// copy/paste the function into your project and modify as you wish.
|
||||
//
|
||||
// There is also Ticker type similar to time.Ticker.
|
||||
// You can use it if you need to work with channels.
|
||||
//
|
||||
// See Examples section below for usage examples.
|
||||
package backoff
|
||||
|
||||
import "time"
|
||||
|
||||
// BackOff is a backoff policy for retrying an operation.
|
||||
type BackOff interface {
|
||||
// NextBackOff returns the duration to wait before retrying the operation,
|
||||
// or backoff. Stop to indicate that no more retries should be made.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// duration := backoff.NextBackOff();
|
||||
// if (duration == backoff.Stop) {
|
||||
// // Do not retry operation.
|
||||
// } else {
|
||||
// // Sleep for duration and retry operation.
|
||||
// }
|
||||
//
|
||||
NextBackOff() time.Duration
|
||||
|
||||
// Reset to initial state.
|
||||
Reset()
|
||||
}
|
||||
|
||||
// Stop indicates that no more retries should be made for use in NextBackOff().
|
||||
const Stop time.Duration = -1
|
||||
|
||||
// ZeroBackOff is a fixed backoff policy whose backoff time is always zero,
|
||||
// meaning that the operation is retried immediately without waiting, indefinitely.
|
||||
type ZeroBackOff struct{}
|
||||
|
||||
func (b *ZeroBackOff) Reset() {}
|
||||
|
||||
func (b *ZeroBackOff) NextBackOff() time.Duration { return 0 }
|
||||
|
||||
// StopBackOff is a fixed backoff policy that always returns backoff.Stop for
|
||||
// NextBackOff(), meaning that the operation should never be retried.
|
||||
type StopBackOff struct{}
|
||||
|
||||
func (b *StopBackOff) Reset() {}
|
||||
|
||||
func (b *StopBackOff) NextBackOff() time.Duration { return Stop }
|
||||
|
||||
// ConstantBackOff is a backoff policy that always returns the same backoff delay.
|
||||
// This is in contrast to an exponential backoff policy,
|
||||
// which returns a delay that grows longer as you call NextBackOff() over and over again.
|
||||
type ConstantBackOff struct {
|
||||
Interval time.Duration
|
||||
}
|
||||
|
||||
func (b *ConstantBackOff) Reset() {}
|
||||
func (b *ConstantBackOff) NextBackOff() time.Duration { return b.Interval }
|
||||
|
||||
func NewConstantBackOff(d time.Duration) *ConstantBackOff {
|
||||
return &ConstantBackOff{Interval: d}
|
||||
}
|
63
vendor/github.com/cenkalti/backoff/context.go
generated
vendored
Normal file
63
vendor/github.com/cenkalti/backoff/context.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BackOffContext is a backoff policy that stops retrying after the context
|
||||
// is canceled.
|
||||
type BackOffContext interface {
|
||||
BackOff
|
||||
Context() context.Context
|
||||
}
|
||||
|
||||
type backOffContext struct {
|
||||
BackOff
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// WithContext returns a BackOffContext with context ctx
|
||||
//
|
||||
// ctx must not be nil
|
||||
func WithContext(b BackOff, ctx context.Context) BackOffContext {
|
||||
if ctx == nil {
|
||||
panic("nil context")
|
||||
}
|
||||
|
||||
if b, ok := b.(*backOffContext); ok {
|
||||
return &backOffContext{
|
||||
BackOff: b.BackOff,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
return &backOffContext{
|
||||
BackOff: b,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
func ensureContext(b BackOff) BackOffContext {
|
||||
if cb, ok := b.(BackOffContext); ok {
|
||||
return cb
|
||||
}
|
||||
return WithContext(b, context.Background())
|
||||
}
|
||||
|
||||
func (b *backOffContext) Context() context.Context {
|
||||
return b.ctx
|
||||
}
|
||||
|
||||
func (b *backOffContext) NextBackOff() time.Duration {
|
||||
select {
|
||||
case <-b.ctx.Done():
|
||||
return Stop
|
||||
default:
|
||||
}
|
||||
next := b.BackOff.NextBackOff()
|
||||
if deadline, ok := b.ctx.Deadline(); ok && deadline.Sub(time.Now()) < next {
|
||||
return Stop
|
||||
}
|
||||
return next
|
||||
}
|
153
vendor/github.com/cenkalti/backoff/exponential.go
generated
vendored
Normal file
153
vendor/github.com/cenkalti/backoff/exponential.go
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
ExponentialBackOff is a backoff implementation that increases the backoff
|
||||
period for each retry attempt using a randomization function that grows exponentially.
|
||||
|
||||
NextBackOff() is calculated using the following formula:
|
||||
|
||||
randomized interval =
|
||||
RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor])
|
||||
|
||||
In other words NextBackOff() will range between the randomization factor
|
||||
percentage below and above the retry interval.
|
||||
|
||||
For example, given the following parameters:
|
||||
|
||||
RetryInterval = 2
|
||||
RandomizationFactor = 0.5
|
||||
Multiplier = 2
|
||||
|
||||
the actual backoff period used in the next retry attempt will range between 1 and 3 seconds,
|
||||
multiplied by the exponential, that is, between 2 and 6 seconds.
|
||||
|
||||
Note: MaxInterval caps the RetryInterval and not the randomized interval.
|
||||
|
||||
If the time elapsed since an ExponentialBackOff instance is created goes past the
|
||||
MaxElapsedTime, then the method NextBackOff() starts returning backoff.Stop.
|
||||
|
||||
The elapsed time can be reset by calling Reset().
|
||||
|
||||
Example: Given the following default arguments, for 10 tries the sequence will be,
|
||||
and assuming we go over the MaxElapsedTime on the 10th try:
|
||||
|
||||
Request # RetryInterval (seconds) Randomized Interval (seconds)
|
||||
|
||||
1 0.5 [0.25, 0.75]
|
||||
2 0.75 [0.375, 1.125]
|
||||
3 1.125 [0.562, 1.687]
|
||||
4 1.687 [0.8435, 2.53]
|
||||
5 2.53 [1.265, 3.795]
|
||||
6 3.795 [1.897, 5.692]
|
||||
7 5.692 [2.846, 8.538]
|
||||
8 8.538 [4.269, 12.807]
|
||||
9 12.807 [6.403, 19.210]
|
||||
10 19.210 backoff.Stop
|
||||
|
||||
Note: Implementation is not thread-safe.
|
||||
*/
|
||||
type ExponentialBackOff struct {
|
||||
InitialInterval time.Duration
|
||||
RandomizationFactor float64
|
||||
Multiplier float64
|
||||
MaxInterval time.Duration
|
||||
// After MaxElapsedTime the ExponentialBackOff stops.
|
||||
// It never stops if MaxElapsedTime == 0.
|
||||
MaxElapsedTime time.Duration
|
||||
Clock Clock
|
||||
|
||||
currentInterval time.Duration
|
||||
startTime time.Time
|
||||
}
|
||||
|
||||
// Clock is an interface that returns current time for BackOff.
|
||||
type Clock interface {
|
||||
Now() time.Time
|
||||
}
|
||||
|
||||
// Default values for ExponentialBackOff.
|
||||
const (
|
||||
DefaultInitialInterval = 500 * time.Millisecond
|
||||
DefaultRandomizationFactor = 0.5
|
||||
DefaultMultiplier = 1.5
|
||||
DefaultMaxInterval = 60 * time.Second
|
||||
DefaultMaxElapsedTime = 15 * time.Minute
|
||||
)
|
||||
|
||||
// NewExponentialBackOff creates an instance of ExponentialBackOff using default values.
|
||||
func NewExponentialBackOff() *ExponentialBackOff {
|
||||
b := &ExponentialBackOff{
|
||||
InitialInterval: DefaultInitialInterval,
|
||||
RandomizationFactor: DefaultRandomizationFactor,
|
||||
Multiplier: DefaultMultiplier,
|
||||
MaxInterval: DefaultMaxInterval,
|
||||
MaxElapsedTime: DefaultMaxElapsedTime,
|
||||
Clock: SystemClock,
|
||||
}
|
||||
b.Reset()
|
||||
return b
|
||||
}
|
||||
|
||||
type systemClock struct{}
|
||||
|
||||
func (t systemClock) Now() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
// SystemClock implements Clock interface that uses time.Now().
|
||||
var SystemClock = systemClock{}
|
||||
|
||||
// Reset the interval back to the initial retry interval and restarts the timer.
|
||||
func (b *ExponentialBackOff) Reset() {
|
||||
b.currentInterval = b.InitialInterval
|
||||
b.startTime = b.Clock.Now()
|
||||
}
|
||||
|
||||
// NextBackOff calculates the next backoff interval using the formula:
|
||||
// Randomized interval = RetryInterval +/- (RandomizationFactor * RetryInterval)
|
||||
func (b *ExponentialBackOff) NextBackOff() time.Duration {
|
||||
// Make sure we have not gone over the maximum elapsed time.
|
||||
if b.MaxElapsedTime != 0 && b.GetElapsedTime() > b.MaxElapsedTime {
|
||||
return Stop
|
||||
}
|
||||
defer b.incrementCurrentInterval()
|
||||
return getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
|
||||
}
|
||||
|
||||
// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance
|
||||
// is created and is reset when Reset() is called.
|
||||
//
|
||||
// The elapsed time is computed using time.Now().UnixNano(). It is
|
||||
// safe to call even while the backoff policy is used by a running
|
||||
// ticker.
|
||||
func (b *ExponentialBackOff) GetElapsedTime() time.Duration {
|
||||
return b.Clock.Now().Sub(b.startTime)
|
||||
}
|
||||
|
||||
// Increments the current interval by multiplying it with the multiplier.
|
||||
func (b *ExponentialBackOff) incrementCurrentInterval() {
|
||||
// Check for overflow, if overflow is detected set the current interval to the max interval.
|
||||
if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
|
||||
b.currentInterval = b.MaxInterval
|
||||
} else {
|
||||
b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a random value from the following interval:
|
||||
// [randomizationFactor * currentInterval, randomizationFactor * currentInterval].
|
||||
func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
|
||||
var delta = randomizationFactor * float64(currentInterval)
|
||||
var minInterval = float64(currentInterval) - delta
|
||||
var maxInterval = float64(currentInterval) + delta
|
||||
|
||||
// Get a random value from the range [minInterval, maxInterval].
|
||||
// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
|
||||
// we want a 33% chance for selecting either 1, 2 or 3.
|
||||
return time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
|
||||
}
|
82
vendor/github.com/cenkalti/backoff/retry.go
generated
vendored
Normal file
82
vendor/github.com/cenkalti/backoff/retry.go
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
package backoff
|
||||
|
||||
import "time"
|
||||
|
||||
// An Operation is executing by Retry() or RetryNotify().
|
||||
// The operation will be retried using a backoff policy if it returns an error.
|
||||
type Operation func() error
|
||||
|
||||
// Notify is a notify-on-error function. It receives an operation error and
|
||||
// backoff delay if the operation failed (with an error).
|
||||
//
|
||||
// NOTE that if the backoff policy stated to stop retrying,
|
||||
// the notify function isn't called.
|
||||
type Notify func(error, time.Duration)
|
||||
|
||||
// Retry the operation o until it does not return error or BackOff stops.
|
||||
// o is guaranteed to be run at least once.
|
||||
//
|
||||
// If o returns a *PermanentError, the operation is not retried, and the
|
||||
// wrapped error is returned.
|
||||
//
|
||||
// Retry sleeps the goroutine for the duration returned by BackOff after a
|
||||
// failed operation returns.
|
||||
func Retry(o Operation, b BackOff) error { return RetryNotify(o, b, nil) }
|
||||
|
||||
// RetryNotify calls notify function with the error and wait duration
|
||||
// for each failed attempt before sleep.
|
||||
func RetryNotify(operation Operation, b BackOff, notify Notify) error {
|
||||
var err error
|
||||
var next time.Duration
|
||||
var t *time.Timer
|
||||
|
||||
cb := ensureContext(b)
|
||||
|
||||
b.Reset()
|
||||
for {
|
||||
if err = operation(); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if permanent, ok := err.(*PermanentError); ok {
|
||||
return permanent.Err
|
||||
}
|
||||
|
||||
if next = cb.NextBackOff(); next == Stop {
|
||||
return err
|
||||
}
|
||||
|
||||
if notify != nil {
|
||||
notify(err, next)
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
t = time.NewTimer(next)
|
||||
defer t.Stop()
|
||||
} else {
|
||||
t.Reset(next)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-cb.Context().Done():
|
||||
return err
|
||||
case <-t.C:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PermanentError signals that the operation should not be retried.
|
||||
type PermanentError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *PermanentError) Error() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
// Permanent wraps the given err in a *PermanentError.
|
||||
func Permanent(err error) *PermanentError {
|
||||
return &PermanentError{
|
||||
Err: err,
|
||||
}
|
||||
}
|
82
vendor/github.com/cenkalti/backoff/ticker.go
generated
vendored
Normal file
82
vendor/github.com/cenkalti/backoff/ticker.go
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Ticker holds a channel that delivers `ticks' of a clock at times reported by a BackOff.
|
||||
//
|
||||
// Ticks will continue to arrive when the previous operation is still running,
|
||||
// so operations that take a while to fail could run in quick succession.
|
||||
type Ticker struct {
|
||||
C <-chan time.Time
|
||||
c chan time.Time
|
||||
b BackOffContext
|
||||
stop chan struct{}
|
||||
stopOnce sync.Once
|
||||
}
|
||||
|
||||
// NewTicker returns a new Ticker containing a channel that will send
|
||||
// the time at times specified by the BackOff argument. Ticker is
|
||||
// guaranteed to tick at least once. The channel is closed when Stop
|
||||
// method is called or BackOff stops. It is not safe to manipulate the
|
||||
// provided backoff policy (notably calling NextBackOff or Reset)
|
||||
// while the ticker is running.
|
||||
func NewTicker(b BackOff) *Ticker {
|
||||
c := make(chan time.Time)
|
||||
t := &Ticker{
|
||||
C: c,
|
||||
c: c,
|
||||
b: ensureContext(b),
|
||||
stop: make(chan struct{}),
|
||||
}
|
||||
t.b.Reset()
|
||||
go t.run()
|
||||
return t
|
||||
}
|
||||
|
||||
// Stop turns off a ticker. After Stop, no more ticks will be sent.
|
||||
func (t *Ticker) Stop() {
|
||||
t.stopOnce.Do(func() { close(t.stop) })
|
||||
}
|
||||
|
||||
func (t *Ticker) run() {
|
||||
c := t.c
|
||||
defer close(c)
|
||||
|
||||
// Ticker is guaranteed to tick at least once.
|
||||
afterC := t.send(time.Now())
|
||||
|
||||
for {
|
||||
if afterC == nil {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case tick := <-afterC:
|
||||
afterC = t.send(tick)
|
||||
case <-t.stop:
|
||||
t.c = nil // Prevent future ticks from being sent to the channel.
|
||||
return
|
||||
case <-t.b.Context().Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Ticker) send(tick time.Time) <-chan time.Time {
|
||||
select {
|
||||
case t.c <- tick:
|
||||
case <-t.stop:
|
||||
return nil
|
||||
}
|
||||
|
||||
next := t.b.NextBackOff()
|
||||
if next == Stop {
|
||||
t.Stop()
|
||||
return nil
|
||||
}
|
||||
|
||||
return time.After(next)
|
||||
}
|
35
vendor/github.com/cenkalti/backoff/tries.go
generated
vendored
Normal file
35
vendor/github.com/cenkalti/backoff/tries.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
package backoff
|
||||
|
||||
import "time"
|
||||
|
||||
/*
|
||||
WithMaxRetries creates a wrapper around another BackOff, which will
|
||||
return Stop if NextBackOff() has been called too many times since
|
||||
the last time Reset() was called
|
||||
|
||||
Note: Implementation is not thread-safe.
|
||||
*/
|
||||
func WithMaxRetries(b BackOff, max uint64) BackOff {
|
||||
return &backOffTries{delegate: b, maxTries: max}
|
||||
}
|
||||
|
||||
type backOffTries struct {
|
||||
delegate BackOff
|
||||
maxTries uint64
|
||||
numTries uint64
|
||||
}
|
||||
|
||||
func (b *backOffTries) NextBackOff() time.Duration {
|
||||
if b.maxTries > 0 {
|
||||
if b.maxTries <= b.numTries {
|
||||
return Stop
|
||||
}
|
||||
b.numTries++
|
||||
}
|
||||
return b.delegate.NextBackOff()
|
||||
}
|
||||
|
||||
func (b *backOffTries) Reset() {
|
||||
b.numTries = 0
|
||||
b.delegate.Reset()
|
||||
}
|
11
vendor/github.com/containous/traefik-extra-service-fabric/servicefabric.go
generated
vendored
11
vendor/github.com/containous/traefik-extra-service-fabric/servicefabric.go
generated
vendored
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/containous/traefik/provider/label"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/jjcollinge/logrus-appinsights"
|
||||
appinsights "github.com/jjcollinge/logrus-appinsights"
|
||||
sf "github.com/jjcollinge/servicefabric"
|
||||
)
|
||||
|
||||
@@ -164,11 +164,12 @@ func getClusterServices(sfClient sfClient) ([]ServiceItemExtended, error) {
|
||||
for _, partition := range partitions.Items {
|
||||
partitionExt := PartitionItemExtended{PartitionItem: partition}
|
||||
|
||||
if isStateful(item) {
|
||||
switch {
|
||||
case isStateful(item):
|
||||
partitionExt.Replicas = getValidReplicas(sfClient, app, service, partition)
|
||||
} else if isStateless(item) {
|
||||
case isStateless(item):
|
||||
partitionExt.Instances = getValidInstances(sfClient, app, service, partition)
|
||||
} else {
|
||||
default:
|
||||
log.Errorf("Unsupported service kind %s in service %s", partition.ServiceKind, service.Name)
|
||||
continue
|
||||
}
|
||||
@@ -291,7 +292,7 @@ func getLabels(sfClient sfClient, service *sf.ServiceItem, app *sf.ApplicationIt
|
||||
}
|
||||
|
||||
func createAppInsightsHook(appInsightsClientName string, instrumentationKey string, maxBatchSize int, interval flaeg.Duration) {
|
||||
hook, err := logrus_appinsights.New(appInsightsClientName, logrus_appinsights.Config{
|
||||
hook, err := appinsights.New(appInsightsClientName, appinsights.Config{
|
||||
InstrumentationKey: instrumentationKey,
|
||||
MaxBatchSize: maxBatchSize, // optional
|
||||
MaxBatchInterval: time.Duration(interval), // optional
|
||||
|
43
vendor/github.com/containous/traefik-extra-service-fabric/servicefabric_tmpl.go
generated
vendored
43
vendor/github.com/containous/traefik-extra-service-fabric/servicefabric_tmpl.go
generated
vendored
@@ -8,8 +8,13 @@ const tmpl = `
|
||||
{{range $partition := $service.Partitions }}
|
||||
{{range $instance := $partition.Instances }}
|
||||
[backends."{{ $aggName }}".servers."{{ $service.ID }}-{{ $instance.ID }}"]
|
||||
url = "{{ getDefaultEndpoint $instance }}"
|
||||
weight = {{ getGroupedWeight $service }}
|
||||
{{ $endpointName := getLabelValue $service "traefik.servicefabric.endpointname" "" }}
|
||||
{{if $endpointName }}
|
||||
url = "{{ getNamedEndpoint $instance $endpointName }}"
|
||||
{{else}}
|
||||
url = "{{ getDefaultEndpoint $instance }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -65,8 +70,13 @@ const tmpl = `
|
||||
|
||||
{{range $instance := $partition.Instances}}
|
||||
[backends."{{ $service.Name }}".servers."{{ $instance.ID }}"]
|
||||
url = "{{ getDefaultEndpoint $instance }}"
|
||||
weight = {{ getWeight $service }}
|
||||
{{ $endpointName := getLabelValue $service "traefik.servicefabric.endpointname" "" }}
|
||||
{{if $endpointName }}
|
||||
url = "{{ getNamedEndpoint $instance $endpointName }}"
|
||||
{{else}}
|
||||
url = "{{ getDefaultEndpoint $instance }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{else if isStateful $service}}
|
||||
@@ -75,11 +85,16 @@ const tmpl = `
|
||||
{{if isPrimary $replica}}
|
||||
{{ $backendName := getBackendName $service $partition }}
|
||||
[backends."{{ $backendName }}".servers."{{ $replica.ID }}"]
|
||||
url = "{{ getDefaultEndpoint $replica }}"
|
||||
weight = 1
|
||||
weight = 1
|
||||
{{ $endpointName := getLabelValue $service "traefik.servicefabric.endpointname" "" }}
|
||||
{{if $endpointName }}
|
||||
url = "{{ getNamedEndpoint $replica $endpointName }}"
|
||||
{{else}}
|
||||
url = "{{ getDefaultEndpoint $replica }}"
|
||||
{{end}}
|
||||
|
||||
[backends."{{$backendName}}".LoadBalancer]
|
||||
method = "drr"
|
||||
[backends."{{$backendName}}".LoadBalancer]
|
||||
method = "drr"
|
||||
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -114,14 +129,14 @@ const tmpl = `
|
||||
passHostHeader = {{ getPassHostHeader $service }}
|
||||
passTLSCert = {{ getPassTLSCert $service }}
|
||||
priority = {{ getPriority $service }}
|
||||
|
||||
|
||||
{{ $entryPoints := getEntryPoints $service }}
|
||||
{{if $entryPoints }}
|
||||
entryPoints = [{{range $entryPoints }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
|
||||
{{ $basicAuth := getBasicAuth $service }}
|
||||
{{if $basicAuth }}
|
||||
basicAuth = [{{range $basicAuth }}
|
||||
@@ -166,33 +181,33 @@ const tmpl = `
|
||||
PublicKey = "{{ $headers.PublicKey }}"
|
||||
ReferrerPolicy = "{{ $headers.ReferrerPolicy }}"
|
||||
IsDevelopment = {{ $headers.IsDevelopment }}
|
||||
|
||||
|
||||
{{if $headers.AllowedHosts }}
|
||||
AllowedHosts = [{{range $headers.AllowedHosts }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
|
||||
{{if $headers.HostsProxyHeaders }}
|
||||
HostsProxyHeaders = [{{range $headers.HostsProxyHeaders }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
|
||||
{{if $headers.CustomRequestHeaders }}
|
||||
[frontends."frontend-{{ $frontendName }}".headers.customRequestHeaders]
|
||||
{{range $k, $v := $headers.CustomRequestHeaders }}
|
||||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
|
||||
{{if $headers.CustomResponseHeaders }}
|
||||
[frontends."frontend-{{ $frontendName }}".headers.customResponseHeaders]
|
||||
{{range $k, $v := $headers.CustomResponseHeaders }}
|
||||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
|
||||
{{if $headers.SSLProxyHeaders }}
|
||||
[frontends."frontend-{{ $frontendName }}".headers.SSLProxyHeaders]
|
||||
{{range $k, $v := $headers.SSLProxyHeaders }}
|
||||
@@ -200,7 +215,7 @@ const tmpl = `
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
|
||||
{{range $key, $value := getFrontendRules $service }}
|
||||
[frontends."frontend-{{ $frontendName }}".routes."{{ $key }}"]
|
||||
rule = "{{ $value }}"
|
||||
|
60
vendor/github.com/go-ini/ini/file.go
generated
vendored
60
vendor/github.com/go-ini/ini/file.go
generated
vendored
@@ -45,6 +45,9 @@ type File struct {
|
||||
|
||||
// newFile initializes File object with given data sources.
|
||||
func newFile(dataSources []dataSource, opts LoadOptions) *File {
|
||||
if len(opts.KeyValueDelimiters) == 0 {
|
||||
opts.KeyValueDelimiters = "=:"
|
||||
}
|
||||
return &File{
|
||||
BlockMode: true,
|
||||
dataSources: dataSources,
|
||||
@@ -140,9 +143,14 @@ func (f *File) Section(name string) *Section {
|
||||
|
||||
// Section returns list of Section.
|
||||
func (f *File) Sections() []*Section {
|
||||
if f.BlockMode {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
}
|
||||
|
||||
sections := make([]*Section, len(f.sectionList))
|
||||
for i := range f.sectionList {
|
||||
sections[i] = f.Section(f.sectionList[i])
|
||||
for i, name := range f.sectionList {
|
||||
sections[i] = f.sections[name]
|
||||
}
|
||||
return sections
|
||||
}
|
||||
@@ -222,8 +230,9 @@ func (f *File) Append(source interface{}, others ...interface{}) error {
|
||||
}
|
||||
|
||||
func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
|
||||
equalSign := "="
|
||||
if PrettyFormat {
|
||||
equalSign := DefaultFormatLeft + "=" + DefaultFormatRight
|
||||
|
||||
if PrettyFormat || PrettyEqual {
|
||||
equalSign = " = "
|
||||
}
|
||||
|
||||
@@ -232,13 +241,18 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
|
||||
for i, sname := range f.sectionList {
|
||||
sec := f.Section(sname)
|
||||
if len(sec.Comment) > 0 {
|
||||
if sec.Comment[0] != '#' && sec.Comment[0] != ';' {
|
||||
sec.Comment = "; " + sec.Comment
|
||||
} else {
|
||||
sec.Comment = sec.Comment[:1] + " " + strings.TrimSpace(sec.Comment[1:])
|
||||
}
|
||||
if _, err := buf.WriteString(sec.Comment + LineBreak); err != nil {
|
||||
return nil, err
|
||||
// Support multiline comments
|
||||
lines := strings.Split(sec.Comment, LineBreak)
|
||||
for i := range lines {
|
||||
if lines[i][0] != '#' && lines[i][0] != ';' {
|
||||
lines[i] = "; " + lines[i]
|
||||
} else {
|
||||
lines[i] = lines[i][:1] + " " + strings.TrimSpace(lines[i][1:])
|
||||
}
|
||||
|
||||
if _, err := buf.WriteString(lines[i] + LineBreak); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,7 +289,7 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
|
||||
for _, kname := range sec.keyList {
|
||||
keyLength := len(kname)
|
||||
// First case will surround key by ` and second by """
|
||||
if strings.ContainsAny(kname, "\"=:") {
|
||||
if strings.Contains(kname, "\"") || strings.ContainsAny(kname, f.options.KeyValueDelimiters) {
|
||||
keyLength += 2
|
||||
} else if strings.Contains(kname, "`") {
|
||||
keyLength += 6
|
||||
@@ -295,13 +309,19 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
|
||||
if len(indent) > 0 && sname != DEFAULT_SECTION {
|
||||
buf.WriteString(indent)
|
||||
}
|
||||
if key.Comment[0] != '#' && key.Comment[0] != ';' {
|
||||
key.Comment = "; " + key.Comment
|
||||
} else {
|
||||
key.Comment = key.Comment[:1] + " " + strings.TrimSpace(key.Comment[1:])
|
||||
}
|
||||
if _, err := buf.WriteString(key.Comment + LineBreak); err != nil {
|
||||
return nil, err
|
||||
|
||||
// Support multiline comments
|
||||
lines := strings.Split(key.Comment, LineBreak)
|
||||
for i := range lines {
|
||||
if lines[i][0] != '#' && lines[i][0] != ';' {
|
||||
lines[i] = "; " + strings.TrimSpace(lines[i])
|
||||
} else {
|
||||
lines[i] = lines[i][:1] + " " + strings.TrimSpace(lines[i][1:])
|
||||
}
|
||||
|
||||
if _, err := buf.WriteString(lines[i] + LineBreak); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +332,7 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
|
||||
switch {
|
||||
case key.isAutoIncrement:
|
||||
kname = "-"
|
||||
case strings.ContainsAny(kname, "\"=:"):
|
||||
case strings.Contains(kname, "\"") || strings.ContainsAny(kname, f.options.KeyValueDelimiters):
|
||||
kname = "`" + kname + "`"
|
||||
case strings.Contains(kname, "`"):
|
||||
kname = `"""` + kname + `"""`
|
||||
|
29
vendor/github.com/go-ini/ini/ini.go
generated
vendored
29
vendor/github.com/go-ini/ini/ini.go
generated
vendored
@@ -1,3 +1,5 @@
|
||||
// +build go1.6
|
||||
|
||||
// Copyright 2014 Unknwon
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
@@ -32,7 +34,7 @@ const (
|
||||
|
||||
// Maximum allowed depth when recursively substituing variable names.
|
||||
_DEPTH_VALUES = 99
|
||||
_VERSION = "1.32.0"
|
||||
_VERSION = "1.41.0"
|
||||
)
|
||||
|
||||
// Version returns current package version literal.
|
||||
@@ -46,6 +48,10 @@ var (
|
||||
// at package init time.
|
||||
LineBreak = "\n"
|
||||
|
||||
// Place custom spaces when PrettyFormat and PrettyEqual are both disabled
|
||||
DefaultFormatLeft = ""
|
||||
DefaultFormatRight = ""
|
||||
|
||||
// Variable regexp pattern: %(variable)s
|
||||
varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`)
|
||||
|
||||
@@ -53,6 +59,9 @@ var (
|
||||
// or reduce all possible spaces for compact format.
|
||||
PrettyFormat = true
|
||||
|
||||
// Place spaces around "=" sign even when PrettyFormat is false
|
||||
PrettyEqual = false
|
||||
|
||||
// Explicitly write DEFAULT section header
|
||||
DefaultHeader = false
|
||||
|
||||
@@ -129,6 +138,8 @@ type LoadOptions struct {
|
||||
IgnoreContinuation bool
|
||||
// IgnoreInlineComment indicates whether to ignore comments at the end of value and treat it as part of value.
|
||||
IgnoreInlineComment bool
|
||||
// SkipUnrecognizableLines indicates whether to skip unrecognizable lines that do not conform to key/value pairs.
|
||||
SkipUnrecognizableLines bool
|
||||
// AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing.
|
||||
// This type of keys are mostly used in my.cnf.
|
||||
AllowBooleanKeys bool
|
||||
@@ -137,6 +148,16 @@ type LoadOptions struct {
|
||||
// AllowNestedValues indicates whether to allow AWS-like nested values.
|
||||
// Docs: http://docs.aws.amazon.com/cli/latest/topic/config-vars.html#nested-values
|
||||
AllowNestedValues bool
|
||||
// AllowPythonMultilineValues indicates whether to allow Python-like multi-line values.
|
||||
// Docs: https://docs.python.org/3/library/configparser.html#supported-ini-file-structure
|
||||
// Relevant quote: Values can also span multiple lines, as long as they are indented deeper
|
||||
// than the first line of the value.
|
||||
AllowPythonMultilineValues bool
|
||||
// SpaceBeforeInlineComment indicates whether to allow comment symbols (\# and \;) inside value.
|
||||
// Docs: https://docs.python.org/2/library/configparser.html
|
||||
// Quote: Comments may appear on their own in an otherwise empty line, or may be entered in lines holding values or section names.
|
||||
// In the latter case, they need to be preceded by a whitespace character to be recognized as a comment.
|
||||
SpaceBeforeInlineComment bool
|
||||
// UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format
|
||||
// when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value"
|
||||
UnescapeValueDoubleQuotes bool
|
||||
@@ -144,9 +165,11 @@ type LoadOptions struct {
|
||||
// when value is NOT surrounded by any quotes.
|
||||
// Note: UNSTABLE, behavior might change to only unescape inside double quotes but may noy necessary at all.
|
||||
UnescapeValueCommentSymbols bool
|
||||
// Some INI formats allow group blocks that store a block of raw content that doesn't otherwise
|
||||
// UnparseableSections stores a list of blocks that are allowed with raw content which do not otherwise
|
||||
// conform to key/value pairs. Specify the names of those blocks here.
|
||||
UnparseableSections []string
|
||||
// KeyValueDelimiters is the sequence of delimiters that are used to separate key and value. By default, it is "=:".
|
||||
KeyValueDelimiters string
|
||||
}
|
||||
|
||||
func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) {
|
||||
@@ -187,7 +210,7 @@ func InsensitiveLoad(source interface{}, others ...interface{}) (*File, error) {
|
||||
return LoadSources(LoadOptions{Insensitive: true}, source, others...)
|
||||
}
|
||||
|
||||
// InsensitiveLoad has exactly same functionality as Load function
|
||||
// ShadowLoad has exactly same functionality as Load function
|
||||
// except it allows have shadow keys.
|
||||
func ShadowLoad(source interface{}, others ...interface{}) (*File, error) {
|
||||
return LoadSources(LoadOptions{AllowShadows: true}, source, others...)
|
||||
|
21
vendor/github.com/go-ini/ini/key.go
generated
vendored
21
vendor/github.com/go-ini/ini/key.go
generated
vendored
@@ -133,8 +133,7 @@ func (k *Key) transformValue(val string) string {
|
||||
}
|
||||
|
||||
// Take off leading '%(' and trailing ')s'.
|
||||
noption := strings.TrimLeft(vr, "%(")
|
||||
noption = strings.TrimRight(noption, ")s")
|
||||
noption := vr[2 : len(vr)-2]
|
||||
|
||||
// Search in the same section.
|
||||
nk, err := k.s.GetKey(noption)
|
||||
@@ -187,23 +186,24 @@ func (k *Key) Float64() (float64, error) {
|
||||
|
||||
// Int returns int type value.
|
||||
func (k *Key) Int() (int, error) {
|
||||
return strconv.Atoi(k.String())
|
||||
v, err := strconv.ParseInt(k.String(), 0, 64)
|
||||
return int(v), err
|
||||
}
|
||||
|
||||
// Int64 returns int64 type value.
|
||||
func (k *Key) Int64() (int64, error) {
|
||||
return strconv.ParseInt(k.String(), 10, 64)
|
||||
return strconv.ParseInt(k.String(), 0, 64)
|
||||
}
|
||||
|
||||
// Uint returns uint type valued.
|
||||
func (k *Key) Uint() (uint, error) {
|
||||
u, e := strconv.ParseUint(k.String(), 10, 64)
|
||||
u, e := strconv.ParseUint(k.String(), 0, 64)
|
||||
return uint(u), e
|
||||
}
|
||||
|
||||
// Uint64 returns uint64 type value.
|
||||
func (k *Key) Uint64() (uint64, error) {
|
||||
return strconv.ParseUint(k.String(), 10, 64)
|
||||
return strconv.ParseUint(k.String(), 0, 64)
|
||||
}
|
||||
|
||||
// Duration returns time.Duration type value.
|
||||
@@ -668,7 +668,8 @@ func (k *Key) parseFloat64s(strs []string, addInvalid, returnOnInvalid bool) ([]
|
||||
func (k *Key) parseInts(strs []string, addInvalid, returnOnInvalid bool) ([]int, error) {
|
||||
vals := make([]int, 0, len(strs))
|
||||
for _, str := range strs {
|
||||
val, err := strconv.Atoi(str)
|
||||
valInt64, err := strconv.ParseInt(str, 0, 64)
|
||||
val := int(valInt64)
|
||||
if err != nil && returnOnInvalid {
|
||||
return nil, err
|
||||
}
|
||||
@@ -683,7 +684,7 @@ func (k *Key) parseInts(strs []string, addInvalid, returnOnInvalid bool) ([]int,
|
||||
func (k *Key) parseInt64s(strs []string, addInvalid, returnOnInvalid bool) ([]int64, error) {
|
||||
vals := make([]int64, 0, len(strs))
|
||||
for _, str := range strs {
|
||||
val, err := strconv.ParseInt(str, 10, 64)
|
||||
val, err := strconv.ParseInt(str, 0, 64)
|
||||
if err != nil && returnOnInvalid {
|
||||
return nil, err
|
||||
}
|
||||
@@ -698,7 +699,7 @@ func (k *Key) parseInt64s(strs []string, addInvalid, returnOnInvalid bool) ([]in
|
||||
func (k *Key) parseUints(strs []string, addInvalid, returnOnInvalid bool) ([]uint, error) {
|
||||
vals := make([]uint, 0, len(strs))
|
||||
for _, str := range strs {
|
||||
val, err := strconv.ParseUint(str, 10, 0)
|
||||
val, err := strconv.ParseUint(str, 0, 0)
|
||||
if err != nil && returnOnInvalid {
|
||||
return nil, err
|
||||
}
|
||||
@@ -713,7 +714,7 @@ func (k *Key) parseUints(strs []string, addInvalid, returnOnInvalid bool) ([]uin
|
||||
func (k *Key) parseUint64s(strs []string, addInvalid, returnOnInvalid bool) ([]uint64, error) {
|
||||
vals := make([]uint64, 0, len(strs))
|
||||
for _, str := range strs {
|
||||
val, err := strconv.ParseUint(str, 10, 64)
|
||||
val, err := strconv.ParseUint(str, 0, 64)
|
||||
if err != nil && returnOnInvalid {
|
||||
return nil, err
|
||||
}
|
||||
|
135
vendor/github.com/go-ini/ini/parser.go
generated
vendored
135
vendor/github.com/go-ini/ini/parser.go
generated
vendored
@@ -19,11 +19,14 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var pythonMultiline = regexp.MustCompile("^(\\s+)([^\n]+)")
|
||||
|
||||
type tokenType int
|
||||
|
||||
const (
|
||||
@@ -97,7 +100,7 @@ func cleanComment(in []byte) ([]byte, bool) {
|
||||
return in[i:], true
|
||||
}
|
||||
|
||||
func readKeyName(in []byte) (string, int, error) {
|
||||
func readKeyName(delimiters string, in []byte) (string, int, error) {
|
||||
line := string(in)
|
||||
|
||||
// Check if key name surrounded by quotes.
|
||||
@@ -124,7 +127,7 @@ func readKeyName(in []byte) (string, int, error) {
|
||||
pos += startIdx
|
||||
|
||||
// Find key-value delimiter
|
||||
i := strings.IndexAny(line[pos+startIdx:], "=:")
|
||||
i := strings.IndexAny(line[pos+startIdx:], delimiters)
|
||||
if i < 0 {
|
||||
return "", -1, ErrDelimiterNotFound{line}
|
||||
}
|
||||
@@ -132,7 +135,7 @@ func readKeyName(in []byte) (string, int, error) {
|
||||
return strings.TrimSpace(line[startIdx:pos]), endIdx + startIdx + 1, nil
|
||||
}
|
||||
|
||||
endIdx = strings.IndexAny(line, "=:")
|
||||
endIdx = strings.IndexAny(line, delimiters)
|
||||
if endIdx < 0 {
|
||||
return "", -1, ErrDelimiterNotFound{line}
|
||||
}
|
||||
@@ -194,7 +197,8 @@ func hasSurroundedQuote(in string, quote byte) bool {
|
||||
}
|
||||
|
||||
func (p *parser) readValue(in []byte,
|
||||
ignoreContinuation, ignoreInlineComment, unescapeValueDoubleQuotes, unescapeValueCommentSymbols bool) (string, error) {
|
||||
parserBufferSize int,
|
||||
ignoreContinuation, ignoreInlineComment, unescapeValueDoubleQuotes, unescapeValueCommentSymbols, allowPythonMultilines, spaceBeforeInlineComment bool) (string, error) {
|
||||
|
||||
line := strings.TrimLeftFunc(string(in), unicode.IsSpace)
|
||||
if len(line) == 0 {
|
||||
@@ -224,21 +228,34 @@ func (p *parser) readValue(in []byte,
|
||||
return line[startIdx : pos+startIdx], nil
|
||||
}
|
||||
|
||||
lastChar := line[len(line)-1]
|
||||
// Won't be able to reach here if value only contains whitespace
|
||||
line = strings.TrimSpace(line)
|
||||
trimmedLastChar := line[len(line)-1]
|
||||
|
||||
// Check continuation lines when desired
|
||||
if !ignoreContinuation && line[len(line)-1] == '\\' {
|
||||
if !ignoreContinuation && trimmedLastChar == '\\' {
|
||||
return p.readContinuationLines(line[:len(line)-1])
|
||||
}
|
||||
|
||||
// Check if ignore inline comment
|
||||
if !ignoreInlineComment {
|
||||
i := strings.IndexAny(line, "#;")
|
||||
var i int
|
||||
if spaceBeforeInlineComment {
|
||||
i = strings.Index(line, " #")
|
||||
if i == -1 {
|
||||
i = strings.Index(line, " ;")
|
||||
}
|
||||
|
||||
} else {
|
||||
i = strings.IndexAny(line, "#;")
|
||||
}
|
||||
|
||||
if i > -1 {
|
||||
p.comment.WriteString(line[i:])
|
||||
line = strings.TrimSpace(line[:i])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Trim single and double quotes
|
||||
@@ -252,7 +269,42 @@ func (p *parser) readValue(in []byte,
|
||||
if strings.Contains(line, `\#`) {
|
||||
line = strings.Replace(line, `\#`, "#", -1)
|
||||
}
|
||||
} else if allowPythonMultilines && lastChar == '\n' {
|
||||
parserBufferPeekResult, _ := p.buf.Peek(parserBufferSize)
|
||||
peekBuffer := bytes.NewBuffer(parserBufferPeekResult)
|
||||
|
||||
val := line
|
||||
|
||||
for {
|
||||
peekData, peekErr := peekBuffer.ReadBytes('\n')
|
||||
if peekErr != nil {
|
||||
if peekErr == io.EOF {
|
||||
return val, nil
|
||||
}
|
||||
return "", peekErr
|
||||
}
|
||||
|
||||
peekMatches := pythonMultiline.FindStringSubmatch(string(peekData))
|
||||
if len(peekMatches) != 3 {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// NOTE: Return if not a python-ini multi-line value.
|
||||
currentIdentSize := len(peekMatches[1])
|
||||
if currentIdentSize <= 0 {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// NOTE: Just advance the parser reader (buffer) in-sync with the peek buffer.
|
||||
_, err := p.readUntil('\n')
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
val += fmt.Sprintf("\n%s", peekMatches[2])
|
||||
}
|
||||
}
|
||||
|
||||
return line, nil
|
||||
}
|
||||
|
||||
@@ -276,6 +328,28 @@ func (f *File) parse(reader io.Reader) (err error) {
|
||||
|
||||
var line []byte
|
||||
var inUnparseableSection bool
|
||||
|
||||
// NOTE: Iterate and increase `currentPeekSize` until
|
||||
// the size of the parser buffer is found.
|
||||
// TODO(unknwon): When Golang 1.10 is the lowest version supported, replace with `parserBufferSize := p.buf.Size()`.
|
||||
parserBufferSize := 0
|
||||
// NOTE: Peek 1kb at a time.
|
||||
currentPeekSize := 1024
|
||||
|
||||
if f.options.AllowPythonMultilineValues {
|
||||
for {
|
||||
peekBytes, _ := p.buf.Peek(currentPeekSize)
|
||||
peekBytesLength := len(peekBytes)
|
||||
|
||||
if parserBufferSize >= peekBytesLength {
|
||||
break
|
||||
}
|
||||
|
||||
currentPeekSize *= 2
|
||||
parserBufferSize = peekBytesLength
|
||||
}
|
||||
}
|
||||
|
||||
for !p.isEOF {
|
||||
line, err = p.readUntil('\n')
|
||||
if err != nil {
|
||||
@@ -307,8 +381,7 @@ func (f *File) parse(reader io.Reader) (err error) {
|
||||
// Section
|
||||
if line[0] == '[' {
|
||||
// Read to the next ']' (TODO: support quoted strings)
|
||||
// TODO(unknwon): use LastIndexByte when stop supporting Go1.4
|
||||
closeIdx := bytes.LastIndex(line, []byte("]"))
|
||||
closeIdx := bytes.LastIndexByte(line, ']')
|
||||
if closeIdx == -1 {
|
||||
return fmt.Errorf("unclosed section: %s", line)
|
||||
}
|
||||
@@ -347,25 +420,34 @@ func (f *File) parse(reader io.Reader) (err error) {
|
||||
continue
|
||||
}
|
||||
|
||||
kname, offset, err := readKeyName(line)
|
||||
kname, offset, err := readKeyName(f.options.KeyValueDelimiters, line)
|
||||
if err != nil {
|
||||
// Treat as boolean key when desired, and whole line is key name.
|
||||
if IsErrDelimiterNotFound(err) && f.options.AllowBooleanKeys {
|
||||
kname, err := p.readValue(line,
|
||||
f.options.IgnoreContinuation,
|
||||
f.options.IgnoreInlineComment,
|
||||
f.options.UnescapeValueDoubleQuotes,
|
||||
f.options.UnescapeValueCommentSymbols)
|
||||
if err != nil {
|
||||
return err
|
||||
if IsErrDelimiterNotFound(err) {
|
||||
switch {
|
||||
case f.options.AllowBooleanKeys:
|
||||
kname, err := p.readValue(line,
|
||||
parserBufferSize,
|
||||
f.options.IgnoreContinuation,
|
||||
f.options.IgnoreInlineComment,
|
||||
f.options.UnescapeValueDoubleQuotes,
|
||||
f.options.UnescapeValueCommentSymbols,
|
||||
f.options.AllowPythonMultilineValues,
|
||||
f.options.SpaceBeforeInlineComment)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key, err := section.NewBooleanKey(kname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key.Comment = strings.TrimSpace(p.comment.String())
|
||||
p.comment.Reset()
|
||||
continue
|
||||
|
||||
case f.options.SkipUnrecognizableLines:
|
||||
continue
|
||||
}
|
||||
key, err := section.NewBooleanKey(kname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key.Comment = strings.TrimSpace(p.comment.String())
|
||||
p.comment.Reset()
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -379,10 +461,13 @@ func (f *File) parse(reader io.Reader) (err error) {
|
||||
}
|
||||
|
||||
value, err := p.readValue(line[offset:],
|
||||
parserBufferSize,
|
||||
f.options.IgnoreContinuation,
|
||||
f.options.IgnoreInlineComment,
|
||||
f.options.UnescapeValueDoubleQuotes,
|
||||
f.options.UnescapeValueCommentSymbols)
|
||||
f.options.UnescapeValueCommentSymbols,
|
||||
f.options.AllowPythonMultilineValues,
|
||||
f.options.SpaceBeforeInlineComment)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
2
vendor/github.com/go-ini/ini/section.go
generated
vendored
2
vendor/github.com/go-ini/ini/section.go
generated
vendored
@@ -82,6 +82,7 @@ func (s *Section) NewKey(name, val string) (*Key, error) {
|
||||
}
|
||||
} else {
|
||||
s.keys[name].value = val
|
||||
s.keysHash[name] = val
|
||||
}
|
||||
return s.keys[name], nil
|
||||
}
|
||||
@@ -237,6 +238,7 @@ func (s *Section) DeleteKey(name string) {
|
||||
if k == name {
|
||||
s.keyList = append(s.keyList[:i], s.keyList[i+1:]...)
|
||||
delete(s.keys, name)
|
||||
delete(s.keysHash, name)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
2
vendor/github.com/go-ini/ini/struct.go
generated
vendored
2
vendor/github.com/go-ini/ini/struct.go
generated
vendored
@@ -180,7 +180,7 @@ func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim stri
|
||||
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
durationVal, err := key.Duration()
|
||||
// Skip zero value
|
||||
if err == nil && int(durationVal) > 0 {
|
||||
if err == nil && uint64(durationVal) > 0 {
|
||||
field.Set(reflect.ValueOf(durationVal))
|
||||
return nil
|
||||
}
|
||||
|
27
vendor/github.com/google/uuid/LICENSE
generated
vendored
Normal file
27
vendor/github.com/google/uuid/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009,2014 Google Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* 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.
|
80
vendor/github.com/google/uuid/dce.go
generated
vendored
Normal file
80
vendor/github.com/google/uuid/dce.go
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// A Domain represents a Version 2 domain
|
||||
type Domain byte
|
||||
|
||||
// Domain constants for DCE Security (Version 2) UUIDs.
|
||||
const (
|
||||
Person = Domain(0)
|
||||
Group = Domain(1)
|
||||
Org = Domain(2)
|
||||
)
|
||||
|
||||
// NewDCESecurity returns a DCE Security (Version 2) UUID.
|
||||
//
|
||||
// The domain should be one of Person, Group or Org.
|
||||
// On a POSIX system the id should be the users UID for the Person
|
||||
// domain and the users GID for the Group. The meaning of id for
|
||||
// the domain Org or on non-POSIX systems is site defined.
|
||||
//
|
||||
// For a given domain/id pair the same token may be returned for up to
|
||||
// 7 minutes and 10 seconds.
|
||||
func NewDCESecurity(domain Domain, id uint32) (UUID, error) {
|
||||
uuid, err := NewUUID()
|
||||
if err == nil {
|
||||
uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
|
||||
uuid[9] = byte(domain)
|
||||
binary.BigEndian.PutUint32(uuid[0:], id)
|
||||
}
|
||||
return uuid, err
|
||||
}
|
||||
|
||||
// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
|
||||
// domain with the id returned by os.Getuid.
|
||||
//
|
||||
// NewDCEPerson(Person, uint32(os.Getuid()))
|
||||
func NewDCEPerson() (UUID, error) {
|
||||
return NewDCESecurity(Person, uint32(os.Getuid()))
|
||||
}
|
||||
|
||||
// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
|
||||
// domain with the id returned by os.Getgid.
|
||||
//
|
||||
// NewDCEGroup(Group, uint32(os.Getgid()))
|
||||
func NewDCEGroup() (UUID, error) {
|
||||
return NewDCESecurity(Group, uint32(os.Getgid()))
|
||||
}
|
||||
|
||||
// Domain returns the domain for a Version 2 UUID. Domains are only defined
|
||||
// for Version 2 UUIDs.
|
||||
func (uuid UUID) Domain() Domain {
|
||||
return Domain(uuid[9])
|
||||
}
|
||||
|
||||
// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2
|
||||
// UUIDs.
|
||||
func (uuid UUID) ID() uint32 {
|
||||
return binary.BigEndian.Uint32(uuid[0:4])
|
||||
}
|
||||
|
||||
func (d Domain) String() string {
|
||||
switch d {
|
||||
case Person:
|
||||
return "Person"
|
||||
case Group:
|
||||
return "Group"
|
||||
case Org:
|
||||
return "Org"
|
||||
}
|
||||
return fmt.Sprintf("Domain%d", int(d))
|
||||
}
|
12
vendor/github.com/google/uuid/doc.go
generated
vendored
Normal file
12
vendor/github.com/google/uuid/doc.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package uuid generates and inspects UUIDs.
|
||||
//
|
||||
// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security
|
||||
// Services.
|
||||
//
|
||||
// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to
|
||||
// maps or compared directly.
|
||||
package uuid
|
53
vendor/github.com/google/uuid/hash.go
generated
vendored
Normal file
53
vendor/github.com/google/uuid/hash.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// Well known namespace IDs and UUIDs
|
||||
var (
|
||||
NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
|
||||
NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
|
||||
NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
|
||||
NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
|
||||
Nil UUID // empty UUID, all zeros
|
||||
)
|
||||
|
||||
// NewHash returns a new UUID derived from the hash of space concatenated with
|
||||
// data generated by h. The hash should be at least 16 byte in length. The
|
||||
// first 16 bytes of the hash are used to form the UUID. The version of the
|
||||
// UUID will be the lower 4 bits of version. NewHash is used to implement
|
||||
// NewMD5 and NewSHA1.
|
||||
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
|
||||
h.Reset()
|
||||
h.Write(space[:])
|
||||
h.Write([]byte(data))
|
||||
s := h.Sum(nil)
|
||||
var uuid UUID
|
||||
copy(uuid[:], s)
|
||||
uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
|
||||
uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
|
||||
return uuid
|
||||
}
|
||||
|
||||
// NewMD5 returns a new MD5 (Version 3) UUID based on the
|
||||
// supplied name space and data. It is the same as calling:
|
||||
//
|
||||
// NewHash(md5.New(), space, data, 3)
|
||||
func NewMD5(space UUID, data []byte) UUID {
|
||||
return NewHash(md5.New(), space, data, 3)
|
||||
}
|
||||
|
||||
// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
|
||||
// supplied name space and data. It is the same as calling:
|
||||
//
|
||||
// NewHash(sha1.New(), space, data, 5)
|
||||
func NewSHA1(space UUID, data []byte) UUID {
|
||||
return NewHash(sha1.New(), space, data, 5)
|
||||
}
|
39
vendor/github.com/google/uuid/marshal.go
generated
vendored
Normal file
39
vendor/github.com/google/uuid/marshal.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import "fmt"
|
||||
|
||||
// MarshalText implements encoding.TextMarshaler.
|
||||
func (uuid UUID) MarshalText() ([]byte, error) {
|
||||
var js [36]byte
|
||||
encodeHex(js[:], uuid)
|
||||
return js[:], nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||
func (uuid *UUID) UnmarshalText(data []byte) error {
|
||||
// See comment in ParseBytes why we do this.
|
||||
// id, err := ParseBytes(data)
|
||||
id, err := ParseBytes(data)
|
||||
if err == nil {
|
||||
*uuid = id
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler.
|
||||
func (uuid UUID) MarshalBinary() ([]byte, error) {
|
||||
return uuid[:], nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
|
||||
func (uuid *UUID) UnmarshalBinary(data []byte) error {
|
||||
if len(data) != 16 {
|
||||
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
|
||||
}
|
||||
copy(uuid[:], data)
|
||||
return nil
|
||||
}
|
103
vendor/github.com/google/uuid/node.go
generated
vendored
Normal file
103
vendor/github.com/google/uuid/node.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
nodeMu sync.Mutex
|
||||
interfaces []net.Interface // cached list of interfaces
|
||||
ifname string // name of interface being used
|
||||
nodeID [6]byte // hardware for version 1 UUIDs
|
||||
zeroID [6]byte // nodeID with only 0's
|
||||
)
|
||||
|
||||
// NodeInterface returns the name of the interface from which the NodeID was
|
||||
// derived. The interface "user" is returned if the NodeID was set by
|
||||
// SetNodeID.
|
||||
func NodeInterface() string {
|
||||
defer nodeMu.Unlock()
|
||||
nodeMu.Lock()
|
||||
return ifname
|
||||
}
|
||||
|
||||
// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
|
||||
// If name is "" then the first usable interface found will be used or a random
|
||||
// Node ID will be generated. If a named interface cannot be found then false
|
||||
// is returned.
|
||||
//
|
||||
// SetNodeInterface never fails when name is "".
|
||||
func SetNodeInterface(name string) bool {
|
||||
defer nodeMu.Unlock()
|
||||
nodeMu.Lock()
|
||||
return setNodeInterface(name)
|
||||
}
|
||||
|
||||
func setNodeInterface(name string) bool {
|
||||
if interfaces == nil {
|
||||
var err error
|
||||
interfaces, err = net.Interfaces()
|
||||
if err != nil && name != "" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, ifs := range interfaces {
|
||||
if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
|
||||
copy(nodeID[:], ifs.HardwareAddr)
|
||||
ifname = ifs.Name
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// We found no interfaces with a valid hardware address. If name
|
||||
// does not specify a specific interface generate a random Node ID
|
||||
// (section 4.1.6)
|
||||
if name == "" {
|
||||
randomBits(nodeID[:])
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
|
||||
// if not already set.
|
||||
func NodeID() []byte {
|
||||
defer nodeMu.Unlock()
|
||||
nodeMu.Lock()
|
||||
if nodeID == zeroID {
|
||||
setNodeInterface("")
|
||||
}
|
||||
nid := nodeID
|
||||
return nid[:]
|
||||
}
|
||||
|
||||
// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes
|
||||
// of id are used. If id is less than 6 bytes then false is returned and the
|
||||
// Node ID is not set.
|
||||
func SetNodeID(id []byte) bool {
|
||||
if len(id) < 6 {
|
||||
return false
|
||||
}
|
||||
defer nodeMu.Unlock()
|
||||
nodeMu.Lock()
|
||||
copy(nodeID[:], id)
|
||||
ifname = "user"
|
||||
return true
|
||||
}
|
||||
|
||||
// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is
|
||||
// not valid. The NodeID is only well defined for version 1 and 2 UUIDs.
|
||||
func (uuid UUID) NodeID() []byte {
|
||||
if len(uuid) != 16 {
|
||||
return nil
|
||||
}
|
||||
var node [6]byte
|
||||
copy(node[:], uuid[10:])
|
||||
return node[:]
|
||||
}
|
58
vendor/github.com/google/uuid/sql.go
generated
vendored
Normal file
58
vendor/github.com/google/uuid/sql.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Scan implements sql.Scanner so UUIDs can be read from databases transparently
|
||||
// Currently, database types that map to string and []byte are supported. Please
|
||||
// consult database-specific driver documentation for matching types.
|
||||
func (uuid *UUID) Scan(src interface{}) error {
|
||||
switch src.(type) {
|
||||
case string:
|
||||
// if an empty UUID comes from a table, we return a null UUID
|
||||
if src.(string) == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// see Parse for required string format
|
||||
u, err := Parse(src.(string))
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Scan: %v", err)
|
||||
}
|
||||
|
||||
*uuid = u
|
||||
case []byte:
|
||||
b := src.([]byte)
|
||||
|
||||
// if an empty UUID comes from a table, we return a null UUID
|
||||
if len(b) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// assumes a simple slice of bytes if 16 bytes
|
||||
// otherwise attempts to parse
|
||||
if len(b) != 16 {
|
||||
return uuid.Scan(string(b))
|
||||
}
|
||||
copy((*uuid)[:], b)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("Scan: unable to scan type %T into UUID", src)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value implements sql.Valuer so that UUIDs can be written to databases
|
||||
// transparently. Currently, UUIDs map to strings. Please consult
|
||||
// database-specific driver documentation for matching types.
|
||||
func (uuid UUID) Value() (driver.Value, error) {
|
||||
return uuid.String(), nil
|
||||
}
|
123
vendor/github.com/google/uuid/time.go
generated
vendored
Normal file
123
vendor/github.com/google/uuid/time.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
|
||||
// 1582.
|
||||
type Time int64
|
||||
|
||||
const (
|
||||
lillian = 2299160 // Julian day of 15 Oct 1582
|
||||
unix = 2440587 // Julian day of 1 Jan 1970
|
||||
epoch = unix - lillian // Days between epochs
|
||||
g1582 = epoch * 86400 // seconds between epochs
|
||||
g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
|
||||
)
|
||||
|
||||
var (
|
||||
timeMu sync.Mutex
|
||||
lasttime uint64 // last time we returned
|
||||
clockSeq uint16 // clock sequence for this run
|
||||
|
||||
timeNow = time.Now // for testing
|
||||
)
|
||||
|
||||
// UnixTime converts t the number of seconds and nanoseconds using the Unix
|
||||
// epoch of 1 Jan 1970.
|
||||
func (t Time) UnixTime() (sec, nsec int64) {
|
||||
sec = int64(t - g1582ns100)
|
||||
nsec = (sec % 10000000) * 100
|
||||
sec /= 10000000
|
||||
return sec, nsec
|
||||
}
|
||||
|
||||
// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
|
||||
// clock sequence as well as adjusting the clock sequence as needed. An error
|
||||
// is returned if the current time cannot be determined.
|
||||
func GetTime() (Time, uint16, error) {
|
||||
defer timeMu.Unlock()
|
||||
timeMu.Lock()
|
||||
return getTime()
|
||||
}
|
||||
|
||||
func getTime() (Time, uint16, error) {
|
||||
t := timeNow()
|
||||
|
||||
// If we don't have a clock sequence already, set one.
|
||||
if clockSeq == 0 {
|
||||
setClockSequence(-1)
|
||||
}
|
||||
now := uint64(t.UnixNano()/100) + g1582ns100
|
||||
|
||||
// If time has gone backwards with this clock sequence then we
|
||||
// increment the clock sequence
|
||||
if now <= lasttime {
|
||||
clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000
|
||||
}
|
||||
lasttime = now
|
||||
return Time(now), clockSeq, nil
|
||||
}
|
||||
|
||||
// ClockSequence returns the current clock sequence, generating one if not
|
||||
// already set. The clock sequence is only used for Version 1 UUIDs.
|
||||
//
|
||||
// The uuid package does not use global static storage for the clock sequence or
|
||||
// the last time a UUID was generated. Unless SetClockSequence is used, a new
|
||||
// random clock sequence is generated the first time a clock sequence is
|
||||
// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1)
|
||||
func ClockSequence() int {
|
||||
defer timeMu.Unlock()
|
||||
timeMu.Lock()
|
||||
return clockSequence()
|
||||
}
|
||||
|
||||
func clockSequence() int {
|
||||
if clockSeq == 0 {
|
||||
setClockSequence(-1)
|
||||
}
|
||||
return int(clockSeq & 0x3fff)
|
||||
}
|
||||
|
||||
// SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to
|
||||
// -1 causes a new sequence to be generated.
|
||||
func SetClockSequence(seq int) {
|
||||
defer timeMu.Unlock()
|
||||
timeMu.Lock()
|
||||
setClockSequence(seq)
|
||||
}
|
||||
|
||||
func setClockSequence(seq int) {
|
||||
if seq == -1 {
|
||||
var b [2]byte
|
||||
randomBits(b[:]) // clock sequence
|
||||
seq = int(b[0])<<8 | int(b[1])
|
||||
}
|
||||
old_seq := clockSeq
|
||||
clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant
|
||||
if old_seq != clockSeq {
|
||||
lasttime = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
|
||||
// uuid. The time is only defined for version 1 and 2 UUIDs.
|
||||
func (uuid UUID) Time() Time {
|
||||
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
|
||||
return Time(time)
|
||||
}
|
||||
|
||||
// ClockSequence returns the clock sequence encoded in uuid.
|
||||
// The clock sequence is only well defined for version 1 and 2 UUIDs.
|
||||
func (uuid UUID) ClockSequence() int {
|
||||
return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff
|
||||
}
|
43
vendor/github.com/google/uuid/util.go
generated
vendored
Normal file
43
vendor/github.com/google/uuid/util.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// randomBits completely fills slice b with random data.
|
||||
func randomBits(b []byte) {
|
||||
if _, err := io.ReadFull(rander, b); err != nil {
|
||||
panic(err.Error()) // rand should never fail
|
||||
}
|
||||
}
|
||||
|
||||
// xvalues returns the value of a byte as a hexadecimal digit or 255.
|
||||
var xvalues = [256]byte{
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
|
||||
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
}
|
||||
|
||||
// xtob converts hex characters x1 and x2 into a byte.
|
||||
func xtob(x1, x2 byte) (byte, bool) {
|
||||
b1 := xvalues[x1]
|
||||
b2 := xvalues[x2]
|
||||
return (b1 << 4) | b2, b1 != 255 && b2 != 255
|
||||
}
|
191
vendor/github.com/google/uuid/uuid.go
generated
vendored
Normal file
191
vendor/github.com/google/uuid/uuid.go
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
|
||||
// 4122.
|
||||
type UUID [16]byte
|
||||
|
||||
// A Version represents a UUID's version.
|
||||
type Version byte
|
||||
|
||||
// A Variant represents a UUID's variant.
|
||||
type Variant byte
|
||||
|
||||
// Constants returned by Variant.
|
||||
const (
|
||||
Invalid = Variant(iota) // Invalid UUID
|
||||
RFC4122 // The variant specified in RFC4122
|
||||
Reserved // Reserved, NCS backward compatibility.
|
||||
Microsoft // Reserved, Microsoft Corporation backward compatibility.
|
||||
Future // Reserved for future definition.
|
||||
)
|
||||
|
||||
var rander = rand.Reader // random function
|
||||
|
||||
// Parse decodes s into a UUID or returns an error. Both the UUID form of
|
||||
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded.
|
||||
func Parse(s string) (UUID, error) {
|
||||
var uuid UUID
|
||||
if len(s) != 36 {
|
||||
if len(s) != 36+9 {
|
||||
return uuid, fmt.Errorf("invalid UUID length: %d", len(s))
|
||||
}
|
||||
if strings.ToLower(s[:9]) != "urn:uuid:" {
|
||||
return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9])
|
||||
}
|
||||
s = s[9:]
|
||||
}
|
||||
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
|
||||
return uuid, errors.New("invalid UUID format")
|
||||
}
|
||||
for i, x := range [16]int{
|
||||
0, 2, 4, 6,
|
||||
9, 11,
|
||||
14, 16,
|
||||
19, 21,
|
||||
24, 26, 28, 30, 32, 34} {
|
||||
if v, ok := xtob(s[x], s[x+1]); !ok {
|
||||
return uuid, errors.New("invalid UUID format")
|
||||
} else {
|
||||
uuid[i] = v
|
||||
}
|
||||
}
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
// ParseBytes is like Parse, except it parses a byte slice instead of a string.
|
||||
func ParseBytes(b []byte) (UUID, error) {
|
||||
var uuid UUID
|
||||
if len(b) != 36 {
|
||||
if len(b) != 36+9 {
|
||||
return uuid, fmt.Errorf("invalid UUID length: %d", len(b))
|
||||
}
|
||||
if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) {
|
||||
return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9])
|
||||
}
|
||||
b = b[9:]
|
||||
}
|
||||
if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' {
|
||||
return uuid, errors.New("invalid UUID format")
|
||||
}
|
||||
for i, x := range [16]int{
|
||||
0, 2, 4, 6,
|
||||
9, 11,
|
||||
14, 16,
|
||||
19, 21,
|
||||
24, 26, 28, 30, 32, 34} {
|
||||
if v, ok := xtob(b[x], b[x+1]); !ok {
|
||||
return uuid, errors.New("invalid UUID format")
|
||||
} else {
|
||||
uuid[i] = v
|
||||
}
|
||||
}
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
// Must returns uuid if err is nil and panics otherwise.
|
||||
func Must(uuid UUID, err error) UUID {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return uuid
|
||||
}
|
||||
|
||||
// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
// , or "" if uuid is invalid.
|
||||
func (uuid UUID) String() string {
|
||||
var buf [36]byte
|
||||
encodeHex(buf[:], uuid)
|
||||
return string(buf[:])
|
||||
}
|
||||
|
||||
// URN returns the RFC 2141 URN form of uuid,
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid.
|
||||
func (uuid UUID) URN() string {
|
||||
var buf [36 + 9]byte
|
||||
copy(buf[:], "urn:uuid:")
|
||||
encodeHex(buf[9:], uuid)
|
||||
return string(buf[:])
|
||||
}
|
||||
|
||||
func encodeHex(dst []byte, uuid UUID) {
|
||||
hex.Encode(dst[:], uuid[:4])
|
||||
dst[8] = '-'
|
||||
hex.Encode(dst[9:13], uuid[4:6])
|
||||
dst[13] = '-'
|
||||
hex.Encode(dst[14:18], uuid[6:8])
|
||||
dst[18] = '-'
|
||||
hex.Encode(dst[19:23], uuid[8:10])
|
||||
dst[23] = '-'
|
||||
hex.Encode(dst[24:], uuid[10:])
|
||||
}
|
||||
|
||||
// Variant returns the variant encoded in uuid.
|
||||
func (uuid UUID) Variant() Variant {
|
||||
switch {
|
||||
case (uuid[8] & 0xc0) == 0x80:
|
||||
return RFC4122
|
||||
case (uuid[8] & 0xe0) == 0xc0:
|
||||
return Microsoft
|
||||
case (uuid[8] & 0xe0) == 0xe0:
|
||||
return Future
|
||||
default:
|
||||
return Reserved
|
||||
}
|
||||
}
|
||||
|
||||
// Version returns the version of uuid.
|
||||
func (uuid UUID) Version() Version {
|
||||
return Version(uuid[6] >> 4)
|
||||
}
|
||||
|
||||
func (v Version) String() string {
|
||||
if v > 15 {
|
||||
return fmt.Sprintf("BAD_VERSION_%d", v)
|
||||
}
|
||||
return fmt.Sprintf("VERSION_%d", v)
|
||||
}
|
||||
|
||||
func (v Variant) String() string {
|
||||
switch v {
|
||||
case RFC4122:
|
||||
return "RFC4122"
|
||||
case Reserved:
|
||||
return "Reserved"
|
||||
case Microsoft:
|
||||
return "Microsoft"
|
||||
case Future:
|
||||
return "Future"
|
||||
case Invalid:
|
||||
return "Invalid"
|
||||
}
|
||||
return fmt.Sprintf("BadVariant%d", int(v))
|
||||
}
|
||||
|
||||
// SetRand sets the random number generator to r, which implents io.Reader.
|
||||
// If r.Read returns an error when the package requests random data then
|
||||
// a panic will be issued.
|
||||
//
|
||||
// Calling SetRand with nil sets the random number generator to the default
|
||||
// generator.
|
||||
func SetRand(r io.Reader) {
|
||||
if r == nil {
|
||||
rander = rand.Reader
|
||||
return
|
||||
}
|
||||
rander = r
|
||||
}
|
44
vendor/github.com/google/uuid/version1.go
generated
vendored
Normal file
44
vendor/github.com/google/uuid/version1.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// NewUUID returns a Version 1 UUID based on the current NodeID and clock
|
||||
// sequence, and the current time. If the NodeID has not been set by SetNodeID
|
||||
// or SetNodeInterface then it will be set automatically. If the NodeID cannot
|
||||
// be set NewUUID returns nil. If clock sequence has not been set by
|
||||
// SetClockSequence then it will be set automatically. If GetTime fails to
|
||||
// return the current NewUUID returns Nil and an error.
|
||||
//
|
||||
// In most cases, New should be used.
|
||||
func NewUUID() (UUID, error) {
|
||||
nodeMu.Lock()
|
||||
if nodeID == zeroID {
|
||||
setNodeInterface("")
|
||||
}
|
||||
nodeMu.Unlock()
|
||||
|
||||
var uuid UUID
|
||||
now, seq, err := GetTime()
|
||||
if err != nil {
|
||||
return uuid, err
|
||||
}
|
||||
|
||||
timeLow := uint32(now & 0xffffffff)
|
||||
timeMid := uint16((now >> 32) & 0xffff)
|
||||
timeHi := uint16((now >> 48) & 0x0fff)
|
||||
timeHi |= 0x1000 // Version 1
|
||||
|
||||
binary.BigEndian.PutUint32(uuid[0:], timeLow)
|
||||
binary.BigEndian.PutUint16(uuid[4:], timeMid)
|
||||
binary.BigEndian.PutUint16(uuid[6:], timeHi)
|
||||
binary.BigEndian.PutUint16(uuid[8:], seq)
|
||||
copy(uuid[10:], nodeID[:])
|
||||
|
||||
return uuid, nil
|
||||
}
|
38
vendor/github.com/google/uuid/version4.go
generated
vendored
Normal file
38
vendor/github.com/google/uuid/version4.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import "io"
|
||||
|
||||
// New is creates a new random UUID or panics. New is equivalent to
|
||||
// the expression
|
||||
//
|
||||
// uuid.Must(uuid.NewRandom())
|
||||
func New() UUID {
|
||||
return Must(NewRandom())
|
||||
}
|
||||
|
||||
// NewRandom returns a Random (Version 4) UUID or panics.
|
||||
//
|
||||
// The strength of the UUIDs is based on the strength of the crypto/rand
|
||||
// package.
|
||||
//
|
||||
// A note about uniqueness derived from from the UUID Wikipedia entry:
|
||||
//
|
||||
// Randomly generated UUIDs have 122 random bits. One's annual risk of being
|
||||
// hit by a meteorite is estimated to be one chance in 17 billion, that
|
||||
// means the probability is about 0.00000000006 (6 × 10−11),
|
||||
// equivalent to the odds of creating a few tens of trillions of UUIDs in a
|
||||
// year and having one duplicate.
|
||||
func NewRandom() (UUID, error) {
|
||||
var uuid UUID
|
||||
_, err := io.ReadFull(rander, uuid[:])
|
||||
if err != nil {
|
||||
return Nil, err
|
||||
}
|
||||
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
|
||||
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
|
||||
return uuid, nil
|
||||
}
|
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
|
437
vendor/github.com/gophercloud/gophercloud/auth_options.go
generated
vendored
Normal file
437
vendor/github.com/gophercloud/gophercloud/auth_options.go
generated
vendored
Normal file
@@ -0,0 +1,437 @@
|
||||
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
|
||||
|
||||
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{}
|
||||
}
|
||||
|
||||
var userRequest *userReq
|
||||
|
||||
if opts.UserID != "" {
|
||||
// UserID could be used without the domain information
|
||||
userRequest = &userReq{
|
||||
ID: &opts.UserID,
|
||||
}
|
||||
}
|
||||
|
||||
if userRequest == nil && opts.Username == "" {
|
||||
// Make sure that Username or UserID are provided
|
||||
return nil, ErrUsernameOrUserID{}
|
||||
}
|
||||
|
||||
if userRequest == nil && opts.DomainID != "" {
|
||||
userRequest = &userReq{
|
||||
Name: &opts.Username,
|
||||
Domain: &domainReq{ID: &opts.DomainID},
|
||||
}
|
||||
}
|
||||
|
||||
if userRequest == nil && opts.DomainName != "" {
|
||||
userRequest = &userReq{
|
||||
Name: &opts.Username,
|
||||
Domain: &domainReq{Name: &opts.DomainName},
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that DomainID or DomainName are provided among Username
|
||||
if userRequest == nil {
|
||||
return nil, ErrDomainIDOrDomainName{}
|
||||
}
|
||||
|
||||
req.Auth.Identity.Methods = []string{"application_credential"}
|
||||
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
|
||||
}
|
52
vendor/github.com/gophercloud/gophercloud/auth_result.go
generated
vendored
Normal file
52
vendor/github.com/gophercloud/gophercloud/auth_result.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package gophercloud
|
||||
|
||||
/*
|
||||
AuthResult is the result from the request that was used to obtain a provider
|
||||
client's Keystone token. It is returned from ProviderClient.GetAuthResult().
|
||||
|
||||
The following types satisfy this interface:
|
||||
|
||||
github.com/gophercloud/gophercloud/openstack/identity/v2/tokens.CreateResult
|
||||
github.com/gophercloud/gophercloud/openstack/identity/v3/tokens.CreateResult
|
||||
|
||||
Usage example:
|
||||
|
||||
import (
|
||||
"github.com/gophercloud/gophercloud"
|
||||
tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
|
||||
tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
|
||||
)
|
||||
|
||||
func GetAuthenticatedUserID(providerClient *gophercloud.ProviderClient) (string, error) {
|
||||
r := providerClient.GetAuthResult()
|
||||
if r == nil {
|
||||
//ProviderClient did not use openstack.Authenticate(), e.g. because token
|
||||
//was set manually with ProviderClient.SetToken()
|
||||
return "", errors.New("no AuthResult available")
|
||||
}
|
||||
switch r := r.(type) {
|
||||
case tokens2.CreateResult:
|
||||
u, err := r.ExtractUser()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.ID, nil
|
||||
case tokens3.CreateResult:
|
||||
u, err := r.ExtractUser()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.ID, nil
|
||||
default:
|
||||
panic(fmt.Sprintf("got unexpected AuthResult type %t", r))
|
||||
}
|
||||
}
|
||||
|
||||
Both implementing types share a lot of methods by name, like ExtractUser() in
|
||||
this example. But those methods cannot be part of the AuthResult interface
|
||||
because the return types are different (in this case, type tokens2.User vs.
|
||||
type tokens3.User).
|
||||
*/
|
||||
type AuthResult interface {
|
||||
ExtractTokenID() (string, error)
|
||||
}
|
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, err := 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"
|
||||
}
|
114
vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go
generated
vendored
Normal file
114
vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
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 userID == "" && username == "" {
|
||||
// Empty username and userID could be ignored, when applicationCredentialID and applicationCredentialSecret are set
|
||||
if applicationCredentialID == "" && applicationCredentialSecret == "" {
|
||||
err := gophercloud.ErrMissingAnyoneOfEnvironmentVariables{
|
||||
EnvironmentVariables: []string{"OS_USERID", "OS_USERNAME"},
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
if applicationCredentialID == "" && applicationCredentialName != "" && applicationCredentialSecret != "" {
|
||||
if userID == "" && username == "" {
|
||||
return nilOptions, gophercloud.ErrMissingAnyoneOfEnvironmentVariables{
|
||||
EnvironmentVariables: []string{"OS_USERID", "OS_USERNAME"},
|
||||
}
|
||||
}
|
||||
if username != "" && domainID == "" && domainName == "" {
|
||||
return nilOptions, gophercloud.ErrMissingAnyoneOfEnvironmentVariables{
|
||||
EnvironmentVariables: []string{"OS_DOMAIN_ID", "OS_DOMAIN_NAME"},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
426
vendor/github.com/gophercloud/gophercloud/openstack/client.go
generated
vendored
Normal file
426
vendor/github.com/gophercloud/gophercloud/openstack/client.go
generated
vendored
Normal file
@@ -0,0 +1,426 @@
|
||||
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)
|
||||
|
||||
err = client.SetTokenAndAuthResult(result)
|
||||
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.SetThrowaway(true)
|
||||
tac.ReauthFunc = nil
|
||||
tac.SetTokenAndAuthResult(nil)
|
||||
tao := options
|
||||
tao.AllowReauth = false
|
||||
client.ReauthFunc = func() error {
|
||||
err := v2auth(&tac, endpoint, tao, eo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client.CopyTokenFrom(&tac)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
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)
|
||||
|
||||
err = client.SetTokenAndAuthResult(result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
catalog, err := result.ExtractServiceCatalog()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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.SetThrowaway(true)
|
||||
tac.ReauthFunc = nil
|
||||
tac.SetTokenAndAuthResult(nil)
|
||||
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.CopyTokenFrom(&tac)
|
||||
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")
|
||||
}
|
54
vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/doc.go
generated
vendored
Normal file
54
vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/doc.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Package recordsets provides information and interaction with the zone API
|
||||
resource for the OpenStack DNS service.
|
||||
|
||||
Example to List RecordSets by Zone
|
||||
|
||||
listOpts := recordsets.ListOpts{
|
||||
Type: "A",
|
||||
}
|
||||
|
||||
zoneID := "fff121f5-c506-410a-a69e-2d73ef9cbdbd"
|
||||
|
||||
allPages, err := recordsets.ListByZone(dnsClient, zoneID, listOpts).AllPages()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
allRRs, err := recordsets.ExtractRecordSets(allPages()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, rr := range allRRs {
|
||||
fmt.Printf("%+v\n", rr)
|
||||
}
|
||||
|
||||
Example to Create a RecordSet
|
||||
|
||||
createOpts := recordsets.CreateOpts{
|
||||
Name: "example.com.",
|
||||
Type: "A",
|
||||
TTL: 3600,
|
||||
Description: "This is a recordset.",
|
||||
Records: []string{"10.1.0.2"},
|
||||
}
|
||||
|
||||
zoneID := "fff121f5-c506-410a-a69e-2d73ef9cbdbd"
|
||||
|
||||
rr, err := recordsets.Create(dnsClient, zoneID, createOpts).Extract()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Delete a RecordSet
|
||||
|
||||
zoneID := "fff121f5-c506-410a-a69e-2d73ef9cbdbd"
|
||||
recordsetID := "d96ed01a-b439-4eb8-9b90-7a9f71017f7b"
|
||||
|
||||
err := recordsets.Delete(dnsClient, zoneID, recordsetID).ExtractErr()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
*/
|
||||
package recordsets
|
166
vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/requests.go
generated
vendored
Normal file
166
vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/requests.go
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
package recordsets
|
||||
|
||||
import (
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// ListOptsBuilder allows extensions to add additional parameters to the
|
||||
// List request.
|
||||
type ListOptsBuilder interface {
|
||||
ToRecordSetListQuery() (string, error)
|
||||
}
|
||||
|
||||
// ListOpts allows the filtering and sorting of paginated collections through
|
||||
// the API. Filtering is achieved by passing in struct field values that map to
|
||||
// the server attributes you want to see returned. Marker and Limit are used
|
||||
// for pagination.
|
||||
// https://developer.openstack.org/api-ref/dns/
|
||||
type ListOpts struct {
|
||||
// Integer value for the limit of values to return.
|
||||
Limit int `q:"limit"`
|
||||
|
||||
// UUID of the recordset at which you want to set a marker.
|
||||
Marker string `q:"marker"`
|
||||
|
||||
Data string `q:"data"`
|
||||
Description string `q:"description"`
|
||||
Name string `q:"name"`
|
||||
SortDir string `q:"sort_dir"`
|
||||
SortKey string `q:"sort_key"`
|
||||
Status string `q:"status"`
|
||||
TTL int `q:"ttl"`
|
||||
Type string `q:"type"`
|
||||
ZoneID string `q:"zone_id"`
|
||||
}
|
||||
|
||||
// ToRecordSetListQuery formats a ListOpts into a query string.
|
||||
func (opts ListOpts) ToRecordSetListQuery() (string, error) {
|
||||
q, err := gophercloud.BuildQueryString(opts)
|
||||
return q.String(), err
|
||||
}
|
||||
|
||||
// ListByZone implements the recordset list request.
|
||||
func ListByZone(client *gophercloud.ServiceClient, zoneID string, opts ListOptsBuilder) pagination.Pager {
|
||||
url := baseURL(client, zoneID)
|
||||
if opts != nil {
|
||||
query, err := opts.ToRecordSetListQuery()
|
||||
if err != nil {
|
||||
return pagination.Pager{Err: err}
|
||||
}
|
||||
url += query
|
||||
}
|
||||
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
||||
return RecordSetPage{pagination.LinkedPageBase{PageResult: r}}
|
||||
})
|
||||
}
|
||||
|
||||
// Get implements the recordset Get request.
|
||||
func Get(client *gophercloud.ServiceClient, zoneID string, rrsetID string) (r GetResult) {
|
||||
_, r.Err = client.Get(rrsetURL(client, zoneID, rrsetID), &r.Body, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// CreateOptsBuilder allows extensions to add additional attributes to the
|
||||
// Create request.
|
||||
type CreateOptsBuilder interface {
|
||||
ToRecordSetCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// CreateOpts specifies the base attributes that may be used to create a
|
||||
// RecordSet.
|
||||
type CreateOpts struct {
|
||||
// Name is the name of the RecordSet.
|
||||
Name string `json:"name" required:"true"`
|
||||
|
||||
// Description is a description of the RecordSet.
|
||||
Description string `json:"description,omitempty"`
|
||||
|
||||
// Records are the DNS records of the RecordSet.
|
||||
Records []string `json:"records,omitempty"`
|
||||
|
||||
// TTL is the time to live of the RecordSet.
|
||||
TTL int `json:"ttl,omitempty"`
|
||||
|
||||
// Type is the RRTYPE of the RecordSet.
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// ToRecordSetCreateMap formats an CreateOpts structure into a request body.
|
||||
func (opts CreateOpts) ToRecordSetCreateMap() (map[string]interface{}, error) {
|
||||
b, err := gophercloud.BuildRequestBody(opts, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Create creates a recordset in a given zone.
|
||||
func Create(client *gophercloud.ServiceClient, zoneID string, opts CreateOptsBuilder) (r CreateResult) {
|
||||
b, err := opts.ToRecordSetCreateMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
_, r.Err = client.Post(baseURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{201, 202},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateOptsBuilder allows extensions to add additional attributes to the
|
||||
// Update request.
|
||||
type UpdateOptsBuilder interface {
|
||||
ToRecordSetUpdateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// UpdateOpts specifies the base attributes that may be updated on an existing
|
||||
// RecordSet.
|
||||
type UpdateOpts struct {
|
||||
// Description is a description of the RecordSet.
|
||||
Description *string `json:"description,omitempty"`
|
||||
|
||||
// TTL is the time to live of the RecordSet.
|
||||
TTL int `json:"ttl,omitempty"`
|
||||
|
||||
// Records are the DNS records of the RecordSet.
|
||||
Records []string `json:"records,omitempty"`
|
||||
}
|
||||
|
||||
// ToRecordSetUpdateMap formats an UpdateOpts structure into a request body.
|
||||
func (opts UpdateOpts) ToRecordSetUpdateMap() (map[string]interface{}, error) {
|
||||
b, err := gophercloud.BuildRequestBody(opts, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opts.TTL > 0 {
|
||||
b["ttl"] = opts.TTL
|
||||
} else {
|
||||
b["ttl"] = nil
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Update updates a recordset in a given zone
|
||||
func Update(client *gophercloud.ServiceClient, zoneID string, rrsetID string, opts UpdateOptsBuilder) (r UpdateResult) {
|
||||
b, err := opts.ToRecordSetUpdateMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
_, r.Err = client.Put(rrsetURL(client, zoneID, rrsetID), &b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200, 202},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Delete removes an existing RecordSet.
|
||||
func Delete(client *gophercloud.ServiceClient, zoneID string, rrsetID string) (r DeleteResult) {
|
||||
_, r.Err = client.Delete(rrsetURL(client, zoneID, rrsetID), &gophercloud.RequestOpts{
|
||||
OkCodes: []int{202},
|
||||
})
|
||||
return
|
||||
}
|
147
vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/results.go
generated
vendored
Normal file
147
vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/results.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
package recordsets
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
type commonResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract interprets a GetResult, CreateResult or UpdateResult as a RecordSet.
|
||||
// An error is returned if the original call or the extraction failed.
|
||||
func (r commonResult) Extract() (*RecordSet, error) {
|
||||
var s *RecordSet
|
||||
err := r.ExtractInto(&s)
|
||||
return s, err
|
||||
}
|
||||
|
||||
// CreateResult is the result of a Create operation. Call its Extract method to
|
||||
// interpret the result as a RecordSet.
|
||||
type CreateResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// GetResult is the result of a Get operation. Call its Extract method to
|
||||
// interpret the result as a RecordSet.
|
||||
type GetResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// RecordSetPage is a single page of RecordSet results.
|
||||
type RecordSetPage struct {
|
||||
pagination.LinkedPageBase
|
||||
}
|
||||
|
||||
// UpdateResult is result of an Update operation. Call its Extract method to
|
||||
// interpret the result as a RecordSet.
|
||||
type UpdateResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// DeleteResult is result of a Delete operation. Call its ExtractErr method to
|
||||
// determine if the operation succeeded or failed.
|
||||
type DeleteResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
||||
|
||||
// IsEmpty returns true if the page contains no results.
|
||||
func (r RecordSetPage) IsEmpty() (bool, error) {
|
||||
s, err := ExtractRecordSets(r)
|
||||
return len(s) == 0, err
|
||||
}
|
||||
|
||||
// ExtractRecordSets extracts a slice of RecordSets from a List result.
|
||||
func ExtractRecordSets(r pagination.Page) ([]RecordSet, error) {
|
||||
var s struct {
|
||||
RecordSets []RecordSet `json:"recordsets"`
|
||||
}
|
||||
err := (r.(RecordSetPage)).ExtractInto(&s)
|
||||
return s.RecordSets, err
|
||||
}
|
||||
|
||||
// RecordSet represents a DNS Record Set.
|
||||
type RecordSet struct {
|
||||
// ID is the unique ID of the recordset
|
||||
ID string `json:"id"`
|
||||
|
||||
// ZoneID is the ID of the zone the recordset belongs to.
|
||||
ZoneID string `json:"zone_id"`
|
||||
|
||||
// ProjectID is the ID of the project that owns the recordset.
|
||||
ProjectID string `json:"project_id"`
|
||||
|
||||
// Name is the name of the recordset.
|
||||
Name string `json:"name"`
|
||||
|
||||
// ZoneName is the name of the zone the recordset belongs to.
|
||||
ZoneName string `json:"zone_name"`
|
||||
|
||||
// Type is the RRTYPE of the recordset.
|
||||
Type string `json:"type"`
|
||||
|
||||
// Records are the DNS records of the recordset.
|
||||
Records []string `json:"records"`
|
||||
|
||||
// TTL is the time to live of the recordset.
|
||||
TTL int `json:"ttl"`
|
||||
|
||||
// Status is the status of the recordset.
|
||||
Status string `json:"status"`
|
||||
|
||||
// Action is the current action in progress of the recordset.
|
||||
Action string `json:"action"`
|
||||
|
||||
// Description is the description of the recordset.
|
||||
Description string `json:"description"`
|
||||
|
||||
// Version is the revision of the recordset.
|
||||
Version int `json:"version"`
|
||||
|
||||
// CreatedAt is the date when the recordset was created.
|
||||
CreatedAt time.Time `json:"-"`
|
||||
|
||||
// UpdatedAt is the date when the recordset was updated.
|
||||
UpdatedAt time.Time `json:"-"`
|
||||
|
||||
// Links includes HTTP references to the itself,
|
||||
// useful for passing along to other APIs that might want a recordset
|
||||
// reference.
|
||||
Links []gophercloud.Link `json:"-"`
|
||||
}
|
||||
|
||||
func (r *RecordSet) UnmarshalJSON(b []byte) error {
|
||||
type tmp RecordSet
|
||||
var s struct {
|
||||
tmp
|
||||
CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
|
||||
UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"`
|
||||
Links map[string]interface{} `json:"links"`
|
||||
}
|
||||
err := json.Unmarshal(b, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*r = RecordSet(s.tmp)
|
||||
|
||||
r.CreatedAt = time.Time(s.CreatedAt)
|
||||
r.UpdatedAt = time.Time(s.UpdatedAt)
|
||||
|
||||
if s.Links != nil {
|
||||
for rel, href := range s.Links {
|
||||
if v, ok := href.(string); ok {
|
||||
link := gophercloud.Link{
|
||||
Rel: rel,
|
||||
Href: v,
|
||||
}
|
||||
r.Links = append(r.Links, link)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
11
vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/urls.go
generated
vendored
Normal file
11
vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/urls.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package recordsets
|
||||
|
||||
import "github.com/gophercloud/gophercloud"
|
||||
|
||||
func baseURL(c *gophercloud.ServiceClient, zoneID string) string {
|
||||
return c.ServiceURL("zones", zoneID, "recordsets")
|
||||
}
|
||||
|
||||
func rrsetURL(c *gophercloud.ServiceClient, zoneID string, rrsetID string) string {
|
||||
return c.ServiceURL("zones", zoneID, "recordsets", rrsetID)
|
||||
}
|
48
vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/doc.go
generated
vendored
Normal file
48
vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/doc.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Package zones provides information and interaction with the zone API
|
||||
resource for the OpenStack DNS service.
|
||||
|
||||
Example to List Zones
|
||||
|
||||
listOpts := zones.ListOpts{
|
||||
Email: "jdoe@example.com",
|
||||
}
|
||||
|
||||
allPages, err := zones.List(dnsClient, listOpts).AllPages()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
allZones, err := zones.ExtractZones(allPages)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, zone := range allZones {
|
||||
fmt.Printf("%+v\n", zone)
|
||||
}
|
||||
|
||||
Example to Create a Zone
|
||||
|
||||
createOpts := zones.CreateOpts{
|
||||
Name: "example.com.",
|
||||
Email: "jdoe@example.com",
|
||||
Type: "PRIMARY",
|
||||
TTL: 7200,
|
||||
Description: "This is a zone.",
|
||||
}
|
||||
|
||||
zone, err := zones.Create(dnsClient, createOpts).Extract()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Delete a Zone
|
||||
|
||||
zoneID := "99d10f68-5623-4491-91a0-6daafa32b60e"
|
||||
err := zones.Delete(dnsClient, zoneID).ExtractErr()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
*/
|
||||
package zones
|
174
vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/requests.go
generated
vendored
Normal file
174
vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/requests.go
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
package zones
|
||||
|
||||
import (
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// ListOptsBuilder allows extensions to add parameters to the List request.
|
||||
type ListOptsBuilder interface {
|
||||
ToZoneListQuery() (string, error)
|
||||
}
|
||||
|
||||
// ListOpts allows the filtering and sorting of paginated collections through
|
||||
// the API. Filtering is achieved by passing in struct field values that map to
|
||||
// the server attributes you want to see returned. Marker and Limit are used
|
||||
// for pagination.
|
||||
// https://developer.openstack.org/api-ref/dns/
|
||||
type ListOpts struct {
|
||||
// Integer value for the limit of values to return.
|
||||
Limit int `q:"limit"`
|
||||
|
||||
// UUID of the zone at which you want to set a marker.
|
||||
Marker string `q:"marker"`
|
||||
|
||||
Description string `q:"description"`
|
||||
Email string `q:"email"`
|
||||
Name string `q:"name"`
|
||||
SortDir string `q:"sort_dir"`
|
||||
SortKey string `q:"sort_key"`
|
||||
Status string `q:"status"`
|
||||
TTL int `q:"ttl"`
|
||||
Type string `q:"type"`
|
||||
}
|
||||
|
||||
// ToZoneListQuery formats a ListOpts into a query string.
|
||||
func (opts ListOpts) ToZoneListQuery() (string, error) {
|
||||
q, err := gophercloud.BuildQueryString(opts)
|
||||
return q.String(), err
|
||||
}
|
||||
|
||||
// List implements a zone List request.
|
||||
func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
|
||||
url := baseURL(client)
|
||||
if opts != nil {
|
||||
query, err := opts.ToZoneListQuery()
|
||||
if err != nil {
|
||||
return pagination.Pager{Err: err}
|
||||
}
|
||||
url += query
|
||||
}
|
||||
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
||||
return ZonePage{pagination.LinkedPageBase{PageResult: r}}
|
||||
})
|
||||
}
|
||||
|
||||
// Get returns information about a zone, given its ID.
|
||||
func Get(client *gophercloud.ServiceClient, zoneID string) (r GetResult) {
|
||||
_, r.Err = client.Get(zoneURL(client, zoneID), &r.Body, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// CreateOptsBuilder allows extensions to add additional attributes to the
|
||||
// Create request.
|
||||
type CreateOptsBuilder interface {
|
||||
ToZoneCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// CreateOpts specifies the attributes used to create a zone.
|
||||
type CreateOpts struct {
|
||||
// Attributes are settings that supply hints and filters for the zone.
|
||||
Attributes map[string]string `json:"attributes,omitempty"`
|
||||
|
||||
// Email contact of the zone.
|
||||
Email string `json:"email,omitempty"`
|
||||
|
||||
// Description of the zone.
|
||||
Description string `json:"description,omitempty"`
|
||||
|
||||
// Name of the zone.
|
||||
Name string `json:"name" required:"true"`
|
||||
|
||||
// Masters specifies zone masters if this is a secondary zone.
|
||||
Masters []string `json:"masters,omitempty"`
|
||||
|
||||
// TTL is the time to live of the zone.
|
||||
TTL int `json:"-"`
|
||||
|
||||
// Type specifies if this is a primary or secondary zone.
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// ToZoneCreateMap formats an CreateOpts structure into a request body.
|
||||
func (opts CreateOpts) ToZoneCreateMap() (map[string]interface{}, error) {
|
||||
b, err := gophercloud.BuildRequestBody(opts, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opts.TTL > 0 {
|
||||
b["ttl"] = opts.TTL
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Create implements a zone create request.
|
||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
||||
b, err := opts.ToZoneCreateMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
_, r.Err = client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{201, 202},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateOptsBuilder allows extensions to add additional attributes to the
|
||||
// Update request.
|
||||
type UpdateOptsBuilder interface {
|
||||
ToZoneUpdateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// UpdateOpts specifies the attributes to update a zone.
|
||||
type UpdateOpts struct {
|
||||
// Email contact of the zone.
|
||||
Email string `json:"email,omitempty"`
|
||||
|
||||
// TTL is the time to live of the zone.
|
||||
TTL int `json:"-"`
|
||||
|
||||
// Masters specifies zone masters if this is a secondary zone.
|
||||
Masters []string `json:"masters,omitempty"`
|
||||
|
||||
// Description of the zone.
|
||||
Description *string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
// ToZoneUpdateMap formats an UpdateOpts structure into a request body.
|
||||
func (opts UpdateOpts) ToZoneUpdateMap() (map[string]interface{}, error) {
|
||||
b, err := gophercloud.BuildRequestBody(opts, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opts.TTL > 0 {
|
||||
b["ttl"] = opts.TTL
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Update implements a zone update request.
|
||||
func Update(client *gophercloud.ServiceClient, zoneID string, opts UpdateOptsBuilder) (r UpdateResult) {
|
||||
b, err := opts.ToZoneUpdateMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
_, r.Err = client.Patch(zoneURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200, 202},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Delete implements a zone delete request.
|
||||
func Delete(client *gophercloud.ServiceClient, zoneID string) (r DeleteResult) {
|
||||
_, r.Err = client.Delete(zoneURL(client, zoneID), &gophercloud.RequestOpts{
|
||||
OkCodes: []int{202},
|
||||
JSONResponse: &r.Body,
|
||||
})
|
||||
return
|
||||
}
|
166
vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/results.go
generated
vendored
Normal file
166
vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/results.go
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
package zones
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
type commonResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract interprets a GetResult, CreateResult or UpdateResult as a Zone.
|
||||
// An error is returned if the original call or the extraction failed.
|
||||
func (r commonResult) Extract() (*Zone, error) {
|
||||
var s *Zone
|
||||
err := r.ExtractInto(&s)
|
||||
return s, err
|
||||
}
|
||||
|
||||
// CreateResult is the result of a Create request. Call its Extract method
|
||||
// to interpret the result as a Zone.
|
||||
type CreateResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// GetResult is the result of a Get request. Call its Extract method
|
||||
// to interpret the result as a Zone.
|
||||
type GetResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// UpdateResult is the result of an Update request. Call its Extract method
|
||||
// to interpret the result as a Zone.
|
||||
type UpdateResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// DeleteResult is the result of a Delete request. Call its ExtractErr method
|
||||
// to determine if the request succeeded or failed.
|
||||
type DeleteResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// ZonePage is a single page of Zone results.
|
||||
type ZonePage struct {
|
||||
pagination.LinkedPageBase
|
||||
}
|
||||
|
||||
// IsEmpty returns true if the page contains no results.
|
||||
func (r ZonePage) IsEmpty() (bool, error) {
|
||||
s, err := ExtractZones(r)
|
||||
return len(s) == 0, err
|
||||
}
|
||||
|
||||
// ExtractZones extracts a slice of Zones from a List result.
|
||||
func ExtractZones(r pagination.Page) ([]Zone, error) {
|
||||
var s struct {
|
||||
Zones []Zone `json:"zones"`
|
||||
}
|
||||
err := (r.(ZonePage)).ExtractInto(&s)
|
||||
return s.Zones, err
|
||||
}
|
||||
|
||||
// Zone represents a DNS zone.
|
||||
type Zone struct {
|
||||
// ID uniquely identifies this zone amongst all other zones, including those
|
||||
// not accessible to the current tenant.
|
||||
ID string `json:"id"`
|
||||
|
||||
// PoolID is the ID for the pool hosting this zone.
|
||||
PoolID string `json:"pool_id"`
|
||||
|
||||
// ProjectID identifies the project/tenant owning this resource.
|
||||
ProjectID string `json:"project_id"`
|
||||
|
||||
// Name is the DNS Name for the zone.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Email for the zone. Used in SOA records for the zone.
|
||||
Email string `json:"email"`
|
||||
|
||||
// Description for this zone.
|
||||
Description string `json:"description"`
|
||||
|
||||
// TTL is the Time to Live for the zone.
|
||||
TTL int `json:"ttl"`
|
||||
|
||||
// Serial is the current serial number for the zone.
|
||||
Serial int `json:"-"`
|
||||
|
||||
// Status is the status of the resource.
|
||||
Status string `json:"status"`
|
||||
|
||||
// Action is the current action in progress on the resource.
|
||||
Action string `json:"action"`
|
||||
|
||||
// Version of the resource.
|
||||
Version int `json:"version"`
|
||||
|
||||
// Attributes for the zone.
|
||||
Attributes map[string]string `json:"attributes"`
|
||||
|
||||
// Type of zone. Primary is controlled by Designate.
|
||||
// Secondary zones are slaved from another DNS Server.
|
||||
// Defaults to Primary.
|
||||
Type string `json:"type"`
|
||||
|
||||
// Masters is the servers for slave servers to get DNS information from.
|
||||
Masters []string `json:"masters"`
|
||||
|
||||
// CreatedAt is the date when the zone was created.
|
||||
CreatedAt time.Time `json:"-"`
|
||||
|
||||
// UpdatedAt is the date when the last change was made to the zone.
|
||||
UpdatedAt time.Time `json:"-"`
|
||||
|
||||
// TransferredAt is the last time an update was retrieved from the
|
||||
// master servers.
|
||||
TransferredAt time.Time `json:"-"`
|
||||
|
||||
// Links includes HTTP references to the itself, useful for passing along
|
||||
// to other APIs that might want a server reference.
|
||||
Links map[string]interface{} `json:"links"`
|
||||
}
|
||||
|
||||
func (r *Zone) UnmarshalJSON(b []byte) error {
|
||||
type tmp Zone
|
||||
var s struct {
|
||||
tmp
|
||||
CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
|
||||
UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"`
|
||||
TransferredAt gophercloud.JSONRFC3339MilliNoZ `json:"transferred_at"`
|
||||
Serial interface{} `json:"serial"`
|
||||
}
|
||||
err := json.Unmarshal(b, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*r = Zone(s.tmp)
|
||||
|
||||
r.CreatedAt = time.Time(s.CreatedAt)
|
||||
r.UpdatedAt = time.Time(s.UpdatedAt)
|
||||
r.TransferredAt = time.Time(s.TransferredAt)
|
||||
|
||||
switch t := s.Serial.(type) {
|
||||
case float64:
|
||||
r.Serial = int(t)
|
||||
case string:
|
||||
switch t {
|
||||
case "":
|
||||
r.Serial = 0
|
||||
default:
|
||||
serial, err := strconv.ParseFloat(t, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Serial = int(serial)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
11
vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/urls.go
generated
vendored
Normal file
11
vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/urls.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package zones
|
||||
|
||||
import "github.com/gophercloud/gophercloud"
|
||||
|
||||
func baseURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL("zones")
|
||||
}
|
||||
|
||||
func zoneURL(c *gophercloud.ServiceClient, zoneID string) string {
|
||||
return c.ServiceURL("zones", zoneID)
|
||||
}
|
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
|
||||
}
|
174
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go
generated
vendored
Normal file
174
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
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
|
||||
}
|
||||
|
||||
// ExtractTokenID implements the gophercloud.AuthResult interface. The returned
|
||||
// string is the same as the ID field of the Token struct returned from
|
||||
// ExtractToken().
|
||||
func (r CreateResult) ExtractTokenID() (string, error) {
|
||||
var s struct {
|
||||
Access struct {
|
||||
Token struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"token"`
|
||||
} `json:"access"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return s.Access.Token.ID, err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
178
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go
generated
vendored
Normal file
178
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go
generated
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
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
|
||||
}
|
||||
|
||||
// ExtractTokenID implements the gophercloud.AuthResult interface. The returned
|
||||
// string is the same as the ID field of the Token struct returned from
|
||||
// ExtractToken().
|
||||
func (r CreateResult) ExtractTokenID() (string, error) {
|
||||
return r.Header.Get("X-Subject-Token"), r.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
|
||||
}
|
491
vendor/github.com/gophercloud/gophercloud/params.go
generated
vendored
Normal file
491
vendor/github.com/gophercloud/gophercloud/params.go
generated
vendored
Normal file
@@ -0,0 +1,491 @@
|
||||
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.Slice || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Slice) {
|
||||
sliceValue := v
|
||||
if sliceValue.Kind() == reflect.Ptr {
|
||||
sliceValue = sliceValue.Elem()
|
||||
}
|
||||
|
||||
for i := 0; i < sliceValue.Len(); i++ {
|
||||
element := sliceValue.Index(i)
|
||||
if element.Kind() == reflect.Struct || (element.Kind() == reflect.Ptr && element.Elem().Kind() == reflect.Struct) {
|
||||
_, err := BuildRequestBody(element.Interface(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
487
vendor/github.com/gophercloud/gophercloud/provider_client.go
generated
vendored
Normal file
487
vendor/github.com/gophercloud/gophercloud/provider_client.go
generated
vendored
Normal file
@@ -0,0 +1,487 @@
|
||||
package gophercloud
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"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
|
||||
|
||||
// Throwaway determines whether if this client is a throw-away client. It's a copy of user's provider client
|
||||
// with the token and reauth func zeroed. Such client can be used to perform reauthorization.
|
||||
Throwaway bool
|
||||
|
||||
mut *sync.RWMutex
|
||||
|
||||
reauthmut *reauthlock
|
||||
|
||||
authResult AuthResult
|
||||
}
|
||||
|
||||
type reauthlock struct {
|
||||
sync.RWMutex
|
||||
reauthing bool
|
||||
reauthingErr error
|
||||
done *sync.Cond
|
||||
}
|
||||
|
||||
// AuthenticatedHeaders returns a map of HTTP headers that are common for all
|
||||
// authenticated service requests. Blocks if Reauthenticate is in progress.
|
||||
func (client *ProviderClient) AuthenticatedHeaders() (m map[string]string) {
|
||||
if client.IsThrowaway() {
|
||||
return
|
||||
}
|
||||
if client.reauthmut != nil {
|
||||
client.reauthmut.Lock()
|
||||
for client.reauthmut.reauthing {
|
||||
client.reauthmut.done.Wait()
|
||||
}
|
||||
client.reauthmut.Unlock()
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
// GetAuthResult returns the result from the request that was used to obtain a
|
||||
// provider client's Keystone token.
|
||||
//
|
||||
// The result is nil when authentication has not yet taken place, when the token
|
||||
// was set manually with SetToken(), or when a ReauthFunc was used that does not
|
||||
// record the AuthResult.
|
||||
func (client *ProviderClient) GetAuthResult() AuthResult {
|
||||
if client.mut != nil {
|
||||
client.mut.RLock()
|
||||
defer client.mut.RUnlock()
|
||||
}
|
||||
return client.authResult
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// WARNING: This function is deprecated. Use SetTokenAndAuthResult() instead.
|
||||
func (client *ProviderClient) SetToken(t string) {
|
||||
if client.mut != nil {
|
||||
client.mut.Lock()
|
||||
defer client.mut.Unlock()
|
||||
}
|
||||
client.TokenID = t
|
||||
client.authResult = nil
|
||||
}
|
||||
|
||||
// SetTokenAndAuthResult safely sets the value of the auth token in the
|
||||
// ProviderClient and also records the AuthResult that was returned from the
|
||||
// token creation request. Applications may call this in a custom ReauthFunc.
|
||||
func (client *ProviderClient) SetTokenAndAuthResult(r AuthResult) error {
|
||||
tokenID := ""
|
||||
var err error
|
||||
if r != nil {
|
||||
tokenID, err = r.ExtractTokenID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if client.mut != nil {
|
||||
client.mut.Lock()
|
||||
defer client.mut.Unlock()
|
||||
}
|
||||
client.TokenID = tokenID
|
||||
client.authResult = r
|
||||
return nil
|
||||
}
|
||||
|
||||
// CopyTokenFrom safely copies the token from another ProviderClient into the
|
||||
// this one.
|
||||
func (client *ProviderClient) CopyTokenFrom(other *ProviderClient) {
|
||||
if client.mut != nil {
|
||||
client.mut.Lock()
|
||||
defer client.mut.Unlock()
|
||||
}
|
||||
if other.mut != nil && other.mut != client.mut {
|
||||
other.mut.RLock()
|
||||
defer other.mut.RUnlock()
|
||||
}
|
||||
client.TokenID = other.TokenID
|
||||
client.authResult = other.authResult
|
||||
}
|
||||
|
||||
// IsThrowaway safely reads the value of the client Throwaway field.
|
||||
func (client *ProviderClient) IsThrowaway() bool {
|
||||
if client.reauthmut != nil {
|
||||
client.reauthmut.RLock()
|
||||
defer client.reauthmut.RUnlock()
|
||||
}
|
||||
return client.Throwaway
|
||||
}
|
||||
|
||||
// SetThrowaway safely sets the value of the client Throwaway field.
|
||||
func (client *ProviderClient) SetThrowaway(v bool) {
|
||||
if client.reauthmut != nil {
|
||||
client.reauthmut.Lock()
|
||||
defer client.reauthmut.Unlock()
|
||||
}
|
||||
client.Throwaway = v
|
||||
}
|
||||
|
||||
// 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.reauthmut.Lock()
|
||||
if client.reauthmut.reauthing {
|
||||
for !client.reauthmut.reauthing {
|
||||
client.reauthmut.done.Wait()
|
||||
}
|
||||
err = client.reauthmut.reauthingErr
|
||||
client.reauthmut.Unlock()
|
||||
return err
|
||||
}
|
||||
client.reauthmut.Unlock()
|
||||
|
||||
client.mut.Lock()
|
||||
defer client.mut.Unlock()
|
||||
|
||||
client.reauthmut.Lock()
|
||||
client.reauthmut.reauthing = true
|
||||
client.reauthmut.done = sync.NewCond(client.reauthmut)
|
||||
client.reauthmut.reauthingErr = nil
|
||||
client.reauthmut.Unlock()
|
||||
|
||||
if previousToken == "" || client.TokenID == previousToken {
|
||||
err = client.ReauthFunc()
|
||||
}
|
||||
|
||||
client.reauthmut.Lock()
|
||||
client.reauthmut.reauthing = false
|
||||
client.reauthmut.reauthingErr = err
|
||||
client.reauthmut.done.Broadcast()
|
||||
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 {
|
||||
return nil, errors.New("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
|
||||
okc := options.OkCodes
|
||||
if okc == nil {
|
||||
okc = defaultOkCodes(method)
|
||||
}
|
||||
|
||||
// Validate the HTTP response status.
|
||||
var ok bool
|
||||
for _, code := range okc {
|
||||
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)
|
||||
}
|
||||
}
|
||||
resp, err = client.Request(method, url, options)
|
||||
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{}
|
||||
}
|
448
vendor/github.com/gophercloud/gophercloud/results.go
generated
vendored
Normal file
448
vendor/github.com/gophercloud/gophercloud/results.go
generated
vendored
Normal file
@@ -0,0 +1,448 @@
|
||||
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)
|
||||
|
||||
if mSlice, ok := m[label].([]interface{}); ok {
|
||||
for _, v := range mSlice {
|
||||
// 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
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user