这是indexloc提供的服务,不要输入任何密码
Skip to content

Begin fleshing out DNS records #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,6 @@ jobs:
VERCEL_TERRAFORM_TESTING_GITHUB_REPO: "dglsparsons/test"
VERCEL_TERRAFORM_TESTING_GITLAB_REPO: "dglsparsons/test"
VERCEL_TERRAFORM_TESTING_BITBUCKET_REPO: "dglsparsons-test/test"

VERCEL_TERRAFORM_TESTING_DOMAIN: "dgls.dev"
run: |
go test -v -cover ./...
55 changes: 55 additions & 0 deletions client/dns_record_create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package client

import (
"context"
"fmt"
"net/http"
"strings"
)

// SRV defines the metata required for creating an SRV type DNS Record.
type SRV struct {
Port int64 `json:"port"`
Priority int64 `json:"priority"`
Target string `json:"target"`
Weight int64 `json:"weight"`
}

// CreateDNSRecordRequest defines the information necessary to create a DNS record within Vercel.
type CreateDNSRecordRequest struct {
Domain string `json:"-"`
MXPriority int64 `json:"mxPriority,omitempty"`
Name string `json:"name"`
SRV *SRV `json:"srv,omitempty"`
TTL int64 `json:"ttl,omitempty"`
Type string `json:"type"`
Value string `json:"value,omitempty"`
}

// CreateProjectDomain creates a DNS record for a specified domain name within Vercel.
func (c *Client) CreateDNSRecord(ctx context.Context, teamID string, request CreateDNSRecordRequest) (r DNSRecord, err error) {
url := fmt.Sprintf("%s/v4/domains/%s/records", c.baseURL, request.Domain)
if teamID != "" {
url = fmt.Sprintf("%s?teamId=%s", url, teamID)
}

req, err := http.NewRequestWithContext(
ctx,
"POST",
url,
strings.NewReader(string(mustMarshal(request))),
)
if err != nil {
return r, err
}

var response struct {
RecordID string `json:"uid"`
}
err = c.doRequest(req, &response)
if err != nil {
return r, err
}

return c.GetDNSRecord(ctx, response.RecordID, teamID)
}
28 changes: 28 additions & 0 deletions client/dns_record_delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package client

import (
"context"
"fmt"
"net/http"
"strings"
)

// DeleteDNSRecord removes a DNS domain from Vercel.
func (c *Client) DeleteDNSRecord(ctx context.Context, domain, recordID, teamID string) error {
url := fmt.Sprintf("%s/v2/domains/%s/records/%s", c.baseURL, domain, recordID)
if teamID != "" {
url = fmt.Sprintf("%s?teamId=%s", url, teamID)
}

req, err := http.NewRequestWithContext(
ctx,
"DELETE",
url,
strings.NewReader(""),
)
if err != nil {
return err
}

return c.doRequest(req, nil)
}
41 changes: 41 additions & 0 deletions client/dns_record_get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package client

import (
"context"
"fmt"
"net/http"
"strings"
)

// DNSRecord is the information Vercel surfaces about a DNS record associated with a particular domain.
type DNSRecord struct {
Creator string `json:"creator"`
Domain string `json:"domain"`
ID string `json:"id"`
Name string `json:"name"`
TTL int64 `json:"ttl"`
Value string `json:"value"`
RecordType string `json:"recordType"`
Priority int64 `json:"priority"`
}

// GetDNSRecord retrieves information about a DNS domain from Vercel.
func (c *Client) GetDNSRecord(ctx context.Context, recordID, teamID string) (r DNSRecord, err error) {
url := fmt.Sprintf("%s/domains/records/%s", c.baseURL, recordID)
if teamID != "" {
url = fmt.Sprintf("%s?teamId=%s", url, teamID)
}

req, err := http.NewRequestWithContext(
ctx,
"GET",
url,
strings.NewReader(""),
)
if err != nil {
return r, err
}

err = c.doRequest(req, &r)
return r, err
}
35 changes: 35 additions & 0 deletions client/dns_record_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package client

import (
"context"
"fmt"
"net/http"
"strings"
)

// ListDNSRecords is a test helper for listing DNS records that exist for a given domain.
// We limit this to 100, as this is the largest limit allowed by the API.
// This is only used by the sweeper script, so this is safe to do so, but converting
// into a production ready function would require some refactoring.
func (c *Client) ListDNSRecords(ctx context.Context, domain, teamID string) (r []DNSRecord, err error) {
url := fmt.Sprintf("%s/v4/domains/%s/records?limit=100", c.baseURL, domain)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be problematic if someone uses more than 100 domains?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good shout, it's currently only used by the sweeper (script to remove any orphaned test resources). So it shouldn't be an issue for anything.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a comment explaining this though.

if teamID != "" {
url = fmt.Sprintf("%s&teamId=%s", url, teamID)
}

req, err := http.NewRequestWithContext(
ctx,
"GET",
url,
strings.NewReader(""),
)
if err != nil {
return r, err
}

dr := struct {
Records []DNSRecord `json:"records"`
}{}
err = c.doRequest(req, &dr)
return dr.Records, err
}
50 changes: 50 additions & 0 deletions client/dns_record_update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package client

import (
"context"
"fmt"
"net/http"
"strings"

"github.com/hashicorp/terraform-plugin-log/tflog"
)

type SRVUpdate struct {
Port *int64 `json:"port"`
Priority *int64 `json:"priority"`
Target *string `json:"target"`
Weight *int64 `json:"weight"`
}

type UpdateDNSRecordRequest struct {
MXPriority *int64 `json:"mxPriority,omitempty"`
Name *string `json:"name,omitempty"`
SRV *SRVUpdate `json:"srv,omitempty"`
TTL *int64 `json:"ttl,omitempty"`
Value *string `json:"value,omitempty"`
}

func (c *Client) UpdateDNSRecord(ctx context.Context, teamID, recordID string, request UpdateDNSRecordRequest) (r DNSRecord, err error) {
url := fmt.Sprintf("%s/v4/domains/records/%s", c.baseURL, recordID)
if teamID != "" {
url = fmt.Sprintf("%s?teamId=%s", url, teamID)
}

payload := string(mustMarshal(request))
req, err := http.NewRequestWithContext(
ctx,
"PATCH",
url,
strings.NewReader(payload),
)
if err != nil {
return r, err
}

tflog.Trace(ctx, "updating DNS record", map[string]interface{}{
"url": url,
"payload": payload,
})
err = c.doRequest(req, &r)
return r, err
}
142 changes: 142 additions & 0 deletions docs/resources/dns_record.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "vercel_dns_record Resource - terraform-provider-vercel"
subcategory: ""
description: |-
Provides a DNS Record resource.
DNS records are instructions that live in authoritative DNS servers and provide information about a domain.
For more detailed information, please see the Vercel documentation https://vercel.com/docs/concepts/projects/custom-domains#dns-records
---

# vercel_dns_record (Resource)

Provides a DNS Record resource.

DNS records are instructions that live in authoritative DNS servers and provide information about a domain.

For more detailed information, please see the [Vercel documentation](https://vercel.com/docs/concepts/projects/custom-domains#dns-records)

## Example Usage

```terraform
resource "vercel_dns_record" "a" {
domain = "example.com"
name = "subdomain" # for subdomain.example.com
type = "A"
ttl = 60
value = "192.168.0.1"
}

resource "vercel_dns_record" "aaaa" {
domain = "example.com"
name = "subdomain"
type = "AAAA"
ttl = 60
value = "::0"
}

resource "vercel_dns_record" "alias" {
domain = "example.com"
name = "subdomain"
type = "ALIAS"
ttl = 60
value = "example2.com."
}

resource "vercel_dns_record" "caa" {
domain = "example.com"
name = "subdomain"
type = "CAA"
ttl = 60
value = "1 issue \"letsencrypt.org\""
}

resource "vercel_dns_record" "cname" {
domain = "example.com"
name = "subdomain"
type = "CNAME"
ttl = 60
value = "example2.com."
}

resource "vercel_dns_record" "mx" {
domain = "example.com"
name = "subdomain"
type = "MX"
ttl = 60
mx_priority = 333
value = "example2.com."
}

resource "vercel_dns_record" "srv" {
domain = "example.com"
name = "subdomain"
type = "SRV"
ttl = 60
srv = {
port = 6000
weight = 60
priority = 127
target = "example2.com."
}
}

resource "vercel_dns_record" "txt" {
domain = "example.com"
name = "subdomain"
type = "TXT"
ttl = 60
value = "some text value"
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `domain` (String) The domain name, or zone, that the DNS record should be created beneath.
- `name` (String) The subdomain name of the record. This should be an empty string if the rercord is for the root domain.
- `type` (String) The type of DNS record.

### Optional

- `mx_priority` (Number) The priority of the MX record. The priority specifies the sequence that an email server receives emails. A smaller value indicates a higher priority.
- `srv` (Attributes) Settings for an SRV record. (see [below for nested schema](#nestedatt--srv))
- `team_id` (String) The team ID that the domain and DNS records belong to.
- `ttl` (Number) The TTL value in seconds. Must be a number between 60 and 2147483647. If unspecified, it will default to 60 seconds.
- `value` (String) The value of the DNS record. The format depends on the 'type' property.
For an 'A' record, this should be a valid IPv4 address.
For an 'AAAA' record, this should be an IPv6 address.
For 'ALIAS' records, this should be a hostname.
For 'CAA' records, this should specify specify which Certificate Authorities (CAs) are allowed to issue certificates for the domain.
For 'CNAME' records, this should be a different domain name.
For 'MX' records, this should specify the mail server responsible for accepting messages on behalf of the domain name.
For 'TXT' records, this can contain arbitrary text.

### Read-Only

- `id` (String) The ID of this resource.

<a id="nestedatt--srv"></a>
### Nested Schema for `srv`

Optional:

- `port` (Number) The TCP or UDP port on which the service is to be found.
- `priority` (Number) The priority of the target host, lower value means more preferred.
- `target` (String) The canonical hostname of the machine providing the service, ending in a dot.
- `weight` (Number) A relative weight for records with the same priority, higher value means higher chance of getting picked.

## Import

Import is supported using the following syntax:

```shell
# Import via the team_id and record ID.
# Record ID can be taken from the network tab on the domains page.
terraform import vercel_dns_record.example team_xxxxxxxxxxxxxxxxxxxxxxxx/rec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx

# If importing without a team, simply use the record ID.
terraform import vercel_dns_record.personal_example rec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
6 changes: 6 additions & 0 deletions examples/resources/vercel_dns_record/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Import via the team_id and record ID.
# Record ID can be taken from the network tab on the domains page.
terraform import vercel_dns_record.example team_xxxxxxxxxxxxxxxxxxxxxxxx/rec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx

# If importing without a team, simply use the record ID.
terraform import vercel_dns_record.personal_example rec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Loading