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 = ["."]
|
||||
revision = "62e9147c64a1ed519147b62a56a14e83e2be02c1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/fatih/structs"
|
||||
packages = ["."]
|
||||
revision = "4966fc68f5b7593aafa6cbbba2d65ec6e1416047"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/flynn/go-shlex"
|
||||
|
@ -812,6 +818,12 @@
|
|||
revision = "59fac5042749a5afb9af70e813da1dd5474f0167"
|
||||
version = "1.0.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/kolo/xmlrpc"
|
||||
packages = ["."]
|
||||
revision = "16bdd962781df9696f40cc2bab924f1a855a7f89"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/konsorten/go-windows-terminal-sequences"
|
||||
|
@ -956,10 +968,10 @@
|
|||
revision = "2bca23e0e452137f789efbc8610126fd8b94f73b"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/mitchellh/mapstructure"
|
||||
packages = ["."]
|
||||
revision = "b4575eea38cca1123ec2dc90c26529b5c5acfcff"
|
||||
revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe"
|
||||
version = "v1.1.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -1167,6 +1179,12 @@
|
|||
revision = "a67f783a3814b8729bd2dac5780b5f78f8dbd64d"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/smueller18/goinwx"
|
||||
packages = ["."]
|
||||
revision = "5d138389109eca96463f44f692408f0d1c731278"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/pflag"
|
||||
packages = ["."]
|
||||
|
@ -1214,6 +1232,16 @@
|
|||
revision = "b2b6a672cf1e5b90748f79b8b81fc8c5cf0571a1"
|
||||
version = "1.0.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/transip/gotransip"
|
||||
packages = [
|
||||
".",
|
||||
"domain",
|
||||
"util"
|
||||
]
|
||||
revision = "1dc93a7db3567a5ccf865106afac88278ba940cf"
|
||||
version = "v5.8.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/tuvistavie/securerandom"
|
||||
|
@ -1330,6 +1358,7 @@
|
|||
"providers/dns/bluecat",
|
||||
"providers/dns/cloudflare",
|
||||
"providers/dns/cloudxns",
|
||||
"providers/dns/conoha",
|
||||
"providers/dns/digitalocean",
|
||||
"providers/dns/dnsimple",
|
||||
"providers/dns/dnsmadeeasy",
|
||||
|
@ -1346,10 +1375,13 @@
|
|||
"providers/dns/glesys",
|
||||
"providers/dns/godaddy",
|
||||
"providers/dns/hostingde",
|
||||
"providers/dns/httpreq",
|
||||
"providers/dns/iij",
|
||||
"providers/dns/inwx",
|
||||
"providers/dns/lightsail",
|
||||
"providers/dns/linode",
|
||||
"providers/dns/linodev4",
|
||||
"providers/dns/mydnsjp",
|
||||
"providers/dns/namecheap",
|
||||
"providers/dns/namedotcom",
|
||||
"providers/dns/netcup",
|
||||
|
@ -1362,11 +1394,14 @@
|
|||
"providers/dns/rfc2136",
|
||||
"providers/dns/route53",
|
||||
"providers/dns/sakuracloud",
|
||||
"providers/dns/selectel",
|
||||
"providers/dns/stackpath",
|
||||
"providers/dns/transip",
|
||||
"providers/dns/vegadns",
|
||||
"providers/dns/vscale",
|
||||
"providers/dns/vultr"
|
||||
]
|
||||
revision = "1151b4e3befc51b7b215179c87791753721dc6d5"
|
||||
revision = "a5f0a3ff8026e05cbdd11c391c0e25122497c736"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -1531,8 +1566,8 @@
|
|||
"ddtrace/opentracer",
|
||||
"ddtrace/tracer"
|
||||
]
|
||||
revision = "d052956664af54dbcff2712d10c67c76fbfc299f"
|
||||
version = "v1.0.0"
|
||||
revision = "48eeff27357376bcb31a15674dc4be9078de88b3"
|
||||
version = "v1.5.0"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/fsnotify.v1"
|
||||
|
@ -1791,6 +1826,6 @@
|
|||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "d4f73c986b64003e14a36894149943e956e0dfa40b8837bfd11bf5fa3ad78c77"
|
||||
inputs-digest = "817d5ad6a6ae085d57953a73f7b615a223fa3e70b6152bf87382508bfbfbd655"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
@ -267,4 +267,4 @@
|
|||
|
||||
[[constraint]]
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
[![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`.
|
||||
|
||||
You can check all fields in the [godoc](https://godoc.org/github.com/containous/traefik/configuration#GlobalConfiguration).
|
||||
|
||||
### How to enable this ?
|
||||
|
||||
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 |
|
||||
|--------------------------------------------------------|----------------|-------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------|
|
||||
| [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 |
|
||||
| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | Not tested yet |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]` | Not tested yet |
|
||||
| [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | Not tested yet |
|
||||
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CF_API_EMAIL`, `CF_API_KEY` - The `Global API Key` needs to be used, not the `Origin CA Key` | YES |
|
||||
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | Not tested yet |
|
||||
| [ConoHa](https://www.conoha.jp) | `conoha` | `CONOHA_TENANT_ID`, `CONOHA_API_USERNAME`, `CONOHA_API_PASSWORD` | YES |
|
||||
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | YES |
|
||||
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | 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 |
|
||||
| [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 |
|
||||
| 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 |
|
||||
| [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 |
|
||||
|
@ -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 |
|
||||
| [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 |
|
||||
| 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 |
|
||||
| [INWX](https://www.inwx.de/en) | `inwx` | `INWX_USERNAME`, `INWX_PASSWORD` | YES |
|
||||
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | Not tested yet |
|
||||
| [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` | Not tested yet |
|
||||
| [Linode v4](https://www.linode.com) | `linodev4` | `LINODE_TOKEN` | Not tested yet |
|
||||
| manual | - | none, but you need to run Traefik interactively, turn on `acmeLogging` to see instructions and press <kbd>Enter</kbd>. | YES |
|
||||
| [MyDNS.jp](https://www.mydns.jp/) | `mydnsjp` | `MYDNSJP_MASTER_ID`, `MYDNSJP_PASSWORD` | YES |
|
||||
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | YES |
|
||||
| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | Not tested yet |
|
||||
| [Netcup](https://www.netcup.eu/) | `netcup` | `NETCUP_CUSTOMER_NUMBER`, `NETCUP_API_KEY`, `NETCUP_API_PASSWORD` | Not tested yet |
|
||||
|
@ -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 |
|
||||
| [Route 53](https://aws.amazon.com/route53/) | `route53` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `[AWS_REGION]`, `[AWS_HOSTED_ZONE_ID]` or a configured user/instance IAM profile. | YES |
|
||||
| [Sakura Cloud](https://cloud.sakura.ad.jp/) | `sakuracloud` | `SAKURACLOUD_ACCESS_TOKEN`, `SAKURACLOUD_ACCESS_TOKEN_SECRET` | Not tested yet |
|
||||
| [Selectel](https://selectel.ru/en/) | `selectel` | `SELECTEL_API_TOKEN` | YES |
|
||||
| [Stackpath](https://www.stackpath.com/) | `stackpath` | `STACKPATH_CLIENT_ID`, `STACKPATH_CLIENT_SECRET`, `STACKPATH_STACK_ID` | Not tested yet |
|
||||
| [TransIP](https://www.transip.nl/) | `transip` | `TRANSIP_ACCOUNT_NAME`, `TRANSIP_PRIVATE_KEY_PATH` | YES |
|
||||
| [VegaDNS](https://github.com/shupp/VegaDNS-API) | `vegadns` | `SECRET_VEGADNS_KEY`, `SECRET_VEGADNS_SECRET`, `VEGADNS_URL` | Not tested yet |
|
||||
| [Vscale](https://vscale.io/) | `vscale` | `VSCALE_API_TOKEN` | YES |
|
||||
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | Not tested yet |
|
||||
|
||||
#### `resolvers`
|
||||
|
|
|
@ -106,10 +106,10 @@ entryPoint = "foo"
|
|||
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`
|
||||
* Admin Panel: `http://hostname:8083/`
|
||||
* Dashboard: `http://hostname:8083/`
|
||||
* 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`.
|
||||
|
|
|
@ -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.stickiness=true` | Enables backend 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.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.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.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. |
|
||||
|
@ -287,8 +287,8 @@ Labels can be used on containers to override default behavior.
|
|||
| `traefik.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) |
|
||||
| `traefik.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | See [whitelist](/configuration/entrypoints/#white-listing) |
|
||||
|
||||
[1] `traefik.docker.network`:
|
||||
If a container is linked to several networks, be sure to set the proper network name (you can check with `docker inspect <container_id>`) otherwise it will randomly pick one (depending on how docker is returning them).
|
||||
[1] `traefik.docker.network`:
|
||||
If a container is linked to several networks, be sure to set the proper network name (you can check with `docker inspect <container_id>`) otherwise it will randomly pick one (depending on how docker is returning them).
|
||||
For instance when deploying docker `stack` from compose files, the compose defined networks will be prefixed with the `stack` name.
|
||||
Or if your service references external network use it's name instead.
|
||||
|
||||
|
@ -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`.
|
||||
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
|
||||
|
||||
| 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.
|
||||
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).
|
||||
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.
|
||||
|
||||
|
@ -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-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) |
|
||||
| `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`:
|
||||
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:
|
||||
|
||||
* Regular path: `http://hostname:80/foo`
|
||||
* Admin panel: `http://hostname:8080/`
|
||||
* Dashboard: `http://hostname:8080/`
|
||||
* 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.
|
||||
|
||||
You have two options:
|
||||
|
@ -40,7 +40,7 @@ You have two options:
|
|||
|
||||
### 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
|
||||
defaultEntryPoints = ["http"]
|
||||
|
|
|
@ -176,7 +176,7 @@ Our recommendation would be to see for yourself how simple it is to enable HTTPS
|
|||
|
||||
## 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.
|
||||
|
||||
[![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 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.
|
||||
|
||||
```yaml
|
||||
|
|
|
@ -13,6 +13,6 @@ services:
|
|||
|
||||
# A container that exposes a simple API
|
||||
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:
|
||||
- "traefik.frontend.rule=Host:whoami.docker.localhost"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
pebble:
|
||||
image: letsencrypt/pebble:2018-07-27
|
||||
image: letsencrypt/pebble:2018-11-02
|
||||
command: pebble --dnsserver ${DOCKER_HOST_IP}:5053
|
||||
ports:
|
||||
- 14000:14000
|
||||
|
|
|
@ -56,7 +56,7 @@ func getLinesFromFile(filename string) ([]string, error) {
|
|||
var filteredLines []string
|
||||
for _, rawLine := range rawLines {
|
||||
line := strings.TrimSpace(rawLine)
|
||||
if line != "" {
|
||||
if line != "" && !strings.HasPrefix(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",
|
||||
givenUsers: []string{"test2:$apr1$mK.GtItK$ncnLYvNLek0weXdxo68690"},
|
||||
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 {
|
||||
if test.desc != "Should skip comments" {
|
||||
continue
|
||||
}
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
|
|
@ -80,10 +80,10 @@ func TestDigestAuthUsersFromFile(t *testing.T) {
|
|||
},
|
||||
{
|
||||
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{},
|
||||
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)
|
||||
if configuration != nil {
|
||||
configurationChan <- types.ConfigMessage{
|
||||
message := types.ConfigMessage{
|
||||
ProviderName: "docker",
|
||||
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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"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
|
||||
// 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
|
||||
}
|
||||
|
||||
// Decode takes a map and uses reflection to convert it into the
|
||||
// given Go native structure. val must be a pointer to a struct.
|
||||
func Decode(m interface{}, rawVal interface{}) error {
|
||||
// Decode takes an input structure and uses reflection to translate it to
|
||||
// the output structure. output must be a pointer to a map or struct.
|
||||
func Decode(input interface{}, output interface{}) error {
|
||||
config := &DecoderConfig{
|
||||
Metadata: nil,
|
||||
Result: rawVal,
|
||||
Result: output,
|
||||
}
|
||||
|
||||
decoder, err := NewDecoder(config)
|
||||
|
@ -127,7 +127,7 @@ func Decode(m interface{}, rawVal interface{}) error {
|
|||
return err
|
||||
}
|
||||
|
||||
return decoder.Decode(m)
|
||||
return decoder.Decode(input)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
// a decoder has been returned, the same configuration must not be used
|
||||
// again.
|
||||
|
@ -184,70 +218,91 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
|
|||
|
||||
// Decode decodes the given raw interface to the target pointer specified
|
||||
// by the configuration.
|
||||
func (d *Decoder) Decode(raw interface{}) error {
|
||||
return d.decode("", raw, reflect.ValueOf(d.config.Result).Elem())
|
||||
func (d *Decoder) Decode(input interface{}) error {
|
||||
return d.decode("", input, reflect.ValueOf(d.config.Result).Elem())
|
||||
}
|
||||
|
||||
// Decodes an unknown data type into a specific reflection value.
|
||||
func (d *Decoder) decode(name string, data interface{}, val reflect.Value) error {
|
||||
if data == nil {
|
||||
// If the data is nil, then we don't set anything.
|
||||
func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error {
|
||||
var inputVal reflect.Value
|
||||
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
|
||||
}
|
||||
|
||||
dataVal := reflect.ValueOf(data)
|
||||
if !dataVal.IsValid() {
|
||||
// If the data value is invalid, then we just set the value
|
||||
if !inputVal.IsValid() {
|
||||
// If the input value is invalid, then we just set the 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
|
||||
}
|
||||
|
||||
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
|
||||
data, err = DecodeHookExec(
|
||||
input, err = DecodeHookExec(
|
||||
d.config.DecodeHook,
|
||||
dataVal.Type(), val.Type(), data)
|
||||
inputVal.Type(), outVal.Type(), input)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding '%s': %s", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
dataKind := getKind(val)
|
||||
switch dataKind {
|
||||
outputKind := getKind(outVal)
|
||||
switch outputKind {
|
||||
case reflect.Bool:
|
||||
err = d.decodeBool(name, data, val)
|
||||
err = d.decodeBool(name, input, outVal)
|
||||
case reflect.Interface:
|
||||
err = d.decodeBasic(name, data, val)
|
||||
err = d.decodeBasic(name, input, outVal)
|
||||
case reflect.String:
|
||||
err = d.decodeString(name, data, val)
|
||||
err = d.decodeString(name, input, outVal)
|
||||
case reflect.Int:
|
||||
err = d.decodeInt(name, data, val)
|
||||
err = d.decodeInt(name, input, outVal)
|
||||
case reflect.Uint:
|
||||
err = d.decodeUint(name, data, val)
|
||||
err = d.decodeUint(name, input, outVal)
|
||||
case reflect.Float32:
|
||||
err = d.decodeFloat(name, data, val)
|
||||
err = d.decodeFloat(name, input, outVal)
|
||||
case reflect.Struct:
|
||||
err = d.decodeStruct(name, data, val)
|
||||
err = d.decodeStruct(name, input, outVal)
|
||||
case reflect.Map:
|
||||
err = d.decodeMap(name, data, val)
|
||||
err = d.decodeMap(name, input, outVal)
|
||||
case reflect.Ptr:
|
||||
err = d.decodePtr(name, data, val)
|
||||
err = d.decodePtr(name, input, outVal)
|
||||
case reflect.Slice:
|
||||
err = d.decodeSlice(name, data, val)
|
||||
err = d.decodeSlice(name, input, outVal)
|
||||
case reflect.Array:
|
||||
err = d.decodeArray(name, data, val)
|
||||
err = d.decodeArray(name, input, outVal)
|
||||
case reflect.Func:
|
||||
err = d.decodeFunc(name, data, val)
|
||||
err = d.decodeFunc(name, input, outVal)
|
||||
default:
|
||||
// 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
|
||||
// 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 != "" {
|
||||
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
|
||||
// value to "data" of that type.
|
||||
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)
|
||||
|
||||
// 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() {
|
||||
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 {
|
||||
dataVal := reflect.ValueOf(data)
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
dataKind := getKind(dataVal)
|
||||
|
||||
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 {
|
||||
dataVal := reflect.ValueOf(data)
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
dataKind := getKind(dataVal)
|
||||
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 {
|
||||
dataVal := reflect.ValueOf(data)
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
dataKind := getKind(dataVal)
|
||||
|
||||
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 {
|
||||
dataVal := reflect.ValueOf(data)
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
dataKind := getKind(dataVal)
|
||||
|
||||
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 {
|
||||
dataVal := reflect.ValueOf(data)
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
dataKind := getKind(dataVal)
|
||||
dataType := dataVal.Type()
|
||||
|
||||
|
@ -499,38 +566,68 @@ func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) er
|
|||
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))
|
||||
if dataVal.Kind() != reflect.Map {
|
||||
// In weak mode, we accept a slice of maps as an input...
|
||||
switch dataVal.Kind() {
|
||||
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 {
|
||||
switch dataVal.Kind() {
|
||||
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
|
||||
}
|
||||
return d.decodeMapFromSlice(name, dataVal, val, valMap)
|
||||
}
|
||||
|
||||
fallthrough
|
||||
|
||||
default:
|
||||
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
|
||||
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() {
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
// 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
|
||||
// into that. Then set the value of the pointer to this type.
|
||||
valType := val.Type()
|
||||
valElemType := valType.Elem()
|
||||
if val.CanSet() {
|
||||
realVal := val
|
||||
if realVal.IsNil() || d.config.ZeroFields {
|
||||
realVal = reflect.New(valElemType)
|
||||
}
|
||||
|
||||
realVal := val
|
||||
if realVal.IsNil() || d.config.ZeroFields {
|
||||
realVal = reflect.New(valElemType)
|
||||
if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -604,30 +807,44 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
|
|||
|
||||
valSlice := val
|
||||
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
|
||||
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(
|
||||
"'%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.
|
||||
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()
|
||||
if dataValKind != reflect.Map {
|
||||
return fmt.Errorf("'%s' expected a map, got '%s'", name, dataValKind)
|
||||
}
|
||||
switch 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()
|
||||
if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface {
|
||||
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) {
|
||||
var authz authorization
|
||||
_, err := getJSON(authzURL, &authz)
|
||||
_, err := postAsGet(c.jws, authzURL, &authz)
|
||||
if err != nil {
|
||||
errc <- domainError{Domain: authz.Identifier.Value, Error: err}
|
||||
return
|
||||
|
@ -789,7 +789,7 @@ func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr
|
|||
case <-stopTimer.C:
|
||||
return nil, errors.New("certificate polling timed out")
|
||||
case <-retryTick.C:
|
||||
_, err := getJSON(order.URL, &retOrder)
|
||||
_, err := postAsGet(c.jws, order.URL, &retOrder)
|
||||
if err != nil {
|
||||
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) {
|
||||
switch order.Status {
|
||||
case statusValid:
|
||||
resp, err := httpGet(order.Certificate)
|
||||
resp, err := postAsGet(c.jws, order.Certificate, nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -871,7 +871,7 @@ func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResou
|
|||
// getIssuerCertificate requests the issuer certificate
|
||||
func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
|
||||
log.Infof("acme: Requesting issuer cert from %s", url)
|
||||
resp, err := httpGet(url)
|
||||
resp, err := postAsGet(c.jws, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -914,7 +914,10 @@ func parseLinks(links []string) map[string]string {
|
|||
func validate(j *jws, domain, uri string, c challenge) error {
|
||||
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 {
|
||||
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.
|
||||
ra = 5
|
||||
}
|
||||
|
||||
time.Sleep(time.Duration(ra) * time.Second)
|
||||
|
||||
hdr, err = getJSON(uri, &chlng)
|
||||
resp, err := postAsGet(j, uri, &chlng)
|
||||
if err != nil {
|
||||
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 (
|
||||
// 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 = "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
|
||||
// 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")
|
||||
}
|
||||
|
||||
resp, err := j.post(uri, jsonBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to post JWS message. -> %v", err)
|
||||
resp, err := post(j, uri, jsonBytes, respBody)
|
||||
if resp == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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 {
|
||||
err = handleHTTPError(resp)
|
||||
switch err.(type) {
|
||||
case NonceError:
|
||||
// Retry once if the nonce was invalidated
|
||||
|
||||
retryResp, errP := j.post(uri, jsonBytes)
|
||||
retryResp, errP := j.post(uri, reqBody)
|
||||
if errP != nil {
|
||||
return nil, fmt.Errorf("failed to post JWS message. -> %v", errP)
|
||||
}
|
||||
|
||||
defer retryResp.Body.Close()
|
||||
|
||||
if retryResp.StatusCode >= http.StatusBadRequest {
|
||||
return retryResp.Header, handleHTTPError(retryResp)
|
||||
return retryResp, handleHTTPError(retryResp)
|
||||
}
|
||||
|
||||
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:
|
||||
return resp.Header, err
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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.
|
||||
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)
|
||||
}
|
||||
|
|
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.
|
||||
// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-01#section-5.1
|
||||
var idPeAcmeIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 30, 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, 31}
|
||||
|
||||
type tlsALPNChallenge struct {
|
||||
jws *jws
|
||||
|
@ -58,7 +58,7 @@ func TLSALPNChallengeBlocks(domain, keyAuth string) ([]byte, []byte, error) {
|
|||
|
||||
// Add the keyAuth digest as the acmeValidation-v1 extension
|
||||
// (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{
|
||||
{
|
||||
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,
|
||||
}
|
||||
|
||||
response, _ := d.client.CreateDNSRecord(zoneID, dnsRecord)
|
||||
response, err := d.client.CreateDNSRecord(zoneID, dnsRecord)
|
||||
if err != nil {
|
||||
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/cloudflare"
|
||||
"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/dnsimple"
|
||||
"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/godaddy"
|
||||
"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/inwx"
|
||||
"github.com/xenolf/lego/providers/dns/lightsail"
|
||||
"github.com/xenolf/lego/providers/dns/linode"
|
||||
"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/namedotcom"
|
||||
"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/route53"
|
||||
"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/transip"
|
||||
"github.com/xenolf/lego/providers/dns/vegadns"
|
||||
"github.com/xenolf/lego/providers/dns/vscale"
|
||||
"github.com/xenolf/lego/providers/dns/vultr"
|
||||
)
|
||||
|
||||
|
@ -65,6 +72,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
|
|||
return cloudflare.NewDNSProvider()
|
||||
case "cloudxns":
|
||||
return cloudxns.NewDNSProvider()
|
||||
case "conoha":
|
||||
return conoha.NewDNSProvider()
|
||||
case "digitalocean":
|
||||
return digitalocean.NewDNSProvider()
|
||||
case "dnsimple":
|
||||
|
@ -97,8 +106,12 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
|
|||
return godaddy.NewDNSProvider()
|
||||
case "hostingde":
|
||||
return hostingde.NewDNSProvider()
|
||||
case "httpreq":
|
||||
return httpreq.NewDNSProvider()
|
||||
case "iij":
|
||||
return iij.NewDNSProvider()
|
||||
case "inwx":
|
||||
return inwx.NewDNSProvider()
|
||||
case "lightsail":
|
||||
return lightsail.NewDNSProvider()
|
||||
case "linode":
|
||||
|
@ -107,6 +120,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
|
|||
return linodev4.NewDNSProvider()
|
||||
case "manual":
|
||||
return acme.NewDNSProviderManual()
|
||||
case "mydnsjp":
|
||||
return mydnsjp.NewDNSProvider()
|
||||
case "namecheap":
|
||||
return namecheap.NewDNSProvider()
|
||||
case "namedotcom":
|
||||
|
@ -133,10 +148,16 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
|
|||
return sakuracloud.NewDNSProvider()
|
||||
case "stackpath":
|
||||
return stackpath.NewDNSProvider()
|
||||
case "selectel":
|
||||
return selectel.NewDNSProvider()
|
||||
case "transip":
|
||||
return transip.NewDNSProvider()
|
||||
case "vegadns":
|
||||
return vegadns.NewDNSProvider()
|
||||
case "vultr":
|
||||
return vultr.NewDNSProvider()
|
||||
case "vscale":
|
||||
return vscale.NewDNSProvider()
|
||||
default:
|
||||
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
|
||||
// finishing.
|
||||
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
|
||||
|
|
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"
|
||||
|
||||
// App types determine how to categorize a trace in the Datadog application.
|
||||
// For more fine-grained behaviour, use the SpanType* constants.
|
||||
const (
|
||||
// DEPRECATED: Use SpanTypeWeb
|
||||
// AppTypeWeb specifies the Web span type and can be used as a tag value
|
||||
// for a span's SpanType tag.
|
||||
AppTypeWeb = "web"
|
||||
|
||||
// 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"
|
||||
|
||||
// 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"
|
||||
|
||||
// AppTypeRPC specifies the RPC span type and can be used as a tag value
|
||||
// for a span's SpanType tag.
|
||||
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 = "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 = "span.type"
|
||||
|
||||
|
@ -47,4 +51,7 @@ const (
|
|||
|
||||
// ErrorStack specifies the stack dump.
|
||||
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) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if !Testing {
|
||||
// avoid infinite loop when calling (*mocktracer.Tracer).Stop
|
||||
globalTracer.Stop()
|
||||
}
|
||||
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
|
||||
// nil it will be disregarded.
|
||||
// WithError marks the span as having had an error. It uses the information from
|
||||
// err to set tags such as the error message, error type and stack trace.
|
||||
func WithError(err error) FinishOption {
|
||||
return func(cfg *ddtrace.FinishConfig) {
|
||||
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
|
||||
}
|
||||
if key == ext.Error {
|
||||
s.setTagError(value)
|
||||
s.setTagError(value, true)
|
||||
return
|
||||
}
|
||||
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.
|
||||
// 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) {
|
||||
case bool:
|
||||
// bool value as per Opentracing spec.
|
||||
|
@ -114,7 +117,9 @@ func (s *span) setTagError(value interface{}) {
|
|||
s.Error = 1
|
||||
s.Meta[ext.ErrorMsg] = v.Error()
|
||||
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:
|
||||
// no error
|
||||
s.Error = 0
|
||||
|
@ -166,7 +171,9 @@ func (s *span) Finish(opts ...ddtrace.FinishOption) {
|
|||
t = cfg.FinishTime.UnixNano()
|
||||
}
|
||||
if cfg.Error != nil {
|
||||
s.SetTag(ext.Error, cfg.Error)
|
||||
s.Lock()
|
||||
s.setTagError(cfg.Error, !cfg.NoDebugStack)
|
||||
s.Unlock()
|
||||
}
|
||||
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 {
|
||||
z.Meta = make(map[string]string, zb0002)
|
||||
} else if len(z.Meta) > 0 {
|
||||
for key, _ := range z.Meta {
|
||||
for key := range z.Meta {
|
||||
delete(z.Meta, key)
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ func (z *span) DecodeMsg(dc *msgp.Reader) (err error) {
|
|||
if z.Metrics == nil && zb0003 > 0 {
|
||||
z.Metrics = make(map[string]float64, zb0003)
|
||||
} else if len(z.Metrics) > 0 {
|
||||
for key, _ := range z.Metrics {
|
||||
for key := range z.Metrics {
|
||||
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.
|
||||
func (c HTTPHeadersCarrier) Set(key, val string) {
|
||||
h := http.Header(c)
|
||||
h.Add(key, val)
|
||||
http.Header(c).Set(key, val)
|
||||
}
|
||||
|
||||
// ForeachKey implements TextMapReader.
|
||||
|
@ -166,12 +165,12 @@ func (p *propagator) extractTextMap(reader TextMapReader) (ddtrace.SpanContext,
|
|||
key := strings.ToLower(k)
|
||||
switch key {
|
||||
case p.cfg.TraceHeader:
|
||||
ctx.traceID, err = strconv.ParseUint(v, 10, 64)
|
||||
ctx.traceID, err = parseUint64(v)
|
||||
if err != nil {
|
||||
return ErrSpanContextCorrupted
|
||||
}
|
||||
case p.cfg.ParentHeader:
|
||||
ctx.spanID, err = strconv.ParseUint(v, 10, 64)
|
||||
ctx.spanID, err = parseUint64(v)
|
||||
if err != nil {
|
||||
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 {
|
||||
return // mock tracer active
|
||||
}
|
||||
t := internal.GetGlobalTracer()
|
||||
internal.SetGlobalTracer(newTracer(opts...))
|
||||
t.Stop()
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
err := t.config.transport.send(t.payload)
|
||||
if err != nil && size > payloadMaxLimit {
|
||||
// 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()
|
||||
if err != nil {
|
||||
t.pushError(&dataLossError{context: err, count: count})
|
||||
}
|
||||
if err == nil {
|
||||
// send succeeded
|
||||
t.payload.reset()
|
||||
}
|
||||
t.payload.reset()
|
||||
}
|
||||
|
||||
// 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"
|
||||
)
|
||||
|
||||
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 (
|
||||
defaultHostname = "localhost"
|
||||
|
@ -57,16 +59,8 @@ func newHTTPTransport(addr string) *httpTransport {
|
|||
"Datadog-Meta-Tracer-Version": tracerVersion,
|
||||
"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{
|
||||
traceURL: fmt.Sprintf("http://%s/v0.3/traces", addr),
|
||||
traceURL: fmt.Sprintf("http://%s/v0.3/traces", resolveAddr(addr)),
|
||||
client: &http.Client{
|
||||
// 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.
|
||||
|
@ -118,3 +112,20 @@ func (t *httpTransport) send(p *payload) error {
|
|||
}
|
||||
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
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// toFloat64 attempts to convert value into a float64. If it succeeds it returns
|
||||
// the value and true, otherwise 0 and false.
|
||||
func toFloat64(value interface{}) (f float64, ok bool) {
|
||||
|
@ -30,3 +35,16 @@ func toFloat64(value interface{}) (f float64, ok bool) {
|
|||
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