mirror of
				https://github.com/containous/traefik.git
				synced 2025-10-30 20:24:28 +03:00 
			
		
		
		
	Compare commits
	
		
			28 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 9203ba5f95 | ||
|  | 302849282c | ||
|  | 2a227fa4c7 | ||
|  | 31b80683c8 | ||
|  | 6aa040ad9f | ||
|  | 617de78d14 | ||
|  | 44605731dc | ||
|  | 9d26839e54 | ||
|  | 7a93bcc869 | ||
|  | 0840fd0f96 | ||
|  | 4a5f5440d7 | ||
|  | 479ee9af49 | ||
|  | ef6c1fac4b | ||
|  | 53201b65b0 | ||
|  | 9cb4d56c03 | ||
|  | f25ebc7e3d | ||
|  | 4c25cda55f | ||
|  | 18962b61d6 | ||
|  | d0aa993661 | ||
|  | df688dba0e | ||
|  | 69585c58cd | ||
|  | b889b0191c | ||
|  | 9b0586cba7 | ||
|  | a20f5bda0b | ||
|  | 28d5731b87 | ||
|  | 66486eacdc | ||
|  | 2b82ed054c | ||
|  | cd6f0c0ded | 
							
								
								
									
										41
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,5 +1,46 @@ | ||||
| # Change Log | ||||
|  | ||||
| ## [v1.7.6](https://github.com/containous/traefik/tree/v1.7.6) (2018-12-07) | ||||
| [All Commits](https://github.com/containous/traefik/compare/v1.7.5...v1.7.6) | ||||
|  | ||||
| **Bug fixes:** | ||||
| - **[consulcatalog]** Fix label segmentation when using custom prefix ([#4272](https://github.com/containous/traefik/pull/4272) by [hsmade](https://github.com/hsmade)) | ||||
| - Update to Go 1.11.3 [CVE-2018-16875](https://nvd.nist.gov/vuln/detail/CVE-2018-16875) | ||||
|  | ||||
| ## [v1.7.5](https://github.com/containous/traefik/tree/v1.7.5) (2018-12-03) | ||||
| [All Commits](https://github.com/containous/traefik/compare/v1.7.4...v1.7.5) | ||||
|  | ||||
| **Enhancements:** | ||||
| - **[docker]** [docker backend] - Add config flag to set refreshSeconds for swarmmode ticker ([#4105](https://github.com/containous/traefik/pull/4105) by [WTFKr0](https://github.com/WTFKr0)) | ||||
| - **[k8s]** Support canary weight for external name service ([#4135](https://github.com/containous/traefik/pull/4135) by [yue9944882](https://github.com/yue9944882)) | ||||
|  | ||||
| **Bug fixes:** | ||||
| - **[acme]** Fix ACME spec and Cloudflare. ([#4201](https://github.com/containous/traefik/pull/4201) by [ldez](https://github.com/ldez)) | ||||
| - **[authentication,middleware]** Remove X-Forwarded-Uri and X-Forwarded-Method from untrusted IP ([#4036](https://github.com/containous/traefik/pull/4036) by [stffabi](https://github.com/stffabi)) | ||||
| - **[authentication,middleware]** Allow usersFile comments ([#4159](https://github.com/containous/traefik/pull/4159) by [thde](https://github.com/thde)) | ||||
| - **[authentication]** Fix partial declaration of authentication. ([#4212](https://github.com/containous/traefik/pull/4212) by [ldez](https://github.com/ldez)) | ||||
| - **[docker]** Verify ctx when we send configuration message in docker provider ([#4185](https://github.com/containous/traefik/pull/4185) by [juliens](https://github.com/juliens)) | ||||
| - **[ecs]** Filter ECS tasks by LastStatus before adding to list of service tasks ([#4255](https://github.com/containous/traefik/pull/4255) by [hwhelan-CB](https://github.com/hwhelan-CB)) | ||||
| - **[healthcheck]** Query params in health check ([#4188](https://github.com/containous/traefik/pull/4188) by [mmatur](https://github.com/mmatur)) | ||||
| - **[metrics]** Upgraded DD APM library ([#4189](https://github.com/containous/traefik/pull/4189) by [aantono](https://github.com/aantono)) | ||||
| - **[middleware]** Fix ssl force host secure middleware ([#4138](https://github.com/containous/traefik/pull/4138) by [mmatur](https://github.com/mmatur)) | ||||
| - **[oxy]** Fix unannonced trailers problem when body is empty ([#4258](https://github.com/containous/traefik/pull/4258) by [juliens](https://github.com/juliens)) | ||||
| - **[provider,server]** Log configuration errors from providers and keeps listening ([#4230](https://github.com/containous/traefik/pull/4230) by [geraldcroes](https://github.com/geraldcroes)) | ||||
| - **[tls]** Implement Case-insensitive SNI matching ([#4132](https://github.com/containous/traefik/pull/4132) by [dtomcej](https://github.com/dtomcej)) | ||||
| - Use ParseInt instead of Atoi for parsing durations ([#4263](https://github.com/containous/traefik/pull/4263) by [mmatur](https://github.com/mmatur)) | ||||
|  | ||||
| **Documentation:** | ||||
| - **[acme]** ACME DNS provider is called `acme-dns` ([#4166](https://github.com/containous/traefik/pull/4166) by [robsdedude](https://github.com/robsdedude)) | ||||
| - **[docker]** Add a "Security Consideration" section in the Docker's backend section of the documentation ([#4225](https://github.com/containous/traefik/pull/4225) by [dduportal](https://github.com/dduportal)) | ||||
| - **[docker]** Clarify swarm loadbalancer documentation ([#4194](https://github.com/containous/traefik/pull/4194) by [jlevesy](https://github.com/jlevesy)) | ||||
| - **[docker]** Fix spelling in comment ([#4169](https://github.com/containous/traefik/pull/4169) by [giocomai](https://github.com/giocomai)) | ||||
| - **[docker]** Update swarm mode endpoint ([#4208](https://github.com/containous/traefik/pull/4208) by [siyu6974](https://github.com/siyu6974)) | ||||
| - **[k8s]** Include an explicit list of kubernetes protocol annotations in docs. ([#4170](https://github.com/containous/traefik/pull/4170) by [shanna](https://github.com/shanna)) | ||||
| - **[k8s]** Improve kubernetes TLS user guide ([#4175](https://github.com/containous/traefik/pull/4175) by [mterring](https://github.com/mterring)) | ||||
| - **[k8s]** frame-deny should be set to true to enable the header ([#4171](https://github.com/containous/traefik/pull/4171) by [swestcott](https://github.com/swestcott)) | ||||
| - **[rules]** Matcher associativity rule. ([#4244](https://github.com/containous/traefik/pull/4244) by [ldez](https://github.com/ldez)) | ||||
| - Documentation: Rename "admin panel" to "dashboard ([#4156](https://github.com/containous/traefik/pull/4156) by [thernstig](https://github.com/thernstig)) | ||||
|  | ||||
| ## [v1.7.4](https://github.com/containous/traefik/tree/v1.7.4) (2018-10-30) | ||||
| [All Commits](https://github.com/containous/traefik/compare/v1.7.3...v1.7.4) | ||||
|  | ||||
|   | ||||
							
								
								
									
										54
									
								
								Gopkg.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										54
									
								
								Gopkg.lock
									
									
									
										generated
									
									
									
								
							| @@ -284,8 +284,8 @@ | ||||
|     ".", | ||||
|     "parse" | ||||
|   ] | ||||
|   revision = "aad81c7ac7f49671a59b9ede8ab22436e132a302" | ||||
|   version = "v1.3.0" | ||||
|   revision = "c93d194b807ef171c43344d60adad8b58217390a" | ||||
|   version = "v1.4.1" | ||||
|  | ||||
| [[projects]] | ||||
|   branch = "master" | ||||
| @@ -565,6 +565,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" | ||||
| @@ -819,7 +825,6 @@ | ||||
|   revision = "9b66602d496a139e4722bdde32f0f1ac1c12d4a8" | ||||
|  | ||||
| [[projects]] | ||||
|   branch = "master" | ||||
|   name = "github.com/jjcollinge/servicefabric" | ||||
|   packages = ["."] | ||||
|   revision = "8eebe170fa1ba25d3dfb928b3f86a7313b13b9fe" | ||||
| @@ -846,6 +851,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" | ||||
| @@ -990,10 +1001,10 @@ | ||||
|   revision = "2bca23e0e452137f789efbc8610126fd8b94f73b" | ||||
|  | ||||
| [[projects]] | ||||
|   branch = "master" | ||||
|   name = "github.com/mitchellh/mapstructure" | ||||
|   packages = ["."] | ||||
|   revision = "b4575eea38cca1123ec2dc90c26529b5c5acfcff" | ||||
|   revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe" | ||||
|   version = "v1.1.2" | ||||
|  | ||||
| [[projects]] | ||||
|   branch = "master" | ||||
| @@ -1201,6 +1212,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 = ["."] | ||||
| @@ -1248,6 +1265,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" | ||||
| @@ -1323,7 +1350,7 @@ | ||||
|     "roundrobin", | ||||
|     "utils" | ||||
|   ] | ||||
|   revision = "7d94d212f808222b72fd0b8bb171bfcd4e27ffca" | ||||
|   revision = "c34b0c501e43223bc816ac9b40b0ac29c44c8952" | ||||
|  | ||||
| [[projects]] | ||||
|   name = "github.com/vulcand/predicate" | ||||
| @@ -1363,6 +1390,7 @@ | ||||
|     "providers/dns/bluecat", | ||||
|     "providers/dns/cloudflare", | ||||
|     "providers/dns/cloudxns", | ||||
|     "providers/dns/conoha", | ||||
|     "providers/dns/digitalocean", | ||||
|     "providers/dns/dnsimple", | ||||
|     "providers/dns/dnsmadeeasy", | ||||
| @@ -1379,10 +1407,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", | ||||
| @@ -1395,11 +1426,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" | ||||
| @@ -1564,8 +1598,8 @@ | ||||
|     "ddtrace/opentracer", | ||||
|     "ddtrace/tracer" | ||||
|   ] | ||||
|   revision = "d052956664af54dbcff2712d10c67c76fbfc299f" | ||||
|   version = "v1.0.0" | ||||
|   revision = "48eeff27357376bcb31a15674dc4be9078de88b3" | ||||
|   version = "v1.5.0" | ||||
|  | ||||
| [[projects]] | ||||
|   name = "gopkg.in/fsnotify.v1" | ||||
| @@ -1824,6 +1858,6 @@ | ||||
| [solve-meta] | ||||
|   analyzer-name = "dep" | ||||
|   analyzer-version = 1 | ||||
|   inputs-digest = "059f9d29d78e7a800b676c529197fd627de968837b01c663a8a00ee72c36271b" | ||||
|   inputs-digest = "26517fdb01d55f549e4692c251587c90f657b7736e2951d673a530f3b86a90fb" | ||||
|   solver-name = "gps-cdcl" | ||||
|   solver-version = 1 | ||||
|   | ||||
| @@ -54,7 +54,7 @@ | ||||
|  | ||||
| [[constraint]] | ||||
|   name = "github.com/containous/flaeg" | ||||
|   version = "1.3.0" | ||||
|   version = "1.4.1" | ||||
|  | ||||
| [[constraint]] | ||||
|   branch = "master" | ||||
| @@ -263,4 +263,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. | ||||
|  | ||||
| [](https://www.youtube.com/watch?v=RgudiksfL-k) | ||||
|   | ||||
| @@ -4,8 +4,7 @@ RUN apk --update upgrade \ | ||||
| && apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar \ | ||||
| && rm -rf /var/cache/apk/* | ||||
|  | ||||
| RUN go get github.com/containous/go-bindata/... \ | ||||
| && go get golang.org/x/lint/golint \ | ||||
| RUN go get golang.org/x/lint/golint \ | ||||
| && go get github.com/kisielk/errcheck \ | ||||
| && go get github.com/client9/misspell/cmd/misspell | ||||
|  | ||||
| @@ -13,6 +12,11 @@ RUN go get github.com/containous/go-bindata/... \ | ||||
| ARG DOCKER_VERSION=17.03.2 | ||||
| ARG DEP_VERSION=0.4.1 | ||||
|  | ||||
| # Download go-bindata binary to bin folder in $GOPATH | ||||
| RUN mkdir -p /usr/local/bin \ | ||||
|     && curl -fsSL -o /usr/local/bin/go-bindata https://github.com/containous/go-bindata/releases/download/v1.0.0/go-bindata \ | ||||
|     && chmod +x /usr/local/bin/go-bindata | ||||
|  | ||||
| # Download dep binary to bin folder in $GOPATH | ||||
| RUN mkdir -p /usr/local/bin \ | ||||
|     && curl -fsSL -o /usr/local/bin/dep https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 \ | ||||
|   | ||||
| @@ -46,6 +46,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration { | ||||
| 	defaultDocker.ExposedByDefault = true | ||||
| 	defaultDocker.Endpoint = "unix:///var/run/docker.sock" | ||||
| 	defaultDocker.SwarmMode = false | ||||
| 	defaultDocker.SwarmModeRefreshSeconds = 15 | ||||
|  | ||||
| 	// default File | ||||
| 	var defaultFile file.Provider | ||||
|   | ||||
| @@ -94,10 +94,12 @@ Following is the list of existing modifier rules: | ||||
|  | ||||
| Matcher rules determine if a particular request should be forwarded to a backend. | ||||
|  | ||||
| Separate multiple rule values by `,` (comma) in order to enable ANY semantics (i.e., forward a request if any rule matches). | ||||
| Does not work for `Headers` and `HeadersRegexp`. | ||||
|  | ||||
| Separate multiple rule values by `;` (semicolon) in order to enable ALL semantics (i.e., forward a request if all rules match). | ||||
| The associativity rule is the following: | ||||
| - `,` is the `OR` operator (works **only inside a matcher**, ex: `Host:foo.com,bar.com`). | ||||
|     - i.e., forward a request if any rule matches. | ||||
|     - Does not work for `Headers` and `HeadersRegexp`. | ||||
| - `;` is the `AND` operator (works **only between matchers**, ex: `Host:foo.com;Path:/bar`)  | ||||
|     - i.e., forward a request if all rules match | ||||
|  | ||||
| Following is the list of existing matcher rules along with examples: | ||||
|  | ||||
| @@ -746,8 +748,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`. | ||||
|   | ||||
| @@ -57,7 +57,7 @@ watch = true | ||||
| exposedByDefault = true | ||||
|  | ||||
| # Use the IP address from the binded port instead of the inner network one. | ||||
| #  | ||||
| # | ||||
| # In case no IP address is attached to the binded port (or in case  | ||||
| # there is no bind), the inner network one will be used as a fallback.      | ||||
| # | ||||
| @@ -73,6 +73,13 @@ usebindportip = true | ||||
| # | ||||
| swarmMode = false | ||||
|  | ||||
| # Polling interval (in seconds) for Swarm Mode. | ||||
| # | ||||
| # Optional | ||||
| # Default: 15 | ||||
| # | ||||
| swarmModeRefreshSeconds = 15 | ||||
|  | ||||
| # Define a default docker network to use for connections to all containers. | ||||
| # Can be overridden by the traefik.docker.network label. | ||||
| # | ||||
| @@ -93,7 +100,6 @@ network = "web" | ||||
|  | ||||
| To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific). | ||||
|  | ||||
|  | ||||
| ## Docker Swarm Mode | ||||
|  | ||||
| ```toml | ||||
| @@ -110,7 +116,10 @@ To enable constraints see [provider-specific constraints section](/configuration | ||||
| # Required | ||||
| # Default: "unix:///var/run/docker.sock" | ||||
| # | ||||
| endpoint = "tcp://127.0.0.1:2375" | ||||
| # swarm classic (1.12-) | ||||
| # endpoint = "tcp://127.0.0.1:2375" | ||||
| # docker swarm mode (1.12+) | ||||
| endpoint = "tcp://127.0.0.1:2377" | ||||
|  | ||||
| # Default base domain used for the frontend rules. | ||||
| # Can be overridden by setting the "traefik.domain" label on a services. | ||||
| @@ -177,6 +186,57 @@ exposedByDefault = false | ||||
|  | ||||
| To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific). | ||||
|  | ||||
| ## Security Considerations | ||||
|  | ||||
| ### Security Challenge with the Docker Socket | ||||
|  | ||||
| Traefik requires access to the docker socket to get its dynamic configuration, | ||||
| by watching the Docker API through this socket. | ||||
|  | ||||
| !!! important | ||||
|     Depending on your context and your usage, accessing the Docker API without any restriction might be a security concern. | ||||
|  | ||||
| As explained on the Docker documentation: ([Docker Daemon Attack Surface page](https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface)): | ||||
|  | ||||
| `[...] only **trusted** users should be allowed to control your Docker daemon [...]` | ||||
|  | ||||
| If the Traefik processes (handling requests from the outside world) is attacked, | ||||
| then the attacker can access the Docker (or Swarm Mode) backend. | ||||
|  | ||||
| Also, when using Swarm Mode, it is mandatory to schedule Traefik's containers on the Swarm manager nodes, | ||||
| to let Traefik accessing the Docker Socket of the Swarm manager node. | ||||
|  | ||||
| More information about Docker's security: | ||||
|  | ||||
| - [KubeCon EU 2018 Keynote, Running with Scissors, from Liz Rice](https://www.youtube.com/watch?v=ltrV-Qmh3oY) | ||||
| - [Don't expose the Docker socket (not even to a container)](https://www.lvh.io/posts/dont-expose-the-docker-socket-not-even-to-a-container.html) | ||||
| - [A thread on Stack Overflow about sharing the `/var/run/docker.sock` file](https://news.ycombinator.com/item?id=17983623) | ||||
| - [To Dind or not to DinD](https://blog.loof.fr/2018/01/to-dind-or-not-do-dind.html) | ||||
|  | ||||
| ### Security Compensation | ||||
|  | ||||
| The main security compensation is to expose the Docker socket over TCP, instead of the default Unix socket file. | ||||
| It allows different implementation levels of the [AAA (Authentication, Authorization, Accounting) concepts](https://en.wikipedia.org/wiki/AAA_(computer_security)), depending on your security assessment: | ||||
|  | ||||
| - Authentication with Client Certificates as described in [the "Protect the Docker daemon socket" page of Docker's documentation](https://docs.docker.com/engine/security/https/) | ||||
|  | ||||
| - Authorization with the [Docker Authorization Plugin Mechanism](https://docs.docker.com/engine/extend/plugins_authorization/) | ||||
|  | ||||
| - Accounting at networking level, by exposing the socket only inside a Docker private network, only available for Traefik. | ||||
|  | ||||
| - Accounting at container level, by exposing the socket on a another container than Traefik's. | ||||
|   With Swarm mode, it allows scheduling of Traefik on worker nodes, with only the "socket exposer" container on the manager nodes. | ||||
|  | ||||
| - Accounting at kernel level, by enforcing kernel calls with mechanisms like [SELinux](https://en.wikipedia.org/wiki/Security-Enhanced_Linux), | ||||
|   to only allows an identified set of actions for Traefik's process (or the "socket exposer" process). | ||||
|  | ||||
| Use the following ressources to get started: | ||||
|  | ||||
| - [Traefik issue GH-4174 about security with Docker socket](https://github.com/containous/traefik/issues/4174) | ||||
| - [Inspecting Docker Activity with Socat](https://developers.redhat.com/blog/2015/02/25/inspecting-docker-activity-with-socat/) | ||||
| - [Letting Traefik run on Worker Nodes](https://blog.mikesir87.io/2018/07/letting-traefik-run-on-worker-nodes/) | ||||
| - [Docker Socket Proxy from Tecnativa](https://github.com/Tecnativa/docker-socket-proxy) | ||||
|  | ||||
| ## Labels: overriding default behavior | ||||
|  | ||||
| ### Using Docker with Swarm Mode | ||||
| @@ -236,7 +296,7 @@ Labels can be used on containers to override default behavior. | ||||
| | `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.sticky=true`                          | Enables backend sticky sessions (DEPRECATED)                                                                                                                                                                                     | | ||||
| | `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).                                                                                                                            | | ||||
| @@ -284,22 +344,26 @@ Labels can be used on containers to override default behavior. | ||||
| | `traefik.frontend.whiteList.sourceRange=RANGE`                      | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | | ||||
| | `traefik.frontend.whiteList.useXForwardedFor=true`                  | Uses `X-Forwarded-For` header as valid source of IP for the white list.                                                                                                                                                          | | ||||
|  | ||||
| [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. | ||||
|  | ||||
| [2] `traefik.frontend.auth.basic.users=EXPR `:   | ||||
| [2] `traefik.frontend.auth.basic.users=EXPR`:   | ||||
| 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                                                                                                                                                                         | | ||||
| |-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | ||||
| | `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> | | ||||
| | `traefik.frontend.headers.customRequestHeaders=EXPR`  | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> | | ||||
| | `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code>        | | ||||
|  | ||||
| #### Security Headers | ||||
| @@ -314,7 +378,7 @@ The result will be `user:$$apr1$$9Cv/OMGj$$ZomWQzuQbL.3TRCS81A1g/`, note additio | ||||
| | `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value.                                                                                                                                       | | ||||
| | `traefik.frontend.headers.forceSTSHeader=false`          | Adds the STS  header to non-SSL requests.                                                                                                                                                           | | ||||
| | `traefik.frontend.headers.frameDeny=false`               | Adds the `X-Frame-Options` header with the value of `DENY`.                                                                                                                                         | | ||||
| | `traefik.frontend.headers.hostsProxyHeaders=EXPR `       | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2`                                                                                                    | | ||||
| | `traefik.frontend.headers.hostsProxyHeaders=EXPR`        | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2`                                                                                                    | | ||||
| | `traefik.frontend.headers.isDevelopment=false`           | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. | | ||||
| | `traefik.frontend.headers.publicKey=VALUE`               | Adds HPKP header.                                                                                                                                                                                   | | ||||
| | `traefik.frontend.headers.referrerPolicy=VALUE`          | Adds referrer policy  header.                                                                                                                                                                       | | ||||
| @@ -335,63 +399,63 @@ You can define as many segments as ports exposed in a container. | ||||
|  | ||||
| Segment labels override the default behavior. | ||||
|  | ||||
| | Label                                                                              | Description                                                            | | ||||
| |------------------------------------------------------------------------------------|------------------------------------------------------------------------| | ||||
| | `traefik.<segment_name>.backend=BACKEND`                                           | Same as `traefik.backend`                                              | | ||||
| | `traefik.<segment_name>.domain=DOMAIN`                                             | Same as `traefik.domain`                                               | | ||||
| | `traefik.<segment_name>.port=PORT`                                                 | Same as `traefik.port`                                                 | | ||||
| | `traefik.<segment_name>.protocol=http`                                             | Same as `traefik.protocol`                                             | | ||||
| | `traefik.<segment_name>.weight=10`                                                 | Same as `traefik.weight`                                               | | ||||
| | `traefik.<segment_name>.frontend.auth.basic=EXPR`                                  | Same as `traefik.frontend.auth.basic`                                  | | ||||
| | `traefik.<segment_name>.frontend.auth.basic.removeHeader=true`                     | Same as `traefik.frontend.auth.basic.removeHeader`                     | | ||||
| | `traefik.<segment_name>.frontend.auth.basic.users=EXPR`                            | Same as `traefik.frontend.auth.basic.users`                            | | ||||
| | `traefik.<segment_name>.frontend.auth.basic.usersFile=/path/.htpasswd`             | Same as `traefik.frontend.auth.basic.usersFile`                        | | ||||
| | `traefik.<segment_name>.frontend.auth.digest.removeHeader=true`                    | Same as `traefik.frontend.auth.digest.removeHeader`                    | | ||||
| | `traefik.<segment_name>.frontend.auth.digest.users=EXPR`                           | Same as `traefik.frontend.auth.digest.users`                           | | ||||
| | `traefik.<segment_name>.frontend.auth.digest.usersFile=/path/.htdigest`            | Same as `traefik.frontend.auth.digest.usersFile`                       | | ||||
| | `traefik.<segment_name>.frontend.auth.forward.address=https://example.com`         | Same as `traefik.frontend.auth.forward.address`                        | | ||||
| | `traefik.<segment_name>.frontend.auth.forward.authResponseHeaders=EXPR`            | Same as `traefik.frontend.auth.forward.authResponseHeaders`            | | ||||
| | `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem`                 | Same as `traefik.frontend.auth.forward.tls.ca`                         | | ||||
| | `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true`                 | Same as `traefik.frontend.auth.forward.tls.caOptional`                 | | ||||
| | `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem`           | Same as `traefik.frontend.auth.forward.tls.cert`                       | | ||||
| | `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true`         | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify`         | | ||||
| | `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key`            | Same as `traefik.frontend.auth.forward.tls.key`                        | | ||||
| | `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true`             | Same as `traefik.frontend.auth.forward.trustForwardHeader`             | | ||||
| | `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User`                  | Same as `traefik.frontend.auth.headerField`                            | | ||||
| | `traefik.<segment_name>.frontend.entryPoints=https`                                | Same as `traefik.frontend.entryPoints`                                 | | ||||
| | `traefik.<segment_name>.frontend.errors.<name>.backend=NAME`                       | Same as `traefik.frontend.errors.<name>.backend`                       | | ||||
| | `traefik.<segment_name>.frontend.errors.<name>.query=PATH`                         | Same as `traefik.frontend.errors.<name>.query`                         | | ||||
| | `traefik.<segment_name>.frontend.errors.<name>.status=RANGE`                       | Same as `traefik.frontend.errors.<name>.status`                        | | ||||
| | `traefik.<segment_name>.frontend.passHostHeader=true`                              | Same as `traefik.frontend.passHostHeader`                              | | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.infos.notAfter=true`            | Same as `traefik.frontend.passTLSClientCert.infos.notAfter`            | | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.infos.notBefore=true`           | Same as `traefik.frontend.passTLSClientCert.infos.notBefore`           | | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.infos.sans=true`                | Same as `traefik.frontend.passTLSClientCert.infos.sans`                | | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.commonName=true`  | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName`  | | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.country=true`     | Same as `traefik.frontend.passTLSClientCert.infos.subject.country`     | | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.locality=true`    | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality`    | | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.organization=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.organization`| | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.province=true`    | Same as `traefik.frontend.passTLSClientCert.infos.subject.province`    | | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber`| | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.pem=true`                       | Same as `traefik.frontend.passTLSClientCert.infos.pem`                 | | ||||
| | `traefik.<segment_name>.frontend.passTLSCert=true`                                 | Same as `traefik.frontend.passTLSCert`                                 | | ||||
| | `traefik.<segment_name>.frontend.priority=10`                                      | Same as `traefik.frontend.priority`                                    | | ||||
| | `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP`                      | Same as `traefik.frontend.rateLimit.extractorFunc`                     | | ||||
| | `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6`                | Same as `traefik.frontend.rateLimit.rateSet.<name>.period`             | | ||||
| | `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6`               | Same as `traefik.frontend.rateLimit.rateSet.<name>.average`            | | ||||
| | `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6`                 | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst`              | | ||||
| | `traefik.<segment_name>.frontend.redirect.entryPoint=https`                        | Same as `traefik.frontend.redirect.entryPoint`                         | | ||||
| | `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)`            | Same as `traefik.frontend.redirect.regex`                              | | ||||
| | `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1`          | Same as `traefik.frontend.redirect.replacement`                        | | ||||
| | `traefik.<segment_name>.frontend.redirect.permanent=true`                          | Same as `traefik.frontend.redirect.permanent`                          | | ||||
| | `traefik.<segment_name>.frontend.rule=EXP`                                         | Same as `traefik.frontend.rule`                                        | | ||||
| | `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE`                      | Same as `traefik.frontend.whiteList.sourceRange`                       | | ||||
| | `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true`                  | Same as `traefik.frontend.whiteList.useXForwardedFor`                  | | ||||
| | Label                                                                               | Description                                                             | | ||||
| |-------------------------------------------------------------------------------------|-------------------------------------------------------------------------| | ||||
| | `traefik.<segment_name>.backend=BACKEND`                                            | Same as `traefik.backend`                                               | | ||||
| | `traefik.<segment_name>.domain=DOMAIN`                                              | Same as `traefik.domain`                                                | | ||||
| | `traefik.<segment_name>.port=PORT`                                                  | Same as `traefik.port`                                                  | | ||||
| | `traefik.<segment_name>.protocol=http`                                              | Same as `traefik.protocol`                                              | | ||||
| | `traefik.<segment_name>.weight=10`                                                  | Same as `traefik.weight`                                                | | ||||
| | `traefik.<segment_name>.frontend.auth.basic=EXPR`                                   | Same as `traefik.frontend.auth.basic`                                   | | ||||
| | `traefik.<segment_name>.frontend.auth.basic.removeHeader=true`                      | Same as `traefik.frontend.auth.basic.removeHeader`                      | | ||||
| | `traefik.<segment_name>.frontend.auth.basic.users=EXPR`                             | Same as `traefik.frontend.auth.basic.users`                             | | ||||
| | `traefik.<segment_name>.frontend.auth.basic.usersFile=/path/.htpasswd`              | Same as `traefik.frontend.auth.basic.usersFile`                         | | ||||
| | `traefik.<segment_name>.frontend.auth.digest.removeHeader=true`                     | Same as `traefik.frontend.auth.digest.removeHeader`                     | | ||||
| | `traefik.<segment_name>.frontend.auth.digest.users=EXPR`                            | Same as `traefik.frontend.auth.digest.users`                            | | ||||
| | `traefik.<segment_name>.frontend.auth.digest.usersFile=/path/.htdigest`             | Same as `traefik.frontend.auth.digest.usersFile`                        | | ||||
| | `traefik.<segment_name>.frontend.auth.forward.address=https://example.com`          | Same as `traefik.frontend.auth.forward.address`                         | | ||||
| | `traefik.<segment_name>.frontend.auth.forward.authResponseHeaders=EXPR`             | Same as `traefik.frontend.auth.forward.authResponseHeaders`             | | ||||
| | `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem`                  | Same as `traefik.frontend.auth.forward.tls.ca`                          | | ||||
| | `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true`                  | Same as `traefik.frontend.auth.forward.tls.caOptional`                  | | ||||
| | `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem`            | Same as `traefik.frontend.auth.forward.tls.cert`                        | | ||||
| | `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true`          | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify`          | | ||||
| | `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key`             | Same as `traefik.frontend.auth.forward.tls.key`                         | | ||||
| | `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true`              | Same as `traefik.frontend.auth.forward.trustForwardHeader`              | | ||||
| | `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User`                   | Same as `traefik.frontend.auth.headerField`                             | | ||||
| | `traefik.<segment_name>.frontend.entryPoints=https`                                 | Same as `traefik.frontend.entryPoints`                                  | | ||||
| | `traefik.<segment_name>.frontend.errors.<name>.backend=NAME`                        | Same as `traefik.frontend.errors.<name>.backend`                        | | ||||
| | `traefik.<segment_name>.frontend.errors.<name>.query=PATH`                          | Same as `traefik.frontend.errors.<name>.query`                          | | ||||
| | `traefik.<segment_name>.frontend.errors.<name>.status=RANGE`                        | Same as `traefik.frontend.errors.<name>.status`                         | | ||||
| | `traefik.<segment_name>.frontend.passHostHeader=true`                               | Same as `traefik.frontend.passHostHeader`                               | | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.infos.notAfter=true`             | Same as `traefik.frontend.passTLSClientCert.infos.notAfter`             | | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.infos.notBefore=true`            | Same as `traefik.frontend.passTLSClientCert.infos.notBefore`            | | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.infos.sans=true`                 | Same as `traefik.frontend.passTLSClientCert.infos.sans`                 | | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.commonName=true`   | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName`   | | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.country=true`      | Same as `traefik.frontend.passTLSClientCert.infos.subject.country`      | | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.locality=true`     | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality`     | | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.organization=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.organization` | | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.province=true`     | Same as `traefik.frontend.passTLSClientCert.infos.subject.province`     | | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.serialNumber=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber` | | ||||
| | `traefik.<segment_name>.frontend.passTLSClientCert.pem=true`                        | Same as `traefik.frontend.passTLSClientCert.infos.pem`                  | | ||||
| | `traefik.<segment_name>.frontend.passTLSCert=true`                                  | Same as `traefik.frontend.passTLSCert`                                  | | ||||
| | `traefik.<segment_name>.frontend.priority=10`                                       | Same as `traefik.frontend.priority`                                     | | ||||
| | `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP`                       | Same as `traefik.frontend.rateLimit.extractorFunc`                      | | ||||
| | `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6`                 | Same as `traefik.frontend.rateLimit.rateSet.<name>.period`              | | ||||
| | `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6`                | Same as `traefik.frontend.rateLimit.rateSet.<name>.average`             | | ||||
| | `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6`                  | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst`               | | ||||
| | `traefik.<segment_name>.frontend.redirect.entryPoint=https`                         | Same as `traefik.frontend.redirect.entryPoint`                          | | ||||
| | `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)`             | Same as `traefik.frontend.redirect.regex`                               | | ||||
| | `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1`           | Same as `traefik.frontend.redirect.replacement`                         | | ||||
| | `traefik.<segment_name>.frontend.redirect.permanent=true`                           | Same as `traefik.frontend.redirect.permanent`                           | | ||||
| | `traefik.<segment_name>.frontend.rule=EXP`                                          | Same as `traefik.frontend.rule`                                         | | ||||
| | `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE`                       | Same as `traefik.frontend.whiteList.sourceRange`                        | | ||||
| | `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true`                   | Same as `traefik.frontend.whiteList.useXForwardedFor`                   | | ||||
|  | ||||
| #### Custom Headers | ||||
|  | ||||
| | Label                                                                | Description                                              | | ||||
| |----------------------------------------------------------------------|----------------------------------------------------------| | ||||
| | `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | Same as `traefik.frontend.headers.customRequestHeaders`  | | ||||
| | `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR`  | Same as `traefik.frontend.headers.customRequestHeaders`  | | ||||
| | `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | Same as `traefik.frontend.headers.customResponseHeaders` | | ||||
|  | ||||
| #### Security Headers | ||||
|   | ||||
| @@ -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. | ||||
|  | ||||
| @@ -166,7 +167,7 @@ The following general annotations are applicable on the Ingress object: | ||||
| | `traefik.ingress.kubernetes.io/service-weights: <YML>`                          | Set ingress backend weights specified as percentage or decimal numbers in YAML. (6)                                                                                                        | | ||||
| | `traefik.ingress.kubernetes.io/whitelist-source-range: "1.2.3.0/24, fe80::/16"` | A comma-separated list of IP ranges permitted for access (7).                                                                                                                              | | ||||
| | `ingress.kubernetes.io/whitelist-x-forwarded-for: "true"`                       | Use `X-Forwarded-For` header as valid source of IP for the white list.                                                                                                                     | | ||||
| | `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. | ||||
| @@ -316,7 +317,7 @@ The following security annotations are applicable on the Ingress object: | ||||
| | `ingress.kubernetes.io/custom-browser-xss-value: VALUE`   | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option.                                                                                                           | | ||||
| | `ingress.kubernetes.io/custom-frame-options-value: VALUE` | Overrides the `X-Frame-Options` header with the custom value.                                                                                                                                       | | ||||
| | `ingress.kubernetes.io/force-hsts: "false"`               | Adds the STS  header to non-SSL requests.                                                                                                                                                           | | ||||
| | `ingress.kubernetes.io/frame-deny: "false"`               | Adds the `X-Frame-Options` header with the value of `DENY`.                                                                                                                                         | | ||||
| | `ingress.kubernetes.io/frame-deny: "true"`                | Adds the `X-Frame-Options` header with the value of `DENY`.                                                                                                                                         | | ||||
| | `ingress.kubernetes.io/hsts-max-age: "315360000"`         | Sets the max-age of the HSTS header.                                                                                                                                                                | | ||||
| | `ingress.kubernetes.io/hsts-include-subdomains: "true"`   | Adds the IncludeSubdomains section of the STS  header.                                                                                                                                              | | ||||
| | `ingress.kubernetes.io/hsts-preload: "true"`              | Adds the preload flag to the HSTS  header.                                                                                                                                                          | | ||||
|   | ||||
| @@ -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. | ||||
|  | ||||
| [](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" | ||||
|   | ||||
| @@ -58,8 +58,10 @@ type BackendConfig struct { | ||||
| } | ||||
|  | ||||
| func (b *BackendConfig) newRequest(serverURL *url.URL) (*http.Request, error) { | ||||
| 	u := &url.URL{} | ||||
| 	*u = *serverURL | ||||
| 	u, err := serverURL.Parse(b.Path) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if len(b.Scheme) > 0 { | ||||
| 		u.Scheme = b.Scheme | ||||
| @@ -69,8 +71,6 @@ func (b *BackendConfig) newRequest(serverURL *url.URL) (*http.Request, error) { | ||||
| 		u.Host = net.JoinHostPort(u.Hostname(), strconv.Itoa(b.Port)) | ||||
| 	} | ||||
|  | ||||
| 	u.Path += b.Path | ||||
|  | ||||
| 	return http.NewRequest(http.MethodGet, u.String(), http.NoBody) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -150,11 +150,16 @@ func TestSetBackendsConfiguration(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestNewRequest(t *testing.T) { | ||||
| 	type expected struct { | ||||
| 		err   bool | ||||
| 		value string | ||||
| 	} | ||||
|  | ||||
| 	testCases := []struct { | ||||
| 		desc      string | ||||
| 		serverURL string | ||||
| 		options   Options | ||||
| 		expected  string | ||||
| 		expected  expected | ||||
| 	}{ | ||||
| 		{ | ||||
| 			desc:      "no port override", | ||||
| @@ -163,7 +168,10 @@ func TestNewRequest(t *testing.T) { | ||||
| 				Path: "/test", | ||||
| 				Port: 0, | ||||
| 			}, | ||||
| 			expected: "http://backend1:80/test", | ||||
| 			expected: expected{ | ||||
| 				err:   false, | ||||
| 				value: "http://backend1:80/test", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc:      "port override", | ||||
| @@ -172,7 +180,10 @@ func TestNewRequest(t *testing.T) { | ||||
| 				Path: "/test", | ||||
| 				Port: 8080, | ||||
| 			}, | ||||
| 			expected: "http://backend2:8080/test", | ||||
| 			expected: expected{ | ||||
| 				err:   false, | ||||
| 				value: "http://backend2:8080/test", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc:      "no port override with no port in server URL", | ||||
| @@ -181,7 +192,10 @@ func TestNewRequest(t *testing.T) { | ||||
| 				Path: "/health", | ||||
| 				Port: 0, | ||||
| 			}, | ||||
| 			expected: "http://backend1/health", | ||||
| 			expected: expected{ | ||||
| 				err:   false, | ||||
| 				value: "http://backend1/health", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc:      "port override with no port in server URL", | ||||
| @@ -190,7 +204,10 @@ func TestNewRequest(t *testing.T) { | ||||
| 				Path: "/health", | ||||
| 				Port: 8080, | ||||
| 			}, | ||||
| 			expected: "http://backend2:8080/health", | ||||
| 			expected: expected{ | ||||
| 				err:   false, | ||||
| 				value: "http://backend2:8080/health", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc:      "scheme override", | ||||
| @@ -200,7 +217,46 @@ func TestNewRequest(t *testing.T) { | ||||
| 				Path:   "/test", | ||||
| 				Port:   0, | ||||
| 			}, | ||||
| 			expected: "http://backend1:80/test", | ||||
| 			expected: expected{ | ||||
| 				err:   false, | ||||
| 				value: "http://backend1:80/test", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc:      "path with param", | ||||
| 			serverURL: "http://backend1:80", | ||||
| 			options: Options{ | ||||
| 				Path: "/health?powpow=do", | ||||
| 				Port: 0, | ||||
| 			}, | ||||
| 			expected: expected{ | ||||
| 				err:   false, | ||||
| 				value: "http://backend1:80/health?powpow=do", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc:      "path with params", | ||||
| 			serverURL: "http://backend1:80", | ||||
| 			options: Options{ | ||||
| 				Path: "/health?powpow=do&do=powpow", | ||||
| 				Port: 0, | ||||
| 			}, | ||||
| 			expected: expected{ | ||||
| 				err:   false, | ||||
| 				value: "http://backend1:80/health?powpow=do&do=powpow", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc:      "path with invalid path", | ||||
| 			serverURL: "http://backend1:80", | ||||
| 			options: Options{ | ||||
| 				Path: ":", | ||||
| 				Port: 0, | ||||
| 			}, | ||||
| 			expected: expected{ | ||||
| 				err:   true, | ||||
| 				value: "", | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| @@ -211,13 +267,18 @@ func TestNewRequest(t *testing.T) { | ||||
|  | ||||
| 			backend := NewBackendConfig(test.options, "backendName") | ||||
|  | ||||
| 			u, err := url.Parse(test.serverURL) | ||||
| 			require.NoError(t, err) | ||||
| 			u := testhelpers.MustParseURL(test.serverURL) | ||||
|  | ||||
| 			req, err := backend.newRequest(u) | ||||
| 			require.NoError(t, err, "failed to create new backend request") | ||||
|  | ||||
| 			assert.Equal(t, test.expected, req.URL.String()) | ||||
| 			if test.expected.err { | ||||
| 				require.Error(t, err) | ||||
| 				assert.Nil(t, nil) | ||||
| 			} else { | ||||
| 				require.NoError(t, err, "failed to create new backend request") | ||||
| 				require.NotNil(t, req) | ||||
| 				assert.Equal(t, test.expected.value, req.URL.String()) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,37 @@ | ||||
| logLevel = "DEBUG" | ||||
|  | ||||
| defaultEntryPoints = ["https"] | ||||
|  | ||||
| [entryPoints] | ||||
|   [entryPoints.https] | ||||
|   address = ":4443" | ||||
|     [entryPoints.https.tls] | ||||
|      [entryPoints.https.tls.defaultCertificate] | ||||
|      certFile = "fixtures/https/wildcard.snitest.com.cert" | ||||
|      keyFile = "fixtures/https/wildcard.snitest.com.key" | ||||
|  | ||||
| [api] | ||||
|  | ||||
| [file] | ||||
|  | ||||
| [backends] | ||||
|   [backends.backend1] | ||||
|     [backends.backend1.servers.server1] | ||||
|     url = "http://127.0.0.1:9010" | ||||
|     weight = 1 | ||||
|  | ||||
| [frontends] | ||||
|   [frontends.frontend1] | ||||
|   backend = "backend1" | ||||
|     [frontends.frontend1.routes.test_1] | ||||
|     rule = "HostRegexp: {subdomain:[a-z1-9-]+}.snitest.com" | ||||
|   [frontends.frontend2] | ||||
|   backend = "backend1" | ||||
|     [frontends.frontend2.routes.test_1] | ||||
|     rule = "HostRegexp: {subdomain:[a-z1-9-]+}.www.snitest.com" | ||||
|  | ||||
| [[tls]] | ||||
|   entryPoints = ["https"] | ||||
|   [tls.certificate] | ||||
|     certFile = "fixtures/https/uppercase_wildcard.www.snitest.com.cert" | ||||
|     keyFile = "fixtures/https/uppercase_wildcard.www.snitest.com.key" | ||||
| @@ -0,0 +1,34 @@ | ||||
| logLevel = "DEBUG" | ||||
|  | ||||
| defaultEntryPoints = ["https"] | ||||
|  | ||||
| [entryPoints] | ||||
|   [entryPoints.https] | ||||
|   address = ":4443" | ||||
|     [entryPoints.https.tls] | ||||
|      [[entryPoints.https.tls.certificates]] | ||||
|      certFile = "fixtures/https/uppercase_wildcard.www.snitest.com.cert" | ||||
|      keyFile = "fixtures/https/uppercase_wildcard.www.snitest.com.key" | ||||
|      [entryPoints.https.tls.defaultCertificate] | ||||
|      certFile = "fixtures/https/wildcard.snitest.com.cert" | ||||
|      keyFile = "fixtures/https/wildcard.snitest.com.key" | ||||
|  | ||||
| [api] | ||||
|  | ||||
| [file] | ||||
|  | ||||
| [backends] | ||||
|   [backends.backend1] | ||||
|     [backends.backend1.servers.server1] | ||||
|     url = "http://127.0.0.1:9010" | ||||
|     weight = 1 | ||||
|  | ||||
| [frontends] | ||||
|   [frontends.frontend1] | ||||
|   backend = "backend1" | ||||
|     [frontends.frontend1.routes.test_1] | ||||
|     rule = "HostRegexp: {subdomain:[a-z1-9-]+}.snitest.com" | ||||
|   [frontends.frontend2] | ||||
|   backend = "backend1" | ||||
|     [frontends.frontend2.routes.test_2] | ||||
|     rule = "HostRegexp: {subdomain:[a-z1-9-]+}.www.snitest.com" | ||||
| @@ -0,0 +1,19 @@ | ||||
| -----BEGIN CERTIFICATE----- | ||||
| MIIDDDCCAfSgAwIBAgIJAI1YpPACcsQnMA0GCSqGSIb3DQEBCwUAMB4xHDAaBgNV | ||||
| BAMME0ZPTy5XV1cuU05JVEVTVC5DT00wHhcNMTgxMDI5MTU1NDI4WhcNMjgxMDI2 | ||||
| MTU1NDI4WjAeMRwwGgYDVQQDDBNGT08uV1dXLlNOSVRFU1QuQ09NMIIBIjANBgkq | ||||
| hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxyWr+1O/tf4yjwhfp3/SDGT5fD0chhGs | ||||
| Qc+QM7Ewb5SOmIL5UskxT5pCKc6Kuie5qqEp9xH8Rrfo18iEJQPdhFC1YkaBEI0L | ||||
| l1qvN4jmXzAK/E/u4+X+FFprHyruXCmuXqsWQt/qEOqU1ciN47GE9+ZW4R+q70uB | ||||
| zrEQ+dzN7IBsyf1lzzS3/TwDgj085QmiZYxKxX40d5hZW6AHxPEKJa2p+Gweqg74 | ||||
| SpzBWL1DYQLcqHUuMKlbigHg+gleqcO8NiHT5UdeSPVokD5VJPO1La1PMqkLmJYr | ||||
| 3vVkQ9YzNQ615bX98VMIi17cmE7LE+vz+v287cdFT2f1pNXr3pCGzQIDAQABo00w | ||||
| SzALBgNVHQ8EBAMCBDAwCQYDVR0TBAIwADATBgNVHSUEDDAKBggrBgEFBQcDATAc | ||||
| BgNVHREEFTATghEqLldXVy5TTklURVNULkNPTTANBgkqhkiG9w0BAQsFAAOCAQEA | ||||
| HJyMCj9oHwECmSGWHnYHkO42zeyj24RKlhNG5skUCqZmpmeDc2BRMYH4fjP75MD2 | ||||
| kuasZBMAxyQnur/DEn8TzQ1mlKxYCqoza1ql5PkfcwNUp/tvQ7Jhf45Z5mQVeUM7 | ||||
| RSiBhpeetjHY5/xQb7gXHa97+OjDoRJ6NL/dzGxqypf37kiQPw2jWI5RTFBkP+h/ | ||||
| sPbeAZJjmiEzvw31SAw9IGj3VvIwcuTxbsdJQITU7hCXDSd1EIocmzAoobY7WRcT | ||||
| B1pLmHlP/BaIsM7m0NF/HgUsgo/kgSsxnGA2MHMYQiTImR2DUgrJYzKlJ5acscLK | ||||
| sMq9xUnjr6KF1C15R2FpDw== | ||||
| -----END CERTIFICATE----- | ||||
| @@ -0,0 +1,28 @@ | ||||
| -----BEGIN PRIVATE KEY----- | ||||
| MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDHJav7U7+1/jKP | ||||
| CF+nf9IMZPl8PRyGEaxBz5AzsTBvlI6YgvlSyTFPmkIpzoq6J7mqoSn3EfxGt+jX | ||||
| yIQlA92EULViRoEQjQuXWq83iOZfMAr8T+7j5f4UWmsfKu5cKa5eqxZC3+oQ6pTV | ||||
| yI3jsYT35lbhH6rvS4HOsRD53M3sgGzJ/WXPNLf9PAOCPTzlCaJljErFfjR3mFlb | ||||
| oAfE8Qolran4bB6qDvhKnMFYvUNhAtyodS4wqVuKAeD6CV6pw7w2IdPlR15I9WiQ | ||||
| PlUk87UtrU8yqQuYlive9WRD1jM1DrXltf3xUwiLXtyYTssT6/P6/bztx0VPZ/Wk | ||||
| 1evekIbNAgMBAAECggEAVOFEnTmD47D1oasjAgRj5a5/+6kcaDROJDqwrqeeCmDa | ||||
| KjzgwZ1JLDGGc8U5scBOzWAlv83lpcqrLpWjZRdxqfywYrPEPOaxAxC+z7/E2Ntk | ||||
| Q0hafL5BfjFPqRgmQhft3yGyukwvuogRadEyUNMP5o1BiHBz7cxUBmHH54dqKZuO | ||||
| ueUMgqraJX/GK+Om2rIUst0oOT9yUED+f6ciIjVAmCx1EVxZmX7sxKig10e70eOJ | ||||
| rfHlRguJWtxy0+Wl8R8TVrpI5r7qsE8y2fet9RqFOof/4ds8uA2nlZ3NpGkAq3Oo | ||||
| +65h/2fjD5uQ7jmT+XZcbC7SGhboV42zIrmn0DyNIQKBgQDneeqzMlooNzLD6x+v | ||||
| bXo6BJAHXuml440zS5i5RawKc3+/GxGQjBvnfhFH6AQ7cL4ohYyfuAo4srgifRle | ||||
| x3Gl8yvFf0uLaQHj811HPWV0fU8bwekI77jmH7WZi2ED/qX7X06R2vvUPGshvJi5 | ||||
| yPCmJpDQQA6wmxBG1U4SqNw0xQKBgQDcPu2DMAJpbMWWeb5xWv5/6h6TUF4tV7fV | ||||
| eIBWuVfe9Jry3gAnb6YUOKYmA5xYJJ+fTz4Nhe4+LQbFS1esT/7ZIATvILogZc3S | ||||
| X9+ZCYG/tmDDZvhZqIWWSzzdrjb7dseP1RI4Wp6OnRqHWErrkfzDJKuN15qgW5vR | ||||
| FUR2ykV6aQKBgQCv5ZQ00dly3+ciu+QbAb00o0zzXOt91Lnytcp7V3dRhc0YYrBp | ||||
| QB7gPYtSMfwtUxIdZsaihE64IQ8NnjSOMk6pRW0Iqh+083mtR7ylKwGSkLpxpFu6 | ||||
| H7hInuX3pNN3HqXwq87fxSFCeRsLyu3fl9NO3tWCenrvNxYaTXMDeO/E5QKBgE7D | ||||
| XlMU/zfOg1bN0PJe1TbPdgG+sv9KKF76CgN5otgD58nE5I812VHP9HMRxX6sEj15 | ||||
| rDpP1CR+G7bAu+jObtgdIEaYEJf3cES0rpTfFnyF71LR5yzBHIzj+S9Z1yXUk4d3 | ||||
| bl2i4qMjwdH3HEvkWF09JvDB0vVX7YA3N9W3fmNJAoGBALRi9EbkEBW1vMPwMzps | ||||
| YoJ1lp/YyDGTFcg6KFgTfNaOYccb6EXL2Cd21qvDsJw6wthXS+cSqX3qlTLAVLY8 | ||||
| az/NfyFmW1fUtGjs2s0ZtplStGBhv8VR+2fpt9fgDOOrGYiN2dtmPm7jCAmyQQq7 | ||||
| JCg7Vq6f0q95DUwiUAo24CBn | ||||
| -----END PRIVATE KEY----- | ||||
| @@ -830,3 +830,71 @@ func (s *HTTPSSuite) TestEntrypointHttpsRedirectAndPathModification(c *check.C) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // TestWithSNIStaticCaseInsensitive involves a client sending a SNI hostname of | ||||
| // "bar.www.snitest.com", which matches the DNS SAN of '*.WWW.SNITEST.COM'. The test | ||||
| // verifies that traefik presents the correct certificate. | ||||
| func (s *HTTPSSuite) TestWithSNIStaticCaseInsensitive(c *check.C) { | ||||
| 	cmd, display := s.traefikCmd(withConfigFile("fixtures/https/https_sni_case_insensitive_static.toml")) | ||||
| 	defer display(c) | ||||
| 	err := cmd.Start() | ||||
| 	c.Assert(err, checker.IsNil) | ||||
| 	defer cmd.Process.Kill() | ||||
|  | ||||
| 	// wait for Traefik | ||||
| 	err = try.GetRequest("http://127.0.0.1:8080/api/providers", 500*time.Millisecond, try.BodyContains("HostRegexp: {subdomain:[a-z1-9-]+}.www.snitest.com")) | ||||
| 	c.Assert(err, checker.IsNil) | ||||
|  | ||||
| 	tlsConfig := &tls.Config{ | ||||
| 		InsecureSkipVerify: true, | ||||
| 		ServerName:         "bar.www.snitest.com", | ||||
| 		NextProtos:         []string{"h2", "http/1.1"}, | ||||
| 	} | ||||
| 	conn, err := tls.Dial("tcp", "127.0.0.1:4443", tlsConfig) | ||||
| 	c.Assert(err, checker.IsNil, check.Commentf("failed to connect to server")) | ||||
|  | ||||
| 	defer conn.Close() | ||||
| 	err = conn.Handshake() | ||||
| 	c.Assert(err, checker.IsNil, check.Commentf("TLS handshake error")) | ||||
|  | ||||
| 	cs := conn.ConnectionState() | ||||
| 	err = cs.PeerCertificates[0].VerifyHostname("*.WWW.SNITEST.COM") | ||||
| 	c.Assert(err, checker.IsNil, check.Commentf("certificate did not match SNI servername")) | ||||
|  | ||||
| 	proto := conn.ConnectionState().NegotiatedProtocol | ||||
| 	c.Assert(proto, checker.Equals, "h2") | ||||
| } | ||||
|  | ||||
| // TestWithSNIDynamicCaseInsensitive involves a client sending a SNI hostname of | ||||
| // "bar.www.snitest.com", which matches the DNS SAN of '*.WWW.SNITEST.COM'. The test | ||||
| // verifies that traefik presents the correct certificate. | ||||
| func (s *HTTPSSuite) TestWithSNIDynamicCaseInsensitive(c *check.C) { | ||||
| 	cmd, display := s.traefikCmd(withConfigFile("fixtures/https/https_sni_case_insensitive_dynamic.toml")) | ||||
| 	defer display(c) | ||||
| 	err := cmd.Start() | ||||
| 	c.Assert(err, checker.IsNil) | ||||
| 	defer cmd.Process.Kill() | ||||
|  | ||||
| 	// wait for Traefik | ||||
| 	err = try.GetRequest("http://127.0.0.1:8080/api/providers", 500*time.Millisecond, try.BodyContains("HostRegexp: {subdomain:[a-z1-9-]+}.www.snitest.com")) | ||||
| 	c.Assert(err, checker.IsNil) | ||||
|  | ||||
| 	tlsConfig := &tls.Config{ | ||||
| 		InsecureSkipVerify: true, | ||||
| 		ServerName:         "bar.www.snitest.com", | ||||
| 		NextProtos:         []string{"h2", "http/1.1"}, | ||||
| 	} | ||||
| 	conn, err := tls.Dial("tcp", "127.0.0.1:4443", tlsConfig) | ||||
| 	c.Assert(err, checker.IsNil, check.Commentf("failed to connect to server")) | ||||
|  | ||||
| 	defer conn.Close() | ||||
| 	err = conn.Handshake() | ||||
| 	c.Assert(err, checker.IsNil, check.Commentf("TLS handshake error")) | ||||
|  | ||||
| 	cs := conn.ConnectionState() | ||||
| 	err = cs.PeerCertificates[0].VerifyHostname("*.WWW.SNITEST.COM") | ||||
| 	c.Assert(err, checker.IsNil, check.Commentf("certificate did not match SNI servername")) | ||||
|  | ||||
| 	proto := conn.ConnectionState().NegotiatedProtocol | ||||
| 	c.Assert(proto, checker.Equals, "h2") | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -134,7 +134,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) | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -45,6 +45,17 @@ func TestAuthUsersFromFile(t *testing.T) { | ||||
| 				return parserDigestUsers(digest) | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			authType: "basic", | ||||
| 			usersStr: "#Comment\ntest:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/\ntest2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0\n", | ||||
| 			userKeys: []string{"test", "test2"}, | ||||
| 			parserFunc: func(fileName string) (map[string]string, error) { | ||||
| 				basic := &types.Basic{ | ||||
| 					UsersFile: fileName, | ||||
| 				} | ||||
| 				return parserBasicUsers(basic) | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, test := range tests { | ||||
|   | ||||
| @@ -17,6 +17,7 @@ func NewSecure(headers *types.Headers) *secure.Secure { | ||||
| 		SSLRedirect:             headers.SSLRedirect, | ||||
| 		SSLTemporaryRedirect:    headers.SSLTemporaryRedirect, | ||||
| 		SSLHost:                 headers.SSLHost, | ||||
| 		SSLForceHost:            headers.SSLForceHost, | ||||
| 		SSLProxyHeaders:         headers.SSLProxyHeaders, | ||||
| 		STSSeconds:              headers.STSSeconds, | ||||
| 		STSIncludeSubdomains:    headers.STSIncludeSubdomains, | ||||
|   | ||||
							
								
								
									
										96
									
								
								middlewares/secure_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								middlewares/secure_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| package middlewares | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"net/http/httptest" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/containous/traefik/testhelpers" | ||||
| 	"github.com/containous/traefik/types" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/unrolled/secure" | ||||
| ) | ||||
|  | ||||
| func TestSSLForceHost(t *testing.T) { | ||||
| 	testCases := []struct { | ||||
| 		desc             string | ||||
| 		host             string | ||||
| 		secureMiddleware *secure.Secure | ||||
| 		expected         int | ||||
| 	}{ | ||||
| 		{ | ||||
| 			desc: "http should return a 301", | ||||
| 			host: "http://powpow.example.com", | ||||
| 			secureMiddleware: NewSecure(&types.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(&types.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(&types.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(&types.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(&types.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(&types.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) | ||||
| 			next := func(rw http.ResponseWriter, r *http.Request) { | ||||
| 				rw.Write([]byte("OK")) | ||||
| 			} | ||||
|  | ||||
| 			rw := httptest.NewRecorder() | ||||
| 			test.secureMiddleware.HandlerFuncWithNextForRequestOnly(rw, req, next) | ||||
|  | ||||
| 			assert.Equal(t, test.expected, rw.Result().StatusCode) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| @@ -822,6 +822,113 @@ func TestProviderBuildConfiguration(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestProviderBuildConfigurationCustomPrefix(t *testing.T) { | ||||
| 	prefix := "traefik-test" | ||||
| 	p := &Provider{ | ||||
| 		Domain:               "localhost", | ||||
| 		Prefix:               prefix, | ||||
| 		ExposedByDefault:     false, | ||||
| 		FrontEndRule:         "Host:{{.ServiceName}}.{{.Domain}}", | ||||
| 		frontEndRuleTemplate: template.New("consul catalog frontend rule"), | ||||
| 	} | ||||
|  | ||||
| 	testCases := []struct { | ||||
| 		desc              string | ||||
| 		nodes             []catalogUpdate | ||||
| 		expectedFrontends map[string]*types.Frontend | ||||
| 		expectedBackends  map[string]*types.Backend | ||||
| 	}{ | ||||
| 		{ | ||||
| 			desc: "Should build config which contains three frontends and one backend", | ||||
| 			nodes: []catalogUpdate{ | ||||
| 				{ | ||||
| 					Service: &serviceUpdate{ | ||||
| 						ServiceName: "test", | ||||
| 						Attributes: []string{ | ||||
| 							"random.foo=bar", | ||||
| 							prefix + ".frontend.rule=Host:A", | ||||
| 							prefix + ".frontends.test1.rule=Host:B", | ||||
| 							prefix + ".frontends.test2.rule=Host:C", | ||||
| 						}, | ||||
| 					}, | ||||
| 					Nodes: []*api.ServiceEntry{ | ||||
| 						{ | ||||
| 							Service: &api.AgentService{ | ||||
| 								Service: "test", | ||||
| 								Address: "127.0.0.1", | ||||
| 								Port:    80, | ||||
| 								Tags: []string{ | ||||
| 									"random.foo=bar", | ||||
| 								}, | ||||
| 							}, | ||||
| 							Node: &api.Node{ | ||||
| 								Node:    "localhost", | ||||
| 								Address: "127.0.0.1", | ||||
| 							}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expectedFrontends: map[string]*types.Frontend{ | ||||
| 				"frontend-test": { | ||||
| 					Backend:        "backend-test", | ||||
| 					PassHostHeader: true, | ||||
| 					Routes: map[string]types.Route{ | ||||
| 						"route-host-test": { | ||||
| 							Rule: "Host:A", | ||||
| 						}, | ||||
| 					}, | ||||
| 					EntryPoints: []string{}, | ||||
| 				}, | ||||
| 				"frontend-test-test1": { | ||||
| 					Backend:        "backend-test", | ||||
| 					PassHostHeader: true, | ||||
| 					Routes: map[string]types.Route{ | ||||
| 						"route-host-test-test1": { | ||||
| 							Rule: "Host:B", | ||||
| 						}, | ||||
| 					}, | ||||
| 					EntryPoints: []string{}, | ||||
| 				}, | ||||
| 				"frontend-test-test2": { | ||||
| 					Backend:        "backend-test", | ||||
| 					PassHostHeader: true, | ||||
| 					Routes: map[string]types.Route{ | ||||
| 						"route-host-test-test2": { | ||||
| 							Rule: "Host:C", | ||||
| 						}, | ||||
| 					}, | ||||
| 					EntryPoints: []string{}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expectedBackends: map[string]*types.Backend{ | ||||
| 				"backend-test": { | ||||
| 					Servers: map[string]types.Server{ | ||||
| 						"test-0-O0Tnh-SwzY69M6SurTKP3wNKkzI": { | ||||
| 							URL:    "http://127.0.0.1:80", | ||||
| 							Weight: 1, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, test := range testCases { | ||||
| 		test := test | ||||
| 		t.Run(test.desc, func(t *testing.T) { | ||||
| 			t.Parallel() | ||||
|  | ||||
| 			nodes := fakeLoadTraefikLabelsSlice(test.nodes, p.Prefix) | ||||
|  | ||||
| 			actualConfig := p.buildConfigurationV2(nodes) | ||||
| 			assert.NotNil(t, actualConfig) | ||||
| 			assert.Equal(t, test.expectedBackends, actualConfig.Backends) | ||||
| 			assert.Equal(t, test.expectedFrontends, actualConfig.Frontends) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestGetTag(t *testing.T) { | ||||
| 	testCases := []struct { | ||||
| 		desc         string | ||||
|   | ||||
| @@ -578,7 +578,7 @@ func (p *Provider) generateFrontends(service *serviceUpdate) []*serviceUpdate { | ||||
| 	}) | ||||
|  | ||||
| 	// loop over children of <prefix>.frontends.* | ||||
| 	for _, frontend := range getSegments(p.Prefix+".frontends", p.Prefix, service.TraefikLabels) { | ||||
| 	for _, frontend := range getSegments(label.Prefix+"frontends", label.Prefix, service.TraefikLabels) { | ||||
| 		frontends = append(frontends, &serviceUpdate{ | ||||
| 			ServiceName:       service.ServiceName + "-" + frontend.Name, | ||||
| 			ParentServiceName: service.ServiceName, | ||||
| @@ -589,6 +589,7 @@ func (p *Provider) generateFrontends(service *serviceUpdate) []*serviceUpdate { | ||||
|  | ||||
| 	return frontends | ||||
| } | ||||
|  | ||||
| func getSegments(path string, prefix string, tree map[string]string) []*frontendSegment { | ||||
| 	segments := make([]*frontendSegment, 0) | ||||
| 	// find segment names | ||||
| @@ -598,13 +599,12 @@ func getSegments(path string, prefix string, tree map[string]string) []*frontend | ||||
| 			segmentNames[strings.SplitN(strings.TrimPrefix(key, path+"."), ".", 2)[0]] = true | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// get labels for each segment found | ||||
| 	for segment := range segmentNames { | ||||
| 		labels := make(map[string]string) | ||||
| 		for key, value := range tree { | ||||
| 			if strings.HasPrefix(key, path+"."+segment) { | ||||
| 				labels[prefix+".frontend"+strings.TrimPrefix(key, path+"."+segment)] = value | ||||
| 				labels[prefix+"frontend"+strings.TrimPrefix(key, path+"."+segment)] = value | ||||
| 			} | ||||
| 		} | ||||
| 		segments = append(segments, &frontendSegment{ | ||||
|   | ||||
| @@ -30,22 +30,21 @@ import ( | ||||
| const ( | ||||
| 	// SwarmAPIVersion is a constant holding the version of the Provider API traefik will use | ||||
| 	SwarmAPIVersion = "1.24" | ||||
| 	// SwarmDefaultWatchTime is the duration of the interval when polling docker | ||||
| 	SwarmDefaultWatchTime = 15 * time.Second | ||||
| ) | ||||
|  | ||||
| var _ provider.Provider = (*Provider)(nil) | ||||
|  | ||||
| // Provider holds configurations of the provider. | ||||
| type Provider struct { | ||||
| 	provider.BaseProvider `mapstructure:",squash" export:"true"` | ||||
| 	Endpoint              string           `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint"` | ||||
| 	Domain                string           `description:"Default domain used"` | ||||
| 	TLS                   *types.ClientTLS `description:"Enable Docker TLS support" export:"true"` | ||||
| 	ExposedByDefault      bool             `description:"Expose containers by default" export:"true"` | ||||
| 	UseBindPortIP         bool             `description:"Use the ip address from the bound port, rather than from the inner network" export:"true"` | ||||
| 	SwarmMode             bool             `description:"Use Docker on Swarm Mode" export:"true"` | ||||
| 	Network               string           `description:"Default Docker network used" export:"true"` | ||||
| 	provider.BaseProvider   `mapstructure:",squash" export:"true"` | ||||
| 	Endpoint                string           `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint"` | ||||
| 	Domain                  string           `description:"Default domain used"` | ||||
| 	TLS                     *types.ClientTLS `description:"Enable Docker TLS support" export:"true"` | ||||
| 	ExposedByDefault        bool             `description:"Expose containers by default" export:"true"` | ||||
| 	UseBindPortIP           bool             `description:"Use the ip address from the bound port, rather than from the inner network" export:"true"` | ||||
| 	SwarmMode               bool             `description:"Use Docker on Swarm Mode" export:"true"` | ||||
| 	Network                 string           `description:"Default Docker network used" export:"true"` | ||||
| 	SwarmModeRefreshSeconds int              `description:"Polling interval for swarm mode (in seconds)" export:"true"` | ||||
| } | ||||
|  | ||||
| // Init the provider | ||||
| @@ -97,7 +96,10 @@ func (p *Provider) createClient() (client.APIClient, error) { | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		sockets.ConfigureTransport(tr, hostURL.Scheme, hostURL.Host) | ||||
|  | ||||
| 		if err := sockets.ConfigureTransport(tr, hostURL.Scheme, hostURL.Host); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		httpClient = &http.Client{ | ||||
| 			Transport: tr, | ||||
| @@ -162,7 +164,7 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s | ||||
| 				if p.SwarmMode { | ||||
| 					errChan := make(chan error) | ||||
| 					// TODO: This need to be change. Linked to Swarm events docker/docker#23827 | ||||
| 					ticker := time.NewTicker(SwarmDefaultWatchTime) | ||||
| 					ticker := time.NewTicker(time.Second * time.Duration(p.SwarmModeRefreshSeconds)) | ||||
| 					pool.GoCtx(func(ctx context.Context) { | ||||
| 						defer close(errChan) | ||||
| 						for { | ||||
| @@ -210,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(): | ||||
| 							} | ||||
|  | ||||
| 						} | ||||
| 					} | ||||
|  | ||||
|   | ||||
| @@ -240,7 +240,9 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI | ||||
| 					log.Errorf("Unable to describe tasks for %v", page.TaskArns) | ||||
| 				} else { | ||||
| 					for _, t := range resp.Tasks { | ||||
| 						tasks[aws.StringValue(t.TaskArn)] = t | ||||
| 						if aws.StringValue(t.LastStatus) == ecs.DesiredStatusRunning { | ||||
| 							tasks[aws.StringValue(t.TaskArn)] = t | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|   | ||||
| @@ -357,15 +357,16 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error) | ||||
| 							continue | ||||
| 						} | ||||
|  | ||||
| 						if service.Spec.Type == "ExternalName" { | ||||
| 						// We have to treat external-name service differently here b/c it doesn't have any endpoints | ||||
| 						if service.Spec.Type == corev1.ServiceTypeExternalName { | ||||
| 							url := protocol + "://" + service.Spec.ExternalName | ||||
| 							if port.Port != 443 && port.Port != 80 { | ||||
| 								url = fmt.Sprintf("%s:%d", url, port.Port) | ||||
| 							} | ||||
|  | ||||
| 							externalNameServiceWeight := weightAllocator.getWeight(r.Host, pa.Path, pa.Backend.ServiceName) | ||||
| 							templateObjects.Backends[baseName].Servers[url] = types.Server{ | ||||
| 								URL:    url, | ||||
| 								Weight: label.DefaultWeight, | ||||
| 								Weight: externalNameServiceWeight, | ||||
| 							} | ||||
| 						} else { | ||||
| 							endpoints, exists, err := k8sClient.GetEndpoints(service.Namespace, service.Name) | ||||
|   | ||||
| @@ -3206,6 +3206,7 @@ func TestPercentageWeightServiceAnnotation(t *testing.T) { | ||||
| 		buildIngress( | ||||
| 			iAnnotation(annotationKubernetesServiceWeights, ` | ||||
| service1: 10% | ||||
| service3: 20% | ||||
| `), | ||||
| 			iNamespace("testing"), | ||||
| 			iRules( | ||||
| @@ -3214,6 +3215,7 @@ service1: 10% | ||||
| 					iPaths( | ||||
| 						onePath(iPath("/foo"), iBackend("service1", intstr.FromString("8080"))), | ||||
| 						onePath(iPath("/foo"), iBackend("service2", intstr.FromString("7070"))), | ||||
| 						onePath(iPath("/foo"), iBackend("service3", intstr.FromString("9090"))), | ||||
| 						onePath(iPath("/bar"), iBackend("service2", intstr.FromString("7070"))), | ||||
| 					)), | ||||
| 			), | ||||
| @@ -3238,6 +3240,16 @@ service1: 10% | ||||
| 				sPorts(sPort(7070, "")), | ||||
| 			), | ||||
| 		), | ||||
| 		buildService( | ||||
| 			sName("service3"), | ||||
| 			sNamespace("testing"), | ||||
| 			sUID("1"), | ||||
| 			sSpec( | ||||
| 				sType(corev1.ServiceTypeExternalName), | ||||
| 				sExternalName("example.com"), | ||||
| 				sPorts(sPort(9090, "")), | ||||
| 			), | ||||
| 		), | ||||
| 	} | ||||
|  | ||||
| 	endpoints := []*corev1.Endpoints{ | ||||
| @@ -3285,8 +3297,9 @@ service1: 10% | ||||
| 				servers( | ||||
| 					server("http://10.10.0.1:8080", weight(int(newPercentageValueFromFloat64(0.05)))), | ||||
| 					server("http://10.10.0.2:8080", weight(int(newPercentageValueFromFloat64(0.05)))), | ||||
| 					server("http://10.10.0.3:7070", weight(int(newPercentageValueFromFloat64(0.45)))), | ||||
| 					server("http://10.10.0.4:7070", weight(int(newPercentageValueFromFloat64(0.45)))), | ||||
| 					server("http://10.10.0.3:7070", weight(int(newPercentageValueFromFloat64(0.35)))), | ||||
| 					server("http://10.10.0.4:7070", weight(int(newPercentageValueFromFloat64(0.35)))), | ||||
| 					server("http://example.com:9090", weight(int(newPercentageValueFromFloat64(0.2)))), | ||||
| 				), | ||||
| 				lbMethod("wrr"), | ||||
| 			), | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import ( | ||||
|  | ||||
| 	"github.com/containous/traefik/provider/label" | ||||
| 	"gopkg.in/yaml.v2" | ||||
| 	corev1 "k8s.io/api/core/v1" | ||||
| 	extensionsv1beta1 "k8s.io/api/extensions/v1beta1" | ||||
| ) | ||||
|  | ||||
| @@ -165,6 +166,23 @@ func getServiceInstanceCounts(ingress *extensionsv1beta1.Ingress, client Client) | ||||
|  | ||||
| 	for _, rule := range ingress.Spec.Rules { | ||||
| 		for _, pa := range rule.HTTP.Paths { | ||||
| 			svc, exists, err := client.GetService(ingress.Namespace, pa.Backend.ServiceName) | ||||
| 			if err != nil { | ||||
| 				return nil, fmt.Errorf("failed to get service %s/%s: %v", ingress.Namespace, pa.Backend.ServiceName, err) | ||||
| 			} | ||||
| 			if !exists { | ||||
| 				return nil, fmt.Errorf("service not found for %s/%s", ingress.Namespace, pa.Backend.ServiceName) | ||||
| 			} | ||||
| 			if svc.Spec.Type == corev1.ServiceTypeExternalName { | ||||
| 				// external-name service has only one instance b/c it will actually be interpreted as a DNS record | ||||
| 				// instead of real server. | ||||
| 				serviceInstanceCounts[ingressService{ | ||||
| 					host:    rule.Host, | ||||
| 					path:    pa.Path, | ||||
| 					service: pa.Backend.ServiceName, | ||||
| 				}] = 1 | ||||
| 				continue | ||||
| 			} | ||||
| 			count := 0 | ||||
| 			endpoints, exists, err := client.GetEndpoints(ingress.Namespace, pa.Backend.ServiceName) | ||||
| 			if err != nil { | ||||
|   | ||||
| @@ -169,6 +169,24 @@ service1: 1000% | ||||
|  | ||||
| func TestComputeServiceWeights(t *testing.T) { | ||||
| 	client := clientMock{ | ||||
| 		services: []*corev1.Service{ | ||||
| 			buildService( | ||||
| 				sName("service1"), | ||||
| 				sNamespace("testing"), | ||||
| 			), | ||||
| 			buildService( | ||||
| 				sName("service2"), | ||||
| 				sNamespace("testing"), | ||||
| 			), | ||||
| 			buildService( | ||||
| 				sName("service3"), | ||||
| 				sNamespace("testing"), | ||||
| 			), | ||||
| 			buildService( | ||||
| 				sName("service4"), | ||||
| 				sNamespace("testing"), | ||||
| 			), | ||||
| 		}, | ||||
| 		endpoints: []*corev1.Endpoints{ | ||||
| 			buildEndpoint( | ||||
| 				eNamespace("testing"), | ||||
| @@ -446,8 +464,12 @@ service2: 80% | ||||
| 			if test.expectError { | ||||
| 				require.Error(t, err) | ||||
| 			} else { | ||||
| 				for ingSvc, percentage := range test.expectedWeights { | ||||
| 					assert.Equal(t, int(percentage), weightAllocator.getWeight(ingSvc.host, ingSvc.path, ingSvc.service)) | ||||
| 				if err != nil { | ||||
| 					t.Errorf("%v failed: %v", test.desc, err) | ||||
| 				} else { | ||||
| 					for ingSvc, percentage := range test.expectedWeights { | ||||
| 						assert.Equal(t, int(percentage), weightAllocator.getWeight(ingSvc.host, ingSvc.path, ingSvc.service)) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		}) | ||||
|   | ||||
| @@ -109,6 +109,8 @@ func GetAuth(labels map[string]string) *types.Auth { | ||||
| 		auth.Digest = getAuthDigest(labels) | ||||
| 	} else if HasPrefix(labels, TraefikFrontendAuthForward) { | ||||
| 		auth.Forward = getAuthForward(labels) | ||||
| 	} else { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return auth | ||||
|   | ||||
| @@ -732,6 +732,13 @@ func TestGetAuth(t *testing.T) { | ||||
| 			labels:   map[string]string{}, | ||||
| 			expected: nil, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc: "should return nil when no real auth", | ||||
| 			labels: map[string]string{ | ||||
| 				TraefikFrontendAuthHeaderField: "myHeaderField", | ||||
| 			}, | ||||
| 			expected: nil, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc: "should return a basic auth", | ||||
| 			labels: map[string]string{ | ||||
|   | ||||
| @@ -7,6 +7,12 @@ import ( | ||||
| 	"github.com/containous/traefik/log" | ||||
| 	"github.com/containous/traefik/whitelist" | ||||
| 	"github.com/vulcand/oxy/forward" | ||||
| 	"github.com/vulcand/oxy/utils" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	xForwardedURI    = "X-Forwarded-Uri" | ||||
| 	xForwardedMethod = "X-Forwarded-Method" | ||||
| ) | ||||
|  | ||||
| // NewHeaderRewriter Create a header rewriter | ||||
| @@ -45,6 +51,8 @@ func (h *headerRewriter) Rewrite(req *http.Request) { | ||||
| 	err := h.ips.IsAuthorized(req) | ||||
| 	if err != nil { | ||||
| 		log.Debug(err) | ||||
| 		// Remove additional X-Forwarded Headers which are used by the forward authentication | ||||
| 		utils.RemoveHeaders(req.Header, xForwardedURI, xForwardedMethod) | ||||
| 		h.secureRewriter.Rewrite(req) | ||||
| 		return | ||||
| 	} | ||||
|   | ||||
| @@ -23,8 +23,10 @@ func TestHeaderRewriter_Rewrite(t *testing.T) { | ||||
| 			trustedIPs: []string{"10.10.10.10"}, | ||||
| 			insecure:   false, | ||||
| 			expected: map[string]string{ | ||||
| 				"X-Foo":           "bar", | ||||
| 				"X-Forwarded-For": "30.30.30.30", | ||||
| 				"X-Foo":              "bar", | ||||
| 				"X-Forwarded-For":    "30.30.30.30", | ||||
| 				"X-Forwarded-Uri":    "/bar", | ||||
| 				"X-Forwarded-Method": "GET", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| @@ -33,8 +35,10 @@ func TestHeaderRewriter_Rewrite(t *testing.T) { | ||||
| 			trustedIPs: []string{"10.10.10.10"}, | ||||
| 			insecure:   false, | ||||
| 			expected: map[string]string{ | ||||
| 				"X-Foo":           "bar", | ||||
| 				"X-Forwarded-For": "", | ||||
| 				"X-Foo":              "bar", | ||||
| 				"X-Forwarded-For":    "", | ||||
| 				"X-Forwarded-Uri":    "", | ||||
| 				"X-Forwarded-Method": "", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| @@ -43,8 +47,10 @@ func TestHeaderRewriter_Rewrite(t *testing.T) { | ||||
| 			trustedIPs: []string{"10.10.10.10"}, | ||||
| 			insecure:   false, | ||||
| 			expected: map[string]string{ | ||||
| 				"X-Foo":           "bar", | ||||
| 				"X-Forwarded-For": "", | ||||
| 				"X-Foo":              "bar", | ||||
| 				"X-Forwarded-For":    "", | ||||
| 				"X-Forwarded-Uri":    "", | ||||
| 				"X-Forwarded-Method": "", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| @@ -53,8 +59,10 @@ func TestHeaderRewriter_Rewrite(t *testing.T) { | ||||
| 			trustedIPs: []string{"10.10.10.10"}, | ||||
| 			insecure:   true, | ||||
| 			expected: map[string]string{ | ||||
| 				"X-Foo":           "bar", | ||||
| 				"X-Forwarded-For": "30.30.30.30", | ||||
| 				"X-Foo":              "bar", | ||||
| 				"X-Forwarded-For":    "30.30.30.30", | ||||
| 				"X-Forwarded-Uri":    "/bar", | ||||
| 				"X-Forwarded-Method": "GET", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| @@ -63,8 +71,10 @@ func TestHeaderRewriter_Rewrite(t *testing.T) { | ||||
| 			trustedIPs: []string{"10.10.10.10"}, | ||||
| 			insecure:   true, | ||||
| 			expected: map[string]string{ | ||||
| 				"X-Foo":           "bar", | ||||
| 				"X-Forwarded-For": "30.30.30.30", | ||||
| 				"X-Foo":              "bar", | ||||
| 				"X-Forwarded-For":    "30.30.30.30", | ||||
| 				"X-Forwarded-Uri":    "/bar", | ||||
| 				"X-Forwarded-Method": "GET", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| @@ -73,8 +83,10 @@ func TestHeaderRewriter_Rewrite(t *testing.T) { | ||||
| 			trustedIPs: []string{"10.10.10.10"}, | ||||
| 			insecure:   true, | ||||
| 			expected: map[string]string{ | ||||
| 				"X-Foo":           "bar", | ||||
| 				"X-Forwarded-For": "30.30.30.30", | ||||
| 				"X-Foo":              "bar", | ||||
| 				"X-Forwarded-For":    "30.30.30.30", | ||||
| 				"X-Forwarded-Uri":    "/bar", | ||||
| 				"X-Forwarded-Method": "GET", | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| @@ -93,6 +105,8 @@ func TestHeaderRewriter_Rewrite(t *testing.T) { | ||||
|  | ||||
| 			req.Header.Set("X-Foo", "bar") | ||||
| 			req.Header.Set("X-Forwarded-For", "30.30.30.30") | ||||
| 			req.Header.Set("X-Forwarded-Uri", "/bar") | ||||
| 			req.Header.Set("X-Forwarded-Method", "GET") | ||||
|  | ||||
| 			rewriter.Rewrite(req) | ||||
|  | ||||
|   | ||||
| @@ -13,6 +13,7 @@ import ( | ||||
| 	"net/url" | ||||
| 	"os" | ||||
| 	"os/signal" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| @@ -355,10 +356,14 @@ func (s *Server) listenProviders(stop chan bool) { | ||||
| 		case <-stop: | ||||
| 			return | ||||
| 		case configMsg, ok := <-s.configurationChan: | ||||
| 			if !ok || configMsg.Configuration == nil { | ||||
| 			if !ok { | ||||
| 				return | ||||
| 			} | ||||
| 			s.preLoadConfiguration(configMsg) | ||||
| 			if configMsg.Configuration != nil { | ||||
| 				s.preLoadConfiguration(configMsg) | ||||
| 			} else { | ||||
| 				log.Debugf("Received nil configuration from provider %q, skipping.", configMsg.ProviderName) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -711,13 +716,13 @@ func (s *Server) buildNameOrIPToCertificate(certs []tls.Certificate) map[string] | ||||
| 			continue | ||||
| 		} | ||||
| 		if len(x509Cert.Subject.CommonName) > 0 { | ||||
| 			certMap[x509Cert.Subject.CommonName] = cert | ||||
| 			certMap[strings.ToLower(x509Cert.Subject.CommonName)] = cert | ||||
| 		} | ||||
| 		for _, san := range x509Cert.DNSNames { | ||||
| 			certMap[san] = cert | ||||
| 			certMap[strings.ToLower(san)] = cert | ||||
| 		} | ||||
| 		for _, ipSan := range x509Cert.IPAddresses { | ||||
| 			certMap[ipSan.String()] = cert | ||||
| 			certMap[strings.ToLower(ipSan.String())] = cert | ||||
| 		} | ||||
| 	} | ||||
| 	return certMap | ||||
|   | ||||
| @@ -154,13 +154,13 @@ func (c *Certificate) AppendCertificates(certs map[string]map[string]*tls.Certif | ||||
|  | ||||
| 	var SANs []string | ||||
| 	if parsedCert.Subject.CommonName != "" { | ||||
| 		SANs = append(SANs, parsedCert.Subject.CommonName) | ||||
| 		SANs = append(SANs, strings.ToLower(parsedCert.Subject.CommonName)) | ||||
| 	} | ||||
| 	if parsedCert.DNSNames != nil { | ||||
| 		sort.Strings(parsedCert.DNSNames) | ||||
| 		for _, dnsName := range parsedCert.DNSNames { | ||||
| 			if dnsName != parsedCert.Subject.CommonName { | ||||
| 				SANs = append(SANs, dnsName) | ||||
| 				SANs = append(SANs, strings.ToLower(dnsName)) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @@ -168,7 +168,7 @@ func (c *Certificate) AppendCertificates(certs map[string]map[string]*tls.Certif | ||||
| 	if parsedCert.IPAddresses != nil { | ||||
| 		for _, ip := range parsedCert.IPAddresses { | ||||
| 			if ip.String() != parsedCert.Subject.CommonName { | ||||
| 				SANs = append(SANs, ip.String()) | ||||
| 				SANs = append(SANs, strings.ToLower(ip.String())) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|   | ||||
| @@ -20,6 +20,7 @@ func TestGetBestCertificate(t *testing.T) { | ||||
| 		staticCert    string | ||||
| 		dynamicCert   string | ||||
| 		expectedCert  string | ||||
| 		uppercase     bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			desc:          "Empty Store, returns no certs", | ||||
| @@ -27,6 +28,7 @@ func TestGetBestCertificate(t *testing.T) { | ||||
| 			staticCert:    "", | ||||
| 			dynamicCert:   "", | ||||
| 			expectedCert:  "", | ||||
| 			uppercase:     false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc:          "Empty static cert store", | ||||
| @@ -34,6 +36,7 @@ func TestGetBestCertificate(t *testing.T) { | ||||
| 			staticCert:    "", | ||||
| 			dynamicCert:   "snitest.com", | ||||
| 			expectedCert:  "snitest.com", | ||||
| 			uppercase:     false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc:          "Empty dynamic cert store", | ||||
| @@ -41,6 +44,7 @@ func TestGetBestCertificate(t *testing.T) { | ||||
| 			staticCert:    "snitest.com", | ||||
| 			dynamicCert:   "", | ||||
| 			expectedCert:  "snitest.com", | ||||
| 			uppercase:     false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc:          "Best Match", | ||||
| @@ -48,6 +52,7 @@ func TestGetBestCertificate(t *testing.T) { | ||||
| 			staticCert:    "snitest.com", | ||||
| 			dynamicCert:   "snitest.org", | ||||
| 			expectedCert:  "snitest.com", | ||||
| 			uppercase:     false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc:          "Best Match with wildcard dynamic and exact static", | ||||
| @@ -55,6 +60,7 @@ func TestGetBestCertificate(t *testing.T) { | ||||
| 			staticCert:    "www.snitest.com", | ||||
| 			dynamicCert:   "*.snitest.com", | ||||
| 			expectedCert:  "www.snitest.com", | ||||
| 			uppercase:     false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc:          "Best Match with wildcard static and exact dynamic", | ||||
| @@ -62,6 +68,7 @@ func TestGetBestCertificate(t *testing.T) { | ||||
| 			staticCert:    "*.snitest.com", | ||||
| 			dynamicCert:   "www.snitest.com", | ||||
| 			expectedCert:  "www.snitest.com", | ||||
| 			uppercase:     false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc:          "Best Match with static wildcard only", | ||||
| @@ -69,6 +76,7 @@ func TestGetBestCertificate(t *testing.T) { | ||||
| 			staticCert:    "*.snitest.com", | ||||
| 			dynamicCert:   "", | ||||
| 			expectedCert:  "*.snitest.com", | ||||
| 			uppercase:     false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc:          "Best Match with dynamic wildcard only", | ||||
| @@ -76,6 +84,7 @@ func TestGetBestCertificate(t *testing.T) { | ||||
| 			staticCert:    "", | ||||
| 			dynamicCert:   "*.snitest.com", | ||||
| 			expectedCert:  "*.snitest.com", | ||||
| 			uppercase:     false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc:          "Best Match with two wildcard certs", | ||||
| @@ -83,6 +92,23 @@ func TestGetBestCertificate(t *testing.T) { | ||||
| 			staticCert:    "*.www.snitest.com", | ||||
| 			dynamicCert:   "*.snitest.com", | ||||
| 			expectedCert:  "*.www.snitest.com", | ||||
| 			uppercase:     false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc:          "Best Match with static wildcard only, case insensitive", | ||||
| 			domainToCheck: "bar.www.snitest.com", | ||||
| 			staticCert:    "*.www.snitest.com", | ||||
| 			dynamicCert:   "", | ||||
| 			expectedCert:  "*.www.snitest.com", | ||||
| 			uppercase:     true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc:          "Best Match with dynamic wildcard only, case insensitive", | ||||
| 			domainToCheck: "bar.www.snitest.com", | ||||
| 			staticCert:    "", | ||||
| 			dynamicCert:   "*.www.snitest.com", | ||||
| 			expectedCert:  "*.www.snitest.com", | ||||
| 			uppercase:     true, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| @@ -94,15 +120,15 @@ func TestGetBestCertificate(t *testing.T) { | ||||
| 			dynamicMap := map[string]*tls.Certificate{} | ||||
|  | ||||
| 			if test.staticCert != "" { | ||||
| 				cert, err := loadTestCert(test.staticCert) | ||||
| 				cert, err := loadTestCert(test.staticCert, test.uppercase) | ||||
| 				require.NoError(t, err) | ||||
| 				staticMap[test.staticCert] = cert | ||||
| 				staticMap[strings.ToLower(test.staticCert)] = cert | ||||
| 			} | ||||
|  | ||||
| 			if test.dynamicCert != "" { | ||||
| 				cert, err := loadTestCert(test.dynamicCert) | ||||
| 				cert, err := loadTestCert(test.dynamicCert, test.uppercase) | ||||
| 				require.NoError(t, err) | ||||
| 				dynamicMap[test.dynamicCert] = cert | ||||
| 				dynamicMap[strings.ToLower(test.dynamicCert)] = cert | ||||
| 			} | ||||
|  | ||||
| 			store := &CertificateStore{ | ||||
| @@ -113,7 +139,7 @@ func TestGetBestCertificate(t *testing.T) { | ||||
|  | ||||
| 			var expected *tls.Certificate | ||||
| 			if test.expectedCert != "" { | ||||
| 				cert, err := loadTestCert(test.expectedCert) | ||||
| 				cert, err := loadTestCert(test.expectedCert, test.uppercase) | ||||
| 				require.NoError(t, err) | ||||
| 				expected = cert | ||||
| 			} | ||||
| @@ -128,10 +154,15 @@ func TestGetBestCertificate(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func loadTestCert(certName string) (*tls.Certificate, error) { | ||||
| func loadTestCert(certName string, uppercase bool) (*tls.Certificate, error) { | ||||
| 	replacement := "wildcard" | ||||
| 	if uppercase { | ||||
| 		replacement = "uppercase_wildcard" | ||||
| 	} | ||||
|  | ||||
| 	staticCert, err := tls.LoadX509KeyPair( | ||||
| 		fmt.Sprintf("../integration/fixtures/https/%s.cert", strings.Replace(certName, "*", "wildcard", -1)), | ||||
| 		fmt.Sprintf("../integration/fixtures/https/%s.key", strings.Replace(certName, "*", "wildcard", -1)), | ||||
| 		fmt.Sprintf("../integration/fixtures/https/%s.cert", strings.Replace(certName, "*", replacement, -1)), | ||||
| 		fmt.Sprintf("../integration/fixtures/https/%s.key", strings.Replace(certName, "*", replacement, -1)), | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
|   | ||||
							
								
								
									
										14
									
								
								vendor/github.com/containous/flaeg/flaeg.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/containous/flaeg/flaeg.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -434,6 +434,11 @@ func LoadWithCommand(cmd *Command, cmdArgs []string, customParsers map[reflect.T | ||||
|  | ||||
| // PrintHelpWithCommand generates and prints command line help for a Command | ||||
| func PrintHelpWithCommand(flagMap map[string]reflect.StructField, defaultValMap map[string]reflect.Value, parsers map[reflect.Type]parse.Parser, cmd *Command, subCmd []*Command) error { | ||||
| 	// Hide command from help | ||||
| 	if cmd != nil && cmd.HideHelp { | ||||
| 		return fmt.Errorf("command %s not found", cmd.Name) | ||||
| 	} | ||||
|  | ||||
| 	// Define a templates | ||||
| 	// Using POSXE STD : http://pubs.opengroup.org/onlinepubs/9699919799/ | ||||
| 	const helper = `{{if .ProgDescription}}{{.ProgDescription}} | ||||
| @@ -457,7 +462,7 @@ Flags: | ||||
| 		SubCommands     map[string]string | ||||
| 	} | ||||
| 	tempStruct := TempStruct{} | ||||
| 	if cmd != nil && !cmd.HideHelp { | ||||
| 	if cmd != nil { | ||||
| 		tempStruct.ProgName = cmd.Name | ||||
| 		tempStruct.ProgDescription = cmd.Description | ||||
| 		tempStruct.SubCommands = map[string]string{} | ||||
| @@ -582,7 +587,10 @@ func PrintErrorWithCommand(err error, flagMap map[string]reflect.StructField, de | ||||
| 		fmt.Printf("Error here : %s\n", err) | ||||
| 	} | ||||
|  | ||||
| 	PrintHelpWithCommand(flagMap, defaultValMap, parsers, cmd, subCmd) | ||||
| 	if errHelp := PrintHelpWithCommand(flagMap, defaultValMap, parsers, cmd, subCmd); errHelp != nil { | ||||
| 		return errHelp | ||||
| 	} | ||||
|  | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| @@ -661,7 +669,7 @@ func (f *Flaeg) findCommandWithCommandArgs() (*Command, []string, error) { | ||||
| 	commandName, f.commandArgs = splitArgs(f.args) | ||||
| 	if len(commandName) > 0 { | ||||
| 		for _, command := range f.commands { | ||||
| 			if commandName == command.Name && !command.HideHelp { | ||||
| 			if commandName == command.Name { | ||||
| 				f.calledCommand = command | ||||
| 				return f.calledCommand, f.commandArgs, nil | ||||
| 			} | ||||
|   | ||||
							
								
								
									
										4
									
								
								vendor/github.com/containous/flaeg/parse/parse.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/containous/flaeg/parse/parse.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -172,7 +172,7 @@ type Duration time.Duration | ||||
|  | ||||
| // Set sets the duration from the given string value. | ||||
| func (d *Duration) Set(s string) error { | ||||
| 	if v, err := strconv.Atoi(s); err == nil { | ||||
| 	if v, err := strconv.ParseInt(s, 10, 64); err == nil { | ||||
| 		*d = Duration(time.Duration(v) * time.Second) | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -211,7 +211,7 @@ func (d *Duration) MarshalJSON() ([]byte, error) { | ||||
|  | ||||
| // UnmarshalJSON deserializes the given text into a duration value. | ||||
| func (d *Duration) UnmarshalJSON(text []byte) error { | ||||
| 	if v, err := strconv.Atoi(string(text)); err == nil { | ||||
| 	if v, err := strconv.ParseInt(string(text), 10, 64); err == nil { | ||||
| 		*d = Duration(time.Duration(v)) | ||||
| 		return nil | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										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 | ||||
| } | ||||
							
								
								
									
										10
									
								
								vendor/github.com/vulcand/oxy/forward/fwd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/vulcand/oxy/forward/fwd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -548,6 +548,16 @@ func (f *httpForwarder) serveHTTP(w http.ResponseWriter, inReq *http.Request, ct | ||||
| 	} else { | ||||
| 		revproxy.ServeHTTP(w, outReq) | ||||
| 	} | ||||
|  | ||||
| 	for key := range w.Header() { | ||||
| 		if strings.HasPrefix(key, http.TrailerPrefix) { | ||||
| 			if fl, ok := w.(http.Flusher); ok { | ||||
| 				fl.Flush() | ||||
| 			} | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| // IsWebsocketRequest determines if the specified HTTP request is a | ||||
|   | ||||
							
								
								
									
										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) | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user