Skip to content

Commit

Permalink
Add DNS provider for Rainyun/雨云 (#2354)
Browse files Browse the repository at this point in the history
Co-authored-by: Fernandez Ludovic <[email protected]>
  • Loading branch information
Cikaros and ldez authored Nov 21, 2024
1 parent 645169e commit 6fccca6
Show file tree
Hide file tree
Showing 14 changed files with 858 additions and 11 deletions.
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,55 +182,55 @@ Detailed documentation is available [here](https://go-acme.github.io/lego/dns).
<td><a href="https://go-acme.github.io/lego/dns/pdns/">PowerDNS</a></td>
<td><a href="https://go-acme.github.io/lego/dns/rackspace/">Rackspace</a></td>
</tr><tr>
<td><a href="https://go-acme.github.io/lego/dns/rainyun/">Rain Yun/雨云</a></td>
<td><a href="https://go-acme.github.io/lego/dns/rcodezero/">RcodeZero</a></td>
<td><a href="https://go-acme.github.io/lego/dns/regru/">reg.ru</a></td>
<td><a href="https://go-acme.github.io/lego/dns/regfish/">Regfish</a></td>
<td><a href="https://go-acme.github.io/lego/dns/rfc2136/">RFC2136</a></td>
</tr><tr>
<td><a href="https://go-acme.github.io/lego/dns/rfc2136/">RFC2136</a></td>
<td><a href="https://go-acme.github.io/lego/dns/rimuhosting/">RimuHosting</a></td>
<td><a href="https://go-acme.github.io/lego/dns/sakuracloud/">Sakura Cloud</a></td>
<td><a href="https://go-acme.github.io/lego/dns/scaleway/">Scaleway</a></td>
<td><a href="https://go-acme.github.io/lego/dns/selectel/">Selectel</a></td>
</tr><tr>
<td><a href="https://go-acme.github.io/lego/dns/selectel/">Selectel</a></td>
<td><a href="https://go-acme.github.io/lego/dns/selectelv2/">Selectel v2</a></td>
<td><a href="https://go-acme.github.io/lego/dns/selfhostde/">SelfHost.(de|eu)</a></td>
<td><a href="https://go-acme.github.io/lego/dns/servercow/">Servercow</a></td>
<td><a href="https://go-acme.github.io/lego/dns/shellrent/">Shellrent</a></td>
</tr><tr>
<td><a href="https://go-acme.github.io/lego/dns/shellrent/">Shellrent</a></td>
<td><a href="https://go-acme.github.io/lego/dns/simply/">Simply.com</a></td>
<td><a href="https://go-acme.github.io/lego/dns/sonic/">Sonic</a></td>
<td><a href="https://go-acme.github.io/lego/dns/stackpath/">Stackpath</a></td>
<td><a href="https://go-acme.github.io/lego/dns/technitium/">Technitium</a></td>
</tr><tr>
<td><a href="https://go-acme.github.io/lego/dns/technitium/">Technitium</a></td>
<td><a href="https://go-acme.github.io/lego/dns/tencentcloud/">Tencent Cloud DNS</a></td>
<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/transip/">TransIP</a></td>
<td><a href="https://go-acme.github.io/lego/dns/safedns/">UKFast SafeDNS</a></td>
</tr><tr>
<td><a href="https://go-acme.github.io/lego/dns/safedns/">UKFast SafeDNS</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/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/vkcloud/">VK Cloud</a></td>
<td><a href="https://go-acme.github.io/lego/dns/volcengine/">Volcano Engine/火山引擎</a></td>
</tr><tr>
<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/webnames/">Webnames</a></td>
<td><a href="https://go-acme.github.io/lego/dns/websupport/">Websupport</a></td>
</tr><tr>
<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/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>
</tr><tr>
<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>
<td><a href="https://go-acme.github.io/lego/dns/zonomi/">Zonomi</a></td>
<td></td>
<td></td>
</tr></table>

<!-- END DNS PROVIDERS LIST -->
Expand Down
21 changes: 21 additions & 0 deletions cmd/zz_gen_cmd_dnshelp.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 67 additions & 0 deletions docs/content/dns/zz_gen_rainyun.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
title: "Rain Yun/雨云"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: rainyun
dnsprovider:
since: "v4.21.0"
code: "rainyun"
url: "https://www.rainyun.com"
---

<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
<!-- providers/dns/rainyun/rainyun.toml -->
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->


Configuration for [Rain Yun/雨云](https://www.rainyun.com).


<!--more-->

- Code: `rainyun`
- Since: v4.21.0


Here is an example bash command using the Rain Yun/雨云 provider:

```bash
RAINYUN_API_KEY="xxxxxxxxxxxxxxxxxxxxx" \
lego --email [email protected] --dns rainyun -d '*.example.com' -d example.com run
```




## Credentials

| Environment Variable Name | Description |
|-----------------------|-------------|
| `RAINYUN_API_KEY` | API 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 |
|--------------------------------|-------------|
| `RAINYUN_HTTP_TIMEOUT` | API request timeout |
| `RAINYUN_POLLING_INTERVAL` | Time between DNS propagation check |
| `RAINYUN_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `RAINYUN_TTL` | The TTL of the TXT record used for the DNS challenge |

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://www.apifox.cn/apidoc/shared-a4595cc8-44c5-4678-a2a3-eed7738dab03/api-151416609)

<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
<!-- providers/dns/rainyun/rainyun.toml -->
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
2 changes: 1 addition & 1 deletion docs/data/zz_cli_help.toml
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ To display the documentation for a specific DNS provider, run:
$ lego dnshelp -c code
Supported DNS providers:
acme-dns, alidns, allinkl, arvancloud, auroradns, autodns, azure, azuredns, bindman, bluecat, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, conoha, constellix, corenetworks, cpanel, derak, desec, designate, digitalocean, directadmin, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dynu, easydns, edgedns, efficientip, epik, exec, exoscale, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, googledomains, hetzner, hostingde, hosttech, httpnet, httpreq, huaweicloud, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ipv64, iwantmyname, joker, liara, lightsail, limacity, linode, liquidweb, loopia, luadns, mailinabox, manual, metaname, mijnhost, mittwald, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nifcloud, njalla, nodion, ns1, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, rcodezero, regfish, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, selectelv2, selfhostde, servercow, shellrent, simply, sonic, stackpath, technitium, tencentcloud, timewebcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, volcengine, vscale, vultr, webnames, websupport, wedos, yandex, yandex360, yandexcloud, zoneee, zonomi
acme-dns, alidns, allinkl, arvancloud, auroradns, autodns, azure, azuredns, bindman, bluecat, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, conoha, constellix, corenetworks, cpanel, derak, desec, designate, digitalocean, directadmin, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dynu, easydns, edgedns, efficientip, epik, exec, exoscale, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, googledomains, hetzner, hostingde, hosttech, httpnet, httpreq, huaweicloud, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ipv64, iwantmyname, joker, liara, lightsail, limacity, linode, liquidweb, loopia, luadns, mailinabox, manual, metaname, mijnhost, mittwald, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nifcloud, njalla, nodion, ns1, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, rainyun, rcodezero, regfish, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, selectelv2, selfhostde, servercow, shellrent, simply, sonic, stackpath, technitium, tencentcloud, timewebcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, volcengine, vscale, vultr, webnames, websupport, wedos, yandex, yandex360, yandexcloud, zoneee, zonomi
More information: https://go-acme.github.io/lego/dns
"""
182 changes: 182 additions & 0 deletions providers/dns/rainyun/internal/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package internal

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"time"

"github.com/go-acme/lego/v4/providers/dns/internal/errutils"
querystring "github.com/google/go-querystring/query"
)

const defaultBaseURL = "https://api.v2.rainyun.com/product/"

// Client the Rain Yun API client.
type Client struct {
apiKey string

baseURL *url.URL
HTTPClient *http.Client
}

// NewClient creates a new Client.
func NewClient(apiKey string) (*Client, error) {
if apiKey == "" {
return nil, errors.New("credentials missing")
}

baseURL, _ := url.Parse(defaultBaseURL)

return &Client{
apiKey: apiKey,
baseURL: baseURL,
HTTPClient: &http.Client{Timeout: 10 * time.Second},
}, nil
}

func (c *Client) AddRecord(ctx context.Context, domainID int, record Record) error {
endpoint := c.baseURL.JoinPath("domain", strconv.Itoa(domainID), "dns")

req, err := newJSONRequest(ctx, http.MethodPost, endpoint, record)
if err != nil {
return err
}

return c.do(req, nil)
}

func (c *Client) DeleteRecord(ctx context.Context, domainID, recordID int) error {
endpoint := c.baseURL.JoinPath("domain", strconv.Itoa(domainID), "dns")

values, err := querystring.Values(Record{ID: recordID})
if err != nil {
return err
}

endpoint.RawQuery = values.Encode()

req, err := newJSONRequest(ctx, http.MethodDelete, endpoint, nil)
if err != nil {
return err
}

return c.do(req, nil)
}

func (c *Client) ListRecords(ctx context.Context, domainID int) ([]Record, error) {
endpoint := c.baseURL.JoinPath("domain", strconv.Itoa(domainID), "dns")

query := endpoint.Query()
query.Set("limit", "100")
query.Set("page_no", "1")
endpoint.RawQuery = query.Encode()

req, err := newJSONRequest(ctx, http.MethodGet, endpoint, nil)
if err != nil {
return nil, err
}

var recordData APIResponse[Record]
err = c.do(req, &recordData)
if err != nil {
return nil, err
}

return recordData.Data.Records, nil
}

func (c *Client) ListDomains(ctx context.Context) ([]Domain, error) {
endpoint := c.baseURL.JoinPath("domain")

query := endpoint.Query()
query.Set("options", `{"columnFilters":{"domains.Domain":""},"sort":[],"page":1,"perPage":100}`)
endpoint.RawQuery = query.Encode()

req, err := newJSONRequest(ctx, http.MethodGet, endpoint, nil)
if err != nil {
return nil, err
}

var domainData APIResponse[Domain]

err = c.do(req, &domainData)
if err != nil {
return nil, err
}

return domainData.Data.Records, nil
}

func (c *Client) do(req *http.Request, result any) error {
req.Header.Add("x-api-key", c.apiKey)

resp, err := c.HTTPClient.Do(req)
if err != nil {
return errutils.NewHTTPDoError(req, err)
}

defer func() { _ = resp.Body.Close() }()

if resp.StatusCode/100 != 2 {
return parseError(req, resp)
}

if result == nil {
return nil
}

raw, err := io.ReadAll(resp.Body)
if err != nil {
return errutils.NewReadResponseError(req, resp.StatusCode, err)
}

err = json.Unmarshal(raw, result)
if err != nil {
return errutils.NewUnmarshalError(req, resp.StatusCode, raw, err)
}

return nil
}

func newJSONRequest(ctx context.Context, method string, endpoint *url.URL, payload any) (*http.Request, error) {
buf := new(bytes.Buffer)

if payload != nil {
err := json.NewEncoder(buf).Encode(payload)
if err != nil {
return nil, fmt.Errorf("failed to create request JSON body: %w", err)
}
}

req, err := http.NewRequestWithContext(ctx, method, endpoint.String(), buf)
if err != nil {
return nil, fmt.Errorf("unable to create request: %w", err)
}

req.Header.Set("Accept", "application/json")

if payload != nil {
req.Header.Set("Content-Type", "application/json")
}

return req, nil
}

func parseError(req *http.Request, resp *http.Response) error {
raw, _ := io.ReadAll(resp.Body)

var errAPI APIError
err := json.Unmarshal(raw, &errAPI)
if err != nil {
return errutils.NewUnexpectedStatusCodeError(req, resp.StatusCode, raw)
}

return &errAPI
}
Loading

0 comments on commit 6fccca6

Please sign in to comment.