mirror of
https://github.com/go-acme/lego.git
synced 2026-06-21 06:36:48 +02:00
Add DNS provider for UCloud (#2972)
This commit is contained in:
committed by
GitHub
parent
c4ab05772b
commit
ca178943d0
@@ -274,37 +274,37 @@ If your DNS provider is not supported, please open an [issue](https://github.com
|
||||
<td><a href="https://go-acme.github.io/lego/dns/timewebcloud/">Timeweb Cloud</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/todaynic/">TodayNIC/时代互联</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/transip/">TransIP</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/ultradns/">Ultradns</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/ucloud/">UCloud</a></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/ultradns/">Ultradns</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/uniteddomains/">United-Domains</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/variomedia/">Variomedia</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/vegadns/">VegaDNS</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/vercel/">Vercel</a></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/vercel/">Vercel</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/versio/">Versio.[nl|eu|uk]</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/vinyldns/">VinylDNS</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/virtualname/">Virtualname</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/vkcloud/">VK Cloud</a></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/vkcloud/">VK Cloud</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/volcengine/">Volcano Engine/火山引擎</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/vscale/">Vscale</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/vultr/">Vultr</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/webnamesca/">webnames.ca</a></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/webnamesca/">webnames.ca</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/webnames/">webnames.ru</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/websupport/">Websupport</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/wedos/">WEDOS</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/westcn/">West.cn/西部数码</a></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/westcn/">West.cn/西部数码</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/yandex360/">Yandex 360</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/yandexcloud/">Yandex Cloud</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/yandex/">Yandex PDD</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/zoneee/">Zone.ee</a></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/zoneee/">Zone.ee</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/zoneedit/">ZoneEdit</a></td>
|
||||
<td><a href="https://go-acme.github.io/lego/dns/zonomi/">Zonomi</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr></table>
|
||||
|
||||
<!-- END DNS PROVIDERS LIST -->
|
||||
|
||||
Generated
+24
@@ -183,6 +183,7 @@ func allDNSCodes() string {
|
||||
"timewebcloud",
|
||||
"todaynic",
|
||||
"transip",
|
||||
"ucloud",
|
||||
"ultradns",
|
||||
"uniteddomains",
|
||||
"variomedia",
|
||||
@@ -3892,6 +3893,29 @@ func displayDNSHelp(w io.Writer, name string) error {
|
||||
ew.writeln()
|
||||
ew.writeln(`More information: https://go-acme.github.io/lego/dns/transip`)
|
||||
|
||||
case "ucloud":
|
||||
// generated from: providers/dns/ucloud/ucloud.toml
|
||||
ew.writeln(`Configuration for UCloud.`)
|
||||
ew.writeln(`Code: 'ucloud'`)
|
||||
ew.writeln(`Since: 'v4.34.0'`)
|
||||
ew.writeln()
|
||||
|
||||
ew.writeln(`Credentials:`)
|
||||
ew.writeln(` - "UCLOUD_PRIVATE_KEY": Private key`)
|
||||
ew.writeln(` - "UCLOUD_PUBLIC_KEY": Public key`)
|
||||
ew.writeln()
|
||||
|
||||
ew.writeln(`Additional Configuration:`)
|
||||
ew.writeln(` - "UCLOUD_HTTP_TIMEOUT": API request timeout in seconds (Default: 30)`)
|
||||
ew.writeln(` - "UCLOUD_POLLING_INTERVAL": Time between DNS propagation check in seconds (Default: 2)`)
|
||||
ew.writeln(` - "UCLOUD_PROJECT_ID": Project ID`)
|
||||
ew.writeln(` - "UCLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation in seconds (Default: 60)`)
|
||||
ew.writeln(` - "UCLOUD_REGION": Region`)
|
||||
ew.writeln(` - "UCLOUD_TTL": The TTL of the TXT record used for the DNS challenge in seconds (Default: 600)`)
|
||||
|
||||
ew.writeln()
|
||||
ew.writeln(`More information: https://go-acme.github.io/lego/dns/ucloud`)
|
||||
|
||||
case "ultradns":
|
||||
// generated from: providers/dns/ultradns/ultradns.toml
|
||||
ew.writeln(`Configuration for Ultradns.`)
|
||||
|
||||
Generated
+72
@@ -0,0 +1,72 @@
|
||||
---
|
||||
title: "UCloud"
|
||||
date: 2019-03-03T16:39:46+01:00
|
||||
draft: false
|
||||
slug: ucloud
|
||||
dnsprovider:
|
||||
since: "v4.34.0"
|
||||
code: "ucloud"
|
||||
url: "https://www.ucloud.cn/"
|
||||
---
|
||||
|
||||
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
||||
<!-- providers/dns/ucloud/ucloud.toml -->
|
||||
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
||||
|
||||
|
||||
Configuration for [UCloud](https://www.ucloud.cn/).
|
||||
|
||||
|
||||
<!--more-->
|
||||
|
||||
- Code: `ucloud`
|
||||
- Since: v4.34.0
|
||||
|
||||
|
||||
Here is an example bash command using the UCloud provider:
|
||||
|
||||
```bash
|
||||
UCLOUD_PUBLIC_KEY="xxx" \
|
||||
UCLOUD_PRIVATE_KEY="yyy" \
|
||||
lego --dns ucloud -d '*.example.com' -d example.com run
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## Credentials
|
||||
|
||||
| Environment Variable Name | Description |
|
||||
|-----------------------|-------------|
|
||||
| `UCLOUD_PRIVATE_KEY` | Private key |
|
||||
| `UCLOUD_PUBLIC_KEY` | Public key |
|
||||
|
||||
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
|
||||
More information [here]({{% ref "dns#configuration-and-credentials" %}}).
|
||||
|
||||
|
||||
## Additional Configuration
|
||||
|
||||
| Environment Variable Name | Description |
|
||||
|--------------------------------|-------------|
|
||||
| `UCLOUD_HTTP_TIMEOUT` | API request timeout in seconds (Default: 30) |
|
||||
| `UCLOUD_POLLING_INTERVAL` | Time between DNS propagation check in seconds (Default: 2) |
|
||||
| `UCLOUD_PROJECT_ID` | Project ID |
|
||||
| `UCLOUD_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation in seconds (Default: 60) |
|
||||
| `UCLOUD_REGION` | Region |
|
||||
| `UCLOUD_TTL` | The TTL of the TXT record used for the DNS challenge in seconds (Default: 600) |
|
||||
|
||||
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
|
||||
More information [here]({{% ref "dns#configuration-and-credentials" %}}).
|
||||
|
||||
|
||||
|
||||
|
||||
## More information
|
||||
|
||||
- [API documentation](https://docs.ucloud.cn/api/udnr-api/README)
|
||||
- [Go client](https://github.com/ucloud/ucloud-sdk-go)
|
||||
|
||||
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
||||
<!-- providers/dns/ucloud/ucloud.toml -->
|
||||
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
||||
Generated
+1
-1
@@ -152,7 +152,7 @@ To display the documentation for a specific DNS provider, run:
|
||||
$ lego dnshelp -c code
|
||||
|
||||
Supported DNS providers:
|
||||
acme-dns, active24, alidns, aliesa, allinkl, alwaysdata, anexia, artfiles, arvancloud, auroradns, autodns, axelname, azion, azure, azuredns, baiducloud, beget, binarylane, bindman, bluecat, bluecatv2, bookmyname, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, com35, conoha, conohav3, constellix, corenetworks, cpanel, czechia, ddnss, derak, desec, designate, digitalocean, directadmin, dnsexit, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dyndnsfree, dynu, easydns, edgecenter, edgedns, edgeone, efficientip, epik, eurodns, excedo, exec, exoscale, f5xc, freemyip, gandi, gandiv5, gcloud, gcore, gigahostno, glesys, godaddy, googledomains, gravity, hetzner, hostingde, hostinger, hostingnl, hosttech, httpnet, httpreq, huaweicloud, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ionoscloud, ipv64, ispconfig, ispconfigddns, iwantmyname, jdcloud, joker, keyhelp, leaseweb, liara, lightsail, limacity, linode, liquidweb, loopia, luadns, mailinabox, manageengine, manual, metaname, metaregistrar, mijnhost, mittwald, myaddr, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, namesurfer, nearlyfreespeech, neodigit, netcup, netlify, netnod, nicmanager, nicru, nifcloud, njalla, nodion, ns1, octenium, onecloudru, onlinenet, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, rainyun, rcodezero, regfish, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, selectelv2, selfhostde, servercow, shellrent, simply, sonic, spaceship, stackpath, syse, technitium, tencentcloud, timewebcloud, todaynic, transip, ultradns, uniteddomains, variomedia, vegadns, vercel, versio, vinyldns, virtualname, vkcloud, volcengine, vscale, vultr, webnames, webnamesca, websupport, wedos, westcn, yandex, yandex360, yandexcloud, zoneedit, zoneee, zonomi
|
||||
acme-dns, active24, alidns, aliesa, allinkl, alwaysdata, anexia, artfiles, arvancloud, auroradns, autodns, axelname, azion, azure, azuredns, baiducloud, beget, binarylane, bindman, bluecat, bluecatv2, bookmyname, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, com35, conoha, conohav3, constellix, corenetworks, cpanel, czechia, ddnss, derak, desec, designate, digitalocean, directadmin, dnsexit, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dyndnsfree, dynu, easydns, edgecenter, edgedns, edgeone, efficientip, epik, eurodns, excedo, exec, exoscale, f5xc, freemyip, gandi, gandiv5, gcloud, gcore, gigahostno, glesys, godaddy, googledomains, gravity, hetzner, hostingde, hostinger, hostingnl, hosttech, httpnet, httpreq, huaweicloud, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ionoscloud, ipv64, ispconfig, ispconfigddns, iwantmyname, jdcloud, joker, keyhelp, leaseweb, liara, lightsail, limacity, linode, liquidweb, loopia, luadns, mailinabox, manageengine, manual, metaname, metaregistrar, mijnhost, mittwald, myaddr, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, namesurfer, nearlyfreespeech, neodigit, netcup, netlify, netnod, nicmanager, nicru, nifcloud, njalla, nodion, ns1, octenium, onecloudru, onlinenet, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, rainyun, rcodezero, regfish, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, selectelv2, selfhostde, servercow, shellrent, simply, sonic, spaceship, stackpath, syse, technitium, tencentcloud, timewebcloud, todaynic, transip, ucloud, ultradns, uniteddomains, variomedia, vegadns, vercel, versio, vinyldns, virtualname, vkcloud, volcengine, vscale, vultr, webnames, webnamesca, websupport, wedos, westcn, yandex, yandex360, yandexcloud, zoneedit, zoneee, zonomi
|
||||
|
||||
More information: https://go-acme.github.io/lego/dns
|
||||
"""
|
||||
|
||||
@@ -84,6 +84,7 @@ require (
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.48
|
||||
github.com/transip/gotransip/v6 v6.26.1
|
||||
github.com/ucloud/ucloud-sdk-go v0.22.61
|
||||
github.com/ultradns/ultradns-go-sdk v1.8.1-20250722213956-faef419
|
||||
github.com/urfave/cli/v2 v2.27.7
|
||||
github.com/vinyldns/go-vinyldns v0.9.17
|
||||
|
||||
@@ -869,6 +869,7 @@ github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
@@ -944,6 +945,8 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1
|
||||
github.com/transip/gotransip/v6 v6.26.1 h1:MeqIjkTBBsZwWAK6giZyMkqLmKMclVHEuTNmoBdx4MA=
|
||||
github.com/transip/gotransip/v6 v6.26.1/go.mod h1:x0/RWGRK/zob817O3tfO2xhFoP1vu8YOHORx6Jpk80s=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/ucloud/ucloud-sdk-go v0.22.61 h1:wFMLEvuUQJTqf3serKG8coIt80O2oY85M/MK2/dEG0o=
|
||||
github.com/ucloud/ucloud-sdk-go v0.22.61/go.mod h1:dyLmFHmUfgb4RZKYQP9IArlvQ2pxzFthfhwxRzOEPIw=
|
||||
github.com/ultradns/ultradns-go-sdk v1.8.1-20250722213956-faef419 h1:/VaznPrb/b68e3iMvkr27fU7JqPKU4j7tIITZnjQX1k=
|
||||
github.com/ultradns/ultradns-go-sdk v1.8.1-20250722213956-faef419/go.mod h1:QN0/PdenvYWB0GRMz6JJbPeZz2Lph2iys1p8AFVHm2c=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/ucloud/ucloud-sdk-go/ucloud"
|
||||
"github.com/ucloud/ucloud-sdk-go/ucloud/auth"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
*ucloud.Client
|
||||
}
|
||||
|
||||
func NewClient(config *ucloud.Config, credential *auth.Credential) *Client {
|
||||
client := ucloud.NewClientWithMeta(config, credential, ucloud.ClientMeta{Product: "UDNR"})
|
||||
client.GetLogger().SetOutput(io.Discard)
|
||||
|
||||
return &Client{
|
||||
Client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) NewDomainDNSAddRequest() *DomainDNSAddRequest {
|
||||
req := &DomainDNSAddRequest{}
|
||||
|
||||
// setup request with client config
|
||||
c.SetupRequest(req)
|
||||
|
||||
// setup retryable with default retry policy (retry for non-create action and common error)
|
||||
req.SetRetryable(false)
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
func (c *Client) NewDeleteDNSRecordRequest() *DeleteDNSRecordRequest {
|
||||
req := &DeleteDNSRecordRequest{}
|
||||
|
||||
// setup request with client config
|
||||
c.SetupRequest(req)
|
||||
|
||||
// setup retryable with default retry policy (retry for non-create action and common error)
|
||||
req.SetRetryable(false)
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
func (c *Client) NewDomainDNSQueryRequest() *DomainDNSQueryRequest {
|
||||
req := &DomainDNSQueryRequest{}
|
||||
|
||||
// setup request with client config
|
||||
c.SetupRequest(req)
|
||||
|
||||
// setup retryable with default retry policy (retry for non-create action and common error)
|
||||
req.SetRetryable(false)
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
// DomainDNSAdd adds a DNS record to a domain.
|
||||
// https://docs.ucloud.cn/api/udnr-api/udnr_domain_dns_add
|
||||
func (c *Client) DomainDNSAdd(req *DomainDNSAddRequest) (*DomainDNSAddResponse, error) {
|
||||
var res DomainDNSAddResponse
|
||||
|
||||
reqCopier := *req
|
||||
|
||||
err := c.InvokeAction("UdnrDomainDNSAdd", &reqCopier, &res)
|
||||
if err != nil {
|
||||
return &res, err
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// DeleteDNSRecord delete a DNS record.
|
||||
// https://docs.ucloud.cn/api/udnr-api/udnr_delete_dns_record
|
||||
func (c *Client) DeleteDNSRecord(req *DeleteDNSRecordRequest) (*DeleteDNSRecordResponse, error) {
|
||||
var res DeleteDNSRecordResponse
|
||||
|
||||
reqCopier := *req
|
||||
|
||||
err := c.InvokeAction("UdnrDeleteDnsRecord", &reqCopier, &res)
|
||||
if err != nil {
|
||||
return &res, err
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// DomainDNSQuery gets DNS records of a domain.
|
||||
// https://docs.ucloud.cn/api/udnr-api/udnr_domain_dns_query
|
||||
func (c *Client) DomainDNSQuery(req *DomainDNSQueryRequest) (*DomainDNSQueryResponse, error) {
|
||||
var res DomainDNSQueryResponse
|
||||
|
||||
reqCopier := *req
|
||||
|
||||
err := c.InvokeAction("UdnrDomainDNSQuery", &reqCopier, &res)
|
||||
if err != nil {
|
||||
return &res, err
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/go-acme/lego/v4/providers/dns/internal/useragent"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/ucloud/ucloud-sdk-go/ucloud"
|
||||
"github.com/ucloud/ucloud-sdk-go/ucloud/auth"
|
||||
)
|
||||
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
credential := auth.NewCredential()
|
||||
credential.PublicKey = "pubkey"
|
||||
credential.PrivateKey = "privkey"
|
||||
|
||||
cfg := ucloud.NewConfig()
|
||||
cfg.UserAgent = useragent.Get()
|
||||
cfg.BaseUrl = server.URL
|
||||
|
||||
client := NewClient(&cfg, &credential)
|
||||
client.SetTransport(server.Client().Transport)
|
||||
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().
|
||||
WithRegexp("U-Timestamp-Ms", `\d+`).
|
||||
WithContentTypeFromURLEncoded(),
|
||||
)
|
||||
}
|
||||
|
||||
func TestClient_DomainDNSAdd(t *testing.T) {
|
||||
client := mockBuilder().
|
||||
Route("POST /",
|
||||
servermock.ResponseFromFixture("udnrDomainDNSAdd.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("Action", "UdnrDomainDNSAdd"),
|
||||
servermock.CheckForm().Strict().
|
||||
WithRegexp("Action", "UdnrDomainDNSAdd").
|
||||
With("Dn", "example.com").
|
||||
With("RecordName", "_acme-challenge.example.com").
|
||||
With("TTL", "600").
|
||||
With("DnsType", "TXT").
|
||||
With("Content", "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY").
|
||||
With("PublicKey", "pubkey").
|
||||
WithRegexp("Signature", ".+"),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
request := client.NewDomainDNSAddRequest()
|
||||
request.Dn = ucloud.String("example.com")
|
||||
request.RecordName = ucloud.String("_acme-challenge.example.com")
|
||||
request.DnsType = ucloud.String("TXT")
|
||||
request.Content = ucloud.String("ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY")
|
||||
request.TTL = ucloud.String("600")
|
||||
|
||||
_, err := client.DomainDNSAdd(request)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_DomainDNSQuery(t *testing.T) {
|
||||
client := mockBuilder().
|
||||
Route("POST /",
|
||||
servermock.ResponseFromFixture("udnrDomainDNSQuery.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("Action", "UdnrDomainDNSQuery"),
|
||||
servermock.CheckForm().Strict().
|
||||
WithRegexp("Action", "UdnrDomainDNSQuery").
|
||||
With("Dn", "example.com").
|
||||
With("PublicKey", "pubkey").
|
||||
WithRegexp("Signature", ".+"),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
request := client.NewDomainDNSQueryRequest()
|
||||
request.Dn = ucloud.String("example.com")
|
||||
|
||||
domains, err := client.DomainDNSQuery(request)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := []DomainDNSRecord{{
|
||||
Type: "TXT",
|
||||
Name: "_acme-challenge.example.com",
|
||||
Content: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
|
||||
Priority: "",
|
||||
TTL: "600",
|
||||
}}
|
||||
|
||||
assert.Equal(t, expected, domains.Data)
|
||||
}
|
||||
|
||||
func TestClient_DeleteDNSRecord(t *testing.T) {
|
||||
client := mockBuilder().
|
||||
Route("POST /",
|
||||
servermock.ResponseFromFixture("udnrDeleteDNSRecord.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("Action", "UdnrDeleteDnsRecord"),
|
||||
servermock.CheckForm().Strict().
|
||||
WithRegexp("Action", "UdnrDeleteDnsRecord").
|
||||
With("Dn", "example.com").
|
||||
With("RecordName", "_acme-challenge.example.com").
|
||||
With("DnsType", "TXT").
|
||||
With("Content", "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY").
|
||||
With("PublicKey", "pubkey").
|
||||
WithRegexp("Signature", ".+"),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
request := client.NewDeleteDNSRecordRequest()
|
||||
request.Dn = ucloud.String("example.com")
|
||||
request.RecordName = ucloud.String("_acme-challenge.example.com")
|
||||
request.DnsType = ucloud.String("TXT")
|
||||
request.Content = ucloud.String("ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY")
|
||||
|
||||
_, err := client.DeleteDNSRecord(request)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"Action": "UdnrDeleteDnsRecordResponse",
|
||||
"Data": {},
|
||||
"Message": "EygAdLFr",
|
||||
"RetCode": 0
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"Action": "UdnrDomainDNSAddResponse",
|
||||
"Data": "BFtLtuKv",
|
||||
"Message": "CardVtDd",
|
||||
"RetCode": 0
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Action": "UdnrDomainDNSQueryResponse",
|
||||
"Data": [
|
||||
{
|
||||
"DnsType": "TXT",
|
||||
"RecordName": "_acme-challenge.example.com",
|
||||
"Content": "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
|
||||
"TTL": "600"
|
||||
}
|
||||
],
|
||||
"Message": "YWMskZFN",
|
||||
"RetCode": 0
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
|
||||
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
|
||||
)
|
||||
|
||||
type DomainDNSAddRequest struct {
|
||||
request.CommonBase
|
||||
|
||||
Dn *string
|
||||
RecordName *string
|
||||
DnsType *string //nolint:revive // Because the struct names are used directly.
|
||||
Content *string
|
||||
TTL *string
|
||||
Prio *string
|
||||
}
|
||||
|
||||
type DomainDNSAddResponse struct {
|
||||
response.CommonBase
|
||||
}
|
||||
|
||||
type DomainDNSQueryRequest struct {
|
||||
request.CommonBase
|
||||
|
||||
Dn *string
|
||||
}
|
||||
|
||||
type DomainDNSQueryResponse struct {
|
||||
response.CommonBase
|
||||
|
||||
Data []DomainDNSRecord
|
||||
}
|
||||
|
||||
type DomainDNSRecord struct {
|
||||
Type string `json:"DnsType,omitempty"`
|
||||
Name string `json:"RecordName,omitempty"`
|
||||
Content string `json:"Content,omitempty"`
|
||||
Priority string `json:"Prio,omitempty"`
|
||||
TTL string `json:"TTL,omitempty"`
|
||||
}
|
||||
|
||||
type DeleteDNSRecordRequest struct {
|
||||
request.CommonBase
|
||||
|
||||
Dn *string
|
||||
RecordName *string
|
||||
DnsType *string //nolint:revive // Because the struct names are used directly.
|
||||
Content *string
|
||||
}
|
||||
|
||||
type DeleteDNSRecordResponse struct {
|
||||
response.CommonBase
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
// Package ucloud implements a DNS provider for solving the DNS-01 challenge using UCloud.
|
||||
package ucloud
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
"github.com/go-acme/lego/v4/providers/dns/internal/useragent"
|
||||
"github.com/go-acme/lego/v4/providers/dns/ucloud/internal"
|
||||
"github.com/ucloud/ucloud-sdk-go/ucloud"
|
||||
"github.com/ucloud/ucloud-sdk-go/ucloud/auth"
|
||||
)
|
||||
|
||||
// Environment variables names.
|
||||
const (
|
||||
envNamespace = "UCLOUD_"
|
||||
|
||||
EnvPublicKey = envNamespace + "PUBLIC_KEY"
|
||||
EnvPrivateKey = envNamespace + "PRIVATE_KEY"
|
||||
|
||||
EnvRegion = envNamespace + "REGION"
|
||||
EnvProjectID = envNamespace + "PROJECT_ID"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||
)
|
||||
|
||||
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||
|
||||
// Config is used to configure the creation of the DNSProvider.
|
||||
type Config struct {
|
||||
Region string
|
||||
ProjectID string
|
||||
|
||||
PublicKey string
|
||||
PrivateKey string
|
||||
|
||||
// only for test
|
||||
baseURL string
|
||||
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int
|
||||
HTTPTimeout time.Duration
|
||||
}
|
||||
|
||||
// NewDefaultConfig returns a default configuration for the DNSProvider.
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: env.GetOrDefaultInt(EnvTTL, 600),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, dns01.DefaultPropagationTimeout),
|
||||
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
// DNSProvider implements the challenge.Provider interface.
|
||||
type DNSProvider struct {
|
||||
config *Config
|
||||
|
||||
client *internal.Client
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for UCloud.
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvPublicKey, EnvPrivateKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ucloud: %w", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.PublicKey = values[EnvPublicKey]
|
||||
config.PrivateKey = values[EnvPrivateKey]
|
||||
|
||||
config.Region = env.GetOrFile(EnvRegion)
|
||||
config.ProjectID = env.GetOrFile(EnvProjectID)
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
// NewDNSProviderConfig return a DNSProvider instance configured for UCloud.
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("ucloud: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
if config.PublicKey == "" || config.PrivateKey == "" {
|
||||
return nil, errors.New("ucloud: credentials missing")
|
||||
}
|
||||
|
||||
credential := auth.NewCredential()
|
||||
credential.PublicKey = config.PublicKey
|
||||
credential.PrivateKey = config.PrivateKey
|
||||
|
||||
cfg := ucloud.NewConfig()
|
||||
cfg.UserAgent = useragent.Get()
|
||||
|
||||
if config.baseURL != "" {
|
||||
cfg.BaseUrl = config.baseURL
|
||||
}
|
||||
|
||||
if config.Region != "" {
|
||||
cfg.Region = config.Region
|
||||
}
|
||||
|
||||
if config.ProjectID != "" {
|
||||
cfg.ProjectId = config.ProjectID
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
config: config,
|
||||
client: internal.NewClient(&cfg, &credential),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Present creates a TXT record using the specified parameters.
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ucloud: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
addRequest := d.client.NewDomainDNSAddRequest()
|
||||
addRequest.Dn = ucloud.String(dns01.UnFqdn(authZone))
|
||||
addRequest.RecordName = ucloud.String(dns01.UnFqdn(info.EffectiveFQDN))
|
||||
addRequest.DnsType = ucloud.String("TXT")
|
||||
addRequest.Content = ucloud.String(info.Value)
|
||||
addRequest.TTL = ucloud.String(strconv.Itoa(d.config.TTL))
|
||||
addRequest.WithTimeout(d.config.HTTPTimeout)
|
||||
|
||||
_, err = d.client.DomainDNSAdd(addRequest)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ucloud: domain DNS add: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters.
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ucloud: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
queryRequest := d.client.NewDomainDNSQueryRequest()
|
||||
queryRequest.Dn = ucloud.String(dns01.UnFqdn(authZone))
|
||||
queryRequest.WithTimeout(d.config.HTTPTimeout)
|
||||
|
||||
dom, err := d.client.DomainDNSQuery(queryRequest)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ucloud: domain DNS query: %w", err)
|
||||
}
|
||||
|
||||
for _, record := range dom.Data {
|
||||
if record.Type != "TXT" || record.Name != dns01.UnFqdn(info.EffectiveFQDN) || record.Content != info.Value {
|
||||
continue
|
||||
}
|
||||
|
||||
deleteRequest := d.client.NewDeleteDNSRecordRequest()
|
||||
deleteRequest.Dn = ucloud.String(dns01.UnFqdn(authZone))
|
||||
deleteRequest.RecordName = ucloud.String(dns01.UnFqdn(info.EffectiveFQDN))
|
||||
deleteRequest.DnsType = ucloud.String(record.Type)
|
||||
deleteRequest.Content = ucloud.String(record.Content)
|
||||
deleteRequest.WithTimeout(d.config.HTTPTimeout)
|
||||
|
||||
_, err = d.client.DeleteDNSRecord(deleteRequest)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ucloud: delete DNS record: %w", 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
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
Name = "UCloud"
|
||||
Description = ''''''
|
||||
URL = "https://www.ucloud.cn/"
|
||||
Code = "ucloud"
|
||||
Since = "v4.34.0"
|
||||
|
||||
Example = '''
|
||||
UCLOUD_PUBLIC_KEY="xxx" \
|
||||
UCLOUD_PRIVATE_KEY="yyy" \
|
||||
lego --dns ucloud -d '*.example.com' -d example.com run
|
||||
'''
|
||||
|
||||
[Configuration]
|
||||
[Configuration.Credentials]
|
||||
UCLOUD_PUBLIC_KEY = "Public key"
|
||||
UCLOUD_PRIVATE_KEY = "Private key"
|
||||
[Configuration.Additional]
|
||||
UCLOUD_REGION = "Region"
|
||||
UCLOUD_PROJECT_ID = "Project ID"
|
||||
UCLOUD_POLLING_INTERVAL = "Time between DNS propagation check in seconds (Default: 2)"
|
||||
UCLOUD_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation in seconds (Default: 60)"
|
||||
UCLOUD_TTL = "The TTL of the TXT record used for the DNS challenge in seconds (Default: 600)"
|
||||
UCLOUD_HTTP_TIMEOUT = "API request timeout in seconds (Default: 30)"
|
||||
|
||||
[Links]
|
||||
API = "https://docs.ucloud.cn/api/udnr-api/README"
|
||||
GoClient = "https://github.com/ucloud/ucloud-sdk-go"
|
||||
@@ -0,0 +1,283 @@
|
||||
package ucloud
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester"
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const envDomain = envNamespace + "DOMAIN"
|
||||
|
||||
var envTest = tester.NewEnvTest(
|
||||
EnvPrivateKey,
|
||||
EnvPublicKey,
|
||||
EnvRegion,
|
||||
EnvProjectID,
|
||||
).WithDomain(envDomain)
|
||||
|
||||
func TestNewDNSProvider(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
envVars map[string]string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "success",
|
||||
envVars: map[string]string{
|
||||
EnvPrivateKey: "xxx",
|
||||
EnvPublicKey: "yyy",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "missing private key",
|
||||
envVars: map[string]string{
|
||||
EnvPrivateKey: "",
|
||||
EnvPublicKey: "yyy",
|
||||
},
|
||||
expected: "ucloud: some credentials information are missing: UCLOUD_PRIVATE_KEY",
|
||||
},
|
||||
{
|
||||
desc: "missing public key",
|
||||
envVars: map[string]string{
|
||||
EnvPrivateKey: "xxx",
|
||||
EnvPublicKey: "",
|
||||
},
|
||||
expected: "ucloud: some credentials information are missing: UCLOUD_PUBLIC_KEY",
|
||||
},
|
||||
{
|
||||
desc: "missing credentials",
|
||||
envVars: map[string]string{},
|
||||
expected: "ucloud: some credentials information are missing: UCLOUD_PUBLIC_KEY,UCLOUD_PRIVATE_KEY",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
defer envTest.RestoreEnv()
|
||||
|
||||
envTest.ClearEnv()
|
||||
|
||||
envTest.Apply(test.envVars)
|
||||
|
||||
p, err := NewDNSProvider()
|
||||
|
||||
if test.expected == "" {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, p)
|
||||
require.NotNil(t, p.config)
|
||||
require.NotNil(t, p.client)
|
||||
} else {
|
||||
require.EqualError(t, err, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDNSProviderConfig(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
privateKey string
|
||||
publicKey string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "success",
|
||||
privateKey: "xxx",
|
||||
publicKey: "yyy",
|
||||
},
|
||||
{
|
||||
desc: "missing private key",
|
||||
publicKey: "yyy",
|
||||
expected: "ucloud: credentials missing",
|
||||
},
|
||||
{
|
||||
desc: "missing public key",
|
||||
privateKey: "xxx",
|
||||
expected: "ucloud: credentials missing",
|
||||
},
|
||||
{
|
||||
desc: "missing credentials",
|
||||
expected: "ucloud: credentials missing",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
config := NewDefaultConfig()
|
||||
config.PrivateKey = test.privateKey
|
||||
config.PublicKey = test.publicKey
|
||||
|
||||
p, err := NewDNSProviderConfig(config)
|
||||
|
||||
if test.expected == "" {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, p)
|
||||
require.NotNil(t, p.config)
|
||||
require.NotNil(t, p.client)
|
||||
} else {
|
||||
require.EqualError(t, err, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLivePresent(t *testing.T) {
|
||||
if !envTest.IsLiveTest() {
|
||||
t.Skip("skipping live test")
|
||||
}
|
||||
|
||||
envTest.RestoreEnv()
|
||||
|
||||
provider, err := NewDNSProvider()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = provider.Present(envTest.GetDomain(), "", "123d==")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestLiveCleanUp(t *testing.T) {
|
||||
if !envTest.IsLiveTest() {
|
||||
t.Skip("skipping live test")
|
||||
}
|
||||
|
||||
envTest.RestoreEnv()
|
||||
|
||||
provider, err := NewDNSProvider()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func mockBuilder() *servermock.Builder[*DNSProvider] {
|
||||
return servermock.NewBuilder(
|
||||
func(server *httptest.Server) (*DNSProvider, error) {
|
||||
config := NewDefaultConfig()
|
||||
config.PrivateKey = "privkey"
|
||||
config.PublicKey = "pubkey"
|
||||
|
||||
config.baseURL = server.URL
|
||||
|
||||
p, err := NewDNSProviderConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.client.SetTransport(server.Client().Transport)
|
||||
|
||||
return p, nil
|
||||
},
|
||||
servermock.CheckHeader().
|
||||
WithRegexp("U-Timestamp-Ms", `\d+`).
|
||||
WithContentTypeFromURLEncoded(),
|
||||
)
|
||||
}
|
||||
|
||||
func TestDNSProvider_Present(t *testing.T) {
|
||||
provider := mockBuilder().
|
||||
Route("/",
|
||||
subRouter().
|
||||
Route(matchAction("UdnrDomainDNSAdd"),
|
||||
servermock.ResponseFromInternal("udnrDomainDNSAdd.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("Action", "UdnrDomainDNSAdd"),
|
||||
servermock.CheckForm().Strict().
|
||||
WithRegexp("Action", "UdnrDomainDNSAdd").
|
||||
With("Dn", "example.com").
|
||||
With("RecordName", "_acme-challenge.example.com").
|
||||
With("TTL", "600").
|
||||
With("DnsType", "TXT").
|
||||
With("Content", "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY").
|
||||
With("PublicKey", "pubkey").
|
||||
WithRegexp("Signature", ".+"),
|
||||
),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
err := provider.Present("example.com", "abc", "123d==")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestDNSProvider_CleanUp(t *testing.T) {
|
||||
provider := mockBuilder().
|
||||
Route("/",
|
||||
subRouter().
|
||||
Route(matchAction("UdnrDomainDNSQuery"),
|
||||
servermock.ResponseFromInternal("udnrDomainDNSQuery.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("Action", "UdnrDomainDNSQuery"),
|
||||
servermock.CheckForm().Strict().
|
||||
WithRegexp("Action", "UdnrDomainDNSQuery").
|
||||
With("Dn", "example.com").
|
||||
With("PublicKey", "pubkey").
|
||||
WithRegexp("Signature", ".+"),
|
||||
).
|
||||
Route(matchAction("UdnrDeleteDnsRecord"),
|
||||
servermock.ResponseFromInternal("udnrDeleteDNSRecord.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("Action", "UdnrDeleteDnsRecord"),
|
||||
servermock.CheckForm().Strict().
|
||||
WithRegexp("Action", "UdnrDeleteDnsRecord").
|
||||
With("Dn", "example.com").
|
||||
With("RecordName", "_acme-challenge.example.com").
|
||||
With("DnsType", "TXT").
|
||||
With("Content", "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY").
|
||||
With("PublicKey", "pubkey").
|
||||
WithRegexp("Signature", ".+"),
|
||||
),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
err := provider.CleanUp("example.com", "abc", "123d==")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func matchAction(action string) func(req *http.Request) bool {
|
||||
return func(req *http.Request) bool {
|
||||
return req.URL.Query().Get("Action") == action
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(ldez): this idea can be reused.
|
||||
type subRoute struct {
|
||||
matcher func(req *http.Request) bool
|
||||
handler http.Handler
|
||||
}
|
||||
|
||||
// NOTE(ldez): this idea can be reused.
|
||||
type sr struct {
|
||||
subRoutes []*subRoute
|
||||
}
|
||||
|
||||
func subRouter() *sr {
|
||||
return &sr{}
|
||||
}
|
||||
|
||||
func (s *sr) Route(matcher func(req *http.Request) bool, handler http.Handler, chain ...servermock.Link) *sr {
|
||||
for _, link := range slices.Backward(chain) {
|
||||
handler = link.Bind(handler)
|
||||
}
|
||||
|
||||
s.subRoutes = append(s.subRoutes, &subRoute{
|
||||
matcher: matcher,
|
||||
handler: handler,
|
||||
})
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *sr) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
for _, route := range s.subRoutes {
|
||||
if route.matcher(req) {
|
||||
route.handler.ServeHTTP(rw, req)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
Generated
+3
@@ -177,6 +177,7 @@ import (
|
||||
"github.com/go-acme/lego/v4/providers/dns/timewebcloud"
|
||||
"github.com/go-acme/lego/v4/providers/dns/todaynic"
|
||||
"github.com/go-acme/lego/v4/providers/dns/transip"
|
||||
"github.com/go-acme/lego/v4/providers/dns/ucloud"
|
||||
"github.com/go-acme/lego/v4/providers/dns/ultradns"
|
||||
"github.com/go-acme/lego/v4/providers/dns/uniteddomains"
|
||||
"github.com/go-acme/lego/v4/providers/dns/variomedia"
|
||||
@@ -547,6 +548,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
|
||||
return todaynic.NewDNSProvider()
|
||||
case "transip":
|
||||
return transip.NewDNSProvider()
|
||||
case "ucloud":
|
||||
return ucloud.NewDNSProvider()
|
||||
case "ultradns":
|
||||
return ultradns.NewDNSProvider()
|
||||
case "uniteddomains":
|
||||
|
||||
Reference in New Issue
Block a user