Cherry pick v1.7 into master
This commit is contained in:
parent
a09dfa3ce1
commit
b6498cdcbc
73 changed files with 6573 additions and 186 deletions
47
Gopkg.lock
generated
47
Gopkg.lock
generated
|
@ -543,6 +543,12 @@
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "62e9147c64a1ed519147b62a56a14e83e2be02c1"
|
revision = "62e9147c64a1ed519147b62a56a14e83e2be02c1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/fatih/structs"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "4966fc68f5b7593aafa6cbbba2d65ec6e1416047"
|
||||||
|
version = "v1.1.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/flynn/go-shlex"
|
name = "github.com/flynn/go-shlex"
|
||||||
|
@ -812,6 +818,12 @@
|
||||||
revision = "59fac5042749a5afb9af70e813da1dd5474f0167"
|
revision = "59fac5042749a5afb9af70e813da1dd5474f0167"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/kolo/xmlrpc"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "16bdd962781df9696f40cc2bab924f1a855a7f89"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/konsorten/go-windows-terminal-sequences"
|
name = "github.com/konsorten/go-windows-terminal-sequences"
|
||||||
|
@ -956,10 +968,10 @@
|
||||||
revision = "2bca23e0e452137f789efbc8610126fd8b94f73b"
|
revision = "2bca23e0e452137f789efbc8610126fd8b94f73b"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
|
||||||
name = "github.com/mitchellh/mapstructure"
|
name = "github.com/mitchellh/mapstructure"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "b4575eea38cca1123ec2dc90c26529b5c5acfcff"
|
revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe"
|
||||||
|
version = "v1.1.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
@ -1167,6 +1179,12 @@
|
||||||
revision = "a67f783a3814b8729bd2dac5780b5f78f8dbd64d"
|
revision = "a67f783a3814b8729bd2dac5780b5f78f8dbd64d"
|
||||||
version = "v1.1.0"
|
version = "v1.1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/smueller18/goinwx"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "5d138389109eca96463f44f692408f0d1c731278"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/spf13/pflag"
|
name = "github.com/spf13/pflag"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
@ -1214,6 +1232,16 @@
|
||||||
revision = "b2b6a672cf1e5b90748f79b8b81fc8c5cf0571a1"
|
revision = "b2b6a672cf1e5b90748f79b8b81fc8c5cf0571a1"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/transip/gotransip"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"domain",
|
||||||
|
"util"
|
||||||
|
]
|
||||||
|
revision = "1dc93a7db3567a5ccf865106afac88278ba940cf"
|
||||||
|
version = "v5.8.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/tuvistavie/securerandom"
|
name = "github.com/tuvistavie/securerandom"
|
||||||
|
@ -1330,6 +1358,7 @@
|
||||||
"providers/dns/bluecat",
|
"providers/dns/bluecat",
|
||||||
"providers/dns/cloudflare",
|
"providers/dns/cloudflare",
|
||||||
"providers/dns/cloudxns",
|
"providers/dns/cloudxns",
|
||||||
|
"providers/dns/conoha",
|
||||||
"providers/dns/digitalocean",
|
"providers/dns/digitalocean",
|
||||||
"providers/dns/dnsimple",
|
"providers/dns/dnsimple",
|
||||||
"providers/dns/dnsmadeeasy",
|
"providers/dns/dnsmadeeasy",
|
||||||
|
@ -1346,10 +1375,13 @@
|
||||||
"providers/dns/glesys",
|
"providers/dns/glesys",
|
||||||
"providers/dns/godaddy",
|
"providers/dns/godaddy",
|
||||||
"providers/dns/hostingde",
|
"providers/dns/hostingde",
|
||||||
|
"providers/dns/httpreq",
|
||||||
"providers/dns/iij",
|
"providers/dns/iij",
|
||||||
|
"providers/dns/inwx",
|
||||||
"providers/dns/lightsail",
|
"providers/dns/lightsail",
|
||||||
"providers/dns/linode",
|
"providers/dns/linode",
|
||||||
"providers/dns/linodev4",
|
"providers/dns/linodev4",
|
||||||
|
"providers/dns/mydnsjp",
|
||||||
"providers/dns/namecheap",
|
"providers/dns/namecheap",
|
||||||
"providers/dns/namedotcom",
|
"providers/dns/namedotcom",
|
||||||
"providers/dns/netcup",
|
"providers/dns/netcup",
|
||||||
|
@ -1362,11 +1394,14 @@
|
||||||
"providers/dns/rfc2136",
|
"providers/dns/rfc2136",
|
||||||
"providers/dns/route53",
|
"providers/dns/route53",
|
||||||
"providers/dns/sakuracloud",
|
"providers/dns/sakuracloud",
|
||||||
|
"providers/dns/selectel",
|
||||||
"providers/dns/stackpath",
|
"providers/dns/stackpath",
|
||||||
|
"providers/dns/transip",
|
||||||
"providers/dns/vegadns",
|
"providers/dns/vegadns",
|
||||||
|
"providers/dns/vscale",
|
||||||
"providers/dns/vultr"
|
"providers/dns/vultr"
|
||||||
]
|
]
|
||||||
revision = "1151b4e3befc51b7b215179c87791753721dc6d5"
|
revision = "a5f0a3ff8026e05cbdd11c391c0e25122497c736"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
@ -1531,8 +1566,8 @@
|
||||||
"ddtrace/opentracer",
|
"ddtrace/opentracer",
|
||||||
"ddtrace/tracer"
|
"ddtrace/tracer"
|
||||||
]
|
]
|
||||||
revision = "d052956664af54dbcff2712d10c67c76fbfc299f"
|
revision = "48eeff27357376bcb31a15674dc4be9078de88b3"
|
||||||
version = "v1.0.0"
|
version = "v1.5.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "gopkg.in/fsnotify.v1"
|
name = "gopkg.in/fsnotify.v1"
|
||||||
|
@ -1791,6 +1826,6 @@
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "d4f73c986b64003e14a36894149943e956e0dfa40b8837bfd11bf5fa3ad78c77"
|
inputs-digest = "817d5ad6a6ae085d57953a73f7b615a223fa3e70b6152bf87382508bfbfbd655"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|
|
@ -267,4 +267,4 @@
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "gopkg.in/DataDog/dd-trace-go.v1"
|
name = "gopkg.in/DataDog/dd-trace-go.v1"
|
||||||
version = "1.0.0"
|
version = "1.5.0"
|
||||||
|
|
|
@ -133,7 +133,7 @@ git clone https://github.com/containous/traefik
|
||||||
|
|
||||||
## Introductory Videos
|
## Introductory Videos
|
||||||
|
|
||||||
Here is a talk given by [Emile Vauge](https://github.com/emilevauge) at [GopherCon 2017](https://gophercon.com/).
|
Here is a talk given by [Emile Vauge](https://github.com/emilevauge) at GopherCon 2017.
|
||||||
You will learn Traefik basics in less than 10 minutes.
|
You will learn Traefik basics in less than 10 minutes.
|
||||||
|
|
||||||
[![Traefik GopherCon 2017](https://img.youtube.com/vi/RgudiksfL-k/0.jpg)](https://www.youtube.com/watch?v=RgudiksfL-k)
|
[![Traefik GopherCon 2017](https://img.youtube.com/vi/RgudiksfL-k/0.jpg)](https://www.youtube.com/watch?v=RgudiksfL-k)
|
||||||
|
|
|
@ -743,8 +743,6 @@ If you want to dig into more details, here is the source code of the collecting
|
||||||
|
|
||||||
By default we anonymize all configuration fields, except fields tagged with `export=true`.
|
By default we anonymize all configuration fields, except fields tagged with `export=true`.
|
||||||
|
|
||||||
You can check all fields in the [godoc](https://godoc.org/github.com/containous/traefik/configuration#GlobalConfiguration).
|
|
||||||
|
|
||||||
### How to enable this ?
|
### How to enable this ?
|
||||||
|
|
||||||
You can enable the collecting system by:
|
You can enable the collecting system by:
|
||||||
|
|
|
@ -275,21 +275,22 @@ Here is a list of supported `provider`s, that can automate the DNS verification,
|
||||||
|
|
||||||
| Provider Name | Provider Code | Environment Variables | Wildcard & Root Domain Support |
|
| Provider Name | Provider Code | Environment Variables | Wildcard & Root Domain Support |
|
||||||
|--------------------------------------------------------|----------------|-------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------|
|
|--------------------------------------------------------|----------------|-------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------|
|
||||||
| [ACME DNS](https://github.com/joohoi/acme-dns) | `acmedns` | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH` | Not tested yet |
|
| [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 |
|
| [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 |
|
| [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 |
|
| [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 |
|
| [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 |
|
| [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 |
|
| [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 |
|
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | YES |
|
||||||
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | Not tested yet |
|
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | Not tested yet |
|
||||||
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | Not tested yet |
|
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | Not tested yet |
|
||||||
| [DNSPod](http://www.dnspod.net/) | `dnspod` | `DNSPOD_API_KEY` | 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 |
|
| [DreamHost](https://www.dreamhost.com/) | `dreamhost` | `DREAMHOST_API_KEY` | YES |
|
||||||
| [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | No |
|
| [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | No |
|
||||||
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | Not tested yet |
|
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | Not tested yet |
|
||||||
| External Program | `exec` | `EXEC_PATH` | Not tested yet |
|
| External Program | `exec` | `EXEC_PATH` | YES |
|
||||||
| [Exoscale](https://www.exoscale.com) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | 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 |
|
| [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](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | Not tested yet |
|
||||||
|
@ -298,11 +299,14 @@ Here is a list of supported `provider`s, that can automate the DNS verification,
|
||||||
| [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | 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`, `GCE_SERVICE_ACCOUNT_FILE` | YES |
|
| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, `GCE_SERVICE_ACCOUNT_FILE` | YES |
|
||||||
| [hosting.de](https://www.hosting.de) | `hostingde` | `HOSTINGDE_API_KEY`, `HOSTINGDE_ZONE_NAME` | Not tested yet |
|
| [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` | YES |
|
||||||
| [IIJ](https://www.iij.ad.jp/) | `iij` | `IIJ_API_ACCESS_KEY`, `IIJ_API_SECRET_KEY`, `IIJ_DO_SERVICE_CODE` | Not tested yet |
|
| [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 |
|
| [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](https://www.linode.com) | `linode` | `LINODE_API_KEY` | Not tested yet |
|
||||||
| [Linode v4](https://www.linode.com) | `linodev4` | `LINODE_TOKEN` | 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 |
|
| 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 |
|
| [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 |
|
| [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 |
|
| [Netcup](https://www.netcup.eu/) | `netcup` | `NETCUP_CUSTOMER_NUMBER`, `NETCUP_API_KEY`, `NETCUP_API_PASSWORD` | Not tested yet |
|
||||||
|
@ -315,8 +319,11 @@ Here is a list of supported `provider`s, that can automate the DNS verification,
|
||||||
| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` | 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 |
|
| [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 |
|
| [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 |
|
| [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 |
|
| [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 |
|
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | Not tested yet |
|
||||||
|
|
||||||
#### `resolvers`
|
#### `resolvers`
|
||||||
|
|
|
@ -106,10 +106,10 @@ entryPoint = "foo"
|
||||||
entryPoint = "bar"
|
entryPoint = "bar"
|
||||||
```
|
```
|
||||||
|
|
||||||
In the above example, you would access a regular path, administration panel, and health-check as follows:
|
In the above example, you would access a regular path, dashboard, and health-check as follows:
|
||||||
|
|
||||||
* Regular path: `http://hostname:80/path`
|
* Regular path: `http://hostname:80/path`
|
||||||
* Admin Panel: `http://hostname:8083/`
|
* Dashboard: `http://hostname:8083/`
|
||||||
* Ping URL: `http://hostname:8082/ping`
|
* Ping URL: `http://hostname:8082/ping`
|
||||||
|
|
||||||
In the above example, it is _very_ important to create a named dedicated entry point, and do **not** include it in `defaultEntryPoints`.
|
In the above example, it is _very_ important to create a named dedicated entry point, and do **not** include it in `defaultEntryPoints`.
|
||||||
|
|
|
@ -236,11 +236,11 @@ Labels can be used on containers to override default behavior.
|
||||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
||||||
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
|
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
|
||||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions |
|
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions |
|
||||||
| `traefik.backend.loadbalancer.swarm=true` | Uses Swarm's inbuilt load balancer (only relevant under Swarm Mode). |
|
| `traefik.backend.loadbalancer.swarm=true` | Uses Swarm's inbuilt load balancer (only relevant under Swarm Mode). [3]. |
|
||||||
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||||
| `traefik.frontend.auth.basic=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2] (DEPRECATED). |
|
| `traefik.frontend.auth.basic=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2] (DEPRECATED). |
|
||||||
| `traefik.frontend.auth.basic.realm=REALM` | Sets the realm of basic authentication to this frontend. |
|
| `traefik.frontend.auth.basic.realm=REALM` | Sets the realm of basic authentication to this frontend. |
|
||||||
| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
||||||
| `traefik.frontend.auth.basic.users=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2]. |
|
| `traefik.frontend.auth.basic.users=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2]. |
|
||||||
| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets the basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets the basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||||
|
@ -297,6 +297,11 @@ To create `user:password` pair, it's possible to use this command:
|
||||||
`echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g`.
|
`echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g`.
|
||||||
The result will be `user:$$apr1$$9Cv/OMGj$$ZomWQzuQbL.3TRCS81A1g/`, note additional symbol `$` makes escaping.
|
The result will be `user:$$apr1$$9Cv/OMGj$$ZomWQzuQbL.3TRCS81A1g/`, note additional symbol `$` makes escaping.
|
||||||
|
|
||||||
|
[3] `traefik.backend.loadbalancer.swarm`:
|
||||||
|
If you enable this option, Traefik will use the virtual IP provided by docker swarm instead of the containers IPs.
|
||||||
|
Which means that Traefik will not perform any kind of load balancing and will delegate this task to swarm.
|
||||||
|
It also means that Traefik will manipulate only one backend, not one backend per container.
|
||||||
|
|
||||||
#### Custom Headers
|
#### Custom Headers
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|
|
|
@ -128,10 +128,11 @@ This will give more flexibility in cloud/dynamic environments.
|
||||||
Traefik automatically requests endpoint information based on the service provided in the ingress spec.
|
Traefik automatically requests endpoint information based on the service provided in the ingress spec.
|
||||||
Although traefik will connect directly to the endpoints (pods), it still checks the service port to see if TLS communication is required.
|
Although traefik will connect directly to the endpoints (pods), it still checks the service port to see if TLS communication is required.
|
||||||
|
|
||||||
There are 2 ways to configure Traefik to use https to communicate with backend pods:
|
There are 3 ways to configure Traefik to use https to communicate with backend pods:
|
||||||
|
|
||||||
1. If the service port defined in the ingress spec is 443 (note that you can still use `targetPort` to use a different port on your pod).
|
1. If the service port defined in the ingress spec is 443 (note that you can still use `targetPort` to use a different port on your pod).
|
||||||
2. If the service port defined in the ingress spec has a name that starts with `https` (such as `https-api`, `https-web` or just `https`).
|
2. If the service port defined in the ingress spec has a name that starts with `https` (such as `https-api`, `https-web` or just `https`).
|
||||||
|
3. If the ingress spec includes the annotation `ingress.kubernetes.io/protocol: https`.
|
||||||
|
|
||||||
If either of those configuration options exist, then the backend communication protocol is assumed to be TLS, and will connect via TLS automatically.
|
If either of those configuration options exist, then the backend communication protocol is assumed to be TLS, and will connect via TLS automatically.
|
||||||
|
|
||||||
|
@ -169,7 +170,7 @@ The following general annotations are applicable on the Ingress object:
|
||||||
| `traefik.ingress.kubernetes.io/whiteList-ipstrategy=true` | Uses the default IPStrategy.<br>Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. |
|
| `traefik.ingress.kubernetes.io/whiteList-ipstrategy=true` | Uses the default IPStrategy.<br>Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. |
|
||||||
| `traefik.ingress.kubernetes.io/whiteList-ipstrategy-depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) |
|
| `traefik.ingress.kubernetes.io/whiteList-ipstrategy-depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) |
|
||||||
| `traefik.ingress.kubernetes.io/whiteList-ipstrategy-excludedIPs=127.0.0. 1` | See [whitelist](/configuration/entrypoints/#white-listing) |
|
| `traefik.ingress.kubernetes.io/whiteList-ipstrategy-excludedIPs=127.0.0. 1` | See [whitelist](/configuration/entrypoints/#white-listing) |
|
||||||
| `ingress.kubernetes.io/protocol: <NAME>` | Set the protocol Traefik will use to communicate with pods. |
|
| `ingress.kubernetes.io/protocol: <NAME>` | Set the protocol Traefik will use to communicate with pods. Acceptable protocols: http,https,h2c |
|
||||||
|
|
||||||
<1> `traefik.ingress.kubernetes.io/app-root`:
|
<1> `traefik.ingress.kubernetes.io/app-root`:
|
||||||
Non-root paths will not be affected by this annotation and handled normally.
|
Non-root paths will not be affected by this annotation and handled normally.
|
||||||
|
|
|
@ -27,10 +27,10 @@ The `/ping` health-check URL is enabled with the command-line `--ping` or config
|
||||||
Thus, if you have a regular path for `/foo` and an entrypoint on `:80`, you would access them as follows:
|
Thus, if you have a regular path for `/foo` and an entrypoint on `:80`, you would access them as follows:
|
||||||
|
|
||||||
* Regular path: `http://hostname:80/foo`
|
* Regular path: `http://hostname:80/foo`
|
||||||
* Admin panel: `http://hostname:8080/`
|
* Dashboard: `http://hostname:8080/`
|
||||||
* Ping URL: `http://hostname:8080/ping`
|
* Ping URL: `http://hostname:8080/ping`
|
||||||
|
|
||||||
However, for security reasons, you may want to be able to expose the `/ping` health-check URL to outside health-checkers, e.g. an Internet service or cloud load-balancer, _without_ exposing your administration panel's port.
|
However, for security reasons, you may want to be able to expose the `/ping` health-check URL to outside health-checkers, e.g. an Internet service or cloud load-balancer, _without_ exposing your dashboard's port.
|
||||||
In many environments, the security staff may not _allow_ you to expose it.
|
In many environments, the security staff may not _allow_ you to expose it.
|
||||||
|
|
||||||
You have two options:
|
You have two options:
|
||||||
|
@ -40,7 +40,7 @@ You have two options:
|
||||||
|
|
||||||
### Ping health check on a regular entry point
|
### Ping health check on a regular entry point
|
||||||
|
|
||||||
To proxy `/ping` from a regular entry point to the administration one without exposing the panel, do the following:
|
To proxy `/ping` from a regular entry point to the administration one without exposing the dashboard, do the following:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
defaultEntryPoints = ["http"]
|
defaultEntryPoints = ["http"]
|
||||||
|
|
|
@ -176,7 +176,7 @@ Our recommendation would be to see for yourself how simple it is to enable HTTPS
|
||||||
|
|
||||||
## Resources
|
## Resources
|
||||||
|
|
||||||
Here is a talk given by [Emile Vauge](https://github.com/emilevauge) at [GopherCon 2017](https://gophercon.com).
|
Here is a talk given by [Emile Vauge](https://github.com/emilevauge) at GopherCon 2017.
|
||||||
You will learn Traefik basics in less than 10 minutes.
|
You will learn Traefik basics in less than 10 minutes.
|
||||||
|
|
||||||
[![Traefik GopherCon 2017](https://img.youtube.com/vi/RgudiksfL-k/0.jpg)](https://www.youtube.com/watch?v=RgudiksfL-k)
|
[![Traefik GopherCon 2017](https://img.youtube.com/vi/RgudiksfL-k/0.jpg)](https://www.youtube.com/watch?v=RgudiksfL-k)
|
||||||
|
|
|
@ -375,6 +375,14 @@ We should now be able to visit [traefik-ui.minikube](http://traefik-ui.minikube)
|
||||||
For this example to work you need a TLS entrypoint. You don't have to provide a TLS certificate at this point.
|
For this example to work you need a TLS entrypoint. You don't have to provide a TLS certificate at this point.
|
||||||
For more details see [here](/configuration/entrypoints/).
|
For more details see [here](/configuration/entrypoints/).
|
||||||
|
|
||||||
|
You can add a TLS entrypoint by adding the following `args` to the container spec:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
--defaultentrypoints=http,https
|
||||||
|
--entrypoints=Name:https Address::443 TLS
|
||||||
|
--entrypoints=Name:http Address::80
|
||||||
|
```
|
||||||
|
|
||||||
To setup an HTTPS-protected ingress, you can leverage the TLS feature of the ingress resource.
|
To setup an HTTPS-protected ingress, you can leverage the TLS feature of the ingress resource.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|
|
@ -13,6 +13,6 @@ services:
|
||||||
|
|
||||||
# A container that exposes a simple API
|
# A container that exposes a simple API
|
||||||
whoami:
|
whoami:
|
||||||
image: emilevauge/whoami # A container that exposes an API to show it's IP address
|
image: emilevauge/whoami # A container that exposes an API to show its IP address
|
||||||
labels:
|
labels:
|
||||||
- "traefik.frontend.rule=Host:whoami.docker.localhost"
|
- "traefik.frontend.rule=Host:whoami.docker.localhost"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
pebble:
|
pebble:
|
||||||
image: letsencrypt/pebble:2018-07-27
|
image: letsencrypt/pebble:2018-11-02
|
||||||
command: pebble --dnsserver ${DOCKER_HOST_IP}:5053
|
command: pebble --dnsserver ${DOCKER_HOST_IP}:5053
|
||||||
ports:
|
ports:
|
||||||
- 14000:14000
|
- 14000:14000
|
||||||
|
|
|
@ -56,7 +56,7 @@ func getLinesFromFile(filename string) ([]string, error) {
|
||||||
var filteredLines []string
|
var filteredLines []string
|
||||||
for _, rawLine := range rawLines {
|
for _, rawLine := range rawLines {
|
||||||
line := strings.TrimSpace(rawLine)
|
line := strings.TrimSpace(rawLine)
|
||||||
if line != "" {
|
if line != "" && !strings.HasPrefix(line, "#") {
|
||||||
filteredLines = append(filteredLines, line)
|
filteredLines = append(filteredLines, line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,11 +198,21 @@ func TestBasicAuthUsersFromFile(t *testing.T) {
|
||||||
userFileContent: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/\ntest2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0\n",
|
userFileContent: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/\ntest2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0\n",
|
||||||
givenUsers: []string{"test2:$apr1$mK.GtItK$ncnLYvNLek0weXdxo68690"},
|
givenUsers: []string{"test2:$apr1$mK.GtItK$ncnLYvNLek0weXdxo68690"},
|
||||||
expectedUsers: map[string]string{"test": "test", "test2": "overridden"},
|
expectedUsers: map[string]string{"test": "test", "test2": "overridden"},
|
||||||
realm: "trafikee",
|
realm: "traefik",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Should skip comments",
|
||||||
|
userFileContent: "#Comment\ntest:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/\ntest2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0\n",
|
||||||
|
givenUsers: []string{},
|
||||||
|
expectedUsers: map[string]string{"test": "test", "test2": "test2"},
|
||||||
|
realm: "traefiker",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
|
if test.desc != "Should skip comments" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
test := test
|
test := test
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
|
@ -80,10 +80,10 @@ func TestDigestAuthUsersFromFile(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Should authenticate the correct user based on the realm",
|
desc: "Should authenticate the correct user based on the realm",
|
||||||
userFileContent: "test:traefik:a2688e031edb4be6a3797f3882655c05\ntest:traefikee:316a669c158c8b7ab1048b03961a7aa5\n",
|
userFileContent: "test:traefik:a2688e031edb4be6a3797f3882655c05\ntest:traefiker:a3d334dff2645b914918de78bec50bf4\n",
|
||||||
givenUsers: []string{},
|
givenUsers: []string{},
|
||||||
expectedUsers: map[string]string{"test": "test2"},
|
expectedUsers: map[string]string{"test": "test2"},
|
||||||
realm: "traefikee",
|
realm: "traefiker",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -103,3 +103,88 @@ func TestSecureHeader(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSSLForceHost(t *testing.T) {
|
||||||
|
next := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
rw.Write([]byte("OK"))
|
||||||
|
})
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
host string
|
||||||
|
secureMiddleware *secureHeader
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "http should return a 301",
|
||||||
|
host: "http://powpow.example.com",
|
||||||
|
secureMiddleware: newSecure(next, config.Headers{
|
||||||
|
SSLRedirect: true,
|
||||||
|
SSLForceHost: true,
|
||||||
|
SSLHost: "powpow.example.com",
|
||||||
|
}),
|
||||||
|
expected: http.StatusMovedPermanently,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "http sub domain should return a 301",
|
||||||
|
host: "http://www.powpow.example.com",
|
||||||
|
secureMiddleware: newSecure(next, config.Headers{
|
||||||
|
SSLRedirect: true,
|
||||||
|
SSLForceHost: true,
|
||||||
|
SSLHost: "powpow.example.com",
|
||||||
|
}),
|
||||||
|
expected: http.StatusMovedPermanently,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "https should return a 200",
|
||||||
|
host: "https://powpow.example.com",
|
||||||
|
secureMiddleware: newSecure(next, config.Headers{
|
||||||
|
SSLRedirect: true,
|
||||||
|
SSLForceHost: true,
|
||||||
|
SSLHost: "powpow.example.com",
|
||||||
|
}),
|
||||||
|
expected: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "https sub domain should return a 301",
|
||||||
|
host: "https://www.powpow.example.com",
|
||||||
|
secureMiddleware: newSecure(next, config.Headers{
|
||||||
|
SSLRedirect: true,
|
||||||
|
SSLForceHost: true,
|
||||||
|
SSLHost: "powpow.example.com",
|
||||||
|
}),
|
||||||
|
expected: http.StatusMovedPermanently,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "http without force host and sub domain should return a 301",
|
||||||
|
host: "http://www.powpow.example.com",
|
||||||
|
secureMiddleware: newSecure(next, config.Headers{
|
||||||
|
SSLRedirect: true,
|
||||||
|
SSLForceHost: false,
|
||||||
|
SSLHost: "powpow.example.com",
|
||||||
|
}),
|
||||||
|
expected: http.StatusMovedPermanently,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "https without force host and sub domain should return a 301",
|
||||||
|
host: "https://www.powpow.example.com",
|
||||||
|
secureMiddleware: newSecure(next, config.Headers{
|
||||||
|
SSLRedirect: true,
|
||||||
|
SSLForceHost: false,
|
||||||
|
SSLHost: "powpow.example.com",
|
||||||
|
}),
|
||||||
|
expected: http.StatusOK,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
req := testhelpers.MustNewRequest(http.MethodGet, test.host, nil)
|
||||||
|
|
||||||
|
rw := httptest.NewRecorder()
|
||||||
|
test.secureMiddleware.ServeHTTP(rw, req)
|
||||||
|
|
||||||
|
assert.Equal(t, test.expected, rw.Result().StatusCode)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -212,10 +212,15 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
|
||||||
}
|
}
|
||||||
configuration := p.buildConfiguration(containers)
|
configuration := p.buildConfiguration(containers)
|
||||||
if configuration != nil {
|
if configuration != nil {
|
||||||
configurationChan <- types.ConfigMessage{
|
message := types.ConfigMessage{
|
||||||
ProviderName: "docker",
|
ProviderName: "docker",
|
||||||
Configuration: configuration,
|
Configuration: configuration,
|
||||||
}
|
}
|
||||||
|
select {
|
||||||
|
case configurationChan <- message:
|
||||||
|
case <-ctx.Done():
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
21
vendor/github.com/fatih/structs/LICENSE
generated
vendored
Normal file
21
vendor/github.com/fatih/structs/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Fatih Arslan
|
||||||
|
|
||||||
|
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.
|
141
vendor/github.com/fatih/structs/field.go
generated
vendored
Normal file
141
vendor/github.com/fatih/structs/field.go
generated
vendored
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
package structs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNotExported = errors.New("field is not exported")
|
||||||
|
errNotSettable = errors.New("field is not settable")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Field represents a single struct field that encapsulates high level
|
||||||
|
// functions around the field.
|
||||||
|
type Field struct {
|
||||||
|
value reflect.Value
|
||||||
|
field reflect.StructField
|
||||||
|
defaultTag string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tag returns the value associated with key in the tag string. If there is no
|
||||||
|
// such key in the tag, Tag returns the empty string.
|
||||||
|
func (f *Field) Tag(key string) string {
|
||||||
|
return f.field.Tag.Get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the underlying value of the field. It panics if the field
|
||||||
|
// is not exported.
|
||||||
|
func (f *Field) Value() interface{} {
|
||||||
|
return f.value.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmbedded returns true if the given field is an anonymous field (embedded)
|
||||||
|
func (f *Field) IsEmbedded() bool {
|
||||||
|
return f.field.Anonymous
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExported returns true if the given field is exported.
|
||||||
|
func (f *Field) IsExported() bool {
|
||||||
|
return f.field.PkgPath == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsZero returns true if the given field is not initialized (has a zero value).
|
||||||
|
// It panics if the field is not exported.
|
||||||
|
func (f *Field) IsZero() bool {
|
||||||
|
zero := reflect.Zero(f.value.Type()).Interface()
|
||||||
|
current := f.Value()
|
||||||
|
|
||||||
|
return reflect.DeepEqual(current, zero)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name of the given field
|
||||||
|
func (f *Field) Name() string {
|
||||||
|
return f.field.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kind returns the fields kind, such as "string", "map", "bool", etc ..
|
||||||
|
func (f *Field) Kind() reflect.Kind {
|
||||||
|
return f.value.Kind()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the field to given value v. It returns an error if the field is not
|
||||||
|
// settable (not addressable or not exported) or if the given value's type
|
||||||
|
// doesn't match the fields type.
|
||||||
|
func (f *Field) Set(val interface{}) error {
|
||||||
|
// we can't set unexported fields, so be sure this field is exported
|
||||||
|
if !f.IsExported() {
|
||||||
|
return errNotExported
|
||||||
|
}
|
||||||
|
|
||||||
|
// do we get here? not sure...
|
||||||
|
if !f.value.CanSet() {
|
||||||
|
return errNotSettable
|
||||||
|
}
|
||||||
|
|
||||||
|
given := reflect.ValueOf(val)
|
||||||
|
|
||||||
|
if f.value.Kind() != given.Kind() {
|
||||||
|
return fmt.Errorf("wrong kind. got: %s want: %s", given.Kind(), f.value.Kind())
|
||||||
|
}
|
||||||
|
|
||||||
|
f.value.Set(given)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero sets the field to its zero value. It returns an error if the field is not
|
||||||
|
// settable (not addressable or not exported).
|
||||||
|
func (f *Field) Zero() error {
|
||||||
|
zero := reflect.Zero(f.value.Type()).Interface()
|
||||||
|
return f.Set(zero)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fields returns a slice of Fields. This is particular handy to get the fields
|
||||||
|
// of a nested struct . A struct tag with the content of "-" ignores the
|
||||||
|
// checking of that particular field. Example:
|
||||||
|
//
|
||||||
|
// // Field is ignored by this package.
|
||||||
|
// Field *http.Request `structs:"-"`
|
||||||
|
//
|
||||||
|
// It panics if field is not exported or if field's kind is not struct
|
||||||
|
func (f *Field) Fields() []*Field {
|
||||||
|
return getFields(f.value, f.defaultTag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field returns the field from a nested struct. It panics if the nested struct
|
||||||
|
// is not exported or if the field was not found.
|
||||||
|
func (f *Field) Field(name string) *Field {
|
||||||
|
field, ok := f.FieldOk(name)
|
||||||
|
if !ok {
|
||||||
|
panic("field not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldOk returns the field from a nested struct. The boolean returns whether
|
||||||
|
// the field was found (true) or not (false).
|
||||||
|
func (f *Field) FieldOk(name string) (*Field, bool) {
|
||||||
|
value := &f.value
|
||||||
|
// value must be settable so we need to make sure it holds the address of the
|
||||||
|
// variable and not a copy, so we can pass the pointer to strctVal instead of a
|
||||||
|
// copy (which is not assigned to any variable, hence not settable).
|
||||||
|
// see "https://blog.golang.org/laws-of-reflection#TOC_8."
|
||||||
|
if f.value.Kind() != reflect.Ptr {
|
||||||
|
a := f.value.Addr()
|
||||||
|
value = &a
|
||||||
|
}
|
||||||
|
v := strctVal(value.Interface())
|
||||||
|
t := v.Type()
|
||||||
|
|
||||||
|
field, ok := t.FieldByName(name)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Field{
|
||||||
|
field: field,
|
||||||
|
value: v.FieldByName(name),
|
||||||
|
}, true
|
||||||
|
}
|
584
vendor/github.com/fatih/structs/structs.go
generated
vendored
Normal file
584
vendor/github.com/fatih/structs/structs.go
generated
vendored
Normal file
|
@ -0,0 +1,584 @@
|
||||||
|
// Package structs contains various utilities functions to work with structs.
|
||||||
|
package structs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// DefaultTagName is the default tag name for struct fields which provides
|
||||||
|
// a more granular to tweak certain structs. Lookup the necessary functions
|
||||||
|
// for more info.
|
||||||
|
DefaultTagName = "structs" // struct's field default tag name
|
||||||
|
)
|
||||||
|
|
||||||
|
// Struct encapsulates a struct type to provide several high level functions
|
||||||
|
// around the struct.
|
||||||
|
type Struct struct {
|
||||||
|
raw interface{}
|
||||||
|
value reflect.Value
|
||||||
|
TagName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new *Struct with the struct s. It panics if the s's kind is
|
||||||
|
// not struct.
|
||||||
|
func New(s interface{}) *Struct {
|
||||||
|
return &Struct{
|
||||||
|
raw: s,
|
||||||
|
value: strctVal(s),
|
||||||
|
TagName: DefaultTagName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map converts the given struct to a map[string]interface{}, where the keys
|
||||||
|
// of the map are the field names and the values of the map the associated
|
||||||
|
// values of the fields. The default key string is the struct field name but
|
||||||
|
// can be changed in the struct field's tag value. The "structs" key in the
|
||||||
|
// struct's field tag value is the key name. Example:
|
||||||
|
//
|
||||||
|
// // Field appears in map as key "myName".
|
||||||
|
// Name string `structs:"myName"`
|
||||||
|
//
|
||||||
|
// A tag value with the content of "-" ignores that particular field. Example:
|
||||||
|
//
|
||||||
|
// // Field is ignored by this package.
|
||||||
|
// Field bool `structs:"-"`
|
||||||
|
//
|
||||||
|
// A tag value with the content of "string" uses the stringer to get the value. Example:
|
||||||
|
//
|
||||||
|
// // The value will be output of Animal's String() func.
|
||||||
|
// // Map will panic if Animal does not implement String().
|
||||||
|
// Field *Animal `structs:"field,string"`
|
||||||
|
//
|
||||||
|
// A tag value with the option of "flatten" used in a struct field is to flatten its fields
|
||||||
|
// in the output map. Example:
|
||||||
|
//
|
||||||
|
// // The FieldStruct's fields will be flattened into the output map.
|
||||||
|
// FieldStruct time.Time `structs:",flatten"`
|
||||||
|
//
|
||||||
|
// A tag value with the option of "omitnested" stops iterating further if the type
|
||||||
|
// is a struct. Example:
|
||||||
|
//
|
||||||
|
// // Field is not processed further by this package.
|
||||||
|
// Field time.Time `structs:"myName,omitnested"`
|
||||||
|
// Field *http.Request `structs:",omitnested"`
|
||||||
|
//
|
||||||
|
// A tag value with the option of "omitempty" ignores that particular field if
|
||||||
|
// the field value is empty. Example:
|
||||||
|
//
|
||||||
|
// // Field appears in map as key "myName", but the field is
|
||||||
|
// // skipped if empty.
|
||||||
|
// Field string `structs:"myName,omitempty"`
|
||||||
|
//
|
||||||
|
// // Field appears in map as key "Field" (the default), but
|
||||||
|
// // the field is skipped if empty.
|
||||||
|
// Field string `structs:",omitempty"`
|
||||||
|
//
|
||||||
|
// Note that only exported fields of a struct can be accessed, non exported
|
||||||
|
// fields will be neglected.
|
||||||
|
func (s *Struct) Map() map[string]interface{} {
|
||||||
|
out := make(map[string]interface{})
|
||||||
|
s.FillMap(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// FillMap is the same as Map. Instead of returning the output, it fills the
|
||||||
|
// given map.
|
||||||
|
func (s *Struct) FillMap(out map[string]interface{}) {
|
||||||
|
if out == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := s.structFields()
|
||||||
|
|
||||||
|
for _, field := range fields {
|
||||||
|
name := field.Name
|
||||||
|
val := s.value.FieldByName(name)
|
||||||
|
isSubStruct := false
|
||||||
|
var finalVal interface{}
|
||||||
|
|
||||||
|
tagName, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||||
|
if tagName != "" {
|
||||||
|
name = tagName
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the value is a zero value and the field is marked as omitempty do
|
||||||
|
// not include
|
||||||
|
if tagOpts.Has("omitempty") {
|
||||||
|
zero := reflect.Zero(val.Type()).Interface()
|
||||||
|
current := val.Interface()
|
||||||
|
|
||||||
|
if reflect.DeepEqual(current, zero) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tagOpts.Has("omitnested") {
|
||||||
|
finalVal = s.nested(val)
|
||||||
|
|
||||||
|
v := reflect.ValueOf(val.Interface())
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Map, reflect.Struct:
|
||||||
|
isSubStruct = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
finalVal = val.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
if tagOpts.Has("string") {
|
||||||
|
s, ok := val.Interface().(fmt.Stringer)
|
||||||
|
if ok {
|
||||||
|
out[name] = s.String()
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if isSubStruct && (tagOpts.Has("flatten")) {
|
||||||
|
for k := range finalVal.(map[string]interface{}) {
|
||||||
|
out[k] = finalVal.(map[string]interface{})[k]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out[name] = finalVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values converts the given s struct's field values to a []interface{}. A
|
||||||
|
// struct tag with the content of "-" ignores the that particular field.
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// // Field is ignored by this package.
|
||||||
|
// Field int `structs:"-"`
|
||||||
|
//
|
||||||
|
// A value with the option of "omitnested" stops iterating further if the type
|
||||||
|
// is a struct. Example:
|
||||||
|
//
|
||||||
|
// // Fields is not processed further by this package.
|
||||||
|
// Field time.Time `structs:",omitnested"`
|
||||||
|
// Field *http.Request `structs:",omitnested"`
|
||||||
|
//
|
||||||
|
// A tag value with the option of "omitempty" ignores that particular field and
|
||||||
|
// is not added to the values if the field value is empty. Example:
|
||||||
|
//
|
||||||
|
// // Field is skipped if empty
|
||||||
|
// Field string `structs:",omitempty"`
|
||||||
|
//
|
||||||
|
// Note that only exported fields of a struct can be accessed, non exported
|
||||||
|
// fields will be neglected.
|
||||||
|
func (s *Struct) Values() []interface{} {
|
||||||
|
fields := s.structFields()
|
||||||
|
|
||||||
|
var t []interface{}
|
||||||
|
|
||||||
|
for _, field := range fields {
|
||||||
|
val := s.value.FieldByName(field.Name)
|
||||||
|
|
||||||
|
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||||
|
|
||||||
|
// if the value is a zero value and the field is marked as omitempty do
|
||||||
|
// not include
|
||||||
|
if tagOpts.Has("omitempty") {
|
||||||
|
zero := reflect.Zero(val.Type()).Interface()
|
||||||
|
current := val.Interface()
|
||||||
|
|
||||||
|
if reflect.DeepEqual(current, zero) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tagOpts.Has("string") {
|
||||||
|
s, ok := val.Interface().(fmt.Stringer)
|
||||||
|
if ok {
|
||||||
|
t = append(t, s.String())
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
||||||
|
// look out for embedded structs, and convert them to a
|
||||||
|
// []interface{} to be added to the final values slice
|
||||||
|
t = append(t, Values(val.Interface())...)
|
||||||
|
} else {
|
||||||
|
t = append(t, val.Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fields returns a slice of Fields. A struct tag with the content of "-"
|
||||||
|
// ignores the checking of that particular field. Example:
|
||||||
|
//
|
||||||
|
// // Field is ignored by this package.
|
||||||
|
// Field bool `structs:"-"`
|
||||||
|
//
|
||||||
|
// It panics if s's kind is not struct.
|
||||||
|
func (s *Struct) Fields() []*Field {
|
||||||
|
return getFields(s.value, s.TagName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Names returns a slice of field names. A struct tag with the content of "-"
|
||||||
|
// ignores the checking of that particular field. Example:
|
||||||
|
//
|
||||||
|
// // Field is ignored by this package.
|
||||||
|
// Field bool `structs:"-"`
|
||||||
|
//
|
||||||
|
// It panics if s's kind is not struct.
|
||||||
|
func (s *Struct) Names() []string {
|
||||||
|
fields := getFields(s.value, s.TagName)
|
||||||
|
|
||||||
|
names := make([]string, len(fields))
|
||||||
|
|
||||||
|
for i, field := range fields {
|
||||||
|
names[i] = field.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFields(v reflect.Value, tagName string) []*Field {
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
t := v.Type()
|
||||||
|
|
||||||
|
var fields []*Field
|
||||||
|
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
|
||||||
|
if tag := field.Tag.Get(tagName); tag == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
f := &Field{
|
||||||
|
field: field,
|
||||||
|
value: v.FieldByName(field.Name),
|
||||||
|
}
|
||||||
|
|
||||||
|
fields = append(fields, f)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field returns a new Field struct that provides several high level functions
|
||||||
|
// around a single struct field entity. It panics if the field is not found.
|
||||||
|
func (s *Struct) Field(name string) *Field {
|
||||||
|
f, ok := s.FieldOk(name)
|
||||||
|
if !ok {
|
||||||
|
panic("field not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldOk returns a new Field struct that provides several high level functions
|
||||||
|
// around a single struct field entity. The boolean returns true if the field
|
||||||
|
// was found.
|
||||||
|
func (s *Struct) FieldOk(name string) (*Field, bool) {
|
||||||
|
t := s.value.Type()
|
||||||
|
|
||||||
|
field, ok := t.FieldByName(name)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Field{
|
||||||
|
field: field,
|
||||||
|
value: s.value.FieldByName(name),
|
||||||
|
defaultTag: s.TagName,
|
||||||
|
}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsZero returns true if all fields in a struct is a zero value (not
|
||||||
|
// initialized) A struct tag with the content of "-" ignores the checking of
|
||||||
|
// that particular field. Example:
|
||||||
|
//
|
||||||
|
// // Field is ignored by this package.
|
||||||
|
// Field bool `structs:"-"`
|
||||||
|
//
|
||||||
|
// A value with the option of "omitnested" stops iterating further if the type
|
||||||
|
// is a struct. Example:
|
||||||
|
//
|
||||||
|
// // Field is not processed further by this package.
|
||||||
|
// Field time.Time `structs:"myName,omitnested"`
|
||||||
|
// Field *http.Request `structs:",omitnested"`
|
||||||
|
//
|
||||||
|
// Note that only exported fields of a struct can be accessed, non exported
|
||||||
|
// fields will be neglected. It panics if s's kind is not struct.
|
||||||
|
func (s *Struct) IsZero() bool {
|
||||||
|
fields := s.structFields()
|
||||||
|
|
||||||
|
for _, field := range fields {
|
||||||
|
val := s.value.FieldByName(field.Name)
|
||||||
|
|
||||||
|
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||||
|
|
||||||
|
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
||||||
|
ok := IsZero(val.Interface())
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// zero value of the given field, such as "" for string, 0 for int
|
||||||
|
zero := reflect.Zero(val.Type()).Interface()
|
||||||
|
|
||||||
|
// current value of the given field
|
||||||
|
current := val.Interface()
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(current, zero) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasZero returns true if a field in a struct is not initialized (zero value).
|
||||||
|
// A struct tag with the content of "-" ignores the checking of that particular
|
||||||
|
// field. Example:
|
||||||
|
//
|
||||||
|
// // Field is ignored by this package.
|
||||||
|
// Field bool `structs:"-"`
|
||||||
|
//
|
||||||
|
// A value with the option of "omitnested" stops iterating further if the type
|
||||||
|
// is a struct. Example:
|
||||||
|
//
|
||||||
|
// // Field is not processed further by this package.
|
||||||
|
// Field time.Time `structs:"myName,omitnested"`
|
||||||
|
// Field *http.Request `structs:",omitnested"`
|
||||||
|
//
|
||||||
|
// Note that only exported fields of a struct can be accessed, non exported
|
||||||
|
// fields will be neglected. It panics if s's kind is not struct.
|
||||||
|
func (s *Struct) HasZero() bool {
|
||||||
|
fields := s.structFields()
|
||||||
|
|
||||||
|
for _, field := range fields {
|
||||||
|
val := s.value.FieldByName(field.Name)
|
||||||
|
|
||||||
|
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||||
|
|
||||||
|
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
||||||
|
ok := HasZero(val.Interface())
|
||||||
|
if ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// zero value of the given field, such as "" for string, 0 for int
|
||||||
|
zero := reflect.Zero(val.Type()).Interface()
|
||||||
|
|
||||||
|
// current value of the given field
|
||||||
|
current := val.Interface()
|
||||||
|
|
||||||
|
if reflect.DeepEqual(current, zero) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the structs's type name within its package. For more info refer
|
||||||
|
// to Name() function.
|
||||||
|
func (s *Struct) Name() string {
|
||||||
|
return s.value.Type().Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
// structFields returns the exported struct fields for a given s struct. This
|
||||||
|
// is a convenient helper method to avoid duplicate code in some of the
|
||||||
|
// functions.
|
||||||
|
func (s *Struct) structFields() []reflect.StructField {
|
||||||
|
t := s.value.Type()
|
||||||
|
|
||||||
|
var f []reflect.StructField
|
||||||
|
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
// we can't access the value of unexported fields
|
||||||
|
if field.PkgPath != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't check if it's omitted
|
||||||
|
if tag := field.Tag.Get(s.TagName); tag == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
f = append(f, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func strctVal(s interface{}) reflect.Value {
|
||||||
|
v := reflect.ValueOf(s)
|
||||||
|
|
||||||
|
// if pointer get the underlying element≤
|
||||||
|
for v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Kind() != reflect.Struct {
|
||||||
|
panic("not struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map converts the given struct to a map[string]interface{}. For more info
|
||||||
|
// refer to Struct types Map() method. It panics if s's kind is not struct.
|
||||||
|
func Map(s interface{}) map[string]interface{} {
|
||||||
|
return New(s).Map()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FillMap is the same as Map. Instead of returning the output, it fills the
|
||||||
|
// given map.
|
||||||
|
func FillMap(s interface{}, out map[string]interface{}) {
|
||||||
|
New(s).FillMap(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values converts the given struct to a []interface{}. For more info refer to
|
||||||
|
// Struct types Values() method. It panics if s's kind is not struct.
|
||||||
|
func Values(s interface{}) []interface{} {
|
||||||
|
return New(s).Values()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fields returns a slice of *Field. For more info refer to Struct types
|
||||||
|
// Fields() method. It panics if s's kind is not struct.
|
||||||
|
func Fields(s interface{}) []*Field {
|
||||||
|
return New(s).Fields()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Names returns a slice of field names. For more info refer to Struct types
|
||||||
|
// Names() method. It panics if s's kind is not struct.
|
||||||
|
func Names(s interface{}) []string {
|
||||||
|
return New(s).Names()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsZero returns true if all fields is equal to a zero value. For more info
|
||||||
|
// refer to Struct types IsZero() method. It panics if s's kind is not struct.
|
||||||
|
func IsZero(s interface{}) bool {
|
||||||
|
return New(s).IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasZero returns true if any field is equal to a zero value. For more info
|
||||||
|
// refer to Struct types HasZero() method. It panics if s's kind is not struct.
|
||||||
|
func HasZero(s interface{}) bool {
|
||||||
|
return New(s).HasZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsStruct returns true if the given variable is a struct or a pointer to
|
||||||
|
// struct.
|
||||||
|
func IsStruct(s interface{}) bool {
|
||||||
|
v := reflect.ValueOf(s)
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
// uninitialized zero value of a struct
|
||||||
|
if v.Kind() == reflect.Invalid {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.Kind() == reflect.Struct
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the structs's type name within its package. It returns an
|
||||||
|
// empty string for unnamed types. It panics if s's kind is not struct.
|
||||||
|
func Name(s interface{}) string {
|
||||||
|
return New(s).Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
// nested retrieves recursively all types for the given value and returns the
|
||||||
|
// nested value.
|
||||||
|
func (s *Struct) nested(val reflect.Value) interface{} {
|
||||||
|
var finalVal interface{}
|
||||||
|
|
||||||
|
v := reflect.ValueOf(val.Interface())
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
n := New(val.Interface())
|
||||||
|
n.TagName = s.TagName
|
||||||
|
m := n.Map()
|
||||||
|
|
||||||
|
// do not add the converted value if there are no exported fields, ie:
|
||||||
|
// time.Time
|
||||||
|
if len(m) == 0 {
|
||||||
|
finalVal = val.Interface()
|
||||||
|
} else {
|
||||||
|
finalVal = m
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
// get the element type of the map
|
||||||
|
mapElem := val.Type()
|
||||||
|
switch val.Type().Kind() {
|
||||||
|
case reflect.Ptr, reflect.Array, reflect.Map,
|
||||||
|
reflect.Slice, reflect.Chan:
|
||||||
|
mapElem = val.Type().Elem()
|
||||||
|
if mapElem.Kind() == reflect.Ptr {
|
||||||
|
mapElem = mapElem.Elem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// only iterate over struct types, ie: map[string]StructType,
|
||||||
|
// map[string][]StructType,
|
||||||
|
if mapElem.Kind() == reflect.Struct ||
|
||||||
|
(mapElem.Kind() == reflect.Slice &&
|
||||||
|
mapElem.Elem().Kind() == reflect.Struct) {
|
||||||
|
m := make(map[string]interface{}, val.Len())
|
||||||
|
for _, k := range val.MapKeys() {
|
||||||
|
m[k.String()] = s.nested(val.MapIndex(k))
|
||||||
|
}
|
||||||
|
finalVal = m
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(arslan): should this be optional?
|
||||||
|
finalVal = val.Interface()
|
||||||
|
case reflect.Slice, reflect.Array:
|
||||||
|
if val.Type().Kind() == reflect.Interface {
|
||||||
|
finalVal = val.Interface()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(arslan): should this be optional?
|
||||||
|
// do not iterate of non struct types, just pass the value. Ie: []int,
|
||||||
|
// []string, co... We only iterate further if it's a struct.
|
||||||
|
// i.e []foo or []*foo
|
||||||
|
if val.Type().Elem().Kind() != reflect.Struct &&
|
||||||
|
!(val.Type().Elem().Kind() == reflect.Ptr &&
|
||||||
|
val.Type().Elem().Elem().Kind() == reflect.Struct) {
|
||||||
|
finalVal = val.Interface()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
slices := make([]interface{}, val.Len())
|
||||||
|
for x := 0; x < val.Len(); x++ {
|
||||||
|
slices[x] = s.nested(val.Index(x))
|
||||||
|
}
|
||||||
|
finalVal = slices
|
||||||
|
default:
|
||||||
|
finalVal = val.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalVal
|
||||||
|
}
|
32
vendor/github.com/fatih/structs/tags.go
generated
vendored
Normal file
32
vendor/github.com/fatih/structs/tags.go
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package structs
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// tagOptions contains a slice of tag options
|
||||||
|
type tagOptions []string
|
||||||
|
|
||||||
|
// Has returns true if the given option is available in tagOptions
|
||||||
|
func (t tagOptions) Has(opt string) bool {
|
||||||
|
for _, tagOpt := range t {
|
||||||
|
if tagOpt == opt {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseTag splits a struct field's tag into its name and a list of options
|
||||||
|
// which comes after a name. A tag is in the form of: "name,option1,option2".
|
||||||
|
// The name can be neglectected.
|
||||||
|
func parseTag(tag string) (string, tagOptions) {
|
||||||
|
// tag is one of followings:
|
||||||
|
// ""
|
||||||
|
// "name"
|
||||||
|
// "name,opt"
|
||||||
|
// "name,opt,opt2"
|
||||||
|
// ",opt"
|
||||||
|
|
||||||
|
res := strings.Split(tag, ",")
|
||||||
|
return res[0], res[1:]
|
||||||
|
}
|
19
vendor/github.com/kolo/xmlrpc/LICENSE
generated
vendored
Normal file
19
vendor/github.com/kolo/xmlrpc/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (C) 2012 Dmitry Maksimov
|
||||||
|
|
||||||
|
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.
|
169
vendor/github.com/kolo/xmlrpc/client.go
generated
vendored
Normal file
169
vendor/github.com/kolo/xmlrpc/client.go
generated
vendored
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
package xmlrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/cookiejar"
|
||||||
|
"net/rpc"
|
||||||
|
"net/url"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
*rpc.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// clientCodec is rpc.ClientCodec interface implementation.
|
||||||
|
type clientCodec struct {
|
||||||
|
// url presents url of xmlrpc service
|
||||||
|
url *url.URL
|
||||||
|
|
||||||
|
// httpClient works with HTTP protocol
|
||||||
|
httpClient *http.Client
|
||||||
|
|
||||||
|
// cookies stores cookies received on last request
|
||||||
|
cookies http.CookieJar
|
||||||
|
|
||||||
|
// responses presents map of active requests. It is required to return request id, that
|
||||||
|
// rpc.Client can mark them as done.
|
||||||
|
responses map[uint64]*http.Response
|
||||||
|
mutex sync.Mutex
|
||||||
|
|
||||||
|
response *Response
|
||||||
|
|
||||||
|
// ready presents channel, that is used to link request and it`s response.
|
||||||
|
ready chan uint64
|
||||||
|
|
||||||
|
// close notifies codec is closed.
|
||||||
|
close chan uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (codec *clientCodec) WriteRequest(request *rpc.Request, args interface{}) (err error) {
|
||||||
|
httpRequest, err := NewRequest(codec.url.String(), request.ServiceMethod, args)
|
||||||
|
|
||||||
|
if codec.cookies != nil {
|
||||||
|
for _, cookie := range codec.cookies.Cookies(codec.url) {
|
||||||
|
httpRequest.AddCookie(cookie)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpResponse *http.Response
|
||||||
|
httpResponse, err = codec.httpClient.Do(httpRequest)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if codec.cookies != nil {
|
||||||
|
codec.cookies.SetCookies(codec.url, httpResponse.Cookies())
|
||||||
|
}
|
||||||
|
|
||||||
|
codec.mutex.Lock()
|
||||||
|
codec.responses[request.Seq] = httpResponse
|
||||||
|
codec.mutex.Unlock()
|
||||||
|
|
||||||
|
codec.ready <- request.Seq
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (codec *clientCodec) ReadResponseHeader(response *rpc.Response) (err error) {
|
||||||
|
var seq uint64
|
||||||
|
|
||||||
|
select {
|
||||||
|
case seq = <-codec.ready:
|
||||||
|
case <-codec.close:
|
||||||
|
return errors.New("codec is closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
codec.mutex.Lock()
|
||||||
|
httpResponse := codec.responses[seq]
|
||||||
|
codec.mutex.Unlock()
|
||||||
|
|
||||||
|
if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 {
|
||||||
|
return fmt.Errorf("request error: bad status code - %d", httpResponse.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
respData, err := ioutil.ReadAll(httpResponse.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
httpResponse.Body.Close()
|
||||||
|
|
||||||
|
resp := NewResponse(respData)
|
||||||
|
|
||||||
|
if resp.Failed() {
|
||||||
|
response.Error = fmt.Sprintf("%v", resp.Err())
|
||||||
|
}
|
||||||
|
|
||||||
|
codec.response = resp
|
||||||
|
|
||||||
|
response.Seq = seq
|
||||||
|
|
||||||
|
codec.mutex.Lock()
|
||||||
|
delete(codec.responses, seq)
|
||||||
|
codec.mutex.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (codec *clientCodec) ReadResponseBody(v interface{}) (err error) {
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = codec.response.Unmarshal(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (codec *clientCodec) Close() error {
|
||||||
|
transport := codec.httpClient.Transport.(*http.Transport)
|
||||||
|
transport.CloseIdleConnections()
|
||||||
|
|
||||||
|
close(codec.close)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns instance of rpc.Client object, that is used to send request to xmlrpc service.
|
||||||
|
func NewClient(requrl string, transport http.RoundTripper) (*Client, error) {
|
||||||
|
if transport == nil {
|
||||||
|
transport = http.DefaultTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient := &http.Client{Transport: transport}
|
||||||
|
|
||||||
|
jar, err := cookiejar.New(nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(requrl)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
codec := clientCodec{
|
||||||
|
url: u,
|
||||||
|
httpClient: httpClient,
|
||||||
|
close: make(chan uint64),
|
||||||
|
ready: make(chan uint64),
|
||||||
|
responses: make(map[uint64]*http.Response),
|
||||||
|
cookies: jar,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Client{rpc.NewClientWithCodec(&codec)}, nil
|
||||||
|
}
|
463
vendor/github.com/kolo/xmlrpc/decoder.go
generated
vendored
Normal file
463
vendor/github.com/kolo/xmlrpc/decoder.go
generated
vendored
Normal file
|
@ -0,0 +1,463 @@
|
||||||
|
package xmlrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
iso8601 = "20060102T15:04:05"
|
||||||
|
iso8601Z = "20060102T15:04:05Z07:00"
|
||||||
|
iso8601Hyphen = "2006-01-02T15:04:05"
|
||||||
|
iso8601HyphenZ = "2006-01-02T15:04:05Z07:00"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// CharsetReader is a function to generate reader which converts a non UTF-8
|
||||||
|
// charset into UTF-8.
|
||||||
|
CharsetReader func(string, io.Reader) (io.Reader, error)
|
||||||
|
|
||||||
|
timeLayouts = []string{iso8601, iso8601Z, iso8601Hyphen, iso8601HyphenZ}
|
||||||
|
invalidXmlError = errors.New("invalid xml")
|
||||||
|
)
|
||||||
|
|
||||||
|
type TypeMismatchError string
|
||||||
|
|
||||||
|
func (e TypeMismatchError) Error() string { return string(e) }
|
||||||
|
|
||||||
|
type decoder struct {
|
||||||
|
*xml.Decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshal(data []byte, v interface{}) (err error) {
|
||||||
|
dec := &decoder{xml.NewDecoder(bytes.NewBuffer(data))}
|
||||||
|
|
||||||
|
if CharsetReader != nil {
|
||||||
|
dec.CharsetReader = CharsetReader
|
||||||
|
}
|
||||||
|
|
||||||
|
var tok xml.Token
|
||||||
|
for {
|
||||||
|
if tok, err = dec.Token(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := tok.(xml.StartElement); ok {
|
||||||
|
if t.Name.Local == "value" {
|
||||||
|
val := reflect.ValueOf(v)
|
||||||
|
if val.Kind() != reflect.Ptr {
|
||||||
|
return errors.New("non-pointer value passed to unmarshal")
|
||||||
|
}
|
||||||
|
if err = dec.decodeValue(val.Elem()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read until end of document
|
||||||
|
err = dec.Skip()
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dec *decoder) decodeValue(val reflect.Value) error {
|
||||||
|
var tok xml.Token
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if val.Kind() == reflect.Ptr {
|
||||||
|
if val.IsNil() {
|
||||||
|
val.Set(reflect.New(val.Type().Elem()))
|
||||||
|
}
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
var typeName string
|
||||||
|
for {
|
||||||
|
if tok, err = dec.Token(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := tok.(xml.EndElement); ok {
|
||||||
|
if t.Name.Local == "value" {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return invalidXmlError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := tok.(xml.StartElement); ok {
|
||||||
|
typeName = t.Name.Local
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Treat value data without type identifier as string
|
||||||
|
if t, ok := tok.(xml.CharData); ok {
|
||||||
|
if value := strings.TrimSpace(string(t)); value != "" {
|
||||||
|
if err = checkType(val, reflect.String); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
val.SetString(value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typeName {
|
||||||
|
case "struct":
|
||||||
|
ismap := false
|
||||||
|
pmap := val
|
||||||
|
valType := val.Type()
|
||||||
|
|
||||||
|
if err = checkType(val, reflect.Struct); err != nil {
|
||||||
|
if checkType(val, reflect.Map) == nil {
|
||||||
|
if valType.Key().Kind() != reflect.String {
|
||||||
|
return fmt.Errorf("only maps with string key type can be unmarshalled")
|
||||||
|
}
|
||||||
|
ismap = true
|
||||||
|
} else if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||||
|
var dummy map[string]interface{}
|
||||||
|
pmap = reflect.New(reflect.TypeOf(dummy)).Elem()
|
||||||
|
valType = pmap.Type()
|
||||||
|
ismap = true
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var fields map[string]reflect.Value
|
||||||
|
|
||||||
|
if !ismap {
|
||||||
|
fields = make(map[string]reflect.Value)
|
||||||
|
|
||||||
|
for i := 0; i < valType.NumField(); i++ {
|
||||||
|
field := valType.Field(i)
|
||||||
|
fieldVal := val.FieldByName(field.Name)
|
||||||
|
|
||||||
|
if fieldVal.CanSet() {
|
||||||
|
if fn := field.Tag.Get("xmlrpc"); fn != "" {
|
||||||
|
fields[fn] = fieldVal
|
||||||
|
} else {
|
||||||
|
fields[field.Name] = fieldVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Create initial empty map
|
||||||
|
pmap.Set(reflect.MakeMap(valType))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process struct members.
|
||||||
|
StructLoop:
|
||||||
|
for {
|
||||||
|
if tok, err = dec.Token(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch t := tok.(type) {
|
||||||
|
case xml.StartElement:
|
||||||
|
if t.Name.Local != "member" {
|
||||||
|
return invalidXmlError
|
||||||
|
}
|
||||||
|
|
||||||
|
tagName, fieldName, err := dec.readTag()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if tagName != "name" {
|
||||||
|
return invalidXmlError
|
||||||
|
}
|
||||||
|
|
||||||
|
var fv reflect.Value
|
||||||
|
ok := true
|
||||||
|
|
||||||
|
if !ismap {
|
||||||
|
fv, ok = fields[string(fieldName)]
|
||||||
|
} else {
|
||||||
|
fv = reflect.New(valType.Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
for {
|
||||||
|
if tok, err = dec.Token(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if t, ok := tok.(xml.StartElement); ok && t.Name.Local == "value" {
|
||||||
|
if err = dec.decodeValue(fv); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// </value>
|
||||||
|
if err = dec.Skip(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// </member>
|
||||||
|
if err = dec.Skip(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ismap {
|
||||||
|
pmap.SetMapIndex(reflect.ValueOf(string(fieldName)), reflect.Indirect(fv))
|
||||||
|
val.Set(pmap)
|
||||||
|
}
|
||||||
|
case xml.EndElement:
|
||||||
|
break StructLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "array":
|
||||||
|
pslice := val
|
||||||
|
if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||||
|
var dummy []interface{}
|
||||||
|
pslice = reflect.New(reflect.TypeOf(dummy)).Elem()
|
||||||
|
} else if err = checkType(val, reflect.Slice); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayLoop:
|
||||||
|
for {
|
||||||
|
if tok, err = dec.Token(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t := tok.(type) {
|
||||||
|
case xml.StartElement:
|
||||||
|
if t.Name.Local != "data" {
|
||||||
|
return invalidXmlError
|
||||||
|
}
|
||||||
|
|
||||||
|
slice := reflect.MakeSlice(pslice.Type(), 0, 0)
|
||||||
|
|
||||||
|
DataLoop:
|
||||||
|
for {
|
||||||
|
if tok, err = dec.Token(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tt := tok.(type) {
|
||||||
|
case xml.StartElement:
|
||||||
|
if tt.Name.Local != "value" {
|
||||||
|
return invalidXmlError
|
||||||
|
}
|
||||||
|
|
||||||
|
v := reflect.New(pslice.Type().Elem())
|
||||||
|
if err = dec.decodeValue(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
slice = reflect.Append(slice, v.Elem())
|
||||||
|
|
||||||
|
// </value>
|
||||||
|
if err = dec.Skip(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case xml.EndElement:
|
||||||
|
pslice.Set(slice)
|
||||||
|
val.Set(pslice)
|
||||||
|
break DataLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case xml.EndElement:
|
||||||
|
break ArrayLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if tok, err = dec.Token(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var data []byte
|
||||||
|
|
||||||
|
switch t := tok.(type) {
|
||||||
|
case xml.EndElement:
|
||||||
|
return nil
|
||||||
|
case xml.CharData:
|
||||||
|
data = []byte(t.Copy())
|
||||||
|
default:
|
||||||
|
return invalidXmlError
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typeName {
|
||||||
|
case "int", "i4", "i8":
|
||||||
|
if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||||
|
i, err := strconv.ParseInt(string(data), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pi := reflect.New(reflect.TypeOf(i)).Elem()
|
||||||
|
pi.SetInt(i)
|
||||||
|
val.Set(pi)
|
||||||
|
} else if err = checkType(val, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
i, err := strconv.ParseInt(string(data), 10, val.Type().Bits())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
val.SetInt(i)
|
||||||
|
}
|
||||||
|
case "string", "base64":
|
||||||
|
str := string(data)
|
||||||
|
if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||||
|
pstr := reflect.New(reflect.TypeOf(str)).Elem()
|
||||||
|
pstr.SetString(str)
|
||||||
|
val.Set(pstr)
|
||||||
|
} else if err = checkType(val, reflect.String); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
val.SetString(str)
|
||||||
|
}
|
||||||
|
case "dateTime.iso8601":
|
||||||
|
var t time.Time
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for _, layout := range timeLayouts {
|
||||||
|
t, err = time.Parse(layout, string(data))
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||||
|
ptime := reflect.New(reflect.TypeOf(t)).Elem()
|
||||||
|
ptime.Set(reflect.ValueOf(t))
|
||||||
|
val.Set(ptime)
|
||||||
|
} else if _, ok := val.Interface().(time.Time); !ok {
|
||||||
|
return TypeMismatchError(fmt.Sprintf("error: type mismatch error - can't decode %v to time", val.Kind()))
|
||||||
|
} else {
|
||||||
|
val.Set(reflect.ValueOf(t))
|
||||||
|
}
|
||||||
|
case "boolean":
|
||||||
|
v, err := strconv.ParseBool(string(data))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||||
|
pv := reflect.New(reflect.TypeOf(v)).Elem()
|
||||||
|
pv.SetBool(v)
|
||||||
|
val.Set(pv)
|
||||||
|
} else if err = checkType(val, reflect.Bool); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
val.SetBool(v)
|
||||||
|
}
|
||||||
|
case "double":
|
||||||
|
if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||||
|
i, err := strconv.ParseFloat(string(data), 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pdouble := reflect.New(reflect.TypeOf(i)).Elem()
|
||||||
|
pdouble.SetFloat(i)
|
||||||
|
val.Set(pdouble)
|
||||||
|
} else if err = checkType(val, reflect.Float32, reflect.Float64); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
i, err := strconv.ParseFloat(string(data), val.Type().Bits())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
val.SetFloat(i)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errors.New("unsupported type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// </type>
|
||||||
|
if err = dec.Skip(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dec *decoder) readTag() (string, []byte, error) {
|
||||||
|
var tok xml.Token
|
||||||
|
var err error
|
||||||
|
|
||||||
|
var name string
|
||||||
|
for {
|
||||||
|
if tok, err = dec.Token(); err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := tok.(xml.StartElement); ok {
|
||||||
|
name = t.Name.Local
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := dec.readCharData()
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return name, value, dec.Skip()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dec *decoder) readCharData() ([]byte, error) {
|
||||||
|
var tok xml.Token
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if tok, err = dec.Token(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := tok.(xml.CharData); ok {
|
||||||
|
return []byte(t.Copy()), nil
|
||||||
|
} else {
|
||||||
|
return nil, invalidXmlError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkType(val reflect.Value, kinds ...reflect.Kind) error {
|
||||||
|
if len(kinds) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if val.Kind() == reflect.Ptr {
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
match := false
|
||||||
|
|
||||||
|
for _, kind := range kinds {
|
||||||
|
if val.Kind() == kind {
|
||||||
|
match = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !match {
|
||||||
|
return TypeMismatchError(fmt.Sprintf("error: type mismatch - can't unmarshal %v to %v",
|
||||||
|
val.Kind(), kinds[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
164
vendor/github.com/kolo/xmlrpc/encoder.go
generated
vendored
Normal file
164
vendor/github.com/kolo/xmlrpc/encoder.go
generated
vendored
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
package xmlrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type encodeFunc func(reflect.Value) ([]byte, error)
|
||||||
|
|
||||||
|
func marshal(v interface{}) ([]byte, error) {
|
||||||
|
if v == nil {
|
||||||
|
return []byte{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
val := reflect.ValueOf(v)
|
||||||
|
return encodeValue(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeValue(val reflect.Value) ([]byte, error) {
|
||||||
|
var b []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface {
|
||||||
|
if val.IsNil() {
|
||||||
|
return []byte("<value/>"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch val.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
switch val.Interface().(type) {
|
||||||
|
case time.Time:
|
||||||
|
t := val.Interface().(time.Time)
|
||||||
|
b = []byte(fmt.Sprintf("<dateTime.iso8601>%s</dateTime.iso8601>", t.Format(iso8601)))
|
||||||
|
default:
|
||||||
|
b, err = encodeStruct(val)
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
b, err = encodeMap(val)
|
||||||
|
case reflect.Slice:
|
||||||
|
b, err = encodeSlice(val)
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
b = []byte(fmt.Sprintf("<int>%s</int>", strconv.FormatInt(val.Int(), 10)))
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
b = []byte(fmt.Sprintf("<i4>%s</i4>", strconv.FormatUint(val.Uint(), 10)))
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
b = []byte(fmt.Sprintf("<double>%s</double>",
|
||||||
|
strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits())))
|
||||||
|
case reflect.Bool:
|
||||||
|
if val.Bool() {
|
||||||
|
b = []byte("<boolean>1</boolean>")
|
||||||
|
} else {
|
||||||
|
b = []byte("<boolean>0</boolean>")
|
||||||
|
}
|
||||||
|
case reflect.String:
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
xml.Escape(&buf, []byte(val.String()))
|
||||||
|
|
||||||
|
if _, ok := val.Interface().(Base64); ok {
|
||||||
|
b = []byte(fmt.Sprintf("<base64>%s</base64>", buf.String()))
|
||||||
|
} else {
|
||||||
|
b = []byte(fmt.Sprintf("<string>%s</string>", buf.String()))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("xmlrpc encode error: unsupported type")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(fmt.Sprintf("<value>%s</value>", string(b))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeStruct(val reflect.Value) ([]byte, error) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
|
||||||
|
b.WriteString("<struct>")
|
||||||
|
|
||||||
|
t := val.Type()
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
b.WriteString("<member>")
|
||||||
|
f := t.Field(i)
|
||||||
|
|
||||||
|
name := f.Tag.Get("xmlrpc")
|
||||||
|
if name == "" {
|
||||||
|
name = f.Name
|
||||||
|
}
|
||||||
|
b.WriteString(fmt.Sprintf("<name>%s</name>", name))
|
||||||
|
|
||||||
|
p, err := encodeValue(val.FieldByName(f.Name))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b.Write(p)
|
||||||
|
|
||||||
|
b.WriteString("</member>")
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString("</struct>")
|
||||||
|
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeMap(val reflect.Value) ([]byte, error) {
|
||||||
|
var t = val.Type()
|
||||||
|
|
||||||
|
if t.Key().Kind() != reflect.String {
|
||||||
|
return nil, fmt.Errorf("xmlrpc encode error: only maps with string keys are supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
|
||||||
|
b.WriteString("<struct>")
|
||||||
|
|
||||||
|
keys := val.MapKeys()
|
||||||
|
|
||||||
|
for i := 0; i < val.Len(); i++ {
|
||||||
|
key := keys[i]
|
||||||
|
kval := val.MapIndex(key)
|
||||||
|
|
||||||
|
b.WriteString("<member>")
|
||||||
|
b.WriteString(fmt.Sprintf("<name>%s</name>", key.String()))
|
||||||
|
|
||||||
|
p, err := encodeValue(kval)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.Write(p)
|
||||||
|
b.WriteString("</member>")
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString("</struct>")
|
||||||
|
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeSlice(val reflect.Value) ([]byte, error) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
|
||||||
|
b.WriteString("<array><data>")
|
||||||
|
|
||||||
|
for i := 0; i < val.Len(); i++ {
|
||||||
|
p, err := encodeValue(val.Index(i))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString("</data></array>")
|
||||||
|
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
57
vendor/github.com/kolo/xmlrpc/request.go
generated
vendored
Normal file
57
vendor/github.com/kolo/xmlrpc/request.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package xmlrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewRequest(url string, method string, args interface{}) (*http.Request, error) {
|
||||||
|
var t []interface{}
|
||||||
|
var ok bool
|
||||||
|
if t, ok = args.([]interface{}); !ok {
|
||||||
|
if args != nil {
|
||||||
|
t = []interface{}{args}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := EncodeMethodCall(method, t...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
request, err := http.NewRequest("POST", url, bytes.NewReader(body))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
request.Header.Set("Content-Type", "text/xml")
|
||||||
|
request.Header.Set("Content-Length", fmt.Sprintf("%d", len(body)))
|
||||||
|
|
||||||
|
return request, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncodeMethodCall(method string, args ...interface{}) ([]byte, error) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
b.WriteString(`<?xml version="1.0" encoding="UTF-8"?>`)
|
||||||
|
b.WriteString(fmt.Sprintf("<methodCall><methodName>%s</methodName>", method))
|
||||||
|
|
||||||
|
if args != nil {
|
||||||
|
b.WriteString("<params>")
|
||||||
|
|
||||||
|
for _, arg := range args {
|
||||||
|
p, err := marshal(arg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString(fmt.Sprintf("<param>%s</param>", string(p)))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString("</params>")
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString("</methodCall>")
|
||||||
|
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
52
vendor/github.com/kolo/xmlrpc/response.go
generated
vendored
Normal file
52
vendor/github.com/kolo/xmlrpc/response.go
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package xmlrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
faultRx = regexp.MustCompile(`<fault>(\s|\S)+</fault>`)
|
||||||
|
)
|
||||||
|
|
||||||
|
type failedResponse struct {
|
||||||
|
Code int `xmlrpc:"faultCode"`
|
||||||
|
Error string `xmlrpc:"faultString"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *failedResponse) err() error {
|
||||||
|
return &xmlrpcError{
|
||||||
|
code: r.Code,
|
||||||
|
err: r.Error,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewResponse(data []byte) *Response {
|
||||||
|
return &Response{
|
||||||
|
data: data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Response) Failed() bool {
|
||||||
|
return faultRx.Match(r.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Response) Err() error {
|
||||||
|
failedResp := new(failedResponse)
|
||||||
|
if err := unmarshal(r.data, failedResp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return failedResp.err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Response) Unmarshal(v interface{}) error {
|
||||||
|
if err := unmarshal(r.data, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
19
vendor/github.com/kolo/xmlrpc/xmlrpc.go
generated
vendored
Normal file
19
vendor/github.com/kolo/xmlrpc/xmlrpc.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package xmlrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// xmlrpcError represents errors returned on xmlrpc request.
|
||||||
|
type xmlrpcError struct {
|
||||||
|
code int
|
||||||
|
err string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error() method implements Error interface
|
||||||
|
func (e *xmlrpcError) Error() string {
|
||||||
|
return fmt.Sprintf("error: \"%s\" code: %d", e.err, e.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base64 represents value in base64 encoding
|
||||||
|
type Base64 string
|
65
vendor/github.com/mitchellh/mapstructure/decode_hooks.go
generated
vendored
65
vendor/github.com/mitchellh/mapstructure/decode_hooks.go
generated
vendored
|
@ -2,6 +2,8 @@ package mapstructure
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -115,6 +117,69 @@ func StringToTimeDurationHookFunc() DecodeHookFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StringToIPHookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to net.IP
|
||||||
|
func StringToIPHookFunc() DecodeHookFunc {
|
||||||
|
return func(
|
||||||
|
f reflect.Type,
|
||||||
|
t reflect.Type,
|
||||||
|
data interface{}) (interface{}, error) {
|
||||||
|
if f.Kind() != reflect.String {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
if t != reflect.TypeOf(net.IP{}) {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert it by parsing
|
||||||
|
ip := net.ParseIP(data.(string))
|
||||||
|
if ip == nil {
|
||||||
|
return net.IP{}, fmt.Errorf("failed parsing ip %v", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToIPNetHookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to net.IPNet
|
||||||
|
func StringToIPNetHookFunc() DecodeHookFunc {
|
||||||
|
return func(
|
||||||
|
f reflect.Type,
|
||||||
|
t reflect.Type,
|
||||||
|
data interface{}) (interface{}, error) {
|
||||||
|
if f.Kind() != reflect.String {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
if t != reflect.TypeOf(net.IPNet{}) {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert it by parsing
|
||||||
|
_, net, err := net.ParseCIDR(data.(string))
|
||||||
|
return net, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToTimeHookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to time.Time.
|
||||||
|
func StringToTimeHookFunc(layout string) DecodeHookFunc {
|
||||||
|
return func(
|
||||||
|
f reflect.Type,
|
||||||
|
t reflect.Type,
|
||||||
|
data interface{}) (interface{}, error) {
|
||||||
|
if f.Kind() != reflect.String {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
if t != reflect.TypeOf(time.Time{}) {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert it by parsing
|
||||||
|
return time.Parse(layout, data.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to
|
// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to
|
||||||
// the decoder.
|
// the decoder.
|
||||||
//
|
//
|
||||||
|
|
414
vendor/github.com/mitchellh/mapstructure/mapstructure.go
generated
vendored
414
vendor/github.com/mitchellh/mapstructure/mapstructure.go
generated
vendored
|
@ -114,12 +114,12 @@ type Metadata struct {
|
||||||
Unused []string
|
Unused []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode takes a map and uses reflection to convert it into the
|
// Decode takes an input structure and uses reflection to translate it to
|
||||||
// given Go native structure. val must be a pointer to a struct.
|
// the output structure. output must be a pointer to a map or struct.
|
||||||
func Decode(m interface{}, rawVal interface{}) error {
|
func Decode(input interface{}, output interface{}) error {
|
||||||
config := &DecoderConfig{
|
config := &DecoderConfig{
|
||||||
Metadata: nil,
|
Metadata: nil,
|
||||||
Result: rawVal,
|
Result: output,
|
||||||
}
|
}
|
||||||
|
|
||||||
decoder, err := NewDecoder(config)
|
decoder, err := NewDecoder(config)
|
||||||
|
@ -127,7 +127,7 @@ func Decode(m interface{}, rawVal interface{}) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return decoder.Decode(m)
|
return decoder.Decode(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WeakDecode is the same as Decode but is shorthand to enable
|
// WeakDecode is the same as Decode but is shorthand to enable
|
||||||
|
@ -147,6 +147,40 @@ func WeakDecode(input, output interface{}) error {
|
||||||
return decoder.Decode(input)
|
return decoder.Decode(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DecodeMetadata is the same as Decode, but is shorthand to
|
||||||
|
// enable metadata collection. See DecoderConfig for more info.
|
||||||
|
func DecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error {
|
||||||
|
config := &DecoderConfig{
|
||||||
|
Metadata: metadata,
|
||||||
|
Result: output,
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder, err := NewDecoder(config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return decoder.Decode(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WeakDecodeMetadata is the same as Decode, but is shorthand to
|
||||||
|
// enable both WeaklyTypedInput and metadata collection. See
|
||||||
|
// DecoderConfig for more info.
|
||||||
|
func WeakDecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error {
|
||||||
|
config := &DecoderConfig{
|
||||||
|
Metadata: metadata,
|
||||||
|
Result: output,
|
||||||
|
WeaklyTypedInput: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder, err := NewDecoder(config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return decoder.Decode(input)
|
||||||
|
}
|
||||||
|
|
||||||
// NewDecoder returns a new decoder for the given configuration. Once
|
// NewDecoder returns a new decoder for the given configuration. Once
|
||||||
// a decoder has been returned, the same configuration must not be used
|
// a decoder has been returned, the same configuration must not be used
|
||||||
// again.
|
// again.
|
||||||
|
@ -184,70 +218,91 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
|
||||||
|
|
||||||
// Decode decodes the given raw interface to the target pointer specified
|
// Decode decodes the given raw interface to the target pointer specified
|
||||||
// by the configuration.
|
// by the configuration.
|
||||||
func (d *Decoder) Decode(raw interface{}) error {
|
func (d *Decoder) Decode(input interface{}) error {
|
||||||
return d.decode("", raw, reflect.ValueOf(d.config.Result).Elem())
|
return d.decode("", input, reflect.ValueOf(d.config.Result).Elem())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decodes an unknown data type into a specific reflection value.
|
// Decodes an unknown data type into a specific reflection value.
|
||||||
func (d *Decoder) decode(name string, data interface{}, val reflect.Value) error {
|
func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error {
|
||||||
if data == nil {
|
var inputVal reflect.Value
|
||||||
// If the data is nil, then we don't set anything.
|
if input != nil {
|
||||||
|
inputVal = reflect.ValueOf(input)
|
||||||
|
|
||||||
|
// We need to check here if input is a typed nil. Typed nils won't
|
||||||
|
// match the "input == nil" below so we check that here.
|
||||||
|
if inputVal.Kind() == reflect.Ptr && inputVal.IsNil() {
|
||||||
|
input = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if input == nil {
|
||||||
|
// If the data is nil, then we don't set anything, unless ZeroFields is set
|
||||||
|
// to true.
|
||||||
|
if d.config.ZeroFields {
|
||||||
|
outVal.Set(reflect.Zero(outVal.Type()))
|
||||||
|
|
||||||
|
if d.config.Metadata != nil && name != "" {
|
||||||
|
d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
dataVal := reflect.ValueOf(data)
|
if !inputVal.IsValid() {
|
||||||
if !dataVal.IsValid() {
|
// If the input value is invalid, then we just set the value
|
||||||
// If the data value is invalid, then we just set the value
|
|
||||||
// to be the zero value.
|
// to be the zero value.
|
||||||
val.Set(reflect.Zero(val.Type()))
|
outVal.Set(reflect.Zero(outVal.Type()))
|
||||||
|
if d.config.Metadata != nil && name != "" {
|
||||||
|
d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.config.DecodeHook != nil {
|
if d.config.DecodeHook != nil {
|
||||||
// We have a DecodeHook, so let's pre-process the data.
|
// We have a DecodeHook, so let's pre-process the input.
|
||||||
var err error
|
var err error
|
||||||
data, err = DecodeHookExec(
|
input, err = DecodeHookExec(
|
||||||
d.config.DecodeHook,
|
d.config.DecodeHook,
|
||||||
dataVal.Type(), val.Type(), data)
|
inputVal.Type(), outVal.Type(), input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error decoding '%s': %s", name, err)
|
return fmt.Errorf("error decoding '%s': %s", name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
dataKind := getKind(val)
|
outputKind := getKind(outVal)
|
||||||
switch dataKind {
|
switch outputKind {
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
err = d.decodeBool(name, data, val)
|
err = d.decodeBool(name, input, outVal)
|
||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
err = d.decodeBasic(name, data, val)
|
err = d.decodeBasic(name, input, outVal)
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
err = d.decodeString(name, data, val)
|
err = d.decodeString(name, input, outVal)
|
||||||
case reflect.Int:
|
case reflect.Int:
|
||||||
err = d.decodeInt(name, data, val)
|
err = d.decodeInt(name, input, outVal)
|
||||||
case reflect.Uint:
|
case reflect.Uint:
|
||||||
err = d.decodeUint(name, data, val)
|
err = d.decodeUint(name, input, outVal)
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
err = d.decodeFloat(name, data, val)
|
err = d.decodeFloat(name, input, outVal)
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
err = d.decodeStruct(name, data, val)
|
err = d.decodeStruct(name, input, outVal)
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
err = d.decodeMap(name, data, val)
|
err = d.decodeMap(name, input, outVal)
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
err = d.decodePtr(name, data, val)
|
err = d.decodePtr(name, input, outVal)
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
err = d.decodeSlice(name, data, val)
|
err = d.decodeSlice(name, input, outVal)
|
||||||
case reflect.Array:
|
case reflect.Array:
|
||||||
err = d.decodeArray(name, data, val)
|
err = d.decodeArray(name, input, outVal)
|
||||||
case reflect.Func:
|
case reflect.Func:
|
||||||
err = d.decodeFunc(name, data, val)
|
err = d.decodeFunc(name, input, outVal)
|
||||||
default:
|
default:
|
||||||
// If we reached this point then we weren't able to decode it
|
// If we reached this point then we weren't able to decode it
|
||||||
return fmt.Errorf("%s: unsupported type: %s", name, dataKind)
|
return fmt.Errorf("%s: unsupported type: %s", name, outputKind)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we reached here, then we successfully decoded SOMETHING, so
|
// If we reached here, then we successfully decoded SOMETHING, so
|
||||||
// mark the key as used if we're tracking metadata.
|
// mark the key as used if we're tracking metainput.
|
||||||
if d.config.Metadata != nil && name != "" {
|
if d.config.Metadata != nil && name != "" {
|
||||||
d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
|
d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
|
||||||
}
|
}
|
||||||
|
@ -258,7 +313,19 @@ func (d *Decoder) decode(name string, data interface{}, val reflect.Value) error
|
||||||
// This decodes a basic type (bool, int, string, etc.) and sets the
|
// This decodes a basic type (bool, int, string, etc.) and sets the
|
||||||
// value to "data" of that type.
|
// value to "data" of that type.
|
||||||
func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error {
|
func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error {
|
||||||
|
if val.IsValid() && val.Elem().IsValid() {
|
||||||
|
return d.decode(name, data, val.Elem())
|
||||||
|
}
|
||||||
|
|
||||||
dataVal := reflect.ValueOf(data)
|
dataVal := reflect.ValueOf(data)
|
||||||
|
|
||||||
|
// If the input data is a pointer, and the assigned type is the dereference
|
||||||
|
// of that exact pointer, then indirect it so that we can assign it.
|
||||||
|
// Example: *string to string
|
||||||
|
if dataVal.Kind() == reflect.Ptr && dataVal.Type().Elem() == val.Type() {
|
||||||
|
dataVal = reflect.Indirect(dataVal)
|
||||||
|
}
|
||||||
|
|
||||||
if !dataVal.IsValid() {
|
if !dataVal.IsValid() {
|
||||||
dataVal = reflect.Zero(val.Type())
|
dataVal = reflect.Zero(val.Type())
|
||||||
}
|
}
|
||||||
|
@ -275,7 +342,7 @@ func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value) error {
|
func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value) error {
|
||||||
dataVal := reflect.ValueOf(data)
|
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||||
dataKind := getKind(dataVal)
|
dataKind := getKind(dataVal)
|
||||||
|
|
||||||
converted := true
|
converted := true
|
||||||
|
@ -327,7 +394,7 @@ func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) error {
|
func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) error {
|
||||||
dataVal := reflect.ValueOf(data)
|
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||||
dataKind := getKind(dataVal)
|
dataKind := getKind(dataVal)
|
||||||
dataType := dataVal.Type()
|
dataType := dataVal.Type()
|
||||||
|
|
||||||
|
@ -369,7 +436,7 @@ func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) er
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error {
|
func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error {
|
||||||
dataVal := reflect.ValueOf(data)
|
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||||
dataKind := getKind(dataVal)
|
dataKind := getKind(dataVal)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
@ -412,7 +479,7 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) error {
|
func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) error {
|
||||||
dataVal := reflect.ValueOf(data)
|
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||||
dataKind := getKind(dataVal)
|
dataKind := getKind(dataVal)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
@ -443,7 +510,7 @@ func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) error {
|
func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) error {
|
||||||
dataVal := reflect.ValueOf(data)
|
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||||
dataKind := getKind(dataVal)
|
dataKind := getKind(dataVal)
|
||||||
dataType := dataVal.Type()
|
dataType := dataVal.Type()
|
||||||
|
|
||||||
|
@ -499,38 +566,68 @@ func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) er
|
||||||
valMap = reflect.MakeMap(mapType)
|
valMap = reflect.MakeMap(mapType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check input type
|
// Check input type and based on the input type jump to the proper func
|
||||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||||
if dataVal.Kind() != reflect.Map {
|
switch dataVal.Kind() {
|
||||||
// In weak mode, we accept a slice of maps as an input...
|
case reflect.Map:
|
||||||
|
return d.decodeMapFromMap(name, dataVal, val, valMap)
|
||||||
|
|
||||||
|
case reflect.Struct:
|
||||||
|
return d.decodeMapFromStruct(name, dataVal, val, valMap)
|
||||||
|
|
||||||
|
case reflect.Array, reflect.Slice:
|
||||||
if d.config.WeaklyTypedInput {
|
if d.config.WeaklyTypedInput {
|
||||||
switch dataVal.Kind() {
|
return d.decodeMapFromSlice(name, dataVal, val, valMap)
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
// Special case for BC reasons (covered by tests)
|
|
||||||
if dataVal.Len() == 0 {
|
|
||||||
val.Set(valMap)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < dataVal.Len(); i++ {
|
|
||||||
err := d.decode(
|
|
||||||
fmt.Sprintf("%s[%d]", name, i),
|
|
||||||
dataVal.Index(i).Interface(), val)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
default:
|
||||||
return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind())
|
return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) decodeMapFromSlice(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {
|
||||||
|
// Special case for BC reasons (covered by tests)
|
||||||
|
if dataVal.Len() == 0 {
|
||||||
|
val.Set(valMap)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < dataVal.Len(); i++ {
|
||||||
|
err := d.decode(
|
||||||
|
fmt.Sprintf("%s[%d]", name, i),
|
||||||
|
dataVal.Index(i).Interface(), val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {
|
||||||
|
valType := val.Type()
|
||||||
|
valKeyType := valType.Key()
|
||||||
|
valElemType := valType.Elem()
|
||||||
|
|
||||||
// Accumulate errors
|
// Accumulate errors
|
||||||
errors := make([]string, 0)
|
errors := make([]string, 0)
|
||||||
|
|
||||||
|
// If the input data is empty, then we just match what the input data is.
|
||||||
|
if dataVal.Len() == 0 {
|
||||||
|
if dataVal.IsNil() {
|
||||||
|
if !val.IsNil() {
|
||||||
|
val.Set(dataVal)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Set to empty allocated value
|
||||||
|
val.Set(valMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
for _, k := range dataVal.MapKeys() {
|
for _, k := range dataVal.MapKeys() {
|
||||||
fieldName := fmt.Sprintf("%s[%s]", name, k)
|
fieldName := fmt.Sprintf("%s[%s]", name, k)
|
||||||
|
|
||||||
|
@ -563,22 +660,128 @@ func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) er
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {
|
||||||
|
typ := dataVal.Type()
|
||||||
|
for i := 0; i < typ.NumField(); i++ {
|
||||||
|
// Get the StructField first since this is a cheap operation. If the
|
||||||
|
// field is unexported, then ignore it.
|
||||||
|
f := typ.Field(i)
|
||||||
|
if f.PkgPath != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next get the actual value of this field and verify it is assignable
|
||||||
|
// to the map value.
|
||||||
|
v := dataVal.Field(i)
|
||||||
|
if !v.Type().AssignableTo(valMap.Type().Elem()) {
|
||||||
|
return fmt.Errorf("cannot assign type '%s' to map value field of type '%s'", v.Type(), valMap.Type().Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
tagValue := f.Tag.Get(d.config.TagName)
|
||||||
|
tagParts := strings.Split(tagValue, ",")
|
||||||
|
|
||||||
|
// Determine the name of the key in the map
|
||||||
|
keyName := f.Name
|
||||||
|
if tagParts[0] != "" {
|
||||||
|
if tagParts[0] == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
keyName = tagParts[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// If "squash" is specified in the tag, we squash the field down.
|
||||||
|
squash := false
|
||||||
|
for _, tag := range tagParts[1:] {
|
||||||
|
if tag == "squash" {
|
||||||
|
squash = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if squash && v.Kind() != reflect.Struct {
|
||||||
|
return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v.Kind() {
|
||||||
|
// this is an embedded struct, so handle it differently
|
||||||
|
case reflect.Struct:
|
||||||
|
x := reflect.New(v.Type())
|
||||||
|
x.Elem().Set(v)
|
||||||
|
|
||||||
|
vType := valMap.Type()
|
||||||
|
vKeyType := vType.Key()
|
||||||
|
vElemType := vType.Elem()
|
||||||
|
mType := reflect.MapOf(vKeyType, vElemType)
|
||||||
|
vMap := reflect.MakeMap(mType)
|
||||||
|
|
||||||
|
err := d.decode(keyName, x.Interface(), vMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if squash {
|
||||||
|
for _, k := range vMap.MapKeys() {
|
||||||
|
valMap.SetMapIndex(k, vMap.MapIndex(k))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
valMap.SetMapIndex(reflect.ValueOf(keyName), vMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
valMap.SetMapIndex(reflect.ValueOf(keyName), v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if val.CanAddr() {
|
||||||
|
val.Set(valMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) error {
|
func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) error {
|
||||||
|
// If the input data is nil, then we want to just set the output
|
||||||
|
// pointer to be nil as well.
|
||||||
|
isNil := data == nil
|
||||||
|
if !isNil {
|
||||||
|
switch v := reflect.Indirect(reflect.ValueOf(data)); v.Kind() {
|
||||||
|
case reflect.Chan,
|
||||||
|
reflect.Func,
|
||||||
|
reflect.Interface,
|
||||||
|
reflect.Map,
|
||||||
|
reflect.Ptr,
|
||||||
|
reflect.Slice:
|
||||||
|
isNil = v.IsNil()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isNil {
|
||||||
|
if !val.IsNil() && val.CanSet() {
|
||||||
|
nilValue := reflect.New(val.Type()).Elem()
|
||||||
|
val.Set(nilValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Create an element of the concrete (non pointer) type and decode
|
// Create an element of the concrete (non pointer) type and decode
|
||||||
// into that. Then set the value of the pointer to this type.
|
// into that. Then set the value of the pointer to this type.
|
||||||
valType := val.Type()
|
valType := val.Type()
|
||||||
valElemType := valType.Elem()
|
valElemType := valType.Elem()
|
||||||
|
if val.CanSet() {
|
||||||
|
realVal := val
|
||||||
|
if realVal.IsNil() || d.config.ZeroFields {
|
||||||
|
realVal = reflect.New(valElemType)
|
||||||
|
}
|
||||||
|
|
||||||
realVal := val
|
if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil {
|
||||||
if realVal.IsNil() || d.config.ZeroFields {
|
return err
|
||||||
realVal = reflect.New(valElemType)
|
}
|
||||||
|
|
||||||
|
val.Set(realVal)
|
||||||
|
} else {
|
||||||
|
if err := d.decode(name, data, reflect.Indirect(val)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
val.Set(realVal)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -604,30 +807,44 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
|
||||||
|
|
||||||
valSlice := val
|
valSlice := val
|
||||||
if valSlice.IsNil() || d.config.ZeroFields {
|
if valSlice.IsNil() || d.config.ZeroFields {
|
||||||
|
if d.config.WeaklyTypedInput {
|
||||||
|
switch {
|
||||||
|
// Slice and array we use the normal logic
|
||||||
|
case dataValKind == reflect.Slice, dataValKind == reflect.Array:
|
||||||
|
break
|
||||||
|
|
||||||
|
// Empty maps turn into empty slices
|
||||||
|
case dataValKind == reflect.Map:
|
||||||
|
if dataVal.Len() == 0 {
|
||||||
|
val.Set(reflect.MakeSlice(sliceType, 0, 0))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Create slice of maps of other sizes
|
||||||
|
return d.decodeSlice(name, []interface{}{data}, val)
|
||||||
|
|
||||||
|
case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8:
|
||||||
|
return d.decodeSlice(name, []byte(dataVal.String()), val)
|
||||||
|
|
||||||
|
// All other types we try to convert to the slice type
|
||||||
|
// and "lift" it into it. i.e. a string becomes a string slice.
|
||||||
|
default:
|
||||||
|
// Just re-try this function with data as a slice.
|
||||||
|
return d.decodeSlice(name, []interface{}{data}, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check input type
|
// Check input type
|
||||||
if dataValKind != reflect.Array && dataValKind != reflect.Slice {
|
if dataValKind != reflect.Array && dataValKind != reflect.Slice {
|
||||||
if d.config.WeaklyTypedInput {
|
|
||||||
switch {
|
|
||||||
// Empty maps turn into empty slices
|
|
||||||
case dataValKind == reflect.Map:
|
|
||||||
if dataVal.Len() == 0 {
|
|
||||||
val.Set(reflect.MakeSlice(sliceType, 0, 0))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// All other types we try to convert to the slice type
|
|
||||||
// and "lift" it into it. i.e. a string becomes a string slice.
|
|
||||||
default:
|
|
||||||
// Just re-try this function with data as a slice.
|
|
||||||
return d.decodeSlice(name, []interface{}{data}, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"'%s': source data must be an array or slice, got %s", name, dataValKind)
|
"'%s': source data must be an array or slice, got %s", name, dataValKind)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the input value is empty, then don't allocate since non-nil != nil
|
||||||
|
if dataVal.Len() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Make a new slice to hold our result, same size as the original data.
|
// Make a new slice to hold our result, same size as the original data.
|
||||||
valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len())
|
valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len())
|
||||||
}
|
}
|
||||||
|
@ -737,10 +954,29 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
dataValKind := dataVal.Kind()
|
dataValKind := dataVal.Kind()
|
||||||
if dataValKind != reflect.Map {
|
switch dataValKind {
|
||||||
return fmt.Errorf("'%s' expected a map, got '%s'", name, dataValKind)
|
case reflect.Map:
|
||||||
}
|
return d.decodeStructFromMap(name, dataVal, val)
|
||||||
|
|
||||||
|
case reflect.Struct:
|
||||||
|
// Not the most efficient way to do this but we can optimize later if
|
||||||
|
// we want to. To convert from struct to struct we go to map first
|
||||||
|
// as an intermediary.
|
||||||
|
m := make(map[string]interface{})
|
||||||
|
mval := reflect.Indirect(reflect.ValueOf(&m))
|
||||||
|
if err := d.decodeMapFromStruct(name, dataVal, mval, mval); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := d.decodeStructFromMap(name, mval, val)
|
||||||
|
return result
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) error {
|
||||||
dataValType := dataVal.Type()
|
dataValType := dataVal.Type()
|
||||||
if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface {
|
if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
|
|
21
vendor/github.com/smueller18/goinwx/LICENSE
generated
vendored
Normal file
21
vendor/github.com/smueller18/goinwx/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 Andrew
|
||||||
|
|
||||||
|
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.
|
54
vendor/github.com/smueller18/goinwx/account.go
generated
vendored
Normal file
54
vendor/github.com/smueller18/goinwx/account.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package goinwx
|
||||||
|
|
||||||
|
const (
|
||||||
|
methodAccountLogin = "account.login"
|
||||||
|
methodAccountLogout = "account.logout"
|
||||||
|
methodAccountLock = "account.lock"
|
||||||
|
methodAccountUnlock = "account.unlock"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AccountService interface {
|
||||||
|
Login() error
|
||||||
|
Logout() error
|
||||||
|
Lock() error
|
||||||
|
Unlock(tan string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccountServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ AccountService = &AccountServiceOp{}
|
||||||
|
|
||||||
|
func (s *AccountServiceOp) Login() error {
|
||||||
|
req := s.client.NewRequest(methodAccountLogin, map[string]interface{}{
|
||||||
|
"user": s.client.Username,
|
||||||
|
"pass": s.client.Password,
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := s.client.Do(*req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AccountServiceOp) Logout() error {
|
||||||
|
req := s.client.NewRequest(methodAccountLogout, nil)
|
||||||
|
|
||||||
|
_, err := s.client.Do(*req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AccountServiceOp) Lock() error {
|
||||||
|
req := s.client.NewRequest(methodAccountLock, nil)
|
||||||
|
|
||||||
|
_, err := s.client.Do(*req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AccountServiceOp) Unlock(tan string) error {
|
||||||
|
req := s.client.NewRequest(methodAccountUnlock, map[string]interface{}{
|
||||||
|
"tan": tan,
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := s.client.Do(*req)
|
||||||
|
return err
|
||||||
|
}
|
150
vendor/github.com/smueller18/goinwx/contact.go
generated
vendored
Normal file
150
vendor/github.com/smueller18/goinwx/contact.go
generated
vendored
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
package goinwx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fatih/structs"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
methodContactInfo = "contact.info"
|
||||||
|
methodContactList = "contact.list"
|
||||||
|
methodContactCreate = "contact.create"
|
||||||
|
methodContactDelete = "contact.delete"
|
||||||
|
methodContactUpdate = "contact.update"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContactService interface {
|
||||||
|
Create(*ContactCreateRequest) (int, error)
|
||||||
|
Update(*ContactUpdateRequest) error
|
||||||
|
Delete(int) error
|
||||||
|
Info(int) (*ContactInfoResponse, error)
|
||||||
|
List(string) (*ContactListResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContactServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ContactService = &ContactServiceOp{}
|
||||||
|
|
||||||
|
type ContactCreateRequest struct {
|
||||||
|
Type string `structs:"type"`
|
||||||
|
Name string `structs:"name"`
|
||||||
|
Org string `structs:"org,omitempty"`
|
||||||
|
Street string `structs:"street"`
|
||||||
|
City string `structs:"city"`
|
||||||
|
PostalCode string `structs:"pc"`
|
||||||
|
StateProvince string `structs:"sp,omitempty"`
|
||||||
|
CountryCode string `structs:"cc"`
|
||||||
|
Voice string `structs:"voice"`
|
||||||
|
Fax string `structs:"fax,omitempty"`
|
||||||
|
Email string `structs:"email"`
|
||||||
|
Remarks string `structs:"remarks,omitempty"`
|
||||||
|
Protection bool `structs:"protection,omitempty"`
|
||||||
|
Testing bool `structs:"testing,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContactUpdateRequest struct {
|
||||||
|
Id int `structs:"id"`
|
||||||
|
Name string `structs:"name,omitempty"`
|
||||||
|
Org string `structs:"org,omitempty"`
|
||||||
|
Street string `structs:"street,omitempty"`
|
||||||
|
City string `structs:"city,omitempty"`
|
||||||
|
PostalCode string `structs:"pc,omitempty"`
|
||||||
|
StateProvince string `structs:"sp,omitempty"`
|
||||||
|
CountryCode string `structs:"cc,omitempty"`
|
||||||
|
Voice string `structs:"voice,omitempty"`
|
||||||
|
Fax string `structs:"fax,omitempty"`
|
||||||
|
Email string `structs:"email,omitempty"`
|
||||||
|
Remarks string `structs:"remarks,omitempty"`
|
||||||
|
Protection bool `structs:"protection,omitempty"`
|
||||||
|
Testing bool `structs:"testing,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContactInfoResponse struct {
|
||||||
|
Contact Contact `mapstructure:"contact"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContactListResponse struct {
|
||||||
|
Count int
|
||||||
|
Contacts []Contact `mapstructure:"contact"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ContactServiceOp) Create(request *ContactCreateRequest) (int, error) {
|
||||||
|
req := s.client.NewRequest(methodContactCreate, structs.Map(request))
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result map[string]int
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result["id"], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ContactServiceOp) Delete(roId int) error {
|
||||||
|
req := s.client.NewRequest(methodContactDelete, map[string]interface{}{
|
||||||
|
"id": roId,
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := s.client.Do(*req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ContactServiceOp) Update(request *ContactUpdateRequest) error {
|
||||||
|
req := s.client.NewRequest(methodContactUpdate, structs.Map(request))
|
||||||
|
|
||||||
|
_, err := s.client.Do(*req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ContactServiceOp) Info(contactId int) (*ContactInfoResponse, error) {
|
||||||
|
var requestMap = make(map[string]interface{})
|
||||||
|
requestMap["wide"] = 1
|
||||||
|
|
||||||
|
if contactId != 0 {
|
||||||
|
requestMap["id"] = contactId
|
||||||
|
}
|
||||||
|
|
||||||
|
req := s.client.NewRequest(methodContactInfo, requestMap)
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result ContactInfoResponse
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ContactServiceOp) List(search string) (*ContactListResponse, error) {
|
||||||
|
var requestMap = make(map[string]interface{})
|
||||||
|
|
||||||
|
if search != "" {
|
||||||
|
requestMap["search"] = search
|
||||||
|
}
|
||||||
|
req := s.client.NewRequest(methodContactList, requestMap)
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result ContactListResponse
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
303
vendor/github.com/smueller18/goinwx/domain.go
generated
vendored
Normal file
303
vendor/github.com/smueller18/goinwx/domain.go
generated
vendored
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
package goinwx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatih/structs"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
methodDomainCheck = "domain.check"
|
||||||
|
methodDomainCreate = "domain.create"
|
||||||
|
methodDomainDelete = "domain.delete"
|
||||||
|
methodDomainGetPrices = "domain.getPrices"
|
||||||
|
methodDomainGetRules = "domain.getRules"
|
||||||
|
methodDomainInfo = "domain.info"
|
||||||
|
methodDomainList = "domain.list"
|
||||||
|
methodDomainLog = "domain.log"
|
||||||
|
methodDomainPush = "domain.push"
|
||||||
|
methodDomainRenew = "domain.renew"
|
||||||
|
methodDomainRestore = "domain.restore"
|
||||||
|
methodDomainStats = "domain.stats"
|
||||||
|
methodDomainTrade = "domain.trade"
|
||||||
|
methodDomainTransfer = "domain.transfer"
|
||||||
|
methodDomainTransferOut = "domain.transferOut"
|
||||||
|
methodDomainUpdate = "domain.update"
|
||||||
|
methodDomainWhois = "domain.whois"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DomainService interface {
|
||||||
|
Check(domains []string) ([]DomainCheckResponse, error)
|
||||||
|
Register(request *DomainRegisterRequest) (*DomainRegisterResponse, error)
|
||||||
|
Delete(domain string, scheduledDate time.Time) error
|
||||||
|
Info(domain string, roId int) (*DomainInfoResponse, error)
|
||||||
|
GetPrices(tlds []string) ([]DomainPriceResponse, error)
|
||||||
|
List(*DomainListRequest) (*DomainList, error)
|
||||||
|
Whois(domain string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DomainServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ DomainService = &DomainServiceOp{}
|
||||||
|
|
||||||
|
type domainCheckResponseRoot struct {
|
||||||
|
Domains []DomainCheckResponse `mapstructure:"domain"`
|
||||||
|
}
|
||||||
|
type DomainCheckResponse struct {
|
||||||
|
Available int `mapstructure:"avail"`
|
||||||
|
Status string `mapstructure:"status"`
|
||||||
|
Name string `mapstructure:"name"`
|
||||||
|
Domain string `mapstructure:"domain"`
|
||||||
|
TLD string `mapstructure:"tld"`
|
||||||
|
CheckMethod string `mapstructure:"checkmethod"`
|
||||||
|
Price float32 `mapstructure:"price"`
|
||||||
|
CheckTime float32 `mapstructure:"checktime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type domainPriceResponseRoot struct {
|
||||||
|
Prices []DomainPriceResponse `mapstructure:"price"`
|
||||||
|
}
|
||||||
|
type DomainPriceResponse struct {
|
||||||
|
Tld string `mapstructure:"tld"`
|
||||||
|
Currency string `mapstructure:"currency"`
|
||||||
|
CreatePrice float32 `mapstructure:"createPrice"`
|
||||||
|
MonthlyCreatePrice float32 `mapstructure:"monthlyCreatePrice"`
|
||||||
|
TransferPrice float32 `mapstructure:"transferPrice"`
|
||||||
|
RenewalPrice float32 `mapstructure:"renewalPrice"`
|
||||||
|
MonthlyRenewalPrice float32 `mapstructure:"monthlyRenewalPrice"`
|
||||||
|
UpdatePrice float32 `mapstructure:"updatePrice"`
|
||||||
|
TradePrice float32 `mapstructure:"tradePrice"`
|
||||||
|
TrusteePrice float32 `mapstructure:"trusteePrice"`
|
||||||
|
MonthlyTrusteePrice float32 `mapstructure:"monthlyTrusteePrice"`
|
||||||
|
CreatePeriod int `mapstructure:"createPeriod"`
|
||||||
|
TransferPeriod int `mapstructure:"transferPeriod"`
|
||||||
|
RenewalPeriod int `mapstructure:"renewalPeriod"`
|
||||||
|
TradePeriod int `mapstructure:"tradePeriod"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DomainRegisterRequest struct {
|
||||||
|
Domain string `structs:"domain"`
|
||||||
|
Period string `structs:"period,omitempty"`
|
||||||
|
Registrant int `structs:"registrant"`
|
||||||
|
Admin int `structs:"admin"`
|
||||||
|
Tech int `structs:"tech"`
|
||||||
|
Billing int `structs:"billing"`
|
||||||
|
Nameservers []string `structs:"ns,omitempty"`
|
||||||
|
TransferLock string `structs:"transferLock,omitempty"`
|
||||||
|
RenewalMode string `structs:"renewalMode,omitempty"`
|
||||||
|
WhoisProvider string `structs:"whoisProvider,omitempty"`
|
||||||
|
WhoisUrl string `structs:"whoisUrl,omitempty"`
|
||||||
|
ScDate string `structs:"scDate,omitempty"`
|
||||||
|
ExtDate string `structs:"extDate,omitempty"`
|
||||||
|
Asynchron string `structs:"asynchron,omitempty"`
|
||||||
|
Voucher string `structs:"voucher,omitempty"`
|
||||||
|
Testing string `structs:"testing,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DomainRegisterResponse struct {
|
||||||
|
RoId int
|
||||||
|
Price float32
|
||||||
|
Currency string
|
||||||
|
}
|
||||||
|
|
||||||
|
type DomainInfoResponse struct {
|
||||||
|
RoId int `mapstructure:"roId"`
|
||||||
|
Domain string `mapstructure:"domain"`
|
||||||
|
DomainAce string `mapstructure:"domainAce"`
|
||||||
|
Period string `mapstructure:"period"`
|
||||||
|
CrDate time.Time `mapstructure:"crDate"`
|
||||||
|
ExDate time.Time `mapstructure:"exDate"`
|
||||||
|
UpDate time.Time `mapstructure:"upDate"`
|
||||||
|
ReDate time.Time `mapstructure:"reDate"`
|
||||||
|
ScDate time.Time `mapstructure:"scDate"`
|
||||||
|
TransferLock int `mapstructure:"transferLock"`
|
||||||
|
Status string `mapstructure:"status"`
|
||||||
|
AuthCode string `mapstructure:"authCode"`
|
||||||
|
RenewalMode string `mapstructure:"renewalMode"`
|
||||||
|
TransferMode string `mapstructure:"transferMode"`
|
||||||
|
Registrant int `mapstructure:"registrant"`
|
||||||
|
Admin int `mapstructure:"admin"`
|
||||||
|
Tech int `mapstructure:"tech"`
|
||||||
|
Billing int `mapstructure:"billing"`
|
||||||
|
Nameservers []string `mapstructure:"ns"`
|
||||||
|
NoDelegation string `mapstructure:"noDelegation"`
|
||||||
|
Contacts map[string]Contact `mapstructure:"contact"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Contact struct {
|
||||||
|
RoId int
|
||||||
|
Id string
|
||||||
|
Type string
|
||||||
|
Name string
|
||||||
|
Org string
|
||||||
|
Street string
|
||||||
|
City string
|
||||||
|
PostalCode string `mapstructure:"pc"`
|
||||||
|
StateProvince string `mapstructure:"sp"`
|
||||||
|
Country string `mapstructure:"cc"`
|
||||||
|
Phone string `mapstructure:"voice"`
|
||||||
|
Fax string
|
||||||
|
Email string
|
||||||
|
Remarks string
|
||||||
|
Protection string
|
||||||
|
}
|
||||||
|
|
||||||
|
type DomainListRequest struct {
|
||||||
|
Domain string `structs:"domain,omitempty"`
|
||||||
|
RoId int `structs:"roId,omitempty"`
|
||||||
|
Status int `structs:"status,omitempty"`
|
||||||
|
Registrant int `structs:"registrant,omitempty"`
|
||||||
|
Admin int `structs:"admin,omitempty"`
|
||||||
|
Tech int `structs:"tech,omitempty"`
|
||||||
|
Billing int `structs:"billing,omitempty"`
|
||||||
|
RenewalMode int `structs:"renewalMode,omitempty"`
|
||||||
|
TransferLock int `structs:"transferLock,omitempty"`
|
||||||
|
NoDelegation int `structs:"noDelegation,omitempty"`
|
||||||
|
Tag int `structs:"tag,omitempty"`
|
||||||
|
Order int `structs:"order,omitempty"`
|
||||||
|
Page int `structs:"page,omitempty"`
|
||||||
|
Pagelimit int `structs:"pagelimit,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DomainList struct {
|
||||||
|
Count int
|
||||||
|
Domains []DomainInfoResponse `mapstructure:"domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DomainServiceOp) Check(domains []string) ([]DomainCheckResponse, error) {
|
||||||
|
req := s.client.NewRequest(methodDomainCheck, map[string]interface{}{
|
||||||
|
"domain": domains,
|
||||||
|
"wide": "2",
|
||||||
|
})
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(domainCheckResponseRoot)
|
||||||
|
err = mapstructure.Decode(*resp, &root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Domains, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DomainServiceOp) GetPrices(tlds []string) ([]DomainPriceResponse, error) {
|
||||||
|
req := s.client.NewRequest(methodDomainGetPrices, map[string]interface{}{
|
||||||
|
"tld": tlds,
|
||||||
|
"vat": false,
|
||||||
|
})
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(domainPriceResponseRoot)
|
||||||
|
err = mapstructure.Decode(*resp, &root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Prices, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DomainServiceOp) Register(request *DomainRegisterRequest) (*DomainRegisterResponse, error) {
|
||||||
|
req := s.client.NewRequest(methodDomainCreate, structs.Map(request))
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result DomainRegisterResponse
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DomainServiceOp) Delete(domain string, scheduledDate time.Time) error {
|
||||||
|
req := s.client.NewRequest(methodDomainDelete, map[string]interface{}{
|
||||||
|
"domain": domain,
|
||||||
|
"scDate": scheduledDate.Format(time.RFC3339),
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := s.client.Do(*req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DomainServiceOp) Info(domain string, roId int) (*DomainInfoResponse, error) {
|
||||||
|
req := s.client.NewRequest(methodDomainInfo, map[string]interface{}{
|
||||||
|
"domain": domain,
|
||||||
|
"wide": "2",
|
||||||
|
})
|
||||||
|
if roId != 0 {
|
||||||
|
req.Args["roId"] = roId
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result DomainInfoResponse
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fmt.Println("Response", result)
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DomainServiceOp) List(request *DomainListRequest) (*DomainList, error) {
|
||||||
|
if request == nil {
|
||||||
|
return nil, errors.New("Request can't be nil")
|
||||||
|
}
|
||||||
|
requestMap := structs.Map(request)
|
||||||
|
requestMap["wide"] = "2"
|
||||||
|
|
||||||
|
req := s.client.NewRequest(methodDomainList, requestMap)
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result DomainList
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DomainServiceOp) Whois(domain string) (string, error) {
|
||||||
|
req := s.client.NewRequest(methodDomainWhois, map[string]interface{}{
|
||||||
|
"domain": domain,
|
||||||
|
})
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result map[string]string
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result["whois"], nil
|
||||||
|
}
|
133
vendor/github.com/smueller18/goinwx/goinwx.go
generated
vendored
Normal file
133
vendor/github.com/smueller18/goinwx/goinwx.go
generated
vendored
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
package goinwx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/kolo/xmlrpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
libraryVersion = "0.4.0"
|
||||||
|
APIBaseUrl = "https://api.domrobot.com/xmlrpc/"
|
||||||
|
APISandboxBaseUrl = "https://api.ote.domrobot.com/xmlrpc/"
|
||||||
|
APILanguage = "eng"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client manages communication with INWX API.
|
||||||
|
type Client struct {
|
||||||
|
// HTTP client used to communicate with the INWX API.
|
||||||
|
RPCClient *xmlrpc.Client
|
||||||
|
|
||||||
|
// Base URL for API requests.
|
||||||
|
BaseURL *url.URL
|
||||||
|
|
||||||
|
// API username
|
||||||
|
Username string
|
||||||
|
|
||||||
|
// API password
|
||||||
|
Password string
|
||||||
|
|
||||||
|
// User agent for client
|
||||||
|
APILanguage string
|
||||||
|
|
||||||
|
// Services used for communicating with the API
|
||||||
|
Account AccountService
|
||||||
|
Domains DomainService
|
||||||
|
Nameservers NameserverService
|
||||||
|
Contacts ContactService
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientOptions struct {
|
||||||
|
Sandbox bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
ServiceMethod string
|
||||||
|
Args map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response is a INWX API response. This wraps the standard http.Response returned from INWX.
|
||||||
|
type Response struct {
|
||||||
|
Code int `xmlrpc:"code"`
|
||||||
|
Message string `xmlrpc:"msg"`
|
||||||
|
ReasonCode string `xmlrpc:"reasonCode"`
|
||||||
|
Reason string `xmlrpc:"reason"`
|
||||||
|
ResponseData map[string]interface{} `xmlrpc:"resData"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// An ErrorResponse reports the error caused by an API request
|
||||||
|
type ErrorResponse struct {
|
||||||
|
Code int `xmlrpc:"code"`
|
||||||
|
Message string `xmlrpc:"msg"`
|
||||||
|
ReasonCode string `xmlrpc:"reasonCode"`
|
||||||
|
Reason string `xmlrpc:"reason"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns a new INWX API client.
|
||||||
|
func NewClient(username, password string, opts *ClientOptions) *Client {
|
||||||
|
var useSandbox bool
|
||||||
|
if opts != nil {
|
||||||
|
useSandbox = opts.Sandbox
|
||||||
|
}
|
||||||
|
|
||||||
|
var baseURL *url.URL
|
||||||
|
|
||||||
|
if useSandbox {
|
||||||
|
baseURL, _ = url.Parse(APISandboxBaseUrl)
|
||||||
|
} else {
|
||||||
|
baseURL, _ = url.Parse(APIBaseUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
rpcClient, _ := xmlrpc.NewClient(baseURL.String(), nil)
|
||||||
|
|
||||||
|
client := &Client{RPCClient: rpcClient,
|
||||||
|
BaseURL: baseURL,
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
}
|
||||||
|
|
||||||
|
client.Account = &AccountServiceOp{client: client}
|
||||||
|
client.Domains = &DomainServiceOp{client: client}
|
||||||
|
client.Nameservers = &NameserverServiceOp{client: client}
|
||||||
|
client.Contacts = &ContactServiceOp{client: client}
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRequest creates an API request.
|
||||||
|
func (c *Client) NewRequest(serviceMethod string, args map[string]interface{}) *Request {
|
||||||
|
if args != nil {
|
||||||
|
args["lang"] = APILanguage
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Request{ServiceMethod: serviceMethod, Args: args}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do sends an API request and returns the API response.
|
||||||
|
func (c *Client) Do(req Request) (*map[string]interface{}, error) {
|
||||||
|
var resp Response
|
||||||
|
err := c.RPCClient.Call(req.ServiceMethod, req.Args, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &resp.ResponseData, CheckResponse(&resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ErrorResponse) Error() string {
|
||||||
|
if r.Reason != "" {
|
||||||
|
return fmt.Sprintf("(%d) %s. Reason: (%s) %s",
|
||||||
|
r.Code, r.Message, r.ReasonCode, r.Reason)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("(%d) %s", r.Code, r.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckResponse checks the API response for errors, and returns them if present.
|
||||||
|
func CheckResponse(r *Response) error {
|
||||||
|
if c := r.Code; c >= 1000 && c <= 1500 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ErrorResponse{Code: r.Code, Message: r.Message, Reason: r.Reason, ReasonCode: r.ReasonCode}
|
||||||
|
}
|
275
vendor/github.com/smueller18/goinwx/nameserver.go
generated
vendored
Normal file
275
vendor/github.com/smueller18/goinwx/nameserver.go
generated
vendored
Normal file
|
@ -0,0 +1,275 @@
|
||||||
|
package goinwx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatih/structs"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
methodNameserverCheck = "nameserver.check"
|
||||||
|
methodNameserverCreate = "nameserver.create"
|
||||||
|
methodNameserverCreateRecord = "nameserver.createRecord"
|
||||||
|
methodNameserverDelete = "nameserver.delete"
|
||||||
|
methodNameserverDeleteRecord = "nameserver.deleteRecord"
|
||||||
|
methodNameserverInfo = "nameserver.info"
|
||||||
|
methodNameserverList = "nameserver.list"
|
||||||
|
methodNameserverUpdate = "nameserver.update"
|
||||||
|
methodNameserverUpdateRecord = "nameserver.updateRecord"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NameserverService interface {
|
||||||
|
Check(domain string, nameservers []string) (*NameserverCheckResponse, error)
|
||||||
|
Create(*NameserverCreateRequest) (int, error)
|
||||||
|
Info(*NameserverInfoRequest) (*NamserverInfoResponse, error)
|
||||||
|
List(domain string) (*NamserverListResponse, error)
|
||||||
|
CreateRecord(*NameserverRecordRequest) (int, error)
|
||||||
|
UpdateRecord(recId int, request *NameserverRecordRequest) error
|
||||||
|
DeleteRecord(recId int) error
|
||||||
|
FindRecordById(recId int) (*NameserverRecord, *NameserverDomain, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type NameserverServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ NameserverService = &NameserverServiceOp{}
|
||||||
|
|
||||||
|
type NameserverCheckResponse struct {
|
||||||
|
Details []string
|
||||||
|
Status string
|
||||||
|
}
|
||||||
|
|
||||||
|
type NameserverRecordRequest struct {
|
||||||
|
RoId int `structs:"roId,omitempty"`
|
||||||
|
Domain string `structs:"domain,omitempty"`
|
||||||
|
Type string `structs:"type"`
|
||||||
|
Content string `structs:"content"`
|
||||||
|
Name string `structs:"name,omitempty"`
|
||||||
|
Ttl int `structs:"ttl,omitempty"`
|
||||||
|
Priority int `structs:"prio,omitempty"`
|
||||||
|
UrlRedirectType string `structs:"urlRedirectType,omitempty"`
|
||||||
|
UrlRedirectTitle string `structs:"urlRedirectTitle,omitempty"`
|
||||||
|
UrlRedirectDescription string `structs:"urlRedirectDescription,omitempty"`
|
||||||
|
UrlRedirectFavIcon string `structs:"urlRedirectFavIcon,omitempty"`
|
||||||
|
UrlRedirectKeywords string `structs:"urlRedirectKeywords,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NameserverCreateRequest struct {
|
||||||
|
Domain string `structs:"domain"`
|
||||||
|
Type string `structs:"type"`
|
||||||
|
Nameservers []string `structs:"ns,omitempty"`
|
||||||
|
MasterIp string `structs:"masterIp,omitempty"`
|
||||||
|
Web string `structs:"web,omitempty"`
|
||||||
|
Mail string `structs:"mail,omitempty"`
|
||||||
|
SoaEmail string `structs:"soaEmail,omitempty"`
|
||||||
|
UrlRedirectType string `structs:"urlRedirectType,omitempty"`
|
||||||
|
UrlRedirectTitle string `structs:"urlRedirectTitle,omitempty"`
|
||||||
|
UrlRedirectDescription string `structs:"urlRedirectDescription,omitempty"`
|
||||||
|
UrlRedirectFavIcon string `structs:"urlRedirectFavIcon,omitempty"`
|
||||||
|
UrlRedirectKeywords string `structs:"urlRedirectKeywords,omitempty"`
|
||||||
|
Testing bool `structs:"testing,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NameserverInfoRequest struct {
|
||||||
|
Domain string `structs:"domain,omitempty"`
|
||||||
|
RoId int `structs:"roId,omitempty"`
|
||||||
|
RecordId int `structs:"recordId,omitempty"`
|
||||||
|
Type string `structs:"type,omitempty"`
|
||||||
|
Name string `structs:"name,omitempty"`
|
||||||
|
Content string `structs:"content,omitempty"`
|
||||||
|
Ttl int `structs:"ttl,omitempty"`
|
||||||
|
Prio int `structs:"prio,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NamserverInfoResponse struct {
|
||||||
|
RoId int
|
||||||
|
Domain string
|
||||||
|
Type string
|
||||||
|
MasterIp string
|
||||||
|
LastZoneCheck time.Time
|
||||||
|
SlaveDns interface{}
|
||||||
|
SOAserial string
|
||||||
|
Count int
|
||||||
|
Records []NameserverRecord `mapstructure:"record"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NameserverRecord struct {
|
||||||
|
Id int
|
||||||
|
Name string
|
||||||
|
Type string
|
||||||
|
Content string
|
||||||
|
Ttl int
|
||||||
|
Prio int
|
||||||
|
UrlRedirectType string
|
||||||
|
UrlRedirectTitle string
|
||||||
|
UrlRedirectDescription string
|
||||||
|
UrlRedirectKeywords string
|
||||||
|
UrlRedirectFavIcon string
|
||||||
|
}
|
||||||
|
|
||||||
|
type NamserverListResponse struct {
|
||||||
|
Count int
|
||||||
|
Domains []NameserverDomain `mapstructure:"domains"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NameserverDomain struct {
|
||||||
|
RoId int `mapstructure:"roId"`
|
||||||
|
Domain string `mapstructure:"domain"`
|
||||||
|
Type string `mapstructure:"type"`
|
||||||
|
MasterIp string `mapstructure:"masterIp"`
|
||||||
|
Mail string `mapstructure:"mail"`
|
||||||
|
Web string `mapstructure:"web"`
|
||||||
|
Url string `mapstructure:"url"`
|
||||||
|
Ipv4 string `mapstructure:"ipv4"`
|
||||||
|
Ipv6 string `mapstructure:"ipv6"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NameserverServiceOp) Check(domain string, nameservers []string) (*NameserverCheckResponse, error) {
|
||||||
|
req := s.client.NewRequest(methodNameserverCheck, map[string]interface{}{
|
||||||
|
"domain": domain,
|
||||||
|
"ns": nameservers,
|
||||||
|
})
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result NameserverCheckResponse
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NameserverServiceOp) Info(request *NameserverInfoRequest) (*NamserverInfoResponse, error) {
|
||||||
|
req := s.client.NewRequest(methodNameserverInfo, structs.Map(request))
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var result NamserverInfoResponse
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NameserverServiceOp) List(domain string) (*NamserverListResponse, error) {
|
||||||
|
requestMap := map[string]interface{}{
|
||||||
|
"domain": "*",
|
||||||
|
"wide": 2,
|
||||||
|
}
|
||||||
|
if domain != "" {
|
||||||
|
requestMap["domain"] = domain
|
||||||
|
}
|
||||||
|
req := s.client.NewRequest(methodNameserverList, requestMap)
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var result NamserverListResponse
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NameserverServiceOp) Create(request *NameserverCreateRequest) (int, error) {
|
||||||
|
req := s.client.NewRequest(methodNameserverCreate, structs.Map(request))
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result map[string]int
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result["roId"], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NameserverServiceOp) CreateRecord(request *NameserverRecordRequest) (int, error) {
|
||||||
|
req := s.client.NewRequest(methodNameserverCreateRecord, structs.Map(request))
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result map[string]int
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result["id"], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NameserverServiceOp) UpdateRecord(recId int, request *NameserverRecordRequest) error {
|
||||||
|
if request == nil {
|
||||||
|
return errors.New("Request can't be nil")
|
||||||
|
}
|
||||||
|
requestMap := structs.Map(request)
|
||||||
|
requestMap["id"] = recId
|
||||||
|
|
||||||
|
req := s.client.NewRequest(methodNameserverUpdateRecord, requestMap)
|
||||||
|
|
||||||
|
_, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NameserverServiceOp) DeleteRecord(recId int) error {
|
||||||
|
req := s.client.NewRequest(methodNameserverDeleteRecord, map[string]interface{}{
|
||||||
|
"id": recId,
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NameserverServiceOp) FindRecordById(recId int) (*NameserverRecord, *NameserverDomain, error) {
|
||||||
|
listResp, err := s.client.Nameservers.List("")
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, domainItem := range listResp.Domains {
|
||||||
|
resp, err := s.client.Nameservers.Info(&NameserverInfoRequest{RoId: domainItem.RoId})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, record := range resp.Records {
|
||||||
|
if record.Id == recId {
|
||||||
|
return &record, &domainItem, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil, fmt.Errorf("couldn't find INWX Record for id %d", recId)
|
||||||
|
|
||||||
|
}
|
21
vendor/github.com/transip/gotransip/LICENSE
generated
vendored
Normal file
21
vendor/github.com/transip/gotransip/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 TransIP B.V.
|
||||||
|
|
||||||
|
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.
|
12
vendor/github.com/transip/gotransip/api.go
generated
vendored
Normal file
12
vendor/github.com/transip/gotransip/api.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package gotransip
|
||||||
|
|
||||||
|
// CancellationTime represents the possible ways of canceling a contract
|
||||||
|
type CancellationTime string
|
||||||
|
|
||||||
|
var (
|
||||||
|
// CancellationTimeEnd specifies to cancel the contract when the contract was
|
||||||
|
// due to end anyway
|
||||||
|
CancellationTimeEnd CancellationTime = "end"
|
||||||
|
// CancellationTimeImmediately specifies to cancel the contract immediately
|
||||||
|
CancellationTimeImmediately CancellationTime = "immediately"
|
||||||
|
)
|
119
vendor/github.com/transip/gotransip/client.go
generated
vendored
Normal file
119
vendor/github.com/transip/gotransip/client.go
generated
vendored
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
package gotransip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
transipAPIHost = "api.transip.nl"
|
||||||
|
transipAPINamespace = "http://www.transip.nl/soap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// APIMode specifies in which mode the API is used. Currently this is only
|
||||||
|
// supports either readonly or readwrite
|
||||||
|
type APIMode string
|
||||||
|
|
||||||
|
var (
|
||||||
|
// APIModeReadOnly specifies that no changes can be made from API calls
|
||||||
|
APIModeReadOnly APIMode = "readonly"
|
||||||
|
// APIModeReadWrite specifies that changes can be made from API calls
|
||||||
|
APIModeReadWrite APIMode = "readwrite"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClientConfig is a tool to easily create a new Client object
|
||||||
|
type ClientConfig struct {
|
||||||
|
AccountName string
|
||||||
|
PrivateKeyPath string
|
||||||
|
PrivateKeyBody []byte
|
||||||
|
Mode APIMode
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client is the interface which all clients should implement
|
||||||
|
type Client interface {
|
||||||
|
Call(SoapRequest, interface{}) error // execute request on client
|
||||||
|
}
|
||||||
|
|
||||||
|
// SOAPClient represents a TransIP API SOAP client, implementing the Client
|
||||||
|
// interface
|
||||||
|
type SOAPClient struct {
|
||||||
|
soapClient soapClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call performs given SOAP request and fills the response into result
|
||||||
|
func (c SOAPClient) Call(req SoapRequest, result interface{}) error {
|
||||||
|
return c.soapClient.call(req, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSOAPClient returns a new SOAPClient object for given config
|
||||||
|
// ClientConfig's PrivateKeyPath will override potentially given PrivateKeyBody
|
||||||
|
func NewSOAPClient(c ClientConfig) (SOAPClient, error) {
|
||||||
|
// check account name
|
||||||
|
if len(c.AccountName) == 0 {
|
||||||
|
return SOAPClient{}, errors.New("AccountName is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if private key was given in any form
|
||||||
|
if len(c.PrivateKeyPath) == 0 && len(c.PrivateKeyBody) == 0 {
|
||||||
|
return SOAPClient{}, errors.New("PrivateKeyPath or PrivateKeyBody is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// if PrivateKeyPath was set, this will override any given PrivateKeyBody
|
||||||
|
if len(c.PrivateKeyPath) > 0 {
|
||||||
|
// try to open private key and read contents
|
||||||
|
if _, err := os.Stat(c.PrivateKeyPath); err != nil {
|
||||||
|
return SOAPClient{}, fmt.Errorf("could not open private key: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// read private key so we can pass the body to the soapClient
|
||||||
|
var err error
|
||||||
|
c.PrivateKeyBody, err = ioutil.ReadFile(c.PrivateKeyPath)
|
||||||
|
if err != nil {
|
||||||
|
return SOAPClient{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// default to APIMode read/write
|
||||||
|
if len(c.Mode) == 0 {
|
||||||
|
c.Mode = APIModeReadWrite
|
||||||
|
}
|
||||||
|
|
||||||
|
// create soapClient and pass it to a new Client pointer
|
||||||
|
sc := soapClient{
|
||||||
|
Login: c.AccountName,
|
||||||
|
Mode: c.Mode,
|
||||||
|
PrivateKey: c.PrivateKeyBody,
|
||||||
|
}
|
||||||
|
|
||||||
|
return SOAPClient{
|
||||||
|
soapClient: sc,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FakeSOAPClient is a client doing nothing except implementing the gotransip.Client
|
||||||
|
// interface
|
||||||
|
// you can however set a fixture XML body which Call will try to Unmarshal into
|
||||||
|
// result
|
||||||
|
// useful for testing
|
||||||
|
type FakeSOAPClient struct {
|
||||||
|
fixture []byte // preset this fixture so Call can use it to Unmarshal
|
||||||
|
}
|
||||||
|
|
||||||
|
// FixtureFromFile reads file and sets content as FakeSOAPClient's fixture
|
||||||
|
func (f *FakeSOAPClient) FixtureFromFile(file string) (err error) {
|
||||||
|
// read fixture file
|
||||||
|
f.fixture, err = ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("could not read fixture from file %s: %s", file, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call doesn't do anything except fill the XML unmarshalled result
|
||||||
|
func (f FakeSOAPClient) Call(req SoapRequest, result interface{}) error {
|
||||||
|
// this fake client just parses given fixture as if it was a SOAP response
|
||||||
|
return parseSoapResponse(f.fixture, req.Padding, 200, result)
|
||||||
|
}
|
314
vendor/github.com/transip/gotransip/domain/api.go
generated
vendored
Normal file
314
vendor/github.com/transip/gotransip/domain/api.go
generated
vendored
Normal file
|
@ -0,0 +1,314 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/transip/gotransip"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This file holds all DomainService methods directly ported from TransIP API
|
||||||
|
|
||||||
|
// BatchCheckAvailability checks the availability of multiple domains
|
||||||
|
func BatchCheckAvailability(c gotransip.Client, domainNames []string) ([]CheckResult, error) {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "batchCheckAvailability",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domainNames", domainNames)
|
||||||
|
|
||||||
|
var v struct {
|
||||||
|
V []CheckResult `xml:"item"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := c.Call(sr, &v)
|
||||||
|
return v.V, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckAvailability returns the availability status of a domain.
|
||||||
|
func CheckAvailability(c gotransip.Client, domainName string) (Status, error) {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "checkAvailability",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domainName", domainName)
|
||||||
|
|
||||||
|
var v Status
|
||||||
|
err := c.Call(sr, &v)
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWhois returns the whois of a domain name
|
||||||
|
func GetWhois(c gotransip.Client, domainName string) (string, error) {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "getWhois",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domainName", domainName)
|
||||||
|
|
||||||
|
var v string
|
||||||
|
err := c.Call(sr, &v)
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDomainNames returns list with domain names or error when this failed
|
||||||
|
func GetDomainNames(c gotransip.Client) ([]string, error) {
|
||||||
|
var d = struct {
|
||||||
|
D []string `xml:"item"`
|
||||||
|
}{}
|
||||||
|
err := c.Call(gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "getDomainNames",
|
||||||
|
}, &d)
|
||||||
|
|
||||||
|
return d.D, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInfo returns Domain for given name or error when this failed
|
||||||
|
func GetInfo(c gotransip.Client, domainName string) (Domain, error) {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "getInfo",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domainName", domainName)
|
||||||
|
|
||||||
|
var d Domain
|
||||||
|
err := c.Call(sr, &d)
|
||||||
|
|
||||||
|
return d, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchGetInfo returns array of Domain for given name or error when this failed
|
||||||
|
func BatchGetInfo(c gotransip.Client, domainNames []string) ([]Domain, error) {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "batchGetInfo",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domainNames", domainNames)
|
||||||
|
|
||||||
|
var d = struct {
|
||||||
|
D []Domain `xml:"item"`
|
||||||
|
}{}
|
||||||
|
err := c.Call(sr, &d)
|
||||||
|
|
||||||
|
return d.D, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAuthCode returns the Auth code for a domainName
|
||||||
|
func GetAuthCode(c gotransip.Client, domainName string) (string, error) {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "getAuthCode",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domainName", domainName)
|
||||||
|
|
||||||
|
var v string
|
||||||
|
err := c.Call(sr, &v)
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIsLocked returns the lock status for a domainName
|
||||||
|
func GetIsLocked(c gotransip.Client, domainName string) (bool, error) {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "getIsLocked",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domainName", domainName)
|
||||||
|
|
||||||
|
var v bool
|
||||||
|
err := c.Call(sr, &v)
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register registers a domain name and will automatically create and sign a proposition for it
|
||||||
|
func Register(c gotransip.Client, domain string) error {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "register",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domain", domain)
|
||||||
|
|
||||||
|
return c.Call(sr, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel cancels a domain name, will automatically create and sign a cancellation document
|
||||||
|
func Cancel(c gotransip.Client, domainName string, endTime gotransip.CancellationTime) error {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "cancel",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domainName", domainName)
|
||||||
|
sr.AddArgument("endTime", string(endTime))
|
||||||
|
|
||||||
|
return c.Call(sr, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransferWithOwnerChange transfers a domain with changing the owner
|
||||||
|
func TransferWithOwnerChange(c gotransip.Client, domain, authCode string) error {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "transferWithOwnerChange",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domain", domain)
|
||||||
|
sr.AddArgument("authCode", authCode)
|
||||||
|
|
||||||
|
return c.Call(sr, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransferWithoutOwnerChange transfers a domain without changing the owner
|
||||||
|
func TransferWithoutOwnerChange(c gotransip.Client, domain, authCode string) error {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "transferWithoutOwnerChange",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domain", domain)
|
||||||
|
sr.AddArgument("authCode", authCode)
|
||||||
|
|
||||||
|
return c.Call(sr, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNameservers starts a nameserver change for this domain, will replace all
|
||||||
|
// existing nameservers with the new nameservers
|
||||||
|
func SetNameservers(c gotransip.Client, domainName string, nameservers Nameservers) error {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "setNameservers",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domainName", domainName)
|
||||||
|
sr.AddArgument("nameservers", nameservers)
|
||||||
|
|
||||||
|
return c.Call(sr, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLock locks this domain
|
||||||
|
func SetLock(c gotransip.Client, domainName string) error {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "setLock",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domainName", domainName)
|
||||||
|
|
||||||
|
return c.Call(sr, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsetLock unlocks this domain
|
||||||
|
func UnsetLock(c gotransip.Client, domainName string) error {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "unsetLock",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domainName", domainName)
|
||||||
|
|
||||||
|
return c.Call(sr, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDNSEntries sets the DnsEntries for this Domain, will replace all existing
|
||||||
|
// dns entries with the new entries
|
||||||
|
func SetDNSEntries(c gotransip.Client, domainName string, dnsEntries DNSEntries) error {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "setDnsEntries",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domainName", domainName)
|
||||||
|
sr.AddArgument("dnsEntries", dnsEntries)
|
||||||
|
|
||||||
|
return c.Call(sr, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOwner starts an owner change of a domain
|
||||||
|
func SetOwner(c gotransip.Client, domainName, registrantWhoisContact WhoisContact) error {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "setOwner",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domainName", domainName)
|
||||||
|
// make sure contact is of type registrant
|
||||||
|
registrantWhoisContact.Type = "registrant"
|
||||||
|
sr.AddArgument("registrantWhoisContact", registrantWhoisContact)
|
||||||
|
|
||||||
|
return c.Call(sr, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetContacts starts a contact change of a domain, this will replace all existing contacts
|
||||||
|
func SetContacts(c gotransip.Client, domainName, contacts WhoisContacts) error {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "setContacts",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domainName", domainName)
|
||||||
|
sr.AddArgument("contacts", contacts)
|
||||||
|
|
||||||
|
return c.Call(sr, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllTLDInfos returns slice with TLD objects or error when this failed
|
||||||
|
func GetAllTLDInfos(c gotransip.Client) ([]TLD, error) {
|
||||||
|
var d = struct {
|
||||||
|
TLD []TLD `xml:"item"`
|
||||||
|
}{}
|
||||||
|
err := c.Call(gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "getAllTldInfos",
|
||||||
|
}, &d)
|
||||||
|
|
||||||
|
return d.TLD, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTldInfo returns info about a specific TLD
|
||||||
|
func GetTldInfo(c gotransip.Client, tldName string) (TLD, error) {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "getTldInfo",
|
||||||
|
}
|
||||||
|
sr.AddArgument("tldName", tldName)
|
||||||
|
|
||||||
|
var v TLD
|
||||||
|
err := c.Call(sr, &v)
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrentDomainAction returns info about the action this domain is currently running
|
||||||
|
func GetCurrentDomainAction(c gotransip.Client, domainName string) (ActionResult, error) {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "getCurrentDomainAction",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domainName", domainName)
|
||||||
|
|
||||||
|
var v ActionResult
|
||||||
|
err := c.Call(sr, &v)
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetryCurrentDomainActionWithNewData retries a failed domain action with new
|
||||||
|
// domain data. The Domain.Name field must contain the name of the Domain. The
|
||||||
|
// Nameservers, Contacts, DNSEntries fields contain the new data for this domain.
|
||||||
|
func RetryCurrentDomainActionWithNewData(c gotransip.Client, domain Domain) error {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "retryCurrentDomainActionWithNewData",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domain", domain)
|
||||||
|
|
||||||
|
return c.Call(sr, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetryTransferWithDifferentAuthCode retries a transfer action with a new authcode
|
||||||
|
func RetryTransferWithDifferentAuthCode(c gotransip.Client, domain Domain, newAuthCode string) error {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "retryTransferWithDifferentAuthCode",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domain", domain)
|
||||||
|
sr.AddArgument("newAuthCode", newAuthCode)
|
||||||
|
|
||||||
|
return c.Call(sr, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CancelDomainAction cancels a failed domain action
|
||||||
|
func CancelDomainAction(c gotransip.Client, domain string) error {
|
||||||
|
sr := gotransip.SoapRequest{
|
||||||
|
Service: serviceName,
|
||||||
|
Method: "cancelDomainAction",
|
||||||
|
}
|
||||||
|
sr.AddArgument("domain", domain)
|
||||||
|
|
||||||
|
return c.Call(sr, nil)
|
||||||
|
}
|
405
vendor/github.com/transip/gotransip/domain/domain.go
generated
vendored
Normal file
405
vendor/github.com/transip/gotransip/domain/domain.go
generated
vendored
Normal file
|
@ -0,0 +1,405 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/transip/gotransip"
|
||||||
|
"github.com/transip/gotransip/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
serviceName string = "DomainService"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Domain represents a Transip_Domain object
|
||||||
|
// as described at https://api.transip.nl/docs/transip.nl/class-Transip_Domain.html
|
||||||
|
type Domain struct {
|
||||||
|
Name string `xml:"name"`
|
||||||
|
Nameservers []Nameserver `xml:"nameservers>item"`
|
||||||
|
Contacts []WhoisContact `xml:"contacts>item"`
|
||||||
|
DNSEntries []DNSEntry `xml:"dnsEntries>item"`
|
||||||
|
Branding Branding `xml:"branding"`
|
||||||
|
AuthorizationCode string `xml:"authCode"`
|
||||||
|
IsLocked bool `xml:"isLocked"`
|
||||||
|
RegistrationDate util.XMLTime `xml:"registrationDate"`
|
||||||
|
RenewalDate util.XMLTime `xml:"renewalDate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeParams returns Domain parameters ready to be used for constructing a signature
|
||||||
|
func (d Domain) EncodeParams(prm gotransip.ParamsContainer) {
|
||||||
|
idx := prm.Len()
|
||||||
|
prm.Add(fmt.Sprintf("%d[name]", idx), d.Name)
|
||||||
|
prm.Add(fmt.Sprintf("%d[authCode]", idx), d.AuthorizationCode)
|
||||||
|
prm.Add(fmt.Sprintf("%d[isLocked]", idx), fmt.Sprintf("%t", d.IsLocked))
|
||||||
|
prm.Add(fmt.Sprintf("%d[registrationDate]", idx), d.RegistrationDate.Format("2006-01-02"))
|
||||||
|
prm.Add(fmt.Sprintf("%d[renewalDate]", idx), d.RenewalDate.Format("2006-01-02"))
|
||||||
|
// nameservers
|
||||||
|
for i, e := range d.Nameservers {
|
||||||
|
var ipv4, ipv6 string
|
||||||
|
if e.IPv4Address != nil {
|
||||||
|
ipv4 = e.IPv4Address.String()
|
||||||
|
}
|
||||||
|
if e.IPv6Address != nil {
|
||||||
|
ipv6 = e.IPv6Address.String()
|
||||||
|
}
|
||||||
|
prm.Add(fmt.Sprintf("%d[nameservers][%d][hostname]", idx, i), e.Hostname)
|
||||||
|
prm.Add(fmt.Sprintf("%d[nameservers][%d][ipv4]", idx, i), ipv4)
|
||||||
|
prm.Add(fmt.Sprintf("%d[nameservers][%d][ipv6]", idx, i), ipv6)
|
||||||
|
}
|
||||||
|
// contacts
|
||||||
|
for i, e := range d.Contacts {
|
||||||
|
prm.Add(fmt.Sprintf("%d[contacts][%d][type]", idx, i), e.Type)
|
||||||
|
prm.Add(fmt.Sprintf("%d[contacts][%d][firstName]", idx, i), e.FirstName)
|
||||||
|
prm.Add(fmt.Sprintf("%d[contacts][%d][middleName]", idx, i), e.MiddleName)
|
||||||
|
prm.Add(fmt.Sprintf("%d[contacts][%d][lastName]", idx, i), e.LastName)
|
||||||
|
prm.Add(fmt.Sprintf("%d[contacts][%d][companyName]", idx, i), e.CompanyName)
|
||||||
|
prm.Add(fmt.Sprintf("%d[contacts][%d][companyKvk]", idx, i), e.CompanyKvk)
|
||||||
|
prm.Add(fmt.Sprintf("%d[contacts][%d][companyType]", idx, i), e.CompanyType)
|
||||||
|
prm.Add(fmt.Sprintf("%d[contacts][%d][street]", idx, i), e.Street)
|
||||||
|
prm.Add(fmt.Sprintf("%d[contacts][%d][number]", idx, i), e.Number)
|
||||||
|
prm.Add(fmt.Sprintf("%d[contacts][%d][postalCode]", idx, i), e.PostalCode)
|
||||||
|
prm.Add(fmt.Sprintf("%d[contacts][%d][city]", idx, i), e.City)
|
||||||
|
prm.Add(fmt.Sprintf("%d[contacts][%d][phoneNumber]", idx, i), e.PhoneNumber)
|
||||||
|
prm.Add(fmt.Sprintf("%d[contacts][%d][faxNumber]", idx, i), e.FaxNumber)
|
||||||
|
prm.Add(fmt.Sprintf("%d[contacts][%d][email]", idx, i), e.Email)
|
||||||
|
prm.Add(fmt.Sprintf("%d[contacts][%d][country]", idx, i), e.Country)
|
||||||
|
}
|
||||||
|
// dnsEntries
|
||||||
|
for i, e := range d.DNSEntries {
|
||||||
|
prm.Add(fmt.Sprintf("%d[dnsEntries][%d][name]", idx, i), e.Name)
|
||||||
|
prm.Add(fmt.Sprintf("%d[dnsEntries][%d][expire]", idx, i), fmt.Sprintf("%d", e.TTL))
|
||||||
|
prm.Add(fmt.Sprintf("%d[dnsEntries][%d][type]", idx, i), string(e.Type))
|
||||||
|
prm.Add(fmt.Sprintf("%d[dnsEntries][%d][content]", idx, i), e.Content)
|
||||||
|
}
|
||||||
|
// branding
|
||||||
|
prm.Add(fmt.Sprintf("%d[branding][companyName]", idx), d.Branding.CompanyName)
|
||||||
|
prm.Add(fmt.Sprintf("%d[branding][supportEmail]", idx), d.Branding.SupportEmail)
|
||||||
|
prm.Add(fmt.Sprintf("%d[branding][companyUrl]", idx), d.Branding.CompanyURL)
|
||||||
|
prm.Add(fmt.Sprintf("%d[branding][termsOfUsageUrl]", idx), d.Branding.TermsOfUsageURL)
|
||||||
|
prm.Add(fmt.Sprintf("%d[branding][bannerLine1]", idx), d.Branding.BannerLine1)
|
||||||
|
prm.Add(fmt.Sprintf("%d[branding][bannerLine2]", idx), d.Branding.BannerLine2)
|
||||||
|
prm.Add(fmt.Sprintf("%d[branding][bannerLine3]", idx), d.Branding.BannerLine3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeArgs returns Domain XML body ready to be passed in the SOAP call
|
||||||
|
func (d Domain) EncodeArgs(key string) string {
|
||||||
|
output := fmt.Sprintf(`<%s xsi:type="ns1:Domain">
|
||||||
|
<name xsi:type="xsd:string">%s</name>
|
||||||
|
<authCode xsi:type="xsd:string">%s</authCode>
|
||||||
|
<isLocked xsi:type="xsd:boolean">%t</isLocked>
|
||||||
|
<registrationDate xsi:type="xsd:string">%s</registrationDate>
|
||||||
|
<renewalDate xsi:type="xsd:string">%s</renewalDate>`,
|
||||||
|
key, d.Name, d.AuthorizationCode, d.IsLocked,
|
||||||
|
d.RegistrationDate.Format("2006-01-02"), d.RenewalDate.Format("2006-01-02"),
|
||||||
|
) + "\n"
|
||||||
|
|
||||||
|
output += Nameservers(d.Nameservers).EncodeArgs("nameservers") + "\n"
|
||||||
|
output += WhoisContacts(d.Contacts).EncodeArgs("contacts") + "\n"
|
||||||
|
output += DNSEntries(d.DNSEntries).EncodeArgs("dnsEntries") + "\n"
|
||||||
|
output += d.Branding.EncodeArgs("branding") + "\n"
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s</%s>", output, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capability represents the possible capabilities a TLD can have
|
||||||
|
type Capability string
|
||||||
|
|
||||||
|
var (
|
||||||
|
// CapabilityRequiresAuthCode defines this TLD requires an auth code
|
||||||
|
// to be transferred
|
||||||
|
CapabilityRequiresAuthCode Capability = "requiresAuthCode"
|
||||||
|
// CapabilityCanRegister defines this TLD can be registered
|
||||||
|
CapabilityCanRegister Capability = "canRegister"
|
||||||
|
// CapabilityCanTransferWithOwnerChange defines this TLD can be transferred
|
||||||
|
// with change of ownership
|
||||||
|
CapabilityCanTransferWithOwnerChange Capability = "canTransferWithOwnerChange"
|
||||||
|
// CapabilityCanTransferWithoutOwnerChange defines this TLD can be
|
||||||
|
// transferred without change of ownership
|
||||||
|
CapabilityCanTransferWithoutOwnerChange Capability = "canTransferWithoutOwnerChange"
|
||||||
|
// CapabilityCanSetLock defines this TLD allows to be locked
|
||||||
|
CapabilityCanSetLock Capability = "canSetLock"
|
||||||
|
// CapabilityCanSetOwner defines this TLD supports setting an owner
|
||||||
|
CapabilityCanSetOwner Capability = "canSetOwner"
|
||||||
|
// CapabilityCanSetContacts defines this TLD supports setting contacts
|
||||||
|
CapabilityCanSetContacts Capability = "canSetContacts"
|
||||||
|
// CapabilityCanSetNameservers defines this TLD supports setting nameservers
|
||||||
|
CapabilityCanSetNameservers Capability = "canSetNameservers"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TLD represents a Transip_Tld object as described at
|
||||||
|
// https://api.transip.nl/docs/transip.nl/class-Transip_Tld.html
|
||||||
|
type TLD struct {
|
||||||
|
Name string `xml:"name"`
|
||||||
|
Price float64 `xml:"price"`
|
||||||
|
RenewalPrice float64 `xml:"renewalPrice"`
|
||||||
|
Capabilities []Capability `xml:"capabilities>item"`
|
||||||
|
RegistrationPeriodLength int64 `xml:"registrationPeriodLength"`
|
||||||
|
CancelTimeFrame int64 `xml:"cancelTimeFrame"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nameserver represents a Transip_Nameserver object as described at
|
||||||
|
// https://api.transip.nl/docs/transip.nl/class-Transip_Nameserver.html
|
||||||
|
type Nameserver struct {
|
||||||
|
Hostname string `xml:"hostname"`
|
||||||
|
IPv4Address net.IP `xml:"ipv4"`
|
||||||
|
IPv6Address net.IP `xml:"ipv6"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nameservers is just an array of Nameserver
|
||||||
|
// basically only here so it can implement paramsEncoder
|
||||||
|
type Nameservers []Nameserver
|
||||||
|
|
||||||
|
// EncodeParams returns Nameservers parameters ready to be used for constructing a signature
|
||||||
|
func (n Nameservers) EncodeParams(prm gotransip.ParamsContainer) {
|
||||||
|
idx := prm.Len()
|
||||||
|
for i, e := range n {
|
||||||
|
var ipv4, ipv6 string
|
||||||
|
if e.IPv4Address != nil {
|
||||||
|
ipv4 = e.IPv4Address.String()
|
||||||
|
}
|
||||||
|
if e.IPv6Address != nil {
|
||||||
|
ipv6 = e.IPv6Address.String()
|
||||||
|
}
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][hostname]", idx, i), e.Hostname)
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][ipv4]", idx, i), ipv4)
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][ipv6]", idx, i), ipv6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeArgs returns Nameservers XML body ready to be passed in the SOAP call
|
||||||
|
func (n Nameservers) EncodeArgs(key string) string {
|
||||||
|
output := fmt.Sprintf(`<%s SOAP-ENC:arrayType="ns1:Nameserver[%d]" xsi:type="ns1:ArrayOfNameserver">`, key, len(n)) + "\n"
|
||||||
|
for _, e := range n {
|
||||||
|
var ipv4, ipv6 string
|
||||||
|
if e.IPv4Address != nil {
|
||||||
|
ipv4 = e.IPv4Address.String()
|
||||||
|
}
|
||||||
|
if e.IPv6Address != nil {
|
||||||
|
ipv6 = e.IPv6Address.String()
|
||||||
|
}
|
||||||
|
output += fmt.Sprintf(` <item xsi:type="ns1:Nameserver">
|
||||||
|
<hostname xsi:type="xsd:string">%s</hostname>
|
||||||
|
<ipv4 xsi:type="xsd:string">%s</ipv4>
|
||||||
|
<ipv6 xsi:type="xsd:string">%s</ipv6>
|
||||||
|
</item>`, e.Hostname, ipv4, ipv6) + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s</%s>", output, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSEntryType represents the possible types of DNS entries
|
||||||
|
type DNSEntryType string
|
||||||
|
|
||||||
|
var (
|
||||||
|
// DNSEntryTypeA represents an A-record
|
||||||
|
DNSEntryTypeA DNSEntryType = "A"
|
||||||
|
// DNSEntryTypeAAAA represents an AAAA-record
|
||||||
|
DNSEntryTypeAAAA DNSEntryType = "AAAA"
|
||||||
|
// DNSEntryTypeCNAME represents a CNAME-record
|
||||||
|
DNSEntryTypeCNAME DNSEntryType = "CNAME"
|
||||||
|
// DNSEntryTypeMX represents an MX-record
|
||||||
|
DNSEntryTypeMX DNSEntryType = "MX"
|
||||||
|
// DNSEntryTypeNS represents an NS-record
|
||||||
|
DNSEntryTypeNS DNSEntryType = "NS"
|
||||||
|
// DNSEntryTypeTXT represents a TXT-record
|
||||||
|
DNSEntryTypeTXT DNSEntryType = "TXT"
|
||||||
|
// DNSEntryTypeSRV represents an SRV-record
|
||||||
|
DNSEntryTypeSRV DNSEntryType = "SRV"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DNSEntry represents a Transip_DnsEntry object as described at
|
||||||
|
// https://api.transip.nl/docs/transip.nl/class-Transip_DnsEntry.html
|
||||||
|
type DNSEntry struct {
|
||||||
|
Name string `xml:"name"`
|
||||||
|
TTL int64 `xml:"expire"`
|
||||||
|
Type DNSEntryType `xml:"type"`
|
||||||
|
Content string `xml:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSEntries is just an array of DNSEntry
|
||||||
|
// basically only here so it can implement paramsEncoder
|
||||||
|
type DNSEntries []DNSEntry
|
||||||
|
|
||||||
|
// EncodeParams returns DNSEntries parameters ready to be used for constructing a signature
|
||||||
|
func (d DNSEntries) EncodeParams(prm gotransip.ParamsContainer) {
|
||||||
|
idx := prm.Len()
|
||||||
|
for i, e := range d {
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][name]", idx, i), e.Name)
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][expire]", idx, i), fmt.Sprintf("%d", e.TTL))
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][type]", idx, i), string(e.Type))
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][content]", idx, i), e.Content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeArgs returns DNSEntries XML body ready to be passed in the SOAP call
|
||||||
|
func (d DNSEntries) EncodeArgs(key string) string {
|
||||||
|
output := fmt.Sprintf(`<%s SOAP-ENC:arrayType="ns1:DnsEntry[%d]" xsi:type="ns1:ArrayOfDnsEntry">`, key, len(d)) + "\n"
|
||||||
|
for _, e := range d {
|
||||||
|
output += fmt.Sprintf(` <item xsi:type="ns1:DnsEntry">
|
||||||
|
<name xsi:type="xsd:string">%s</name>
|
||||||
|
<expire xsi:type="xsd:int">%d</expire>
|
||||||
|
<type xsi:type="xsd:string">%s</type>
|
||||||
|
<content xsi:type="xsd:string">%s</content>
|
||||||
|
</item>`, e.Name, e.TTL, e.Type, e.Content) + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s</%s>", output, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status reflects the current status of a domain in a check result
|
||||||
|
type Status string
|
||||||
|
|
||||||
|
var (
|
||||||
|
// StatusInYourAccount means he domain name is already in your account
|
||||||
|
StatusInYourAccount Status = "inyouraccount"
|
||||||
|
// StatusUnavailable means the domain name is currently unavailable and can not be registered due to unknown reasons.
|
||||||
|
StatusUnavailable Status = "unavailable"
|
||||||
|
// StatusNotFree means the domain name has already been registered
|
||||||
|
StatusNotFree Status = "notfree"
|
||||||
|
// StatusFree means the domain name is currently free, is available and can be registered
|
||||||
|
StatusFree Status = "free"
|
||||||
|
// StatusInternalPull means the domain name is currently registered at TransIP and is available to be pulled from another account to yours.
|
||||||
|
StatusInternalPull Status = "internalpull"
|
||||||
|
// StatusInternalPush means the domain name is currently registered at TransIP in your accounta and is available to be pushed to another account.
|
||||||
|
StatusInternalPush Status = "internalpush"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckResult represents a Transip_DomainCheckResult object as described at
|
||||||
|
// https://api.transip.nl/docs/transip.nl/class-Transip_DomainCheckResult.html
|
||||||
|
type CheckResult struct {
|
||||||
|
DomainName string `xml:"domainName"`
|
||||||
|
Status Status `xml:"status"`
|
||||||
|
Actions []Action `xml:"actions>item"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Branding represents a Transip_DomainBranding object as described at
|
||||||
|
// https://api.transip.nl/docs/transip.nl/class-Transip_DomainBranding.html
|
||||||
|
type Branding struct {
|
||||||
|
CompanyName string `xml:"companyName"`
|
||||||
|
SupportEmail string `xml:"supportEmail"`
|
||||||
|
CompanyURL string `xml:"companyUrl"`
|
||||||
|
TermsOfUsageURL string `xml:"termsOfUsageUrl"`
|
||||||
|
BannerLine1 string `xml:"bannerLine1"`
|
||||||
|
BannerLine2 string `xml:"bannerLine2"`
|
||||||
|
BannerLine3 string `xml:"bannerLine3"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeParams returns WhoisContacts parameters ready to be used for constructing a signature
|
||||||
|
func (b Branding) EncodeParams(prm gotransip.ParamsContainer) {
|
||||||
|
idx := prm.Len()
|
||||||
|
prm.Add(fmt.Sprintf("%d[companyName]", idx), b.CompanyName)
|
||||||
|
prm.Add(fmt.Sprintf("%d[supportEmail]", idx), b.SupportEmail)
|
||||||
|
prm.Add(fmt.Sprintf("%d[companyUrl]", idx), b.CompanyURL)
|
||||||
|
prm.Add(fmt.Sprintf("%d[termsOfUsageUrl]", idx), b.TermsOfUsageURL)
|
||||||
|
prm.Add(fmt.Sprintf("%d[bannerLine1]", idx), b.BannerLine1)
|
||||||
|
prm.Add(fmt.Sprintf("%d[bannerLine2]", idx), b.BannerLine2)
|
||||||
|
prm.Add(fmt.Sprintf("%d[bannerLine3]", idx), b.BannerLine3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeArgs returns Branding XML body ready to be passed in the SOAP call
|
||||||
|
func (b Branding) EncodeArgs(key string) string {
|
||||||
|
return fmt.Sprintf(`<branding xsi:type="ns1:DomainBranding">
|
||||||
|
<companyName xsi:type="xsd:string">%s</companyName>
|
||||||
|
<supportEmail xsi:type="xsd:string">%s</supportEmail>
|
||||||
|
<companyUrl xsi:type="xsd:string">%s</companyUrl>
|
||||||
|
<termsOfUsageUrl xsi:type="xsd:string">%s</termsOfUsageUrl>
|
||||||
|
<bannerLine1 xsi:type="xsd:string">%s</bannerLine1>
|
||||||
|
<bannerLine2 xsi:type="xsd:string">%s</bannerLine2>
|
||||||
|
<bannerLine3 xsi:type="xsd:string">%s</bannerLine3>
|
||||||
|
</branding>`, b.CompanyName, b.SupportEmail, b.CompanyURL, b.TermsOfUsageURL, b.BannerLine1, b.BannerLine2, b.BannerLine3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action reflects the available actions to perform on a domain
|
||||||
|
type Action string
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ActionRegister registers a domain
|
||||||
|
ActionRegister Action = "register"
|
||||||
|
// ActionTransfer transfers a domain to another provider
|
||||||
|
ActionTransfer Action = "transfer"
|
||||||
|
// ActionInternalPull transfers a domain to another account at TransIP
|
||||||
|
ActionInternalPull Action = "internalpull"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ActionResult represents a Transip_DomainAction object as described at
|
||||||
|
// https://api.transip.nl/docs/transip.nl/class-Transip_DomainAction.html
|
||||||
|
type ActionResult struct {
|
||||||
|
Name string `xml:"name"`
|
||||||
|
HasFailed bool `xml:"hasFailed"`
|
||||||
|
Message string `xml:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhoisContact represents a TransIP_WhoisContact object
|
||||||
|
// as described at https://api.transip.nl/docs/transip.nl/class-Transip_WhoisContact.html
|
||||||
|
type WhoisContact struct {
|
||||||
|
Type string `xml:"type"`
|
||||||
|
FirstName string `xml:"firstName"`
|
||||||
|
MiddleName string `xml:"middleName"`
|
||||||
|
LastName string `xml:"lastName"`
|
||||||
|
CompanyName string `xml:"companyName"`
|
||||||
|
CompanyKvk string `xml:"companyKvk"`
|
||||||
|
CompanyType string `xml:"companyType"`
|
||||||
|
Street string `xml:"street"`
|
||||||
|
Number string `xml:"number"`
|
||||||
|
PostalCode string `xml:"postalCode"`
|
||||||
|
City string `xml:"city"`
|
||||||
|
PhoneNumber string `xml:"phoneNumber"`
|
||||||
|
FaxNumber string `xml:"faxNumber"`
|
||||||
|
Email string `xml:"email"`
|
||||||
|
Country string `xml:"country"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhoisContacts is just an array of WhoisContact
|
||||||
|
// basically only here so it can implement paramsEncoder
|
||||||
|
type WhoisContacts []WhoisContact
|
||||||
|
|
||||||
|
// EncodeParams returns WhoisContacts parameters ready to be used for constructing a signature
|
||||||
|
func (w WhoisContacts) EncodeParams(prm gotransip.ParamsContainer) {
|
||||||
|
idx := prm.Len()
|
||||||
|
for i, e := range w {
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][type]", idx, i), e.Type)
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][firstName]", idx, i), e.FirstName)
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][middleName]", idx, i), e.MiddleName)
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][lastName]", idx, i), e.LastName)
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][companyName]", idx, i), e.CompanyName)
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][companyKvk]", idx, i), e.CompanyKvk)
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][companyType]", idx, i), e.CompanyType)
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][street]", idx, i), e.Street)
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][number]", idx, i), e.Number)
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][postalCode]", idx, i), e.PostalCode)
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][city]", idx, i), e.City)
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][phoneNumber]", idx, i), e.PhoneNumber)
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][faxNumber]", idx, i), e.FaxNumber)
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][email]", idx, i), e.Email)
|
||||||
|
prm.Add(fmt.Sprintf("%d[%d][country]", idx, i), e.Country)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeArgs returns WhoisContacts XML body ready to be passed in the SOAP call
|
||||||
|
func (w WhoisContacts) EncodeArgs(key string) string {
|
||||||
|
output := fmt.Sprintf(`<%s SOAP-ENC:arrayType="ns1:WhoisContact[%d]" xsi:type="ns1:ArrayOfWhoisContact">`, key, len(w)) + "\n"
|
||||||
|
for _, e := range w {
|
||||||
|
output += fmt.Sprintf(` <item xsi:type="ns1:WhoisContact">
|
||||||
|
<type xsi:type="xsd:string">%s</type>
|
||||||
|
<firstName xsi:type="xsd:string">%s</firstName>
|
||||||
|
<middleName xsi:type="xsd:string">%s</middleName>
|
||||||
|
<lastName xsi:type="xsd:string">%s</lastName>
|
||||||
|
<companyName xsi:type="xsd:string">%s</companyName>
|
||||||
|
<companyKvk xsi:type="xsd:string">%s</companyKvk>
|
||||||
|
<companyType xsi:type="xsd:string">%s</companyType>
|
||||||
|
<street xsi:type="xsd:string">%s</street>
|
||||||
|
<number xsi:type="xsd:string">%s</number>
|
||||||
|
<postalCode xsi:type="xsd:string">%s</postalCode>
|
||||||
|
<city xsi:type="xsd:string">%s</city>
|
||||||
|
<phoneNumber xsi:type="xsd:string">%s</phoneNumber>
|
||||||
|
<faxNumber xsi:type="xsd:string">%s</faxNumber>
|
||||||
|
<email xsi:type="xsd:string">%s</email>
|
||||||
|
<country xsi:type="xsd:string">%s</country>
|
||||||
|
</item>`, e.Type, e.FirstName, e.MiddleName, e.LastName, e.CompanyName,
|
||||||
|
e.CompanyKvk, e.CompanyType, e.Street, e.Number, e.PostalCode, e.City,
|
||||||
|
e.PhoneNumber, e.FaxNumber, e.Email, e.Country) + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
return output + fmt.Sprintf("</%s>", key)
|
||||||
|
}
|
49
vendor/github.com/transip/gotransip/sign.go
generated
vendored
Normal file
49
vendor/github.com/transip/gotransip/sign.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package gotransip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha512"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
asn1Header = []byte{
|
||||||
|
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
|
||||||
|
0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func signWithKey(params *soapParams, key []byte) (string, error) {
|
||||||
|
// create SHA512 hash of given parameters
|
||||||
|
h := sha512.New()
|
||||||
|
h.Write([]byte(params.Encode()))
|
||||||
|
|
||||||
|
// prefix ASN1 header to SHA512 hash
|
||||||
|
digest := append(asn1Header, h.Sum(nil)...)
|
||||||
|
|
||||||
|
// prepare key struct
|
||||||
|
block, _ := pem.Decode(key)
|
||||||
|
if block == nil {
|
||||||
|
return "", errors.New("could not decode private key")
|
||||||
|
}
|
||||||
|
parsed, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not parse private key: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
pkey := parsed.(*rsa.PrivateKey)
|
||||||
|
|
||||||
|
enc, err := rsa.SignPKCS1v15(rand.Reader, pkey, crypto.Hash(0), digest)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not sign data: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.QueryEscape(base64.StdEncoding.EncodeToString(enc)), nil
|
||||||
|
}
|
419
vendor/github.com/transip/gotransip/soap.go
generated
vendored
Normal file
419
vendor/github.com/transip/gotransip/soap.go
generated
vendored
Normal file
|
@ -0,0 +1,419 @@
|
||||||
|
package gotransip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// format for SOAP envelopes
|
||||||
|
soapEnvelopeFixture string = `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="%s">
|
||||||
|
<SOAP-ENV:Body>%s</SOAP-ENV:Body>
|
||||||
|
</SOAP-ENV:Envelope>`
|
||||||
|
)
|
||||||
|
|
||||||
|
// getSOAPArgs returns XML representing given name and argument as SOAP body
|
||||||
|
func getSOAPArgs(name string, input ...string) []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
buf.WriteString(fmt.Sprintf("<ns1:%s>", name))
|
||||||
|
for _, x := range input {
|
||||||
|
buf.WriteString(x)
|
||||||
|
}
|
||||||
|
buf.WriteString(fmt.Sprintf("</ns1:%s>", name))
|
||||||
|
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSOAPArg returns XML representing given input argument as SOAP parameters
|
||||||
|
// in combination with getSOAPArgs you can build SOAP body
|
||||||
|
func getSOAPArg(name string, input interface{}) (output string) {
|
||||||
|
switch input.(type) {
|
||||||
|
case []string:
|
||||||
|
i := input.([]string)
|
||||||
|
output = fmt.Sprintf(`<%s SOAP-ENC:arrayType="xsd:string[%d]" xsi:type="ns1:ArrayOfString">`, name, len(i))
|
||||||
|
for _, x := range i {
|
||||||
|
output = output + fmt.Sprintf(`<item xsi:type="xsd:string">%s</item>`, x)
|
||||||
|
}
|
||||||
|
output = output + fmt.Sprintf("</%s>", name)
|
||||||
|
case string:
|
||||||
|
output = fmt.Sprintf(`<%s xsi:type="xsd:string">%s</%s>`, name, input, name)
|
||||||
|
case int, int32, int64:
|
||||||
|
output = fmt.Sprintf(`<%s xsi:type="xsd:integer">%d</%s>`, name, input, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type soapFault struct {
|
||||||
|
Code string `xml:"faultcode,omitempty"`
|
||||||
|
Description string `xml:"faultstring,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s soapFault) String() string {
|
||||||
|
return fmt.Sprintf("SOAP Fault %s: %s", s.Code, s.Description)
|
||||||
|
}
|
||||||
|
|
||||||
|
// paramsEncoder allows SoapParams to hook into encoding theirselves, useful when
|
||||||
|
// fields consist of complex structs
|
||||||
|
type paramsEncoder interface {
|
||||||
|
EncodeParams(ParamsContainer)
|
||||||
|
EncodeArgs(string) string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParamsContainer is the interface a type should implement to be able to hold
|
||||||
|
// SOAP parameters
|
||||||
|
type ParamsContainer interface {
|
||||||
|
Len() int
|
||||||
|
Add(string, interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// soapParams is a utility to make sure parameter data is encoded into a query
|
||||||
|
// in the same order as we set them. The TransIP API requires this order for
|
||||||
|
// verifying the signature
|
||||||
|
type soapParams struct {
|
||||||
|
keys []string
|
||||||
|
values []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds parameter data to the end of this SoapParams
|
||||||
|
func (s *soapParams) Add(k string, v interface{}) {
|
||||||
|
if s.keys == nil {
|
||||||
|
s.keys = make([]string, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.values == nil {
|
||||||
|
s.values = make([]interface{}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.keys = append(s.keys, k)
|
||||||
|
s.values = append(s.values, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns amount of parameters set in this SoapParams
|
||||||
|
func (s soapParams) Len() int {
|
||||||
|
return len(s.keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode returns a URL-like query string that can be used to generate a request's
|
||||||
|
// signature. It's similar to url.Values.Encode() but without sorting of the keys
|
||||||
|
// and based on the value's type it tries to encode accordingly.
|
||||||
|
func (s soapParams) Encode() string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
var key string
|
||||||
|
|
||||||
|
for i, v := range s.values {
|
||||||
|
// if this is not the first parameter, prefix with &
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString("&")
|
||||||
|
}
|
||||||
|
|
||||||
|
// for empty data fields, don't encode anything
|
||||||
|
if v == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
key = s.keys[i]
|
||||||
|
|
||||||
|
switch v.(type) {
|
||||||
|
case []string:
|
||||||
|
c := v.([]string)
|
||||||
|
for j, cc := range c {
|
||||||
|
if j > 0 {
|
||||||
|
buf.WriteString("&")
|
||||||
|
}
|
||||||
|
buf.WriteString(fmt.Sprintf("%s[%d]=", key, j))
|
||||||
|
buf.WriteString(strings.Replace(url.QueryEscape(cc), "+", "%20", -1))
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
c := v.(string)
|
||||||
|
buf.WriteString(fmt.Sprintf("%s=", key))
|
||||||
|
buf.WriteString(strings.Replace(url.QueryEscape(c), "+", "%20", -1))
|
||||||
|
case int, int8, int16, int32, int64:
|
||||||
|
buf.WriteString(fmt.Sprintf("%s=", key))
|
||||||
|
buf.WriteString(fmt.Sprintf("%d", v))
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
type soapHeader struct {
|
||||||
|
XMLName struct{} `xml:"Header"`
|
||||||
|
Contents []byte `xml:",innerxml"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type soapBody struct {
|
||||||
|
XMLName struct{} `xml:"Body"`
|
||||||
|
Contents []byte `xml:",innerxml"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type soapResponse struct {
|
||||||
|
Response struct {
|
||||||
|
InnerXML []byte `xml:",innerxml"`
|
||||||
|
} `xml:"return"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type soapEnvelope struct {
|
||||||
|
XMLName struct{} `xml:"Envelope"`
|
||||||
|
Header soapHeader
|
||||||
|
Body soapBody
|
||||||
|
}
|
||||||
|
|
||||||
|
// SoapRequest holds all information for perfoming a SOAP request
|
||||||
|
// Arguments to the request can be specified with AddArgument
|
||||||
|
// If padding is defined, the SOAP response will be parsed after it being padded
|
||||||
|
// with items in Padding in reverse order
|
||||||
|
type SoapRequest struct {
|
||||||
|
Service string
|
||||||
|
Method string
|
||||||
|
params *soapParams // params used for creating signature
|
||||||
|
args []string // XML body arguments
|
||||||
|
Padding []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddArgument adds an argument to the SoapRequest; the arguments ared used to
|
||||||
|
// fill the XML request body as well as to create a valid signature for the
|
||||||
|
// request
|
||||||
|
func (sr *SoapRequest) AddArgument(key string, value interface{}) {
|
||||||
|
if sr.params == nil {
|
||||||
|
sr.params = &soapParams{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if value implements paramsEncoder
|
||||||
|
if pe, ok := value.(paramsEncoder); ok {
|
||||||
|
sr.args = append(sr.args, pe.EncodeArgs(key))
|
||||||
|
pe.EncodeParams(sr.params)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch value.(type) {
|
||||||
|
case []string:
|
||||||
|
sr.params.Add(fmt.Sprintf("%d", sr.params.Len()), value)
|
||||||
|
sr.args = append(sr.args, getSOAPArg(key, value))
|
||||||
|
case string:
|
||||||
|
sr.params.Add(fmt.Sprintf("%d", sr.params.Len()), value)
|
||||||
|
sr.args = append(sr.args, getSOAPArg(key, value.(string)))
|
||||||
|
case int, int8, int16, int32, int64:
|
||||||
|
sr.params.Add(fmt.Sprintf("%d", sr.params.Len()), value)
|
||||||
|
sr.args = append(sr.args, getSOAPArg(key, fmt.Sprintf("%d", value)))
|
||||||
|
default:
|
||||||
|
// check if value implements the String interface
|
||||||
|
if str, ok := value.(fmt.Stringer); ok {
|
||||||
|
sr.params.Add(fmt.Sprintf("%d", sr.params.Len()), str.String())
|
||||||
|
sr.args = append(sr.args, getSOAPArg(key, str.String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr SoapRequest) getEnvelope() string {
|
||||||
|
return fmt.Sprintf(soapEnvelopeFixture, transipAPIHost, getSOAPArgs(sr.Method, sr.args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
type soapClient struct {
|
||||||
|
Login string
|
||||||
|
Mode APIMode
|
||||||
|
PrivateKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// httpReqForSoapRequest creates the HTTP request for a specific SoapRequest
|
||||||
|
// this includes setting the URL, POST body and cookies
|
||||||
|
func (s soapClient) httpReqForSoapRequest(req SoapRequest) (*http.Request, error) {
|
||||||
|
// format URL
|
||||||
|
url := fmt.Sprintf("https://%s/soap/?service=%s", transipAPIHost, req.Service)
|
||||||
|
|
||||||
|
// create HTTP request
|
||||||
|
// TransIP API SOAP requests are always POST requests
|
||||||
|
httpReq, err := http.NewRequest("POST", url, strings.NewReader(req.getEnvelope()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate a number-used-once, a.k.a. nonce
|
||||||
|
// seeding the RNG is important if we want to do prevent using the same nonce
|
||||||
|
// in 2 sequential requests
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
nonce := fmt.Sprintf("%d", rand.Int())
|
||||||
|
// set time of request, used later for signature as well
|
||||||
|
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||||
|
|
||||||
|
// set cookies required for the request
|
||||||
|
// most of these cookies are used for the signature as well so they should
|
||||||
|
// obviously match
|
||||||
|
httpReq.AddCookie(&http.Cookie{
|
||||||
|
Name: "login",
|
||||||
|
Value: s.Login,
|
||||||
|
})
|
||||||
|
httpReq.AddCookie(&http.Cookie{
|
||||||
|
Name: "mode",
|
||||||
|
Value: string(s.Mode),
|
||||||
|
})
|
||||||
|
httpReq.AddCookie(&http.Cookie{
|
||||||
|
Name: "timestamp",
|
||||||
|
Value: timestamp,
|
||||||
|
})
|
||||||
|
httpReq.AddCookie(&http.Cookie{
|
||||||
|
Name: "nonce",
|
||||||
|
Value: nonce,
|
||||||
|
})
|
||||||
|
|
||||||
|
// add params required for signature to the request parameters
|
||||||
|
if req.params == nil {
|
||||||
|
req.params = &soapParams{}
|
||||||
|
}
|
||||||
|
// TransIP API is quite picky on the order of the parameters
|
||||||
|
// so don't change anything in the order below
|
||||||
|
req.params.Add("__method", req.Method)
|
||||||
|
req.params.Add("__service", req.Service)
|
||||||
|
req.params.Add("__hostname", transipAPIHost)
|
||||||
|
req.params.Add("__timestamp", timestamp)
|
||||||
|
req.params.Add("__nonce", nonce)
|
||||||
|
|
||||||
|
signature, err := signWithKey(req.params, s.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// add signature of the request to the cookies as well
|
||||||
|
httpReq.AddCookie(&http.Cookie{
|
||||||
|
Name: "signature",
|
||||||
|
Value: signature,
|
||||||
|
})
|
||||||
|
|
||||||
|
return httpReq, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseSoapResponse(data []byte, padding []string, statusCode int, result interface{}) error {
|
||||||
|
// try to decode the resulting XML
|
||||||
|
var env soapEnvelope
|
||||||
|
if err := xml.Unmarshal(data, &env); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to decode the body to a soapFault
|
||||||
|
var fault soapFault
|
||||||
|
if err := xml.Unmarshal(env.Body.Contents, &fault); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// by checking fault's Code, we can determine if the response body in fact
|
||||||
|
// was a SOAP fault and if it was: return it as an error
|
||||||
|
if len(fault.Code) > 0 {
|
||||||
|
return errors.New(fault.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to decode into soapResponse
|
||||||
|
sr := soapResponse{}
|
||||||
|
if err := xml.Unmarshal(env.Body.Contents, &sr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the response was empty and HTTP status was 200, consider it a success
|
||||||
|
if len(sr.Response.InnerXML) == 0 && statusCode == 200 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// it seems like xml.Unmarshal won't work well on the most outer element
|
||||||
|
// so even when no Padding is defined, always pad with "transip" element
|
||||||
|
p := append([]string{"transip"}, padding...)
|
||||||
|
innerXML := padXMLData(sr.Response.InnerXML, p)
|
||||||
|
|
||||||
|
// try to decode to given result interface
|
||||||
|
return xml.Unmarshal([]byte(innerXML), &result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *soapClient) call(req SoapRequest, result interface{}) error {
|
||||||
|
// get http request for soap request
|
||||||
|
httpReq, err := s.httpReqForSoapRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// create HTTP client and do the actual request
|
||||||
|
client := &http.Client{Timeout: time.Second * 10}
|
||||||
|
// make sure to verify the validity of remote certificate
|
||||||
|
// this is the default, but adding this flag here makes it easier to toggle
|
||||||
|
// it for testing/debugging
|
||||||
|
client.Transport = &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resp, err := client.Do(httpReq)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("request error:\n%s", err.Error())
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// read entire response body
|
||||||
|
b, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse SOAP response into given result interface
|
||||||
|
return parseSoapResponse(b, req.Padding, resp.StatusCode, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply given padding around the XML data fed into this function
|
||||||
|
// padding is applied in reverse order, so last element of padding is the
|
||||||
|
// innermost element in the resulting XML
|
||||||
|
func padXMLData(data []byte, padding []string) []byte {
|
||||||
|
// get right information from padding elements by matching to regex
|
||||||
|
re, _ := regexp.Compile("^<?(?:([^ ]+) )?([^>]+)>?$")
|
||||||
|
|
||||||
|
var prefix, suffix []byte
|
||||||
|
var tag, attr string
|
||||||
|
// go over each padding element
|
||||||
|
for i := len(padding); i > 0; i-- {
|
||||||
|
res := re.FindStringSubmatch(padding[i-1])
|
||||||
|
// no attribute was given
|
||||||
|
if len(res[1]) == 0 {
|
||||||
|
tag = res[2]
|
||||||
|
attr = ""
|
||||||
|
} else {
|
||||||
|
tag = res[1]
|
||||||
|
attr = " " + res[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix = []byte(fmt.Sprintf("<%s%s>", tag, attr))
|
||||||
|
suffix = []byte(fmt.Sprintf("</%s>", tag))
|
||||||
|
data = append(append(prefix, data...), suffix...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestParamsContainer is only useful for unit testing the ParamsContainer
|
||||||
|
// implementation of other type
|
||||||
|
type TestParamsContainer struct {
|
||||||
|
Prm string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add just makes sure we use Len(), key and value in the result so it can be
|
||||||
|
// tested
|
||||||
|
func (t *TestParamsContainer) Add(key string, value interface{}) {
|
||||||
|
var prefix string
|
||||||
|
if t.Len() > 0 {
|
||||||
|
prefix = "&"
|
||||||
|
}
|
||||||
|
t.Prm = t.Prm + prefix + fmt.Sprintf("%d%s=%s", t.Len(), key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns current length of test data in TestParamsContainer
|
||||||
|
func (t TestParamsContainer) Len() int {
|
||||||
|
return len(t.Prm)
|
||||||
|
}
|
37
vendor/github.com/transip/gotransip/util/util.go
generated
vendored
Normal file
37
vendor/github.com/transip/gotransip/util/util.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KeyValueXML resembles the complex struct for getting key/value pairs from XML
|
||||||
|
type KeyValueXML struct {
|
||||||
|
Cont []struct {
|
||||||
|
Item []struct {
|
||||||
|
Key string `xml:"key"`
|
||||||
|
Value string `xml:"value"`
|
||||||
|
} `xml:"item"`
|
||||||
|
} `xml:"item"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// XMLTime is a custom type to decode XML values to time.Time directly
|
||||||
|
type XMLTime struct {
|
||||||
|
time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalXML is implemented to be able act as custom XML type
|
||||||
|
// it tries to parse time from given elements value
|
||||||
|
func (x *XMLTime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
|
var v string
|
||||||
|
if err := d.DecodeElement(&v, &start); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if p, _ := time.Parse("2006-01-02 15:04:05", v); !p.IsZero() {
|
||||||
|
*x = XMLTime{p}
|
||||||
|
} else if p, _ := time.Parse("2006-01-02", v); !p.IsZero() {
|
||||||
|
*x = XMLTime{p}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
19
vendor/github.com/xenolf/lego/acme/client.go
generated
vendored
19
vendor/github.com/xenolf/lego/acme/client.go
generated
vendored
|
@ -665,7 +665,7 @@ func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, error)
|
||||||
|
|
||||||
go func(authzURL string) {
|
go func(authzURL string) {
|
||||||
var authz authorization
|
var authz authorization
|
||||||
_, err := getJSON(authzURL, &authz)
|
_, err := postAsGet(c.jws, authzURL, &authz)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errc <- domainError{Domain: authz.Identifier.Value, Error: err}
|
errc <- domainError{Domain: authz.Identifier.Value, Error: err}
|
||||||
return
|
return
|
||||||
|
@ -789,7 +789,7 @@ func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr
|
||||||
case <-stopTimer.C:
|
case <-stopTimer.C:
|
||||||
return nil, errors.New("certificate polling timed out")
|
return nil, errors.New("certificate polling timed out")
|
||||||
case <-retryTick.C:
|
case <-retryTick.C:
|
||||||
_, err := getJSON(order.URL, &retOrder)
|
_, err := postAsGet(c.jws, order.URL, &retOrder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -813,7 +813,7 @@ func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr
|
||||||
func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResource, bundle bool) (bool, error) {
|
func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResource, bundle bool) (bool, error) {
|
||||||
switch order.Status {
|
switch order.Status {
|
||||||
case statusValid:
|
case statusValid:
|
||||||
resp, err := httpGet(order.Certificate)
|
resp, err := postAsGet(c.jws, order.Certificate, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -871,7 +871,7 @@ func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResou
|
||||||
// getIssuerCertificate requests the issuer certificate
|
// getIssuerCertificate requests the issuer certificate
|
||||||
func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
|
func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
|
||||||
log.Infof("acme: Requesting issuer cert from %s", url)
|
log.Infof("acme: Requesting issuer cert from %s", url)
|
||||||
resp, err := httpGet(url)
|
resp, err := postAsGet(c.jws, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -914,7 +914,10 @@ func parseLinks(links []string) map[string]string {
|
||||||
func validate(j *jws, domain, uri string, c challenge) error {
|
func validate(j *jws, domain, uri string, c challenge) error {
|
||||||
var chlng challenge
|
var chlng challenge
|
||||||
|
|
||||||
hdr, err := postJSON(j, uri, c, &chlng)
|
// Challenge initiation is done by sending a JWS payload containing the
|
||||||
|
// trivial JSON object `{}`. We use an empty struct instance as the postJSON
|
||||||
|
// payload here to achieve this result.
|
||||||
|
hdr, err := postJSON(j, uri, struct{}{}, &chlng)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -940,11 +943,15 @@ func validate(j *jws, domain, uri string, c challenge) error {
|
||||||
// If it doesn't, we'll just poll hard.
|
// If it doesn't, we'll just poll hard.
|
||||||
ra = 5
|
ra = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(time.Duration(ra) * time.Second)
|
time.Sleep(time.Duration(ra) * time.Second)
|
||||||
|
|
||||||
hdr, err = getJSON(uri, &chlng)
|
resp, err := postAsGet(j, uri, &chlng)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if resp != nil {
|
||||||
|
hdr = resp.Header
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
52
vendor/github.com/xenolf/lego/acme/http.go
generated
vendored
52
vendor/github.com/xenolf/lego/acme/http.go
generated
vendored
|
@ -42,12 +42,14 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// defaultGoUserAgent is the Go HTTP package user agent string. Too
|
|
||||||
// bad it isn't exported. If it changes, we should update it here, too.
|
|
||||||
defaultGoUserAgent = "Go-http-client/1.1"
|
|
||||||
|
|
||||||
// ourUserAgent is the User-Agent of this underlying library package.
|
// ourUserAgent is the User-Agent of this underlying library package.
|
||||||
ourUserAgent = "xenolf-acme"
|
// NOTE: Update this with each tagged release.
|
||||||
|
ourUserAgent = "xenolf-acme/1.2.1"
|
||||||
|
|
||||||
|
// ourUserAgentComment is part of the UA comment linked to the version status of this underlying library package.
|
||||||
|
// values: detach|release
|
||||||
|
// NOTE: Update this with each tagged release.
|
||||||
|
ourUserAgentComment = "detach"
|
||||||
|
|
||||||
// caCertificatesEnvVar is the environment variable name that can be used to
|
// caCertificatesEnvVar is the environment variable name that can be used to
|
||||||
// specify the path to PEM encoded CA Certificates that can be used to
|
// specify the path to PEM encoded CA Certificates that can be used to
|
||||||
|
@ -151,52 +153,60 @@ func postJSON(j *jws, uri string, reqBody, respBody interface{}) (http.Header, e
|
||||||
return nil, errors.New("failed to marshal network message")
|
return nil, errors.New("failed to marshal network message")
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := j.post(uri, jsonBytes)
|
resp, err := post(j, uri, jsonBytes, respBody)
|
||||||
if err != nil {
|
if resp == nil {
|
||||||
return nil, fmt.Errorf("failed to post JWS message. -> %v", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
return resp.Header, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func postAsGet(j *jws, uri string, respBody interface{}) (*http.Response, error) {
|
||||||
|
return post(j, uri, []byte{}, respBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
func post(j *jws, uri string, reqBody []byte, respBody interface{}) (*http.Response, error) {
|
||||||
|
resp, err := j.post(uri, reqBody)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to post JWS message. -> %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if resp.StatusCode >= http.StatusBadRequest {
|
if resp.StatusCode >= http.StatusBadRequest {
|
||||||
err = handleHTTPError(resp)
|
err = handleHTTPError(resp)
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case NonceError:
|
case NonceError:
|
||||||
// Retry once if the nonce was invalidated
|
// Retry once if the nonce was invalidated
|
||||||
|
|
||||||
retryResp, errP := j.post(uri, jsonBytes)
|
retryResp, errP := j.post(uri, reqBody)
|
||||||
if errP != nil {
|
if errP != nil {
|
||||||
return nil, fmt.Errorf("failed to post JWS message. -> %v", errP)
|
return nil, fmt.Errorf("failed to post JWS message. -> %v", errP)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer retryResp.Body.Close()
|
|
||||||
|
|
||||||
if retryResp.StatusCode >= http.StatusBadRequest {
|
if retryResp.StatusCode >= http.StatusBadRequest {
|
||||||
return retryResp.Header, handleHTTPError(retryResp)
|
return retryResp, handleHTTPError(retryResp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if respBody == nil {
|
if respBody == nil {
|
||||||
return retryResp.Header, nil
|
return retryResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return retryResp.Header, json.NewDecoder(retryResp.Body).Decode(respBody)
|
return retryResp, json.NewDecoder(retryResp.Body).Decode(respBody)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return resp.Header, err
|
return resp, err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if respBody == nil {
|
if respBody == nil {
|
||||||
return resp.Header, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp.Header, json.NewDecoder(resp.Body).Decode(respBody)
|
return resp, json.NewDecoder(resp.Body).Decode(respBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
// userAgent builds and returns the User-Agent string to use in requests.
|
// userAgent builds and returns the User-Agent string to use in requests.
|
||||||
func userAgent() string {
|
func userAgent() string {
|
||||||
ua := fmt.Sprintf("%s %s (%s; %s) %s", UserAgent, ourUserAgent, runtime.GOOS, runtime.GOARCH, defaultGoUserAgent)
|
ua := fmt.Sprintf("%s %s (%s; %s; %s)", UserAgent, ourUserAgent, ourUserAgentComment, runtime.GOOS, runtime.GOARCH)
|
||||||
return strings.TrimSpace(ua)
|
return strings.TrimSpace(ua)
|
||||||
}
|
}
|
||||||
|
|
6
vendor/github.com/xenolf/lego/acme/tls_alpn_challenge.go
generated
vendored
6
vendor/github.com/xenolf/lego/acme/tls_alpn_challenge.go
generated
vendored
|
@ -12,8 +12,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// idPeAcmeIdentifierV1 is the SMI Security for PKIX Certification Extension OID referencing the ACME extension.
|
// idPeAcmeIdentifierV1 is the SMI Security for PKIX Certification Extension OID referencing the ACME extension.
|
||||||
// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-01#section-5.1
|
// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05#section-5.1
|
||||||
var idPeAcmeIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 30, 1}
|
var idPeAcmeIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31}
|
||||||
|
|
||||||
type tlsALPNChallenge struct {
|
type tlsALPNChallenge struct {
|
||||||
jws *jws
|
jws *jws
|
||||||
|
@ -58,7 +58,7 @@ func TLSALPNChallengeBlocks(domain, keyAuth string) ([]byte, []byte, error) {
|
||||||
|
|
||||||
// Add the keyAuth digest as the acmeValidation-v1 extension
|
// Add the keyAuth digest as the acmeValidation-v1 extension
|
||||||
// (marked as critical such that it won't be used by non-ACME software).
|
// (marked as critical such that it won't be used by non-ACME software).
|
||||||
// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-01#section-3
|
// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05#section-3
|
||||||
extensions := []pkix.Extension{
|
extensions := []pkix.Extension{
|
||||||
{
|
{
|
||||||
Id: idPeAcmeIdentifierV1,
|
Id: idPeAcmeIdentifierV1,
|
||||||
|
|
2
vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go
generated
vendored
2
vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go
generated
vendored
|
@ -126,7 +126,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
TTL: d.config.TTL,
|
TTL: d.config.TTL,
|
||||||
}
|
}
|
||||||
|
|
||||||
response, _ := d.client.CreateDNSRecord(zoneID, dnsRecord)
|
response, err := d.client.CreateDNSRecord(zoneID, dnsRecord)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cloudflare: failed to create TXT record: %v", err)
|
return fmt.Errorf("cloudflare: failed to create TXT record: %v", err)
|
||||||
}
|
}
|
||||||
|
|
205
vendor/github.com/xenolf/lego/providers/dns/conoha/client.go
generated
vendored
Normal file
205
vendor/github.com/xenolf/lego/providers/dns/conoha/client.go
generated
vendored
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
package conoha
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
identityBaseURL = "https://identity.%s.conoha.io"
|
||||||
|
dnsServiceBaseURL = "https://dns-service.%s.conoha.io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IdentityRequest is an authentication request body.
|
||||||
|
type IdentityRequest struct {
|
||||||
|
Auth Auth `json:"auth"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auth is an authentication information.
|
||||||
|
type Auth struct {
|
||||||
|
TenantID string `json:"tenantId"`
|
||||||
|
PasswordCredentials PasswordCredentials `json:"passwordCredentials"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PasswordCredentials is API-user's credentials.
|
||||||
|
type PasswordCredentials struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IdentityResponse is an authentication response body.
|
||||||
|
type IdentityResponse struct {
|
||||||
|
Access Access `json:"access"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access is an identity information.
|
||||||
|
type Access struct {
|
||||||
|
Token Token `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token is an api access token.
|
||||||
|
type Token struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainListResponse is a response of a domain listing request.
|
||||||
|
type DomainListResponse struct {
|
||||||
|
Domains []Domain `json:"domains"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Domain is a hosted domain entry.
|
||||||
|
type Domain struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecordListResponse is a response of record listing request.
|
||||||
|
type RecordListResponse struct {
|
||||||
|
Records []Record `json:"records"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record is a record entry.
|
||||||
|
type Record struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Data string `json:"data"`
|
||||||
|
TTL int `json:"ttl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client is a ConoHa API client.
|
||||||
|
type Client struct {
|
||||||
|
token string
|
||||||
|
endpoint string
|
||||||
|
httpClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns a client instance logged into the ConoHa service.
|
||||||
|
func NewClient(region string, auth Auth, httpClient *http.Client) (*Client, error) {
|
||||||
|
if httpClient == nil {
|
||||||
|
httpClient = &http.Client{}
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &Client{httpClient: httpClient}
|
||||||
|
|
||||||
|
c.endpoint = fmt.Sprintf(identityBaseURL, region)
|
||||||
|
|
||||||
|
identity, err := c.getIdentity(auth)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to login: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.token = identity.Access.Token.ID
|
||||||
|
c.endpoint = fmt.Sprintf(dnsServiceBaseURL, region)
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getIdentity(auth Auth) (*IdentityResponse, error) {
|
||||||
|
req := &IdentityRequest{Auth: auth}
|
||||||
|
|
||||||
|
identity := &IdentityResponse{}
|
||||||
|
|
||||||
|
err := c.do(http.MethodPost, "/v2.0/tokens", req, identity)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return identity, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDomainID returns an ID of specified domain.
|
||||||
|
func (c *Client) GetDomainID(domainName string) (string, error) {
|
||||||
|
domainList := &DomainListResponse{}
|
||||||
|
|
||||||
|
err := c.do(http.MethodGet, "/v1/domains", nil, domainList)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, domain := range domainList.Domains {
|
||||||
|
if domain.Name == domainName {
|
||||||
|
return domain.ID, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("no such domain: %s", domainName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRecordID returns an ID of specified record.
|
||||||
|
func (c *Client) GetRecordID(domainID, recordName, recordType, data string) (string, error) {
|
||||||
|
recordList := &RecordListResponse{}
|
||||||
|
|
||||||
|
err := c.do(http.MethodGet, fmt.Sprintf("/v1/domains/%s/records", domainID), nil, recordList)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, record := range recordList.Records {
|
||||||
|
if record.Name == recordName && record.Type == recordType && record.Data == data {
|
||||||
|
return record.ID, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", errors.New("no such record")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRecord adds new record.
|
||||||
|
func (c *Client) CreateRecord(domainID string, record Record) error {
|
||||||
|
return c.do(http.MethodPost, fmt.Sprintf("/v1/domains/%s/records", domainID), record, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRecord removes specified record.
|
||||||
|
func (c *Client) DeleteRecord(domainID, recordID string) error {
|
||||||
|
return c.do(http.MethodDelete, fmt.Sprintf("/v1/domains/%s/records/%s", domainID, recordID), nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) do(method, path string, payload, result interface{}) error {
|
||||||
|
body := bytes.NewReader(nil)
|
||||||
|
|
||||||
|
if payload != nil {
|
||||||
|
bodyBytes, err := json.Marshal(payload)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
body = bytes.NewReader(bodyBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(method, c.endpoint+path, body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Accept", "application/json")
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("X-Auth-Token", c.token)
|
||||||
|
|
||||||
|
resp, err := c.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
respBody, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
return fmt.Errorf("HTTP request failed with status code %d: %s", resp.StatusCode, string(respBody))
|
||||||
|
}
|
||||||
|
|
||||||
|
if result != nil {
|
||||||
|
respBody, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
return json.Unmarshal(respBody, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
148
vendor/github.com/xenolf/lego/providers/dns/conoha/conoha.go
generated
vendored
Normal file
148
vendor/github.com/xenolf/lego/providers/dns/conoha/conoha.go
generated
vendored
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
// Package conoha implements a DNS provider for solving the DNS-01 challenge
|
||||||
|
// using ConoHa DNS.
|
||||||
|
package conoha
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xenolf/lego/acme"
|
||||||
|
"github.com/xenolf/lego/platform/config/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config is used to configure the creation of the DNSProvider
|
||||||
|
type Config struct {
|
||||||
|
Region string
|
||||||
|
TenantID string
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
TTL int
|
||||||
|
PropagationTimeout time.Duration
|
||||||
|
PollingInterval time.Duration
|
||||||
|
HTTPClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultConfig returns a default configuration for the DNSProvider
|
||||||
|
func NewDefaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
Region: env.GetOrDefaultString("CONOHA_REGION", "tyo1"),
|
||||||
|
TTL: env.GetOrDefaultInt("CONOHA_TTL", 60),
|
||||||
|
PropagationTimeout: env.GetOrDefaultSecond("CONOHA_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout),
|
||||||
|
PollingInterval: env.GetOrDefaultSecond("CONOHA_POLLING_INTERVAL", acme.DefaultPollingInterval),
|
||||||
|
HTTPClient: &http.Client{
|
||||||
|
Timeout: env.GetOrDefaultSecond("CONOHA_HTTP_TIMEOUT", 30*time.Second),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||||
|
type DNSProvider struct {
|
||||||
|
config *Config
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProvider returns a DNSProvider instance configured for ConoHa DNS.
|
||||||
|
// Credentials must be passed in the environment variables: CONOHA_TENANT_ID, CONOHA_API_USERNAME, CONOHA_API_PASSWORD
|
||||||
|
func NewDNSProvider() (*DNSProvider, error) {
|
||||||
|
values, err := env.Get("CONOHA_TENANT_ID", "CONOHA_API_USERNAME", "CONOHA_API_PASSWORD")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("conoha: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := NewDefaultConfig()
|
||||||
|
config.TenantID = values["CONOHA_TENANT_ID"]
|
||||||
|
config.Username = values["CONOHA_API_USERNAME"]
|
||||||
|
config.Password = values["CONOHA_API_PASSWORD"]
|
||||||
|
|
||||||
|
return NewDNSProviderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProviderConfig return a DNSProvider instance configured for ConoHa DNS.
|
||||||
|
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("conoha: the configuration of the DNS provider is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.TenantID == "" || config.Username == "" || config.Password == "" {
|
||||||
|
return nil, errors.New("conoha: some credentials information are missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
auth := Auth{
|
||||||
|
TenantID: config.TenantID,
|
||||||
|
PasswordCredentials: PasswordCredentials{
|
||||||
|
Username: config.Username,
|
||||||
|
Password: config.Password,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := NewClient(config.Region, auth, config.HTTPClient)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("conoha: failed to create client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DNSProvider{config: config, client: client}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Present creates a TXT record to fulfill the dns-01 challenge.
|
||||||
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
|
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := d.client.GetDomainID(authZone)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("conoha: failed to get domain ID: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
record := Record{
|
||||||
|
Name: fqdn,
|
||||||
|
Type: "TXT",
|
||||||
|
Data: value,
|
||||||
|
TTL: d.config.TTL,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.client.CreateRecord(id, record)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("conoha: failed to create record: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanUp clears ConoHa DNS TXT record
|
||||||
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
|
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
domID, err := d.client.GetDomainID(authZone)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("conoha: failed to get domain ID: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
recID, err := d.client.GetRecordID(domID, fqdn, "TXT", value)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("conoha: failed to get record ID: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.client.DeleteRecord(domID, recID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("conoha: failed to delete record: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout returns the timeout and interval to use when checking for DNS propagation.
|
||||||
|
// Adjusting here to cope with spikes in propagation times.
|
||||||
|
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
|
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||||
|
}
|
21
vendor/github.com/xenolf/lego/providers/dns/dns_providers.go
generated
vendored
21
vendor/github.com/xenolf/lego/providers/dns/dns_providers.go
generated
vendored
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/xenolf/lego/providers/dns/bluecat"
|
"github.com/xenolf/lego/providers/dns/bluecat"
|
||||||
"github.com/xenolf/lego/providers/dns/cloudflare"
|
"github.com/xenolf/lego/providers/dns/cloudflare"
|
||||||
"github.com/xenolf/lego/providers/dns/cloudxns"
|
"github.com/xenolf/lego/providers/dns/cloudxns"
|
||||||
|
"github.com/xenolf/lego/providers/dns/conoha"
|
||||||
"github.com/xenolf/lego/providers/dns/digitalocean"
|
"github.com/xenolf/lego/providers/dns/digitalocean"
|
||||||
"github.com/xenolf/lego/providers/dns/dnsimple"
|
"github.com/xenolf/lego/providers/dns/dnsimple"
|
||||||
"github.com/xenolf/lego/providers/dns/dnsmadeeasy"
|
"github.com/xenolf/lego/providers/dns/dnsmadeeasy"
|
||||||
|
@ -27,10 +28,13 @@ import (
|
||||||
"github.com/xenolf/lego/providers/dns/glesys"
|
"github.com/xenolf/lego/providers/dns/glesys"
|
||||||
"github.com/xenolf/lego/providers/dns/godaddy"
|
"github.com/xenolf/lego/providers/dns/godaddy"
|
||||||
"github.com/xenolf/lego/providers/dns/hostingde"
|
"github.com/xenolf/lego/providers/dns/hostingde"
|
||||||
|
"github.com/xenolf/lego/providers/dns/httpreq"
|
||||||
"github.com/xenolf/lego/providers/dns/iij"
|
"github.com/xenolf/lego/providers/dns/iij"
|
||||||
|
"github.com/xenolf/lego/providers/dns/inwx"
|
||||||
"github.com/xenolf/lego/providers/dns/lightsail"
|
"github.com/xenolf/lego/providers/dns/lightsail"
|
||||||
"github.com/xenolf/lego/providers/dns/linode"
|
"github.com/xenolf/lego/providers/dns/linode"
|
||||||
"github.com/xenolf/lego/providers/dns/linodev4"
|
"github.com/xenolf/lego/providers/dns/linodev4"
|
||||||
|
"github.com/xenolf/lego/providers/dns/mydnsjp"
|
||||||
"github.com/xenolf/lego/providers/dns/namecheap"
|
"github.com/xenolf/lego/providers/dns/namecheap"
|
||||||
"github.com/xenolf/lego/providers/dns/namedotcom"
|
"github.com/xenolf/lego/providers/dns/namedotcom"
|
||||||
"github.com/xenolf/lego/providers/dns/netcup"
|
"github.com/xenolf/lego/providers/dns/netcup"
|
||||||
|
@ -43,8 +47,11 @@ import (
|
||||||
"github.com/xenolf/lego/providers/dns/rfc2136"
|
"github.com/xenolf/lego/providers/dns/rfc2136"
|
||||||
"github.com/xenolf/lego/providers/dns/route53"
|
"github.com/xenolf/lego/providers/dns/route53"
|
||||||
"github.com/xenolf/lego/providers/dns/sakuracloud"
|
"github.com/xenolf/lego/providers/dns/sakuracloud"
|
||||||
|
"github.com/xenolf/lego/providers/dns/selectel"
|
||||||
"github.com/xenolf/lego/providers/dns/stackpath"
|
"github.com/xenolf/lego/providers/dns/stackpath"
|
||||||
|
"github.com/xenolf/lego/providers/dns/transip"
|
||||||
"github.com/xenolf/lego/providers/dns/vegadns"
|
"github.com/xenolf/lego/providers/dns/vegadns"
|
||||||
|
"github.com/xenolf/lego/providers/dns/vscale"
|
||||||
"github.com/xenolf/lego/providers/dns/vultr"
|
"github.com/xenolf/lego/providers/dns/vultr"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -65,6 +72,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
|
||||||
return cloudflare.NewDNSProvider()
|
return cloudflare.NewDNSProvider()
|
||||||
case "cloudxns":
|
case "cloudxns":
|
||||||
return cloudxns.NewDNSProvider()
|
return cloudxns.NewDNSProvider()
|
||||||
|
case "conoha":
|
||||||
|
return conoha.NewDNSProvider()
|
||||||
case "digitalocean":
|
case "digitalocean":
|
||||||
return digitalocean.NewDNSProvider()
|
return digitalocean.NewDNSProvider()
|
||||||
case "dnsimple":
|
case "dnsimple":
|
||||||
|
@ -97,8 +106,12 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
|
||||||
return godaddy.NewDNSProvider()
|
return godaddy.NewDNSProvider()
|
||||||
case "hostingde":
|
case "hostingde":
|
||||||
return hostingde.NewDNSProvider()
|
return hostingde.NewDNSProvider()
|
||||||
|
case "httpreq":
|
||||||
|
return httpreq.NewDNSProvider()
|
||||||
case "iij":
|
case "iij":
|
||||||
return iij.NewDNSProvider()
|
return iij.NewDNSProvider()
|
||||||
|
case "inwx":
|
||||||
|
return inwx.NewDNSProvider()
|
||||||
case "lightsail":
|
case "lightsail":
|
||||||
return lightsail.NewDNSProvider()
|
return lightsail.NewDNSProvider()
|
||||||
case "linode":
|
case "linode":
|
||||||
|
@ -107,6 +120,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
|
||||||
return linodev4.NewDNSProvider()
|
return linodev4.NewDNSProvider()
|
||||||
case "manual":
|
case "manual":
|
||||||
return acme.NewDNSProviderManual()
|
return acme.NewDNSProviderManual()
|
||||||
|
case "mydnsjp":
|
||||||
|
return mydnsjp.NewDNSProvider()
|
||||||
case "namecheap":
|
case "namecheap":
|
||||||
return namecheap.NewDNSProvider()
|
return namecheap.NewDNSProvider()
|
||||||
case "namedotcom":
|
case "namedotcom":
|
||||||
|
@ -133,10 +148,16 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
|
||||||
return sakuracloud.NewDNSProvider()
|
return sakuracloud.NewDNSProvider()
|
||||||
case "stackpath":
|
case "stackpath":
|
||||||
return stackpath.NewDNSProvider()
|
return stackpath.NewDNSProvider()
|
||||||
|
case "selectel":
|
||||||
|
return selectel.NewDNSProvider()
|
||||||
|
case "transip":
|
||||||
|
return transip.NewDNSProvider()
|
||||||
case "vegadns":
|
case "vegadns":
|
||||||
return vegadns.NewDNSProvider()
|
return vegadns.NewDNSProvider()
|
||||||
case "vultr":
|
case "vultr":
|
||||||
return vultr.NewDNSProvider()
|
return vultr.NewDNSProvider()
|
||||||
|
case "vscale":
|
||||||
|
return vscale.NewDNSProvider()
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unrecognised DNS provider: %s", name)
|
return nil, fmt.Errorf("unrecognised DNS provider: %s", name)
|
||||||
}
|
}
|
||||||
|
|
193
vendor/github.com/xenolf/lego/providers/dns/httpreq/httpreq.go
generated
vendored
Normal file
193
vendor/github.com/xenolf/lego/providers/dns/httpreq/httpreq.go
generated
vendored
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
// Package httpreq implements a DNS provider for solving the DNS-01 challenge through a HTTP server.
|
||||||
|
package httpreq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xenolf/lego/acme"
|
||||||
|
"github.com/xenolf/lego/platform/config/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
type message struct {
|
||||||
|
FQDN string `json:"fqdn"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type messageRaw struct {
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
KeyAuth string `json:"keyAuth"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config is used to configure the creation of the DNSProvider
|
||||||
|
type Config struct {
|
||||||
|
Endpoint *url.URL
|
||||||
|
Mode string
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
PropagationTimeout time.Duration
|
||||||
|
PollingInterval time.Duration
|
||||||
|
HTTPClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultConfig returns a default configuration for the DNSProvider
|
||||||
|
func NewDefaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
PropagationTimeout: env.GetOrDefaultSecond("HTTPREQ_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout),
|
||||||
|
PollingInterval: env.GetOrDefaultSecond("HTTPREQ_POLLING_INTERVAL", acme.DefaultPollingInterval),
|
||||||
|
HTTPClient: &http.Client{
|
||||||
|
Timeout: env.GetOrDefaultSecond("HTTPREQ_HTTP_TIMEOUT", 30*time.Second),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSProvider describes a provider for acme-proxy
|
||||||
|
type DNSProvider struct {
|
||||||
|
config *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProvider returns a DNSProvider instance.
|
||||||
|
func NewDNSProvider() (*DNSProvider, error) {
|
||||||
|
values, err := env.Get("HTTPREQ_ENDPOINT")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("httpreq: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint, err := url.Parse(values["HTTPREQ_ENDPOINT"])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("httpreq: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := NewDefaultConfig()
|
||||||
|
config.Mode = os.Getenv("HTTPREQ_MODE")
|
||||||
|
config.Username = os.Getenv("HTTPREQ_USERNAME")
|
||||||
|
config.Password = os.Getenv("HTTPREQ_PASSWORD")
|
||||||
|
config.Endpoint = endpoint
|
||||||
|
return NewDNSProviderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProviderConfig return a DNSProvider .
|
||||||
|
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("httpreq: the configuration of the DNS provider is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Endpoint == nil {
|
||||||
|
return nil, errors.New("httpreq: the endpoint is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DNSProvider{config: config}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout returns the timeout and interval to use when checking for DNS propagation.
|
||||||
|
// Adjusting here to cope with spikes in propagation times.
|
||||||
|
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
|
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
// Present creates a TXT record to fulfill the dns-01 challenge
|
||||||
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
|
if d.config.Mode == "RAW" {
|
||||||
|
msg := &messageRaw{
|
||||||
|
Domain: domain,
|
||||||
|
Token: token,
|
||||||
|
KeyAuth: keyAuth,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := d.doPost("/present", msg)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("httpreq: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
msg := &message{
|
||||||
|
FQDN: fqdn,
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := d.doPost("/present", msg)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("httpreq: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanUp removes the TXT record matching the specified parameters
|
||||||
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
if d.config.Mode == "RAW" {
|
||||||
|
msg := &messageRaw{
|
||||||
|
Domain: domain,
|
||||||
|
Token: token,
|
||||||
|
KeyAuth: keyAuth,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := d.doPost("/cleanup", msg)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("httpreq: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
msg := &message{
|
||||||
|
FQDN: fqdn,
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := d.doPost("/cleanup", msg)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("httpreq: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) doPost(uri string, msg interface{}) error {
|
||||||
|
reqBody := &bytes.Buffer{}
|
||||||
|
err := json.NewEncoder(reqBody).Encode(msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint, err := d.config.Endpoint.Parse(uri)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodPost, endpoint.String(), reqBody)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
if len(d.config.Username) > 0 && len(d.config.Password) > 0 {
|
||||||
|
req.SetBasicAuth(d.config.Username, d.config.Password)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := d.config.HTTPClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode >= http.StatusBadRequest {
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%d: failed to read response body: %v", resp.StatusCode, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("%d: request failed: %v", resp.StatusCode, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
166
vendor/github.com/xenolf/lego/providers/dns/inwx/inwx.go
generated
vendored
Normal file
166
vendor/github.com/xenolf/lego/providers/dns/inwx/inwx.go
generated
vendored
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
// Package inwx implements a DNS provider for solving the DNS-01 challenge using inwx dom robot
|
||||||
|
package inwx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/smueller18/goinwx"
|
||||||
|
"github.com/xenolf/lego/acme"
|
||||||
|
"github.com/xenolf/lego/log"
|
||||||
|
"github.com/xenolf/lego/platform/config/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config is used to configure the creation of the DNSProvider
|
||||||
|
type Config struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
Sandbox bool
|
||||||
|
PropagationTimeout time.Duration
|
||||||
|
PollingInterval time.Duration
|
||||||
|
TTL int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultConfig returns a default configuration for the DNSProvider
|
||||||
|
func NewDefaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
PropagationTimeout: env.GetOrDefaultSecond("INWX_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout),
|
||||||
|
PollingInterval: env.GetOrDefaultSecond("INWX_POLLING_INTERVAL", acme.DefaultPollingInterval),
|
||||||
|
TTL: env.GetOrDefaultInt("INWX_TTL", 300),
|
||||||
|
Sandbox: env.GetOrDefaultBool("INWX_SANDBOX", false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||||
|
type DNSProvider struct {
|
||||||
|
config *Config
|
||||||
|
client *goinwx.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProvider returns a DNSProvider instance configured for Dyn DNS.
|
||||||
|
// Credentials must be passed in the environment variables:
|
||||||
|
// INWX_USERNAME and INWX_PASSWORD.
|
||||||
|
func NewDNSProvider() (*DNSProvider, error) {
|
||||||
|
values, err := env.Get("INWX_USERNAME", "INWX_PASSWORD")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("inwx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := NewDefaultConfig()
|
||||||
|
config.Username = values["INWX_USERNAME"]
|
||||||
|
config.Password = values["INWX_PASSWORD"]
|
||||||
|
|
||||||
|
return NewDNSProviderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProviderConfig return a DNSProvider instance configured for Dyn DNS
|
||||||
|
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("inwx: the configuration of the DNS provider is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Username == "" || config.Password == "" {
|
||||||
|
return nil, fmt.Errorf("inwx: credentials missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Sandbox {
|
||||||
|
log.Infof("inwx: sandbox mode is enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
client := goinwx.NewClient(config.Username, config.Password, &goinwx.ClientOptions{Sandbox: config.Sandbox})
|
||||||
|
|
||||||
|
return &DNSProvider{config: config, client: client}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Present creates a TXT record using the specified parameters
|
||||||
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
|
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("inwx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.client.Account.Login()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("inwx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
errL := d.client.Account.Logout()
|
||||||
|
if errL != nil {
|
||||||
|
log.Infof("inwx: failed to logout: %v", errL)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var request = &goinwx.NameserverRecordRequest{
|
||||||
|
Domain: acme.UnFqdn(authZone),
|
||||||
|
Name: acme.UnFqdn(fqdn),
|
||||||
|
Type: "TXT",
|
||||||
|
Content: value,
|
||||||
|
Ttl: d.config.TTL,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = d.client.Nameservers.CreateRecord(request)
|
||||||
|
if err != nil {
|
||||||
|
switch err.(type) {
|
||||||
|
case *goinwx.ErrorResponse:
|
||||||
|
if err.(*goinwx.ErrorResponse).Message == "Object exists" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("inwx: %v", err)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("inwx: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanUp removes the TXT record matching the specified parameters
|
||||||
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
|
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("inwx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.client.Account.Login()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("inwx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
errL := d.client.Account.Logout()
|
||||||
|
if errL != nil {
|
||||||
|
log.Infof("inwx: failed to logout: %v", errL)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
response, err := d.client.Nameservers.Info(&goinwx.NameserverInfoRequest{
|
||||||
|
Domain: acme.UnFqdn(authZone),
|
||||||
|
Name: acme.UnFqdn(fqdn),
|
||||||
|
Type: "TXT",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("inwx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastErr error
|
||||||
|
for _, record := range response.Records {
|
||||||
|
err = d.client.Nameservers.DeleteRecord(record.Id)
|
||||||
|
if err != nil {
|
||||||
|
lastErr = fmt.Errorf("inwx: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout returns the timeout and interval to use when checking for DNS propagation.
|
||||||
|
// Adjusting here to cope with spikes in propagation times.
|
||||||
|
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
|
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||||
|
}
|
140
vendor/github.com/xenolf/lego/providers/dns/mydnsjp/mydnsjp.go
generated
vendored
Normal file
140
vendor/github.com/xenolf/lego/providers/dns/mydnsjp/mydnsjp.go
generated
vendored
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
// Package mydnsjp implements a DNS provider for solving the DNS-01
|
||||||
|
// challenge using MyDNS.jp.
|
||||||
|
package mydnsjp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xenolf/lego/acme"
|
||||||
|
"github.com/xenolf/lego/platform/config/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultBaseURL = "https://www.mydns.jp/directedit.html"
|
||||||
|
|
||||||
|
// Config is used to configure the creation of the DNSProvider
|
||||||
|
type Config struct {
|
||||||
|
MasterID string
|
||||||
|
Password string
|
||||||
|
PropagationTimeout time.Duration
|
||||||
|
PollingInterval time.Duration
|
||||||
|
HTTPClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultConfig returns a default configuration for the DNSProvider
|
||||||
|
func NewDefaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
PropagationTimeout: env.GetOrDefaultSecond("MYDNSJP_PROPAGATION_TIMEOUT", 2*time.Minute),
|
||||||
|
PollingInterval: env.GetOrDefaultSecond("MYDNSJP_POLLING_INTERVAL", 2*time.Second),
|
||||||
|
HTTPClient: &http.Client{
|
||||||
|
Timeout: env.GetOrDefaultSecond("MYDNSJP_HTTP_TIMEOUT", 30*time.Second),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||||
|
type DNSProvider struct {
|
||||||
|
config *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProvider returns a DNSProvider instance configured for MyDNS.jp.
|
||||||
|
// Credentials must be passed in the environment variables: MYDNSJP_MASTER_ID and MYDNSJP_PASSWORD.
|
||||||
|
func NewDNSProvider() (*DNSProvider, error) {
|
||||||
|
values, err := env.Get("MYDNSJP_MASTER_ID", "MYDNSJP_PASSWORD")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("mydnsjp: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := NewDefaultConfig()
|
||||||
|
config.MasterID = values["MYDNSJP_MASTER_ID"]
|
||||||
|
config.Password = values["MYDNSJP_PASSWORD"]
|
||||||
|
|
||||||
|
return NewDNSProviderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProviderConfig return a DNSProvider instance configured for MyDNS.jp.
|
||||||
|
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("mydnsjp: the configuration of the DNS provider is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.MasterID == "" || config.Password == "" {
|
||||||
|
return nil, errors.New("mydnsjp: some credentials information are missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DNSProvider{config: config}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout returns the timeout and interval to use when checking for DNS propagation.
|
||||||
|
// Adjusting here to cope with spikes in propagation times.
|
||||||
|
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
|
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
// Present creates a TXT record to fulfill the dns-01 challenge
|
||||||
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
|
_, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
err := d.doRequest(domain, value, "REGIST")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("mydnsjp: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanUp removes the TXT record matching the specified parameters
|
||||||
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
_, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
err := d.doRequest(domain, value, "DELETE")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("mydnsjp: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) doRequest(domain, value string, cmd string) error {
|
||||||
|
req, err := d.buildRequest(domain, value, cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := d.config.HTTPClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error querying API: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode >= 400 {
|
||||||
|
var content []byte
|
||||||
|
content, err = ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("request %s failed [status code %d]: %s", req.URL, resp.StatusCode, string(content))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) buildRequest(domain, value string, cmd string) (*http.Request, error) {
|
||||||
|
params := url.Values{}
|
||||||
|
params.Set("CERTBOT_DOMAIN", domain)
|
||||||
|
params.Set("CERTBOT_VALIDATION", value)
|
||||||
|
params.Set("EDIT_CMD", cmd)
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodPost, defaultBaseURL, strings.NewReader(params.Encode()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
req.SetBasicAuth(d.config.MasterID, d.config.Password)
|
||||||
|
|
||||||
|
return req, nil
|
||||||
|
}
|
211
vendor/github.com/xenolf/lego/providers/dns/selectel/client.go
generated
vendored
Normal file
211
vendor/github.com/xenolf/lego/providers/dns/selectel/client.go
generated
vendored
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
package selectel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Domain represents domain name.
|
||||||
|
type Domain struct {
|
||||||
|
ID int `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record represents DNS record.
|
||||||
|
type Record struct {
|
||||||
|
ID int `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"` // Record type (SOA, NS, A/AAAA, CNAME, SRV, MX, TXT, SPF)
|
||||||
|
TTL int `json:"ttl,omitempty"`
|
||||||
|
Email string `json:"email,omitempty"` // Email of domain's admin (only for SOA records)
|
||||||
|
Content string `json:"content,omitempty"` // Record content (not for SRV)
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIError API error message
|
||||||
|
type APIError struct {
|
||||||
|
Description string `json:"error"`
|
||||||
|
Code int `json:"code"`
|
||||||
|
Field string `json:"field"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *APIError) Error() string {
|
||||||
|
return fmt.Sprintf("API error: %d - %s - %s", a.Code, a.Description, a.Field)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientOpts represents options to init client.
|
||||||
|
type ClientOpts struct {
|
||||||
|
BaseURL string
|
||||||
|
Token string
|
||||||
|
UserAgent string
|
||||||
|
HTTPClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client represents DNS client.
|
||||||
|
type Client struct {
|
||||||
|
baseURL string
|
||||||
|
token string
|
||||||
|
userAgent string
|
||||||
|
httpClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns a client instance.
|
||||||
|
func NewClient(opts ClientOpts) *Client {
|
||||||
|
if opts.HTTPClient == nil {
|
||||||
|
opts.HTTPClient = &http.Client{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Client{
|
||||||
|
token: opts.Token,
|
||||||
|
baseURL: opts.BaseURL,
|
||||||
|
httpClient: opts.HTTPClient,
|
||||||
|
userAgent: opts.UserAgent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDomainByName gets Domain object by its name.
|
||||||
|
func (c *Client) GetDomainByName(domainName string) (*Domain, error) {
|
||||||
|
uri := fmt.Sprintf("/%s", domainName)
|
||||||
|
req, err := c.newRequest(http.MethodGet, uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
domain := &Domain{}
|
||||||
|
_, err = c.do(req, domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return domain, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRecord adds Record for given domain.
|
||||||
|
func (c *Client) AddRecord(domainID int, body Record) (*Record, error) {
|
||||||
|
uri := fmt.Sprintf("/%d/records/", domainID)
|
||||||
|
req, err := c.newRequest(http.MethodPost, uri, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
record := &Record{}
|
||||||
|
_, err = c.do(req, record)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return record, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRecords returns list records for specific domain.
|
||||||
|
func (c *Client) ListRecords(domainID int) ([]*Record, error) {
|
||||||
|
uri := fmt.Sprintf("/%d/records/", domainID)
|
||||||
|
req, err := c.newRequest(http.MethodGet, uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var records []*Record
|
||||||
|
_, err = c.do(req, &records)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return records, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRecord deletes specific record.
|
||||||
|
func (c *Client) DeleteRecord(domainID, recordID int) error {
|
||||||
|
uri := fmt.Sprintf("/%d/records/%d", domainID, recordID)
|
||||||
|
req, err := c.newRequest(http.MethodDelete, uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = c.do(req, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) newRequest(method, uri string, body interface{}) (*http.Request, error) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
if body != nil {
|
||||||
|
err := json.NewEncoder(buf).Encode(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to encode request body with error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(method, c.baseURL+uri, buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create new http request with error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("X-Token", c.token)
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
req.Header.Add("Accept", "application/json")
|
||||||
|
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) do(req *http.Request, to interface{}) (*http.Response, error) {
|
||||||
|
resp, err := c.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("request failed with error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = checkResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if to != nil {
|
||||||
|
if err = unmarshalBody(resp, to); err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkResponse(resp *http.Response) error {
|
||||||
|
if resp.StatusCode >= http.StatusBadRequest &&
|
||||||
|
resp.StatusCode <= http.StatusNetworkAuthenticationRequired {
|
||||||
|
|
||||||
|
if resp.Body == nil {
|
||||||
|
return fmt.Errorf("request failed with status code %d and empty body", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
apiError := APIError{}
|
||||||
|
err = json.Unmarshal(body, &apiError)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("request failed with status code %d, response body: %s", resp.StatusCode, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("request failed with status code %d: %v", resp.StatusCode, apiError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalBody(resp *http.Response, to interface{}) error {
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, to)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unmarshaling error: %v: %s", err, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
153
vendor/github.com/xenolf/lego/providers/dns/selectel/selectel.go
generated
vendored
Normal file
153
vendor/github.com/xenolf/lego/providers/dns/selectel/selectel.go
generated
vendored
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
// Package selectel implements a DNS provider for solving the DNS-01 challenge using Selectel Domains API.
|
||||||
|
// Selectel Domain API reference: https://kb.selectel.com/23136054.html
|
||||||
|
// Token: https://my.selectel.ru/profile/apikeys
|
||||||
|
package selectel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xenolf/lego/acme"
|
||||||
|
"github.com/xenolf/lego/platform/config/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultBaseURL = "https://api.selectel.ru/domains/v1"
|
||||||
|
minTTL = 60
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
envNamespace = "SELECTEL_"
|
||||||
|
baseURLEnvVar = envNamespace + "BASE_URL"
|
||||||
|
apiTokenEnvVar = envNamespace + "API_TOKEN"
|
||||||
|
ttlEnvVar = envNamespace + "TTL"
|
||||||
|
propagationTimeoutEnvVar = envNamespace + "PROPAGATION_TIMEOUT"
|
||||||
|
pollingIntervalEnvVar = envNamespace + "POLLING_INTERVAL"
|
||||||
|
httpTimeoutEnvVar = envNamespace + "HTTP_TIMEOUT"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config is used to configure the creation of the DNSProvider.
|
||||||
|
type Config struct {
|
||||||
|
BaseURL string
|
||||||
|
Token string
|
||||||
|
PropagationTimeout time.Duration
|
||||||
|
PollingInterval time.Duration
|
||||||
|
TTL int
|
||||||
|
HTTPClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultConfig returns a default configuration for the DNSProvider.
|
||||||
|
func NewDefaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
BaseURL: env.GetOrDefaultString(baseURLEnvVar, defaultBaseURL),
|
||||||
|
TTL: env.GetOrDefaultInt(ttlEnvVar, minTTL),
|
||||||
|
PropagationTimeout: env.GetOrDefaultSecond(propagationTimeoutEnvVar, 120*time.Second),
|
||||||
|
PollingInterval: env.GetOrDefaultSecond(pollingIntervalEnvVar, 2*time.Second),
|
||||||
|
HTTPClient: &http.Client{
|
||||||
|
Timeout: env.GetOrDefaultSecond(httpTimeoutEnvVar, 30*time.Second),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSProvider is an implementation of the acme.ChallengeProvider interface.
|
||||||
|
type DNSProvider struct {
|
||||||
|
config *Config
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProvider returns a DNSProvider instance configured for Selectel Domains API.
|
||||||
|
// API token must be passed in the environment variable SELECTEL_API_TOKEN.
|
||||||
|
func NewDNSProvider() (*DNSProvider, error) {
|
||||||
|
values, err := env.Get(apiTokenEnvVar)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("selectel: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := NewDefaultConfig()
|
||||||
|
config.Token = values[apiTokenEnvVar]
|
||||||
|
|
||||||
|
return NewDNSProviderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProviderConfig return a DNSProvider instance configured for selectel.
|
||||||
|
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("selectel: the configuration of the DNS provider is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Token == "" {
|
||||||
|
return nil, errors.New("selectel: credentials missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.TTL < minTTL {
|
||||||
|
return nil, fmt.Errorf("selectel: invalid TTL, TTL (%d) must be greater than %d", config.TTL, minTTL)
|
||||||
|
}
|
||||||
|
|
||||||
|
client := NewClient(ClientOpts{
|
||||||
|
BaseURL: config.BaseURL,
|
||||||
|
Token: config.Token,
|
||||||
|
UserAgent: acme.UserAgent,
|
||||||
|
HTTPClient: config.HTTPClient,
|
||||||
|
})
|
||||||
|
|
||||||
|
return &DNSProvider{config: config, client: client}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout returns the Timeout and interval to use when checking for DNS propagation.
|
||||||
|
// Adjusting here to cope with spikes in propagation times.
|
||||||
|
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
|
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
// Present creates a TXT record to fulfill DNS-01 challenge.
|
||||||
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
|
domainObj, err := d.client.GetDomainByName(domain)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("selectel: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
txtRecord := Record{
|
||||||
|
Type: "TXT",
|
||||||
|
TTL: d.config.TTL,
|
||||||
|
Name: fqdn,
|
||||||
|
Content: value,
|
||||||
|
}
|
||||||
|
_, err = d.client.AddRecord(domainObj.ID, txtRecord)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("selectel: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanUp removes a TXT record used for DNS-01 challenge.
|
||||||
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
|
domainObj, err := d.client.GetDomainByName(domain)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("selectel: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
records, err := d.client.ListRecords(domainObj.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("selectel: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete records with specific FQDN
|
||||||
|
var lastErr error
|
||||||
|
for _, record := range records {
|
||||||
|
if record.Name == fqdn {
|
||||||
|
err = d.client.DeleteRecord(domainObj.ID, record.ID)
|
||||||
|
if err != nil {
|
||||||
|
lastErr = fmt.Errorf("selectel: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastErr
|
||||||
|
}
|
150
vendor/github.com/xenolf/lego/providers/dns/transip/transip.go
generated
vendored
Normal file
150
vendor/github.com/xenolf/lego/providers/dns/transip/transip.go
generated
vendored
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
// Package transip implements a DNS provider for solving the DNS-01 challenge using TransIP.
|
||||||
|
package transip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/transip/gotransip"
|
||||||
|
transipdomain "github.com/transip/gotransip/domain"
|
||||||
|
"github.com/xenolf/lego/acme"
|
||||||
|
"github.com/xenolf/lego/platform/config/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config is used to configure the creation of the DNSProvider
|
||||||
|
type Config struct {
|
||||||
|
AccountName string
|
||||||
|
PrivateKeyPath string
|
||||||
|
PropagationTimeout time.Duration
|
||||||
|
PollingInterval time.Duration
|
||||||
|
TTL int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultConfig returns a default configuration for the DNSProvider
|
||||||
|
func NewDefaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
TTL: int64(env.GetOrDefaultInt("TRANSIP_TTL", 10)),
|
||||||
|
PropagationTimeout: env.GetOrDefaultSecond("TRANSIP_PROPAGATION_TIMEOUT", 10*time.Minute),
|
||||||
|
PollingInterval: env.GetOrDefaultSecond("TRANSIP_POLLING_INTERVAL", 10*time.Second),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSProvider describes a provider for TransIP
|
||||||
|
type DNSProvider struct {
|
||||||
|
config *Config
|
||||||
|
client gotransip.SOAPClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProvider returns a DNSProvider instance configured for TransIP.
|
||||||
|
// Credentials must be passed in the environment variables:
|
||||||
|
// TRANSIP_ACCOUNTNAME, TRANSIP_PRIVATEKEYPATH.
|
||||||
|
func NewDNSProvider() (*DNSProvider, error) {
|
||||||
|
values, err := env.Get("TRANSIP_ACCOUNT_NAME", "TRANSIP_PRIVATE_KEY_PATH")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("transip: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := NewDefaultConfig()
|
||||||
|
config.AccountName = values["TRANSIP_ACCOUNT_NAME"]
|
||||||
|
config.PrivateKeyPath = values["TRANSIP_PRIVATE_KEY_PATH"]
|
||||||
|
|
||||||
|
return NewDNSProviderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProviderConfig return a DNSProvider instance configured for TransIP.
|
||||||
|
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("transip: the configuration of the DNS provider is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := gotransip.NewSOAPClient(gotransip.ClientConfig{
|
||||||
|
AccountName: config.AccountName,
|
||||||
|
PrivateKeyPath: config.PrivateKeyPath,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("transip: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DNSProvider{client: client, config: config}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout returns the timeout and interval to use when checking for DNS propagation.
|
||||||
|
// Adjusting here to cope with spikes in propagation times.
|
||||||
|
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
|
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
// Present creates a TXT record to fulfill the dns-01 challenge
|
||||||
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
|
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
domainName := acme.UnFqdn(authZone)
|
||||||
|
|
||||||
|
// get the subDomain
|
||||||
|
subDomain := strings.TrimSuffix(acme.UnFqdn(fqdn), "."+domainName)
|
||||||
|
|
||||||
|
// get all DNS entries
|
||||||
|
info, err := transipdomain.GetInfo(d.client, domainName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("transip: error for %s in Present: %v", domain, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// include the new DNS entry
|
||||||
|
dnsEntries := append(info.DNSEntries, transipdomain.DNSEntry{
|
||||||
|
Name: subDomain,
|
||||||
|
TTL: d.config.TTL,
|
||||||
|
Type: transipdomain.DNSEntryTypeTXT,
|
||||||
|
Content: value,
|
||||||
|
})
|
||||||
|
|
||||||
|
// set the updated DNS entries
|
||||||
|
err = transipdomain.SetDNSEntries(d.client, domainName, dnsEntries)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("transip: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanUp removes the TXT record matching the specified parameters
|
||||||
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
|
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
domainName := acme.UnFqdn(authZone)
|
||||||
|
|
||||||
|
// get the subDomain
|
||||||
|
subDomain := strings.TrimSuffix(acme.UnFqdn(fqdn), "."+domainName)
|
||||||
|
|
||||||
|
// get all DNS entries
|
||||||
|
info, err := transipdomain.GetInfo(d.client, domainName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("transip: error for %s in CleanUp: %v", fqdn, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// loop through the existing entries and remove the specific record
|
||||||
|
updatedEntries := info.DNSEntries[:0]
|
||||||
|
for _, e := range info.DNSEntries {
|
||||||
|
if e.Name != subDomain {
|
||||||
|
updatedEntries = append(updatedEntries, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the updated DNS entries
|
||||||
|
err = transipdomain.SetDNSEntries(d.client, domainName, updatedEntries)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("transip: couldn't get Record ID in CleanUp: %sv", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
211
vendor/github.com/xenolf/lego/providers/dns/vscale/client.go
generated
vendored
Normal file
211
vendor/github.com/xenolf/lego/providers/dns/vscale/client.go
generated
vendored
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
package vscale
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Domain represents domain name.
|
||||||
|
type Domain struct {
|
||||||
|
ID int `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record represents DNS record.
|
||||||
|
type Record struct {
|
||||||
|
ID int `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"` // Record type (SOA, NS, A/AAAA, CNAME, SRV, MX, TXT, SPF)
|
||||||
|
TTL int `json:"ttl,omitempty"`
|
||||||
|
Email string `json:"email,omitempty"` // Email of domain's admin (only for SOA records)
|
||||||
|
Content string `json:"content,omitempty"` // Record content (not for SRV)
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIError API error message
|
||||||
|
type APIError struct {
|
||||||
|
Description string `json:"error"`
|
||||||
|
Code int `json:"code"`
|
||||||
|
Field string `json:"field"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *APIError) Error() string {
|
||||||
|
return fmt.Sprintf("API error: %d - %s - %s", a.Code, a.Description, a.Field)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientOpts represents options to init client.
|
||||||
|
type ClientOpts struct {
|
||||||
|
BaseURL string
|
||||||
|
Token string
|
||||||
|
UserAgent string
|
||||||
|
HTTPClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client represents DNS client.
|
||||||
|
type Client struct {
|
||||||
|
baseURL string
|
||||||
|
token string
|
||||||
|
userAgent string
|
||||||
|
httpClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns a client instance.
|
||||||
|
func NewClient(opts ClientOpts) *Client {
|
||||||
|
if opts.HTTPClient == nil {
|
||||||
|
opts.HTTPClient = &http.Client{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Client{
|
||||||
|
token: opts.Token,
|
||||||
|
baseURL: opts.BaseURL,
|
||||||
|
httpClient: opts.HTTPClient,
|
||||||
|
userAgent: opts.UserAgent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDomainByName gets Domain object by its name.
|
||||||
|
func (c *Client) GetDomainByName(domainName string) (*Domain, error) {
|
||||||
|
uri := fmt.Sprintf("/%s", domainName)
|
||||||
|
req, err := c.newRequest(http.MethodGet, uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
domain := &Domain{}
|
||||||
|
_, err = c.do(req, domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return domain, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRecord adds Record for given domain.
|
||||||
|
func (c *Client) AddRecord(domainID int, body Record) (*Record, error) {
|
||||||
|
uri := fmt.Sprintf("/%d/records/", domainID)
|
||||||
|
req, err := c.newRequest(http.MethodPost, uri, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
record := &Record{}
|
||||||
|
_, err = c.do(req, record)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return record, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRecords returns list records for specific domain.
|
||||||
|
func (c *Client) ListRecords(domainID int) ([]*Record, error) {
|
||||||
|
uri := fmt.Sprintf("/%d/records/", domainID)
|
||||||
|
req, err := c.newRequest(http.MethodGet, uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var records []*Record
|
||||||
|
_, err = c.do(req, &records)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return records, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRecord deletes specific record.
|
||||||
|
func (c *Client) DeleteRecord(domainID, recordID int) error {
|
||||||
|
uri := fmt.Sprintf("/%d/records/%d", domainID, recordID)
|
||||||
|
req, err := c.newRequest(http.MethodDelete, uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = c.do(req, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) newRequest(method, uri string, body interface{}) (*http.Request, error) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
if body != nil {
|
||||||
|
err := json.NewEncoder(buf).Encode(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to encode request body with error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(method, c.baseURL+uri, buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create new http request with error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("X-Token", c.token)
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
req.Header.Add("Accept", "application/json")
|
||||||
|
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) do(req *http.Request, to interface{}) (*http.Response, error) {
|
||||||
|
resp, err := c.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("request failed with error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = checkResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if to != nil {
|
||||||
|
if err = unmarshalBody(resp, to); err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkResponse(resp *http.Response) error {
|
||||||
|
if resp.StatusCode >= http.StatusBadRequest &&
|
||||||
|
resp.StatusCode <= http.StatusNetworkAuthenticationRequired {
|
||||||
|
|
||||||
|
if resp.Body == nil {
|
||||||
|
return fmt.Errorf("request failed with status code %d and empty body", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
apiError := APIError{}
|
||||||
|
err = json.Unmarshal(body, &apiError)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("request failed with status code %d, response body: %s", resp.StatusCode, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("request failed with status code %d: %v", resp.StatusCode, apiError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalBody(resp *http.Response, to interface{}) error {
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, to)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unmarshaling error: %v: %s", err, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
153
vendor/github.com/xenolf/lego/providers/dns/vscale/vscale.go
generated
vendored
Normal file
153
vendor/github.com/xenolf/lego/providers/dns/vscale/vscale.go
generated
vendored
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
// Package selectel implements a DNS provider for solving the DNS-01 challenge using Vscale Domains API.
|
||||||
|
// Vscale Domain API reference: https://developers.vscale.io/documentation/api/v1/#api-Domains
|
||||||
|
// Token: https://vscale.io/panel/settings/tokens/
|
||||||
|
package vscale
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xenolf/lego/acme"
|
||||||
|
"github.com/xenolf/lego/platform/config/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultBaseURL = "https://api.vscale.io/v1/domains"
|
||||||
|
minTTL = 60
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
envNamespace = "VSCALE_"
|
||||||
|
baseURLEnvVar = envNamespace + "BASE_URL"
|
||||||
|
apiTokenEnvVar = envNamespace + "API_TOKEN"
|
||||||
|
ttlEnvVar = envNamespace + "TTL"
|
||||||
|
propagationTimeoutEnvVar = envNamespace + "PROPAGATION_TIMEOUT"
|
||||||
|
pollingIntervalEnvVar = envNamespace + "POLLING_INTERVAL"
|
||||||
|
httpTimeoutEnvVar = envNamespace + "HTTP_TIMEOUT"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config is used to configure the creation of the DNSProvider.
|
||||||
|
type Config struct {
|
||||||
|
BaseURL string
|
||||||
|
Token string
|
||||||
|
PropagationTimeout time.Duration
|
||||||
|
PollingInterval time.Duration
|
||||||
|
TTL int
|
||||||
|
HTTPClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultConfig returns a default configuration for the DNSProvider.
|
||||||
|
func NewDefaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
BaseURL: env.GetOrDefaultString(baseURLEnvVar, defaultBaseURL),
|
||||||
|
TTL: env.GetOrDefaultInt(ttlEnvVar, minTTL),
|
||||||
|
PropagationTimeout: env.GetOrDefaultSecond(propagationTimeoutEnvVar, 120*time.Second),
|
||||||
|
PollingInterval: env.GetOrDefaultSecond(pollingIntervalEnvVar, 2*time.Second),
|
||||||
|
HTTPClient: &http.Client{
|
||||||
|
Timeout: env.GetOrDefaultSecond(httpTimeoutEnvVar, 30*time.Second),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSProvider is an implementation of the acme.ChallengeProvider interface.
|
||||||
|
type DNSProvider struct {
|
||||||
|
config *Config
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProvider returns a DNSProvider instance configured for Vscale Domains API.
|
||||||
|
// API token must be passed in the environment variable VSCALE_API_TOKEN.
|
||||||
|
func NewDNSProvider() (*DNSProvider, error) {
|
||||||
|
values, err := env.Get(apiTokenEnvVar)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("vscale: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := NewDefaultConfig()
|
||||||
|
config.Token = values[apiTokenEnvVar]
|
||||||
|
|
||||||
|
return NewDNSProviderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProviderConfig return a DNSProvider instance configured for Vscale.
|
||||||
|
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("vscale: the configuration of the DNS provider is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Token == "" {
|
||||||
|
return nil, errors.New("vscale: credentials missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.TTL < minTTL {
|
||||||
|
return nil, fmt.Errorf("vscale: invalid TTL, TTL (%d) must be greater than %d", config.TTL, minTTL)
|
||||||
|
}
|
||||||
|
|
||||||
|
client := NewClient(ClientOpts{
|
||||||
|
BaseURL: config.BaseURL,
|
||||||
|
Token: config.Token,
|
||||||
|
UserAgent: acme.UserAgent,
|
||||||
|
HTTPClient: config.HTTPClient,
|
||||||
|
})
|
||||||
|
|
||||||
|
return &DNSProvider{config: config, client: client}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout returns the Timeout and interval to use when checking for DNS propagation.
|
||||||
|
// Adjusting here to cope with spikes in propagation times.
|
||||||
|
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
|
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
// Present creates a TXT record to fulfill DNS-01 challenge.
|
||||||
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
|
domainObj, err := d.client.GetDomainByName(domain)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("vscale: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
txtRecord := Record{
|
||||||
|
Type: "TXT",
|
||||||
|
TTL: d.config.TTL,
|
||||||
|
Name: fqdn,
|
||||||
|
Content: value,
|
||||||
|
}
|
||||||
|
_, err = d.client.AddRecord(domainObj.ID, txtRecord)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("vscale: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanUp removes a TXT record used for DNS-01 challenge.
|
||||||
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
|
domainObj, err := d.client.GetDomainByName(domain)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("vscale: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
records, err := d.client.ListRecords(domainObj.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("vscale: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete records with specific FQDN
|
||||||
|
var lastErr error
|
||||||
|
for _, record := range records {
|
||||||
|
if record.Name == fqdn {
|
||||||
|
err = d.client.DeleteRecord(domainObj.ID, record.ID)
|
||||||
|
if err != nil {
|
||||||
|
lastErr = fmt.Errorf("vscale: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastErr
|
||||||
|
}
|
3
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ddtrace.go
generated
vendored
3
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ddtrace.go
generated
vendored
|
@ -88,6 +88,9 @@ type FinishConfig struct {
|
||||||
// Error holds an optional error that should be set on the span before
|
// Error holds an optional error that should be set on the span before
|
||||||
// finishing.
|
// finishing.
|
||||||
Error error
|
Error error
|
||||||
|
|
||||||
|
// NoDebugStack will prevent any set errors from generating an attached stack trace tag.
|
||||||
|
NoDebugStack bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartSpanConfig holds the configuration for starting a new span. It is usually passed
|
// StartSpanConfig holds the configuration for starting a new span. It is usually passed
|
||||||
|
|
55
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/app_types.go
generated
vendored
55
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/app_types.go
generated
vendored
|
@ -1,19 +1,70 @@
|
||||||
package ext // import "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
|
package ext // import "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
|
||||||
|
|
||||||
|
// App types determine how to categorize a trace in the Datadog application.
|
||||||
|
// For more fine-grained behaviour, use the SpanType* constants.
|
||||||
const (
|
const (
|
||||||
|
// DEPRECATED: Use SpanTypeWeb
|
||||||
// AppTypeWeb specifies the Web span type and can be used as a tag value
|
// AppTypeWeb specifies the Web span type and can be used as a tag value
|
||||||
// for a span's SpanType tag.
|
// for a span's SpanType tag.
|
||||||
AppTypeWeb = "web"
|
AppTypeWeb = "web"
|
||||||
|
|
||||||
// AppTypeDB specifies the DB span type and can be used as a tag value
|
// AppTypeDB specifies the DB span type and can be used as a tag value
|
||||||
// for a span's SpanType tag.
|
// for a span's SpanType tag. If possible, use one of the SpanType*
|
||||||
|
// constants for a more accurate indication.
|
||||||
AppTypeDB = "db"
|
AppTypeDB = "db"
|
||||||
|
|
||||||
// AppTypeCache specifies the Cache span type and can be used as a tag value
|
// AppTypeCache specifies the Cache span type and can be used as a tag value
|
||||||
// for a span's SpanType tag.
|
// for a span's SpanType tag. If possible, consider using SpanTypeRedis or
|
||||||
|
// SpanTypeMemcached.
|
||||||
AppTypeCache = "cache"
|
AppTypeCache = "cache"
|
||||||
|
|
||||||
// AppTypeRPC specifies the RPC span type and can be used as a tag value
|
// AppTypeRPC specifies the RPC span type and can be used as a tag value
|
||||||
// for a span's SpanType tag.
|
// for a span's SpanType tag.
|
||||||
AppTypeRPC = "rpc"
|
AppTypeRPC = "rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Span types have similar behaviour to "app types" and help categorize
|
||||||
|
// traces in the Datadog application. They can also help fine grain agent
|
||||||
|
// level bahviours such as obfuscation and quantization, when these are
|
||||||
|
// enabled in the agent's configuration.
|
||||||
|
const (
|
||||||
|
// SpanTypeWeb marks a span as an HTTP server request.
|
||||||
|
SpanTypeWeb = "web"
|
||||||
|
|
||||||
|
// SpanTypeHTTP marks a span as an HTTP client request.
|
||||||
|
SpanTypeHTTP = "http"
|
||||||
|
|
||||||
|
// SpanTypeSQL marks a span as an SQL operation. These spans may
|
||||||
|
// have an "sql.command" tag.
|
||||||
|
SpanTypeSQL = "sql"
|
||||||
|
|
||||||
|
// SpanTypeCassandra marks a span as a Cassandra operation. These
|
||||||
|
// spans may have an "sql.command" tag.
|
||||||
|
SpanTypeCassandra = "cassandra"
|
||||||
|
|
||||||
|
// SpanTypeRedis marks a span as a Redis operation. These spans may
|
||||||
|
// also have a "redis.raw_command" tag.
|
||||||
|
SpanTypeRedis = "redis"
|
||||||
|
|
||||||
|
// SpanTypeMemcached marks a span as a memcached operation.
|
||||||
|
SpanTypeMemcached = "memcached"
|
||||||
|
|
||||||
|
// SpanTypeMongoDB marks a span as a MongoDB operation.
|
||||||
|
SpanTypeMongoDB = "mongodb"
|
||||||
|
|
||||||
|
// SpanTypeElasticSearch marks a span as an ElasticSearch operation.
|
||||||
|
// These spans may also have an "elasticsearch.body" tag.
|
||||||
|
SpanTypeElasticSearch = "elasticsearch"
|
||||||
|
|
||||||
|
// SpanTypeLevelDB marks a span as a leveldb operation
|
||||||
|
SpanTypeLevelDB = "leveldb"
|
||||||
|
|
||||||
|
// SpanTypeDNS marks a span as a DNS operation.
|
||||||
|
SpanTypeDNS = "dns"
|
||||||
|
|
||||||
|
// SpanTypeMessageConsumer marks a span as a queue operation
|
||||||
|
SpanTypeMessageConsumer = "queue"
|
||||||
|
|
||||||
|
// SpanTypeMessageProducer marks a span as a queue operation.
|
||||||
|
SpanTypeMessageProducer = "queue"
|
||||||
|
)
|
||||||
|
|
16
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/db.go
generated
vendored
Normal file
16
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/db.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package ext
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DBApplication indicates the application using the database.
|
||||||
|
DBApplication = "db.application"
|
||||||
|
// DBName indicates the database name.
|
||||||
|
DBName = "db.name"
|
||||||
|
// DBType indicates the type of Database.
|
||||||
|
DBType = "db.type"
|
||||||
|
// DBInstance indicates the instance name of Database.
|
||||||
|
DBInstance = "db.instance"
|
||||||
|
// DBUser indicates the user name of Database, e.g. "readonly_user" or "reporting_user".
|
||||||
|
DBUser = "db.user"
|
||||||
|
// DBStatement records a database statement for the given database type.
|
||||||
|
DBStatement = "db.statement"
|
||||||
|
)
|
14
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/peer.go
generated
vendored
Normal file
14
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/peer.go
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package ext
|
||||||
|
|
||||||
|
const (
|
||||||
|
// PeerHostIPV4 records IPv4 host address of the peer.
|
||||||
|
PeerHostIPV4 = "peer.ipv4"
|
||||||
|
// PeerHostIPV6 records the IPv6 host address of the peer.
|
||||||
|
PeerHostIPV6 = "peer.ipv6"
|
||||||
|
// PeerService records the service name of the peer service.
|
||||||
|
PeerService = "peer.service"
|
||||||
|
// PeerHostname records the host name of the peer.
|
||||||
|
PeerHostname = "peer.hostname"
|
||||||
|
// PeerPort records the port number of the peer.
|
||||||
|
PeerPort = "peer.port"
|
||||||
|
)
|
7
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/tags.go
generated
vendored
7
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/tags.go
generated
vendored
|
@ -27,6 +27,10 @@ const (
|
||||||
// HTTPURL sets the HTTP URL for a span.
|
// HTTPURL sets the HTTP URL for a span.
|
||||||
HTTPURL = "http.url"
|
HTTPURL = "http.url"
|
||||||
|
|
||||||
|
// TODO: In the next major version, suffix these constants (SpanType, etc)
|
||||||
|
// with "*Key" (SpanTypeKey, etc) to more easily differentiate between
|
||||||
|
// constants representing tag values and constants representing keys.
|
||||||
|
|
||||||
// SpanType defines the Span type (web, db, cache).
|
// SpanType defines the Span type (web, db, cache).
|
||||||
SpanType = "span.type"
|
SpanType = "span.type"
|
||||||
|
|
||||||
|
@ -47,4 +51,7 @@ const (
|
||||||
|
|
||||||
// ErrorStack specifies the stack dump.
|
// ErrorStack specifies the stack dump.
|
||||||
ErrorStack = "error.stack"
|
ErrorStack = "error.stack"
|
||||||
|
|
||||||
|
// Environment specifies the environment to use with a trace.
|
||||||
|
Environment = "env"
|
||||||
)
|
)
|
||||||
|
|
4
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal/globaltracer.go
generated
vendored
4
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal/globaltracer.go
generated
vendored
|
@ -15,6 +15,10 @@ var (
|
||||||
func SetGlobalTracer(t ddtrace.Tracer) {
|
func SetGlobalTracer(t ddtrace.Tracer) {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
defer mu.Unlock()
|
defer mu.Unlock()
|
||||||
|
if !Testing {
|
||||||
|
// avoid infinite loop when calling (*mocktracer.Tracer).Stop
|
||||||
|
globalTracer.Stop()
|
||||||
|
}
|
||||||
globalTracer = t
|
globalTracer = t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
13
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/option.go
generated
vendored
13
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/option.go
generated
vendored
|
@ -154,10 +154,19 @@ func FinishTime(t time.Time) FinishOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithError adds the given error to the span before marking it as finished. If it is
|
// WithError marks the span as having had an error. It uses the information from
|
||||||
// nil it will be disregarded.
|
// err to set tags such as the error message, error type and stack trace.
|
||||||
func WithError(err error) FinishOption {
|
func WithError(err error) FinishOption {
|
||||||
return func(cfg *ddtrace.FinishConfig) {
|
return func(cfg *ddtrace.FinishConfig) {
|
||||||
cfg.Error = err
|
cfg.Error = err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NoDebugStack prevents any error presented using the WithError finishing option
|
||||||
|
// from generating a stack trace. This is useful in situations where errors are frequent
|
||||||
|
// and performance is critical.
|
||||||
|
func NoDebugStack() FinishOption {
|
||||||
|
return func(cfg *ddtrace.FinishConfig) {
|
||||||
|
cfg.NoDebugStack = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
15
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/span.go
generated
vendored
15
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/span.go
generated
vendored
|
@ -81,7 +81,7 @@ func (s *span) SetTag(key string, value interface{}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if key == ext.Error {
|
if key == ext.Error {
|
||||||
s.setTagError(value)
|
s.setTagError(value, true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if v, ok := value.(string); ok {
|
if v, ok := value.(string); ok {
|
||||||
|
@ -99,7 +99,10 @@ func (s *span) SetTag(key string, value interface{}) {
|
||||||
|
|
||||||
// setTagError sets the error tag. It accounts for various valid scenarios.
|
// setTagError sets the error tag. It accounts for various valid scenarios.
|
||||||
// This method is not safe for concurrent use.
|
// This method is not safe for concurrent use.
|
||||||
func (s *span) setTagError(value interface{}) {
|
func (s *span) setTagError(value interface{}, debugStack bool) {
|
||||||
|
if s.finished {
|
||||||
|
return
|
||||||
|
}
|
||||||
switch v := value.(type) {
|
switch v := value.(type) {
|
||||||
case bool:
|
case bool:
|
||||||
// bool value as per Opentracing spec.
|
// bool value as per Opentracing spec.
|
||||||
|
@ -114,7 +117,9 @@ func (s *span) setTagError(value interface{}) {
|
||||||
s.Error = 1
|
s.Error = 1
|
||||||
s.Meta[ext.ErrorMsg] = v.Error()
|
s.Meta[ext.ErrorMsg] = v.Error()
|
||||||
s.Meta[ext.ErrorType] = reflect.TypeOf(v).String()
|
s.Meta[ext.ErrorType] = reflect.TypeOf(v).String()
|
||||||
s.Meta[ext.ErrorStack] = string(debug.Stack())
|
if debugStack {
|
||||||
|
s.Meta[ext.ErrorStack] = string(debug.Stack())
|
||||||
|
}
|
||||||
case nil:
|
case nil:
|
||||||
// no error
|
// no error
|
||||||
s.Error = 0
|
s.Error = 0
|
||||||
|
@ -166,7 +171,9 @@ func (s *span) Finish(opts ...ddtrace.FinishOption) {
|
||||||
t = cfg.FinishTime.UnixNano()
|
t = cfg.FinishTime.UnixNano()
|
||||||
}
|
}
|
||||||
if cfg.Error != nil {
|
if cfg.Error != nil {
|
||||||
s.SetTag(ext.Error, cfg.Error)
|
s.Lock()
|
||||||
|
s.setTagError(cfg.Error, !cfg.NoDebugStack)
|
||||||
|
s.Unlock()
|
||||||
}
|
}
|
||||||
s.finish(t)
|
s.finish(t)
|
||||||
}
|
}
|
||||||
|
|
4
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/span_msgp.go
generated
vendored
4
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/span_msgp.go
generated
vendored
|
@ -63,7 +63,7 @@ func (z *span) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||||
if z.Meta == nil && zb0002 > 0 {
|
if z.Meta == nil && zb0002 > 0 {
|
||||||
z.Meta = make(map[string]string, zb0002)
|
z.Meta = make(map[string]string, zb0002)
|
||||||
} else if len(z.Meta) > 0 {
|
} else if len(z.Meta) > 0 {
|
||||||
for key, _ := range z.Meta {
|
for key := range z.Meta {
|
||||||
delete(z.Meta, key)
|
delete(z.Meta, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ func (z *span) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||||
if z.Metrics == nil && zb0003 > 0 {
|
if z.Metrics == nil && zb0003 > 0 {
|
||||||
z.Metrics = make(map[string]float64, zb0003)
|
z.Metrics = make(map[string]float64, zb0003)
|
||||||
} else if len(z.Metrics) > 0 {
|
} else if len(z.Metrics) > 0 {
|
||||||
for key, _ := range z.Metrics {
|
for key := range z.Metrics {
|
||||||
delete(z.Metrics, key)
|
delete(z.Metrics, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
7
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/textmap.go
generated
vendored
7
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/textmap.go
generated
vendored
|
@ -17,8 +17,7 @@ var _ TextMapReader = (*HTTPHeadersCarrier)(nil)
|
||||||
|
|
||||||
// Set implements TextMapWriter.
|
// Set implements TextMapWriter.
|
||||||
func (c HTTPHeadersCarrier) Set(key, val string) {
|
func (c HTTPHeadersCarrier) Set(key, val string) {
|
||||||
h := http.Header(c)
|
http.Header(c).Set(key, val)
|
||||||
h.Add(key, val)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForeachKey implements TextMapReader.
|
// ForeachKey implements TextMapReader.
|
||||||
|
@ -166,12 +165,12 @@ func (p *propagator) extractTextMap(reader TextMapReader) (ddtrace.SpanContext,
|
||||||
key := strings.ToLower(k)
|
key := strings.ToLower(k)
|
||||||
switch key {
|
switch key {
|
||||||
case p.cfg.TraceHeader:
|
case p.cfg.TraceHeader:
|
||||||
ctx.traceID, err = strconv.ParseUint(v, 10, 64)
|
ctx.traceID, err = parseUint64(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrSpanContextCorrupted
|
return ErrSpanContextCorrupted
|
||||||
}
|
}
|
||||||
case p.cfg.ParentHeader:
|
case p.cfg.ParentHeader:
|
||||||
ctx.spanID, err = strconv.ParseUint(v, 10, 64)
|
ctx.spanID, err = parseUint64(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrSpanContextCorrupted
|
return ErrSpanContextCorrupted
|
||||||
}
|
}
|
||||||
|
|
12
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/tracer.go
generated
vendored
12
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/tracer.go
generated
vendored
|
@ -64,9 +64,7 @@ func Start(opts ...StartOption) {
|
||||||
if internal.Testing {
|
if internal.Testing {
|
||||||
return // mock tracer active
|
return // mock tracer active
|
||||||
}
|
}
|
||||||
t := internal.GetGlobalTracer()
|
|
||||||
internal.SetGlobalTracer(newTracer(opts...))
|
internal.SetGlobalTracer(newTracer(opts...))
|
||||||
t.Stop()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop stops the started tracer. Subsequent calls are valid but become no-op.
|
// Stop stops the started tracer. Subsequent calls are valid but become no-op.
|
||||||
|
@ -302,16 +300,10 @@ func (t *tracer) flushTraces() {
|
||||||
log.Printf("Sending payload: size: %d traces: %d\n", size, count)
|
log.Printf("Sending payload: size: %d traces: %d\n", size, count)
|
||||||
}
|
}
|
||||||
err := t.config.transport.send(t.payload)
|
err := t.config.transport.send(t.payload)
|
||||||
if err != nil && size > payloadMaxLimit {
|
if err != nil {
|
||||||
// we couldn't send the payload and it is getting too big to be
|
|
||||||
// accepted by the agent, we have to drop it.
|
|
||||||
t.payload.reset()
|
|
||||||
t.pushError(&dataLossError{context: err, count: count})
|
t.pushError(&dataLossError{context: err, count: count})
|
||||||
}
|
}
|
||||||
if err == nil {
|
t.payload.reset()
|
||||||
// send succeeded
|
|
||||||
t.payload.reset()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// flushErrors will process log messages that were queued
|
// flushErrors will process log messages that were queued
|
||||||
|
|
31
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/transport.go
generated
vendored
31
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/transport.go
generated
vendored
|
@ -10,7 +10,9 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var tracerVersion = "v1.0"
|
// TODO(gbbr): find a more effective way to keep this up to date,
|
||||||
|
// e.g. via `go generate`
|
||||||
|
var tracerVersion = "v1.5.0"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultHostname = "localhost"
|
defaultHostname = "localhost"
|
||||||
|
@ -57,16 +59,8 @@ func newHTTPTransport(addr string) *httpTransport {
|
||||||
"Datadog-Meta-Tracer-Version": tracerVersion,
|
"Datadog-Meta-Tracer-Version": tracerVersion,
|
||||||
"Content-Type": "application/msgpack",
|
"Content-Type": "application/msgpack",
|
||||||
}
|
}
|
||||||
host, port, _ := net.SplitHostPort(addr)
|
|
||||||
if host == "" {
|
|
||||||
host = defaultHostname
|
|
||||||
}
|
|
||||||
if port == "" {
|
|
||||||
port = defaultPort
|
|
||||||
}
|
|
||||||
addr = fmt.Sprintf("%s:%s", host, port)
|
|
||||||
return &httpTransport{
|
return &httpTransport{
|
||||||
traceURL: fmt.Sprintf("http://%s/v0.3/traces", addr),
|
traceURL: fmt.Sprintf("http://%s/v0.3/traces", resolveAddr(addr)),
|
||||||
client: &http.Client{
|
client: &http.Client{
|
||||||
// We copy the transport to avoid using the default one, as it might be
|
// We copy the transport to avoid using the default one, as it might be
|
||||||
// augmented with tracing and we don't want these calls to be recorded.
|
// augmented with tracing and we don't want these calls to be recorded.
|
||||||
|
@ -118,3 +112,20 @@ func (t *httpTransport) send(p *payload) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolveAddr resolves the given agent address and fills in any missing host
|
||||||
|
// and port using the defaults.
|
||||||
|
func resolveAddr(addr string) string {
|
||||||
|
host, port, err := net.SplitHostPort(addr)
|
||||||
|
if err != nil {
|
||||||
|
// no port in addr
|
||||||
|
host = addr
|
||||||
|
}
|
||||||
|
if host == "" {
|
||||||
|
host = defaultHostname
|
||||||
|
}
|
||||||
|
if port == "" {
|
||||||
|
port = defaultPort
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s:%s", host, port)
|
||||||
|
}
|
||||||
|
|
18
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/util.go
generated
vendored
18
vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/util.go
generated
vendored
|
@ -1,5 +1,10 @@
|
||||||
package tracer
|
package tracer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
// toFloat64 attempts to convert value into a float64. If it succeeds it returns
|
// toFloat64 attempts to convert value into a float64. If it succeeds it returns
|
||||||
// the value and true, otherwise 0 and false.
|
// the value and true, otherwise 0 and false.
|
||||||
func toFloat64(value interface{}) (f float64, ok bool) {
|
func toFloat64(value interface{}) (f float64, ok bool) {
|
||||||
|
@ -30,3 +35,16 @@ func toFloat64(value interface{}) (f float64, ok bool) {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseUint64 parses a uint64 from either an unsigned 64 bit base-10 string
|
||||||
|
// or a signed 64 bit base-10 string representing an unsigned integer
|
||||||
|
func parseUint64(str string) (uint64, error) {
|
||||||
|
if strings.HasPrefix(str, "-") {
|
||||||
|
id, err := strconv.ParseInt(str, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return uint64(id), nil
|
||||||
|
}
|
||||||
|
return strconv.ParseUint(str, 10, 64)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue