Skip to content

Commit de0eb4a

Browse files
Add data "vercel_domain_config" (#380)
* Add `data "vercel_domain_config"` * `recommended_ipv4` -> `recommended_ipv4s` * go fmt * Create domain_config.md * Add example usage for `vercel_domain_config` * Update domain_config.md * Update client/domain_config.go Co-authored-by: Douglas Harcourt Parsons <dglsparsons@users.noreply.github.com> * Update client/domain_config.go Co-authored-by: Douglas Harcourt Parsons <dglsparsons@users.noreply.github.com> --------- Co-authored-by: Douglas Harcourt Parsons <dglsparsons@users.noreply.github.com>
1 parent 24c6fe8 commit de0eb4a

File tree

6 files changed

+367
-0
lines changed

6 files changed

+367
-0
lines changed

client/domain_config.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package client
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-log/tflog"
8+
)
9+
10+
type recommendedValue struct {
11+
Rank int `json:"rank"`
12+
Value []string `json:"value"`
13+
}
14+
15+
type recommendedCNAMEValue struct {
16+
Rank int `json:"rank"`
17+
Value string `json:"value"`
18+
}
19+
20+
type domainConfigAPIResponse struct {
21+
RecommendedCNAME []recommendedCNAMEValue `json:"recommendedCNAME"`
22+
RecommendedIPv4 []recommendedValue `json:"recommendedIPv4"`
23+
}
24+
25+
type DomainConfigResponse struct {
26+
RecommendedCNAME string
27+
RecommendedIPv4s []string
28+
}
29+
30+
func (c *Client) GetDomainConfig(ctx context.Context, domain, projectIdOrName, teamID string) (DomainConfigResponse, error) {
31+
url := fmt.Sprintf("%s/v6/domains/%s/config?projectIdOrName=%s", c.baseURL, domain, projectIdOrName)
32+
if c.TeamID(teamID) != "" {
33+
url = fmt.Sprintf("%s&teamId=%s", url, c.TeamID(teamID))
34+
}
35+
tflog.Info(ctx, "getting domain config", map[string]any{
36+
"url": url,
37+
"domain": domain,
38+
"projectIdOrName": projectIdOrName,
39+
})
40+
41+
var apiResponse domainConfigAPIResponse
42+
err := c.doRequest(clientRequest{
43+
ctx: ctx,
44+
method: "GET",
45+
url: url,
46+
body: "",
47+
}, &apiResponse)
48+
if err != nil {
49+
return DomainConfigResponse{}, fmt.Errorf("unable to get domain config: %w", err)
50+
}
51+
52+
response := DomainConfigResponse{}
53+
54+
for _, reccomendation := range apiResponse.RecommendedCNAME {
55+
if reccomendation.Rank == 1 {
56+
response.RecommendedCNAME = reccomendation.Value
57+
break
58+
}
59+
}
60+
61+
for _, reccomendation := range apiResponse.RecommendedIPv4 {
62+
if reccomendation.Rank == 1 {
63+
response.RecommendedIPv4s = reccomendation.Value
64+
break
65+
}
66+
}
67+
68+
return response, nil
69+
}

docs/data-sources/domain_config.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "vercel_domain_config Data Source - terraform-provider-vercel"
4+
subcategory: ""
5+
description: |-
6+
Provides domain configuration information for a Vercel project.
7+
This data source returns configuration details for a domain associated with a specific project,
8+
including recommended CNAME and IPv4 values.
9+
---
10+
11+
# vercel_domain_config (Data Source)
12+
13+
Provides domain configuration information for a Vercel project.
14+
15+
This data source returns configuration details for a domain associated with a specific project,
16+
including recommended CNAME and IPv4 values.
17+
18+
## Example Usage
19+
20+
```terraform
21+
resource "vercel_project" "my_awesome_project" {
22+
name = "my-awesome-project"
23+
}
24+
25+
#
26+
# "vercel_domain_config" Usage
27+
#
28+
data "vercel_domain_config" "example_com" {
29+
domain = "example.com"
30+
project_id_or_name = vercel_project.my_awesome_project.id
31+
}
32+
33+
data "vercel_domain_config" "www_example_com" {
34+
domain = "www.example.com"
35+
project_id_or_name = vercel_project.my_awesome_project.id
36+
}
37+
38+
#
39+
# External DNS provider example
40+
#
41+
resource "aws_route53_record" "example_com_a" {
42+
zone_id = "...zone_id_from_somewhere..."
43+
name = data.vercel_domain_config.example_com.domain
44+
type = "A"
45+
ttl = 300
46+
records = data.vercel_domain_config.example_com.recommended_ipv4s
47+
}
48+
49+
resource "aws_route53_record" "www_example_com_cname" {
50+
zone_id = "...zone_id_from_somewhere..."
51+
name = data.vercel_domain_config.www_example_com.domain
52+
type = "CNAME"
53+
ttl = 300
54+
records = [data.vercel_domain_config.www_example_com.recommended_cname]
55+
}
56+
```
57+
58+
<!-- schema generated by tfplugindocs -->
59+
## Schema
60+
61+
### Required
62+
63+
- `domain` (String) The domain name to get configuration for.
64+
- `project_id_or_name` (String) The project ID or name associated with the domain.
65+
66+
### Optional
67+
68+
- `team_id` (String) The ID of the team the domain config exists under. Required when configuring a team resource if a default team has not been set in the provider.
69+
70+
### Read-Only
71+
72+
- `recommended_cname` (String) The recommended CNAME value for the domain.
73+
- `recommended_ipv4s` (List of String) The recommended IPv4 values for the domain.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
resource "vercel_project" "my_awesome_project" {
2+
name = "my-awesome-project"
3+
}
4+
5+
#
6+
# "vercel_domain_config" Usage
7+
#
8+
data "vercel_domain_config" "example_com" {
9+
domain = "example.com"
10+
project_id_or_name = vercel_project.my_awesome_project.id
11+
}
12+
13+
data "vercel_domain_config" "www_example_com" {
14+
domain = "www.example.com"
15+
project_id_or_name = vercel_project.my_awesome_project.id
16+
}
17+
18+
#
19+
# External DNS provider example
20+
#
21+
resource "aws_route53_record" "example_com_a" {
22+
zone_id = "...zone_id_from_somewhere..."
23+
name = data.vercel_domain_config.example_com.domain
24+
type = "A"
25+
ttl = 300
26+
records = data.vercel_domain_config.example_com.recommended_ipv4s
27+
}
28+
29+
resource "aws_route53_record" "www_example_com_cname" {
30+
zone_id = "...zone_id_from_somewhere..."
31+
name = data.vercel_domain_config.www_example_com.domain
32+
type = "CNAME"
33+
ttl = 300
34+
records = [data.vercel_domain_config.www_example_com.recommended_cname]
35+
}

vercel/data_source_domain_config.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package vercel
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-framework/attr"
8+
"github.com/hashicorp/terraform-plugin-framework/datasource"
9+
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
10+
"github.com/hashicorp/terraform-plugin-framework/types"
11+
"github.com/hashicorp/terraform-plugin-log/tflog"
12+
"github.com/vercel/terraform-provider-vercel/v3/client"
13+
)
14+
15+
// Ensure the implementation satisfies the expected interfaces.
16+
var (
17+
_ datasource.DataSource = &domainConfigDataSource{}
18+
_ datasource.DataSourceWithConfigure = &domainConfigDataSource{}
19+
)
20+
21+
func newDomainConfigDataSource() datasource.DataSource {
22+
return &domainConfigDataSource{}
23+
}
24+
25+
type domainConfigDataSource struct {
26+
client *client.Client
27+
}
28+
29+
func (d *domainConfigDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
30+
resp.TypeName = req.ProviderTypeName + "_domain_config"
31+
}
32+
33+
func (d *domainConfigDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
34+
// Prevent panic if the provider has not been configured.
35+
if req.ProviderData == nil {
36+
return
37+
}
38+
39+
client, ok := req.ProviderData.(*client.Client)
40+
if !ok {
41+
resp.Diagnostics.AddError(
42+
"Unexpected Data Source Configure Type",
43+
fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
44+
)
45+
return
46+
}
47+
48+
d.client = client
49+
}
50+
51+
// Schema returns the schema information for a domain config data source
52+
func (d *domainConfigDataSource) Schema(_ context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
53+
resp.Schema = schema.Schema{
54+
Description: `
55+
Provides domain configuration information for a Vercel project.
56+
57+
This data source returns configuration details for a domain associated with a specific project,
58+
including recommended CNAME and IPv4 values.
59+
`,
60+
Attributes: map[string]schema.Attribute{
61+
"domain": schema.StringAttribute{
62+
Required: true,
63+
Description: "The domain name to get configuration for.",
64+
},
65+
"project_id_or_name": schema.StringAttribute{
66+
Required: true,
67+
Description: "The project ID or name associated with the domain.",
68+
},
69+
"team_id": schema.StringAttribute{
70+
Optional: true,
71+
Computed: true,
72+
Description: "The ID of the team the domain config exists under. Required when configuring a team resource if a default team has not been set in the provider.",
73+
},
74+
"recommended_cname": schema.StringAttribute{
75+
Computed: true,
76+
Description: "The recommended CNAME value for the domain.",
77+
},
78+
"recommended_ipv4s": schema.ListAttribute{
79+
ElementType: types.StringType,
80+
Computed: true,
81+
Description: "The recommended IPv4 values for the domain.",
82+
},
83+
},
84+
}
85+
}
86+
87+
// DomainConfigDataSource reflects the state terraform stores internally for a domain config.
88+
type DomainConfigDataSource struct {
89+
Domain types.String `tfsdk:"domain"`
90+
ProjectIdOrName types.String `tfsdk:"project_id_or_name"`
91+
TeamID types.String `tfsdk:"team_id"`
92+
RecommendedCNAME types.String `tfsdk:"recommended_cname"`
93+
RecommendedIPv4s types.List `tfsdk:"recommended_ipv4s"`
94+
}
95+
96+
// Read will read domain config information by requesting it from the Vercel API, and will update terraform
97+
// with this information.
98+
// It is called by the provider whenever data source values should be read to update state.
99+
func (d *domainConfigDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
100+
var config DomainConfigDataSource
101+
diags := req.Config.Get(ctx, &config)
102+
resp.Diagnostics.Append(diags...)
103+
if resp.Diagnostics.HasError() {
104+
return
105+
}
106+
107+
out, err := d.client.GetDomainConfig(ctx, config.Domain.ValueString(), config.ProjectIdOrName.ValueString(), config.TeamID.ValueString())
108+
if err != nil {
109+
resp.Diagnostics.AddError(
110+
"Error reading domain config",
111+
fmt.Sprintf("Could not read domain config for domain %s and project %s, unexpected error: %s",
112+
config.Domain.ValueString(),
113+
config.ProjectIdOrName.ValueString(),
114+
err,
115+
),
116+
)
117+
return
118+
}
119+
120+
var ipv4Values []attr.Value
121+
for _, ip := range out.RecommendedIPv4s {
122+
ipv4Values = append(ipv4Values, types.StringValue(ip))
123+
}
124+
125+
result := DomainConfigDataSource{
126+
Domain: config.Domain,
127+
ProjectIdOrName: config.ProjectIdOrName,
128+
TeamID: config.TeamID,
129+
RecommendedCNAME: types.StringValue(out.RecommendedCNAME),
130+
RecommendedIPv4s: types.ListValueMust(types.StringType, ipv4Values),
131+
}
132+
133+
tflog.Info(ctx, "read domain config", map[string]any{
134+
"domain": result.Domain.ValueString(),
135+
"projectIdOrName": result.ProjectIdOrName.ValueString(),
136+
"teamId": result.TeamID.ValueString(),
137+
"recommendedCNAME": result.RecommendedCNAME.ValueString(),
138+
"recommendedIPv4s": result.RecommendedIPv4s.Elements(),
139+
})
140+
141+
diags = resp.State.Set(ctx, result)
142+
resp.Diagnostics.Append(diags...)
143+
if resp.Diagnostics.HasError() {
144+
return
145+
}
146+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package vercel_test
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
8+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
9+
)
10+
11+
func TestAcc_DomainConfigDataSource(t *testing.T) {
12+
projectSuffix := acctest.RandString(16)
13+
domain := acctest.RandString(30) + ".example.com"
14+
15+
resource.Test(t, resource.TestCase{
16+
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
17+
CheckDestroy: noopDestroyCheck,
18+
Steps: []resource.TestStep{
19+
{
20+
Config: cfg(testAccDomainConfigDataSourceConfig(projectSuffix, domain)),
21+
Check: resource.ComposeAggregateTestCheckFunc(
22+
resource.TestCheckResourceAttr("data.vercel_domain_config.test", "domain", domain),
23+
resource.TestCheckResourceAttrSet("data.vercel_domain_config.test", "project_id_or_name"),
24+
resource.TestCheckResourceAttrSet("data.vercel_domain_config.test", "recommended_cname"),
25+
resource.TestCheckResourceAttrSet("data.vercel_domain_config.test", "recommended_ipv4s.#"),
26+
),
27+
},
28+
},
29+
})
30+
}
31+
32+
func testAccDomainConfigDataSourceConfig(projectSuffix, domain string) string {
33+
return fmt.Sprintf(`
34+
resource "vercel_project" "test" {
35+
name = "test-acc-domain-config-%s"
36+
}
37+
38+
data "vercel_domain_config" "test" {
39+
domain = "%s"
40+
project_id_or_name = vercel_project.test.id
41+
}
42+
`, projectSuffix, domain)
43+
}

vercel/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ func (p *vercelProvider) DataSources(_ context.Context) []func() datasource.Data
9393
newAttackChallengeModeDataSource,
9494
newCustomEnvironmentDataSource,
9595
newDeploymentDataSource,
96+
newDomainConfigDataSource,
9697
newEdgeConfigDataSource,
9798
newEdgeConfigItemDataSource,
9899
newEdgeConfigSchemaDataSource,

0 commit comments

Comments
 (0)