Skip to content

Commit b541fa3

Browse files
committed
feat(tem) : add ressource for validate tem domain
1 parent 1fb2370 commit b541fa3

10 files changed

+2420
-33
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
subcategory: "Transactional Email"
3+
page_title: "Scaleway: scaleway_tem_domain"
4+
---
5+
6+
# Resource: scaleway_tem_domain_validation
7+
8+
This Terraform resource manages the validation of domains for use with Scaleway's Transactional Email Management (TEM) service. It ensures that domains used for sending emails are verified and comply with Scaleway's requirements for email sending.
9+
For more information see [the documentation](https://developers.scaleway.com/en/products/transactional_email/api/).
10+
11+
## Example Usage
12+
13+
### Basic
14+
15+
```terraform
16+
resource "scaleway_tem_domain_validation" "example" {
17+
domain_id = "your-domain-id"
18+
region = "fr-par"
19+
timeout = 300
20+
}
21+
```
22+
23+
## Argument Reference
24+
25+
The following arguments are supported:
26+
27+
- `domain_id` - (Required) The ID of the domain name used when sending emails. This ID must correspond to a domain already registered with Scaleway's Transactional Email service.
28+
29+
- `region` - (Defaults to [provider](../index.md#region) `region`). Specifies the [region](../guides/regions_and_zones.md#regions) where the domain is registered. If not specified, it defaults to the provider's region.
30+
31+
- `timeout` - (Optional) The maximum wait time in seconds before returning an error if the domain validation does not complete. The default is 300 seconds.
32+
33+
## Attributes Reference
34+
35+
In addition to all arguments above, the following attributes are exported:
36+
37+
- `validated` - Indicates if the domain has been verified for email sending. This is computed after the creation or update of the domain validation resource.
38+

scaleway/helpers_tem.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ import (
1010
)
1111

1212
const (
13-
defaultTemDomainTimeout = 5 * time.Minute
14-
defaultTemDomainRetryInterval = 15 * time.Second
13+
defaultTemDomainTimeout = 5 * time.Minute
14+
defaultTemDomainValidationTimeout = 60 * time.Minute
15+
defaultTemDomainRetryInterval = 15 * time.Second
1516
)
1617

17-
// temAPIWithRegion returns a new Tem API and the region for a Create request
18+
// teemAPIWithRegion returns a new Tem API and the region for a Create request
1819
func temAPIWithRegion(d *schema.ResourceData, m interface{}) (*tem.API, scw.Region, error) {
1920
meta := m.(*Meta)
2021
api := tem.NewAPI(meta.scwClient)

scaleway/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ func Provider(config *ProviderConfig) plugin.ProviderFunc {
149149
"scaleway_lb_route": resourceScalewayLbRoute(),
150150
"scaleway_registry_namespace": resourceScalewayRegistryNamespace(),
151151
"scaleway_tem_domain": resourceScalewayTemDomain(),
152+
"scaleway_tem_domain_validation": resourceScalewayTemDomainValidation(),
152153
"scaleway_container": resourceScalewayContainer(),
153154
"scaleway_container_token": resourceScalewayContainerToken(),
154155
"scaleway_rdb_acl": resourceScalewayRdbACL(),

scaleway/resource_tem_domain_test.go

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,36 +19,6 @@ func init() {
1919
})
2020
}
2121

22-
func testSweepTemDomain(_ string) error {
23-
return sweepRegions([]scw.Region{scw.RegionFrPar, scw.RegionNlAms}, func(scwClient *scw.Client, region scw.Region) error {
24-
temAPI := tem.NewAPI(scwClient)
25-
l.Debugf("sweeper: revoking the tem domains in (%s)", region)
26-
27-
listDomains, err := temAPI.ListDomains(&tem.ListDomainsRequest{Region: region}, scw.WithAllPages())
28-
if err != nil {
29-
return fmt.Errorf("error listing domains in (%s) in sweeper: %s", region, err)
30-
}
31-
32-
for _, ns := range listDomains.Domains {
33-
if ns.Name == "test.scaleway-terraform.com" {
34-
l.Debugf("sweeper: skipping deletion of domain %s", ns.Name)
35-
continue
36-
}
37-
_, err := temAPI.RevokeDomain(&tem.RevokeDomainRequest{
38-
DomainID: ns.ID,
39-
Region: region,
40-
})
41-
if err != nil {
42-
l.Debugf("sweeper: error (%s)", err)
43-
44-
return fmt.Errorf("error revoking domain in sweeper: %s", err)
45-
}
46-
}
47-
48-
return nil
49-
})
50-
}
51-
5222
func TestAccScalewayTemDomain_Basic(t *testing.T) {
5323
tt := NewTestTools(t)
5424
defer tt.Cleanup()
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package scaleway
2+
3+
import (
4+
"context"
5+
"errors"
6+
"strings"
7+
"time"
8+
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
11+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
12+
tem "github.com/scaleway/scaleway-sdk-go/api/tem/v1alpha1"
13+
"github.com/scaleway/scaleway-sdk-go/scw"
14+
)
15+
16+
func resourceScalewayTemDomainValidation() *schema.Resource {
17+
return &schema.Resource{
18+
CreateContext: resourceScalewayTemDomainValidationCreate,
19+
ReadContext: resourceScalewayTemDomainValidationRead,
20+
DeleteContext: resourceScalewayTemDomainValidationDelete,
21+
Importer: &schema.ResourceImporter{
22+
StateContext: schema.ImportStatePassthroughContext,
23+
},
24+
Timeouts: &schema.ResourceTimeout{
25+
Create: schema.DefaultTimeout(defaultTemDomainValidationTimeout),
26+
Delete: schema.DefaultTimeout(defaultTemDomainValidationTimeout),
27+
Default: schema.DefaultTimeout(defaultTemDomainValidationTimeout),
28+
},
29+
SchemaVersion: 0,
30+
Schema: map[string]*schema.Schema{
31+
"domain_id": {
32+
Type: schema.TypeString,
33+
Required: true,
34+
ForceNew: true,
35+
Description: "The id of domain name used when sending emails.",
36+
},
37+
"region": regionSchema(),
38+
"timeout": {
39+
Type: schema.TypeInt,
40+
Optional: true,
41+
ForceNew: true,
42+
Default: 300,
43+
Description: "Maximum wait time in second before returning an error.",
44+
},
45+
"validated": {
46+
Type: schema.TypeBool,
47+
Computed: true,
48+
Description: "Indicates if the domain is verified for email sending",
49+
},
50+
},
51+
}
52+
}
53+
54+
func resourceScalewayTemDomainValidationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
55+
api, region, err := temAPIWithRegion(d, meta)
56+
if err != nil {
57+
return diag.FromErr(err)
58+
}
59+
d.SetId(d.Get("domain_id").(string))
60+
diagnostics := validateDomain(ctx, d, err, api, region)
61+
if diagnostics != nil {
62+
return diagnostics
63+
}
64+
return resourceScalewayTemDomainValidationRead(ctx, d, meta)
65+
}
66+
67+
func resourceScalewayTemDomainValidationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
68+
api, region, err := temAPIWithRegion(d, meta)
69+
if err != nil {
70+
return diag.FromErr(err)
71+
}
72+
73+
domainID := d.Id()
74+
getDomainRequest := &tem.GetDomainRequest{
75+
Region: region,
76+
DomainID: extractAfterSlash(domainID),
77+
}
78+
domain, err := api.GetDomain(getDomainRequest, scw.WithContext(ctx))
79+
if err != nil {
80+
if is404Error(err) {
81+
d.SetId("")
82+
return nil
83+
}
84+
return diag.FromErr(err)
85+
}
86+
87+
_ = d.Set("validated", domain.Status == "checked")
88+
89+
return nil
90+
}
91+
92+
func resourceScalewayTemDomainValidationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
93+
_ = ctx
94+
_ = meta
95+
d.SetId("")
96+
return nil
97+
}
98+
99+
func extractAfterSlash(s string) string {
100+
lastIndex := strings.LastIndex(s, "/")
101+
if lastIndex == -1 {
102+
return s
103+
}
104+
return s[lastIndex+1:]
105+
}
106+
107+
func validateDomain(ctx context.Context, d *schema.ResourceData, err error, api *tem.API, region scw.Region) diag.Diagnostics {
108+
domain, err := api.GetDomain(&tem.GetDomainRequest{
109+
Region: region,
110+
DomainID: extractAfterSlash(d.Get("domain_id").(string)),
111+
}, scw.WithContext(ctx))
112+
if err != nil {
113+
if is404Error(err) {
114+
d.SetId("")
115+
return nil
116+
}
117+
return diag.FromErr(err)
118+
}
119+
duration := d.Get("timeout").(int)
120+
timeout := time.Duration(duration) * time.Second
121+
_ = retry.RetryContext(ctx, timeout, func() *retry.RetryError {
122+
domainCheck, _ := api.CheckDomain(&tem.CheckDomainRequest{
123+
Region: region,
124+
DomainID: domain.ID,
125+
})
126+
if domainCheck == nil || domainCheck.Status == "pending" || domainCheck.Status == "unchecked" {
127+
return retry.RetryableError(errors.New("retry"))
128+
}
129+
return nil
130+
})
131+
return nil
132+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package scaleway
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
8+
)
9+
10+
const domainName = "terraform-rs.test.local"
11+
12+
func init() {
13+
resource.AddTestSweepers("scaleway_tem_domain_validation", &resource.Sweeper{
14+
Name: "scaleway_tem_domain_validation",
15+
F: testSweepTemDomain,
16+
})
17+
}
18+
19+
func TestAccScalewayTemDomainValidation_NoValidation(t *testing.T) {
20+
tt := NewTestTools(t)
21+
defer tt.Cleanup()
22+
23+
resource.ParallelTest(t, resource.TestCase{
24+
PreCheck: func() { testAccPreCheck(t) },
25+
ProviderFactories: tt.ProviderFactories,
26+
CheckDestroy: testAccCheckScalewayTemDomainDestroy(tt),
27+
Steps: []resource.TestStep{
28+
{
29+
Config: fmt.Sprintf(`
30+
resource scaleway_tem_domain cr01 {
31+
name = "%s"
32+
accept_tos = true
33+
}
34+
35+
resource scaleway_tem_domain_validation valid {
36+
domain_id = scaleway_tem_domain.cr01.id
37+
region = scaleway_tem_domain.cr01.region
38+
timeout = 1
39+
}
40+
`, domainName),
41+
Check: resource.ComposeTestCheckFunc(
42+
resource.TestCheckResourceAttr("scaleway_tem_domain_validation.valid", "validated", "false"),
43+
),
44+
},
45+
},
46+
})
47+
}
48+
49+
func TestAccScalewayTemDomainValidation_Validation(t *testing.T) {
50+
tt := NewTestTools(t)
51+
defer tt.Cleanup()
52+
53+
resource.ParallelTest(t, resource.TestCase{
54+
PreCheck: func() { testAccPreCheck(t) },
55+
ProviderFactories: tt.ProviderFactories,
56+
CheckDestroy: testAccCheckScalewayTemDomainDestroy(tt),
57+
Steps: []resource.TestStep{
58+
{
59+
Config: fmt.Sprintf(`
60+
resource scaleway_tem_domain cr01 {
61+
name = "%s"
62+
accept_tos = true
63+
}
64+
resource "scaleway_domain_record" "spf" {
65+
dns_zone = "%s"
66+
type = "TXT"
67+
data = "v=spf1 ${scaleway_tem_domain.cr01.spf_config} -all"
68+
}
69+
resource "scaleway_domain_record" "dkim" {
70+
dns_zone = "%s"
71+
name = "${scaleway_tem_domain.cr01.project_id}._domainkey"
72+
type = "TXT"
73+
data = scaleway_tem_domain.cr01.dkim_config
74+
}
75+
resource "scaleway_domain_record" "mx" {
76+
dns_zone = "%s"
77+
type = "MX"
78+
data = "."
79+
}
80+
resource scaleway_tem_domain_validation valid {
81+
domain_id = scaleway_tem_domain.cr01.id
82+
region = scaleway_tem_domain.cr01.region
83+
timeout = 3600
84+
}
85+
`, domainName, domainName, domainName, domainName),
86+
Check: resource.ComposeTestCheckFunc(
87+
resource.TestCheckResourceAttr("scaleway_tem_domain_validation.valid", "validated", "true"),
88+
),
89+
},
90+
},
91+
})
92+
}

scaleway/sweeper_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ package scaleway
22

33
import (
44
"context"
5+
"fmt"
56
"strings"
67
"testing"
78

89
"github.com/aws/aws-sdk-go/service/s3"
910
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
11+
tem "github.com/scaleway/scaleway-sdk-go/api/tem/v1alpha1"
1012
"github.com/scaleway/scaleway-sdk-go/scw"
1113
"github.com/stretchr/testify/assert"
1214
)
@@ -93,3 +95,33 @@ func sharedS3ClientForRegion(region scw.Region) (*s3.S3, error) {
9395
func TestIsTestResource(t *testing.T) {
9496
assert.True(t, isTestResource("tf_tests_mnq_sqs_queue_default_project"))
9597
}
98+
99+
func testSweepTemDomain(_ string) error {
100+
return sweepRegions([]scw.Region{scw.RegionFrPar, scw.RegionNlAms}, func(scwClient *scw.Client, region scw.Region) error {
101+
temAPI := tem.NewAPI(scwClient)
102+
l.Debugf("sweeper: revoking the tem domains in (%s)", region)
103+
104+
listDomains, err := temAPI.ListDomains(&tem.ListDomainsRequest{Region: region}, scw.WithAllPages())
105+
if err != nil {
106+
return fmt.Errorf("error listing domains in (%s) in sweeper: %s", region, err)
107+
}
108+
109+
for _, ns := range listDomains.Domains {
110+
if ns.Name == "test.scaleway-terraform.com" {
111+
l.Debugf("sweeper: skipping deletion of domain %s", ns.Name)
112+
continue
113+
}
114+
_, err := temAPI.RevokeDomain(&tem.RevokeDomainRequest{
115+
DomainID: ns.ID,
116+
Region: region,
117+
})
118+
if err != nil {
119+
l.Debugf("sweeper: error (%s)", err)
120+
121+
return fmt.Errorf("error revoking domain in sweeper: %s", err)
122+
}
123+
}
124+
125+
return nil
126+
})
127+
}

0 commit comments

Comments
 (0)