Feature: CDN custom domain resource and data source (#801)
* Feature: CDN custom domain resource and data source * stabilize acceptance tests * add guide * review changes --------- Co-authored-by: Malte Ehrlen <malte.ehrlen@freiheit.com>
This commit is contained in:
parent
0a86417cbb
commit
2d757a93fd
13 changed files with 914 additions and 19 deletions
39
docs/data-sources/cdn_custom_domain.md
Normal file
39
docs/data-sources/cdn_custom_domain.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "stackit_cdn_custom_domain Data Source - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
CDN distribution data source schema.
|
||||
~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our guide https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources for how to opt-in to use beta resources.
|
||||
---
|
||||
|
||||
# stackit_cdn_custom_domain (Data Source)
|
||||
|
||||
CDN distribution data source schema.
|
||||
|
||||
~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our [guide](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources) for how to opt-in to use beta resources.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
data "stackit_cdn_custom_domain" "example" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
distribution_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
name = "https://xxx.xxx"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `distribution_id` (String) CDN distribution ID
|
||||
- `name` (String)
|
||||
- `project_id` (String) STACKIT project ID associated with the distribution
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `errors` (List of String) List of distribution errors
|
||||
- `id` (String) Terraform's internal resource identifier. It is structured as "`project_id`,`distribution_id`".
|
||||
- `status` (String) Status of the distribution
|
||||
60
docs/guides/stackit_cdn_with_custom_domain.md
Normal file
60
docs/guides/stackit_cdn_with_custom_domain.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
---
|
||||
page_title: "Using STACKIT CDN with your own domain"
|
||||
---
|
||||
# Using STACKIT CDN with your own domain
|
||||
|
||||
## Overview
|
||||
|
||||
This guide outlines the process of creating a STACKIT CDN distribution and configuring it to make use of an existing domain using STACKIT DNS.
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Create a STACKIT CDN and DNS Zone**
|
||||
|
||||
Create the CDN distribution and the DNS zone.
|
||||
|
||||
```terraform
|
||||
resource "stackit_cdn_distribution" "example_distribution" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
config = {
|
||||
backend = {
|
||||
type = "http"
|
||||
origin_url = "mybackend.onstackit.cloud"
|
||||
}
|
||||
regions = ["EU", "US", "ASIA", "AF", "SA"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "stackit_dns_zone" "example_zone" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
name = "My DNS zone"
|
||||
dns_name = "myapp.runs.onstackit.cloud"
|
||||
contact_email = "aa@bb.ccc"
|
||||
type = "primary"
|
||||
}
|
||||
```
|
||||
|
||||
2. **Add CNAME record to your DNS zone**
|
||||
|
||||
If you want to redirect your entire domain to the CDN, you can instead use an A record.
|
||||
```terraform
|
||||
resource "stackit_dns_record_set" "example" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
zone_id = stackit_dns_zone.example_zone.zone_id
|
||||
name = "cdn"
|
||||
type = "CNAME"
|
||||
records = ["${stackit_cdn_distribution.domains[0].name}."]
|
||||
}
|
||||
```
|
||||
|
||||
3. **Create a STACKIT CDN Custom Domain**
|
||||
```terraform
|
||||
# Create a CDN custom domain
|
||||
resource "stackit_cdn_custom_domain" "example" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
distribution_id = stackit_cdn_distribution.example_distribution.distribution_id
|
||||
name = "${stackit_dns_record_set.example.name}.${stackit_dns_zone.dns_name}"
|
||||
}
|
||||
```
|
||||
|
||||
Now, you can access your content on the url `cdn.myapp.runs.onstackit.cloud`.
|
||||
40
docs/resources/cdn_custom_domain.md
Normal file
40
docs/resources/cdn_custom_domain.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "stackit_cdn_custom_domain Resource - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
CDN distribution data source schema.
|
||||
~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our guide https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources for how to opt-in to use beta resources.
|
||||
---
|
||||
|
||||
# stackit_cdn_custom_domain (Resource)
|
||||
|
||||
CDN distribution data source schema.
|
||||
|
||||
~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our [guide](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources) for how to opt-in to use beta resources.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
# Create a CDN custom domain
|
||||
resource "stackit_cdn_custom_domain" "example" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
distribution_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
name = "https://xxx.xxx"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `distribution_id` (String) CDN distribution ID
|
||||
- `name` (String)
|
||||
- `project_id` (String) STACKIT project ID associated with the distribution
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `errors` (List of String) List of distribution errors
|
||||
- `id` (String) Terraform's internal resource identifier. It is structured as "`project_id`,`distribution_id`".
|
||||
- `status` (String) Status of the distribution
|
||||
|
|
@ -40,7 +40,7 @@ resource "stackit_cdn_distribution" "example_distribution" {
|
|||
### Read-Only
|
||||
|
||||
- `created_at` (String) Time when the distribution was created
|
||||
- `distribution_id` (String) STACKIT project ID associated with the distribution
|
||||
- `distribution_id` (String) CDN distribution ID
|
||||
- `domains` (Attributes List) List of configured domains for the distribution (see [below for nested schema](#nestedatt--domains))
|
||||
- `errors` (List of String) List of distribution errors
|
||||
- `id` (String) Terraform's internal resource identifier. It is structured as "`project_id`,`distribution_id`".
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
data "stackit_cdn_custom_domain" "example" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
distribution_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
name = "https://xxx.xxx"
|
||||
}
|
||||
|
||||
6
examples/resources/stackit_cdn_custom_domain/resource.tf
Normal file
6
examples/resources/stackit_cdn_custom_domain/resource.tf
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Create a CDN custom domain
|
||||
resource "stackit_cdn_custom_domain" "example" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
distribution_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
name = "https://xxx.xxx"
|
||||
}
|
||||
|
|
@ -3,9 +3,12 @@ package cdn_test
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-testing/terraform"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||
|
|
@ -21,6 +24,7 @@ var instanceResource = map[string]string{
|
|||
"config_backend_origin_url": "https://test-backend-1.cdn-dev.runs.onstackit.cloud",
|
||||
"config_regions": "\"EU\", \"US\"",
|
||||
"config_regions_updated": "\"EU\", \"US\", \"ASIA\"",
|
||||
"custom_domain_prefix": uuid.NewString(), // we use a different domain prefix each test run due to inconsistent upstream release of domains, which might impair consecutive test runs
|
||||
}
|
||||
|
||||
func configResources(regions string) string {
|
||||
|
|
@ -37,7 +41,35 @@ func configResources(regions string) string {
|
|||
regions = [%s]
|
||||
}
|
||||
}
|
||||
`, testutil.CdnProviderConfig(), testutil.ProjectId, instanceResource["config_backend_origin_url"], regions)
|
||||
|
||||
resource "stackit_dns_zone" "dns_zone" {
|
||||
project_id = "%s"
|
||||
name = "cdn_acc_test_zone"
|
||||
dns_name = "cdntestzone.stackit.gg"
|
||||
contact_email = "aa@bb.cc"
|
||||
type = "primary"
|
||||
default_ttl = 3600
|
||||
}
|
||||
resource "stackit_dns_record_set" "dns_record" {
|
||||
project_id = "%s"
|
||||
zone_id = stackit_dns_zone.dns_zone.zone_id
|
||||
name = "%s"
|
||||
type = "CNAME"
|
||||
records = ["${stackit_cdn_distribution.distribution.domains[0].name}."]
|
||||
}
|
||||
`, testutil.CdnProviderConfig(), testutil.ProjectId, instanceResource["config_backend_origin_url"], regions, testutil.ProjectId, testutil.ProjectId, instanceResource["custom_domain_prefix"])
|
||||
}
|
||||
|
||||
func configCustomDomainResources(regions string) string {
|
||||
return fmt.Sprintf(`
|
||||
%s
|
||||
|
||||
resource "stackit_cdn_custom_domain" "custom_domain" {
|
||||
project_id = stackit_cdn_distribution.distribution.project_id
|
||||
distribution_id = stackit_cdn_distribution.distribution.distribution_id
|
||||
name = "${stackit_dns_record_set.dns_record.name}.cdntestzone.stackit.gg"
|
||||
}
|
||||
`, configResources(regions))
|
||||
}
|
||||
|
||||
func configDatasources(regions string) string {
|
||||
|
|
@ -48,7 +80,13 @@ func configDatasources(regions string) string {
|
|||
project_id = stackit_cdn_distribution.distribution.project_id
|
||||
distribution_id = stackit_cdn_distribution.distribution.distribution_id
|
||||
}
|
||||
`, configResources(regions))
|
||||
|
||||
data "stackit_cdn_custom_domain" "custom_domain" {
|
||||
project_id = stackit_cdn_custom_domain.custom_domain.project_id
|
||||
distribution_id = stackit_cdn_custom_domain.custom_domain.distribution_id
|
||||
name = stackit_cdn_custom_domain.custom_domain.name
|
||||
}
|
||||
`, configCustomDomainResources(regions))
|
||||
}
|
||||
|
||||
func TestAccCDNDistributionResource(t *testing.T) {
|
||||
|
|
@ -56,7 +94,7 @@ func TestAccCDNDistributionResource(t *testing.T) {
|
|||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
CheckDestroy: testAccCheckCDNDistributionDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
// Create
|
||||
// Distribution Create
|
||||
{
|
||||
Config: configResources(instanceResource["config_regions"]),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
|
|
@ -65,6 +103,7 @@ func TestAccCDNDistributionResource(t *testing.T) {
|
|||
resource.TestCheckResourceAttrSet("stackit_cdn_distribution.distribution", "updated_at"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("stackit_cdn_distribution.distribution", "domains.0.name"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.0.type", "managed"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.0.status", "ACTIVE"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.0", "EU"),
|
||||
|
|
@ -73,6 +112,24 @@ func TestAccCDNDistributionResource(t *testing.T) {
|
|||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "status", "ACTIVE"),
|
||||
),
|
||||
},
|
||||
// Wait step, that confirms the CNAME record has "propagated"
|
||||
{
|
||||
Config: configResources(instanceResource["config_regions"]),
|
||||
Check: func(_ *terraform.State) error {
|
||||
_, err := blockUntilDomainResolves(instanceResource["custom_domain_prefix"] + ".cdntestzone.stackit.gg")
|
||||
return err
|
||||
},
|
||||
},
|
||||
// Custom Domain Create
|
||||
{
|
||||
Config: configCustomDomainResources(instanceResource["config_regions"]),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "status", "ACTIVE"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "name", instanceResource["custom_domain_prefix"]+".cdntestzone.stackit.gg"),
|
||||
resource.TestCheckResourceAttrPair("stackit_cdn_distribution.distribution", "distribution_id", "stackit_cdn_custom_domain.custom_domain", "distribution_id"),
|
||||
resource.TestCheckResourceAttrPair("stackit_cdn_distribution.distribution", "project_id", "stackit_cdn_custom_domain.custom_domain", "project_id"),
|
||||
),
|
||||
},
|
||||
// Import
|
||||
{
|
||||
ResourceName: "stackit_cdn_distribution.distribution",
|
||||
|
|
@ -88,6 +145,28 @@ func TestAccCDNDistributionResource(t *testing.T) {
|
|||
|
||||
return fmt.Sprintf("%s,%s", testutil.ProjectId, distributionId), nil
|
||||
},
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateVerifyIgnore: []string{"domains"}, // we added a domain in the meantime...
|
||||
},
|
||||
{
|
||||
ResourceName: "stackit_cdn_custom_domain.custom_domain",
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
r, ok := s.RootModule().Resources["stackit_cdn_custom_domain.custom_domain"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find resource stackit_cdn_custom_domain.custom_domain")
|
||||
}
|
||||
distributionId, ok := r.Primary.Attributes["distribution_id"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find attribute distribution_id")
|
||||
}
|
||||
name, ok := r.Primary.Attributes["name"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find attribute name")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, distributionId, name), nil
|
||||
},
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
|
|
@ -95,35 +174,50 @@ func TestAccCDNDistributionResource(t *testing.T) {
|
|||
{
|
||||
Config: configDatasources(instanceResource["config_regions"]),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("stackit_cdn_distribution.distribution", "distribution_id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_cdn_distribution.distribution", "created_at"),
|
||||
resource.TestCheckResourceAttrSet("stackit_cdn_distribution.distribution", "updated_at"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("stackit_cdn_distribution.distribution", "domains.0.name"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.0.status", "ACTIVE"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.0", "EU"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.1", "US"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "project_id", testutil.ProjectId),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "status", "ACTIVE"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_cdn_distribution.distribution", "distribution_id"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_cdn_distribution.distribution", "created_at"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_cdn_distribution.distribution", "updated_at"),
|
||||
resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "domains.#", "2"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_cdn_distribution.distribution", "domains.0.name"),
|
||||
resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "domains.1.name", instanceResource["custom_domain_prefix"]+".cdntestzone.stackit.gg"),
|
||||
resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "domains.0.status", "ACTIVE"),
|
||||
resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "domains.1.status", "ACTIVE"),
|
||||
resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "domains.0.type", "managed"),
|
||||
resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "domains.1.type", "custom"),
|
||||
resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.regions.#", "2"),
|
||||
resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.regions.0", "EU"),
|
||||
resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.regions.1", "US"),
|
||||
resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "project_id", testutil.ProjectId),
|
||||
resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "status", "ACTIVE"),
|
||||
resource.TestCheckResourceAttr("data.stackit_cdn_custom_domain.custom_domain", "status", "ACTIVE"),
|
||||
resource.TestCheckResourceAttr("data.stackit_cdn_custom_domain.custom_domain", "name", instanceResource["custom_domain_prefix"]+".cdntestzone.stackit.gg"),
|
||||
resource.TestCheckResourceAttrPair("stackit_cdn_distribution.distribution", "distribution_id", "stackit_cdn_custom_domain.custom_domain", "distribution_id"),
|
||||
),
|
||||
},
|
||||
// Update
|
||||
{
|
||||
Config: configResources(instanceResource["config_regions_updated"]),
|
||||
Config: configCustomDomainResources(instanceResource["config_regions_updated"]),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("stackit_cdn_distribution.distribution", "distribution_id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_cdn_distribution.distribution", "created_at"),
|
||||
resource.TestCheckResourceAttrSet("stackit_cdn_distribution.distribution", "updated_at"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.#", "2"),
|
||||
resource.TestCheckResourceAttrSet("stackit_cdn_distribution.distribution", "domains.0.name"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.1.name", instanceResource["custom_domain_prefix"]+".cdntestzone.stackit.gg"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.0.status", "ACTIVE"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.1.status", "ACTIVE"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.0.type", "managed"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.1.type", "custom"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.#", "3"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.0", "EU"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.1", "US"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.2", "ASIA"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "project_id", testutil.ProjectId),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "status", "ACTIVE"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "status", "ACTIVE"),
|
||||
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "name", instanceResource["custom_domain_prefix"]+".cdntestzone.stackit.gg"),
|
||||
resource.TestCheckResourceAttrPair("stackit_cdn_distribution.distribution", "distribution_id", "stackit_cdn_custom_domain.custom_domain", "distribution_id"),
|
||||
resource.TestCheckResourceAttrPair("stackit_cdn_distribution.distribution", "project_id", "stackit_cdn_custom_domain.custom_domain", "project_id"),
|
||||
),
|
||||
},
|
||||
},
|
||||
|
|
@ -165,3 +259,39 @@ func testAccCheckCDNDistributionDestroy(s *terraform.State) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
recordCheckInterval time.Duration = 3 * time.Second
|
||||
recordCheckAttempts = 100 // wait up to 5 minutes for record to be come available (normally takes less than 2 minutes)
|
||||
)
|
||||
|
||||
func blockUntilDomainResolves(domain string) (net.IP, error) {
|
||||
// wait until it becomes ready
|
||||
isReady := func() (net.IP, error) {
|
||||
ips, err := net.LookupIP(domain)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error looking up IP for domain %s: %w", domain, err)
|
||||
}
|
||||
for _, ip := range ips {
|
||||
if ip.String() != "<nil>" {
|
||||
return ip, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("no IP for domain: %v", domain)
|
||||
}
|
||||
return retry(recordCheckAttempts, recordCheckInterval, isReady)
|
||||
}
|
||||
|
||||
func retry[T any](attempts int, sleep time.Duration, f func() (T, error)) (T, error) {
|
||||
var zero T
|
||||
var errOuter error
|
||||
for i := 0; i < attempts; i++ {
|
||||
dist, err := f()
|
||||
if err == nil {
|
||||
return dist, nil
|
||||
}
|
||||
errOuter = err
|
||||
time.Sleep(sleep)
|
||||
}
|
||||
return zero, fmt.Errorf("retry timed out, last error: %w", errOuter)
|
||||
}
|
||||
|
|
|
|||
150
stackit/internal/services/cdn/customdomain/datasource.go
Normal file
150
stackit/internal/services/cdn/customdomain/datasource.go
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
package cdn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/cdn"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
||||
)
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ datasource.DataSource = &customDomainDataSource{}
|
||||
_ datasource.DataSourceWithConfigure = &customDomainDataSource{}
|
||||
)
|
||||
|
||||
type customDomainDataSource struct {
|
||||
client *cdn.APIClient
|
||||
}
|
||||
|
||||
func NewCustomDomainDataSource() datasource.DataSource {
|
||||
return &customDomainDataSource{}
|
||||
}
|
||||
|
||||
func (d *customDomainDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
providerData, ok := req.ProviderData.(core.ProviderData)
|
||||
if !ok {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData))
|
||||
return
|
||||
}
|
||||
features.CheckBetaResourcesEnabled(ctx, &providerData, &resp.Diagnostics, "stackit_cdn_custom_domain", "datasource")
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var apiClient *cdn.APIClient
|
||||
var err error
|
||||
if providerData.CdnCustomEndpoint != "" {
|
||||
ctx = tflog.SetField(ctx, "cdn_custom_endpoint", providerData.CdnCustomEndpoint)
|
||||
apiClient, err = cdn.NewAPIClient(
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
config.WithEndpoint(providerData.CdnCustomEndpoint),
|
||||
)
|
||||
} else {
|
||||
apiClient, err = cdn.NewAPIClient(
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration", err))
|
||||
return
|
||||
}
|
||||
d.client = apiClient
|
||||
tflog.Info(ctx, "CDN client configured")
|
||||
}
|
||||
|
||||
func (r *customDomainDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_cdn_custom_domain"
|
||||
}
|
||||
|
||||
func (r *customDomainDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
MarkdownDescription: features.AddBetaDescription("CDN distribution data source schema."),
|
||||
Description: "CDN distribution data source schema.",
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Description: customDomainSchemaDescriptions["id"],
|
||||
Computed: true,
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Description: customDomainSchemaDescriptions["name"],
|
||||
Required: true,
|
||||
},
|
||||
"distribution_id": schema.StringAttribute{
|
||||
Description: customDomainSchemaDescriptions["distribution_id"],
|
||||
Required: true,
|
||||
Validators: []validator.String{validate.UUID()},
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Description: customDomainSchemaDescriptions["project_id"],
|
||||
Required: true,
|
||||
},
|
||||
"status": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: customDomainSchemaDescriptions["status"],
|
||||
},
|
||||
"errors": schema.ListAttribute{
|
||||
ElementType: types.StringType,
|
||||
Computed: true,
|
||||
Description: customDomainSchemaDescriptions["errors"],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *customDomainDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model CustomDomainModel
|
||||
diags := req.Config.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
distributionId := model.DistributionId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "distribution_id", distributionId)
|
||||
name := model.Name.ValueString()
|
||||
ctx = tflog.SetField(ctx, "name", name)
|
||||
|
||||
customDomainResp, err := r.client.GetCustomDomain(ctx, projectId, distributionId, name).Execute()
|
||||
if err != nil {
|
||||
var oapiErr *oapierror.GenericOpenAPIError
|
||||
// n.b. err is caught here if of type *oapierror.GenericOpenAPIError, which the stackit SDK client returns
|
||||
if errors.As(err, &oapiErr) {
|
||||
if oapiErr.StatusCode == http.StatusNotFound {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
}
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading CDN custom domain", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
err = mapCustomDomainFields(customDomainResp.CustomDomain, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading CDN custom domain", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Set refreshed state
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "CDN custom domain read")
|
||||
}
|
||||
310
stackit/internal/services/cdn/customdomain/resource.go
Normal file
310
stackit/internal/services/cdn/customdomain/resource.go
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
package cdn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/cdn"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/cdn/wait"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
||||
)
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ resource.Resource = &customDomainResource{}
|
||||
_ resource.ResourceWithConfigure = &customDomainResource{}
|
||||
_ resource.ResourceWithImportState = &customDomainResource{}
|
||||
)
|
||||
|
||||
var customDomainSchemaDescriptions = map[string]string{
|
||||
"id": "Terraform's internal resource identifier. It is structured as \"`project_id`,`distribution_id`\".",
|
||||
"distribution_id": "CDN distribution ID",
|
||||
"project_id": "STACKIT project ID associated with the distribution",
|
||||
"status": "Status of the distribution",
|
||||
"errors": "List of distribution errors",
|
||||
}
|
||||
|
||||
type CustomDomainModel struct {
|
||||
ID types.String `tfsdk:"id"` // Required by Terraform
|
||||
DistributionId types.String `tfsdk:"distribution_id"` // DistributionID associated with the cdn distribution
|
||||
ProjectId types.String `tfsdk:"project_id"` // ProjectId associated with the cdn distribution
|
||||
Name types.String `tfsdk:"name"` // The custom domain
|
||||
Status types.String `tfsdk:"status"` // The status of the cdn distribution
|
||||
Errors types.List `tfsdk:"errors"` // Any errors that the distribution has
|
||||
}
|
||||
|
||||
type customDomainResource struct {
|
||||
client *cdn.APIClient
|
||||
providerData core.ProviderData
|
||||
}
|
||||
|
||||
func NewCustomDomainResource() resource.Resource {
|
||||
return &customDomainResource{}
|
||||
}
|
||||
|
||||
func (r *customDomainResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
var ok bool
|
||||
if r.providerData, ok = req.ProviderData.(core.ProviderData); !ok {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData))
|
||||
return
|
||||
}
|
||||
features.CheckBetaResourcesEnabled(ctx, &r.providerData, &resp.Diagnostics, "stackit_cdn_custom_domain", "resource")
|
||||
|
||||
var apiClient *cdn.APIClient
|
||||
var err error
|
||||
if r.providerData.CdnCustomEndpoint != "" {
|
||||
ctx = tflog.SetField(ctx, "cdn_custom_endpoint", r.providerData.CdnCustomEndpoint)
|
||||
apiClient, err = cdn.NewAPIClient(
|
||||
config.WithCustomAuth(r.providerData.RoundTripper),
|
||||
config.WithEndpoint(r.providerData.CdnCustomEndpoint),
|
||||
)
|
||||
} else {
|
||||
apiClient, err = cdn.NewAPIClient(
|
||||
config.WithCustomAuth(r.providerData.RoundTripper),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration", err))
|
||||
return
|
||||
}
|
||||
r.client = apiClient
|
||||
tflog.Info(ctx, "CDN client configured")
|
||||
}
|
||||
|
||||
func (r *customDomainResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_cdn_custom_domain"
|
||||
}
|
||||
|
||||
func (r *customDomainResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
MarkdownDescription: features.AddBetaDescription("CDN distribution data source schema."),
|
||||
Description: "CDN distribution data source schema.",
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Description: customDomainSchemaDescriptions["id"],
|
||||
Computed: true,
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Description: customDomainSchemaDescriptions["name"],
|
||||
Required: true,
|
||||
Optional: false,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"distribution_id": schema.StringAttribute{
|
||||
Description: customDomainSchemaDescriptions["distribution_id"],
|
||||
Required: true,
|
||||
Optional: false,
|
||||
Validators: []validator.String{validate.UUID()},
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Description: customDomainSchemaDescriptions["project_id"],
|
||||
Required: true,
|
||||
Optional: false,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
},
|
||||
"status": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: customDomainSchemaDescriptions["status"],
|
||||
},
|
||||
"errors": schema.ListAttribute{
|
||||
ElementType: types.StringType,
|
||||
Computed: true,
|
||||
Description: customDomainSchemaDescriptions["errors"],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *customDomainResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model CustomDomainModel
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
projectId := model.ProjectId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
distributionId := model.DistributionId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "distribution_id", distributionId)
|
||||
name := model.Name.ValueString()
|
||||
ctx = tflog.SetField(ctx, "name", name)
|
||||
|
||||
payload := cdn.PutCustomDomainPayload{IntentId: cdn.PtrString(uuid.NewString())}
|
||||
|
||||
_, err := r.client.PutCustomDomain(ctx, projectId, distributionId, name).PutCustomDomainPayload(payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating CDN custom domain", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
waitResp, err := wait.CreateCDNCustomDomainWaitHandler(ctx, r.client, projectId, distributionId, name).SetTimeout(5 * time.Minute).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating CDN custom domain", fmt.Sprintf("Waiting for create: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
err = mapCustomDomainFields(waitResp, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating CDN custom domain", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "CDN custom domain created")
|
||||
}
|
||||
|
||||
func (r *customDomainResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model CustomDomainModel
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
distributionId := model.DistributionId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "distribution_id", distributionId)
|
||||
name := model.Name.ValueString()
|
||||
ctx = tflog.SetField(ctx, "name", name)
|
||||
|
||||
customDomainResp, err := r.client.GetCustomDomain(ctx, projectId, distributionId, name).Execute()
|
||||
if err != nil {
|
||||
var oapiErr *oapierror.GenericOpenAPIError
|
||||
// n.b. err is caught here if of type *oapierror.GenericOpenAPIError, which the stackit SDK client returns
|
||||
if errors.As(err, &oapiErr) {
|
||||
if oapiErr.StatusCode == http.StatusNotFound {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
}
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading CDN custom domain", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
err = mapCustomDomainFields(customDomainResp.CustomDomain, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading CDN custom domain", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Set refreshed state
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "CDN custom domain read")
|
||||
}
|
||||
|
||||
func (r *customDomainResource) Update(ctx context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
// Update shouldn't be called; custom domains have only computed fields and fields that require replacement when changed
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating CDN custom domain", "Custom domain cannot be updated")
|
||||
}
|
||||
|
||||
func (r *customDomainResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model CustomDomainModel
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
distributionId := model.DistributionId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "distribution_id", distributionId)
|
||||
name := model.Name.ValueString()
|
||||
ctx = tflog.SetField(ctx, "name", name)
|
||||
|
||||
_, err := r.client.DeleteCustomDomain(ctx, projectId, distributionId, name).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Delete CDN custom domain", fmt.Sprintf("Delete custom domain: %v", err))
|
||||
}
|
||||
_, err = wait.DeleteCDNCustomDomainWaitHandler(ctx, r.client, projectId, distributionId, name).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Delete CDN custom domain", fmt.Sprintf("Waiting for deletion: %v", err))
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "CDN custom domain deleted")
|
||||
}
|
||||
|
||||
func (r *customDomainResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
idParts := strings.Split(req.ID, core.Separator)
|
||||
|
||||
if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error importing CDN custom domain", fmt.Sprintf("Expected import identifier on the format: [project_id]%q[distribution_id]%q[custom_domain_name], got %q", core.Separator, core.Separator, req.ID))
|
||||
}
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("distribution_id"), idParts[1])...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("name"), idParts[2])...)
|
||||
tflog.Info(ctx, "CDN custom domain state imported")
|
||||
}
|
||||
|
||||
func mapCustomDomainFields(customDomain *cdn.CustomDomain, model *CustomDomainModel) error {
|
||||
if customDomain == nil {
|
||||
return fmt.Errorf("response input is nil")
|
||||
}
|
||||
if model == nil {
|
||||
return fmt.Errorf("model input is nil")
|
||||
}
|
||||
|
||||
if customDomain.Name == nil {
|
||||
return fmt.Errorf("Name is missing in response")
|
||||
}
|
||||
|
||||
if customDomain.Status == nil {
|
||||
return fmt.Errorf("Status missing in response")
|
||||
}
|
||||
|
||||
id := model.ProjectId.ValueString() + core.Separator + model.DistributionId.ValueString() + core.Separator + *customDomain.Name
|
||||
|
||||
model.ID = types.StringValue(id)
|
||||
model.Status = types.StringValue(string(*customDomain.Status))
|
||||
|
||||
customDomainErrors := []attr.Value{}
|
||||
if customDomain.Errors != nil {
|
||||
for _, e := range *customDomain.Errors {
|
||||
if e.En == nil {
|
||||
return fmt.Errorf("Error description missing")
|
||||
}
|
||||
customDomainErrors = append(customDomainErrors, types.StringValue(*e.En))
|
||||
}
|
||||
}
|
||||
modelErrors, diags := types.ListValue(types.StringType, customDomainErrors)
|
||||
if diags.HasError() {
|
||||
return core.DiagsToError(diags)
|
||||
}
|
||||
model.Errors = modelErrors
|
||||
|
||||
return nil
|
||||
}
|
||||
90
stackit/internal/services/cdn/customdomain/resource_test.go
Normal file
90
stackit/internal/services/cdn/customdomain/resource_test.go
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
package cdn
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/cdn"
|
||||
)
|
||||
|
||||
func TestMapFields(t *testing.T) {
|
||||
emtpyErrorsList := types.ListValueMust(types.StringType, []attr.Value{})
|
||||
expectedModel := func(mods ...func(*CustomDomainModel)) *CustomDomainModel {
|
||||
model := &CustomDomainModel{
|
||||
ID: types.StringValue("test-project-id,test-distribution-id,https://testdomain.com"),
|
||||
DistributionId: types.StringValue("test-distribution-id"),
|
||||
ProjectId: types.StringValue("test-project-id"),
|
||||
Status: types.StringValue("ACTIVE"),
|
||||
Errors: emtpyErrorsList,
|
||||
}
|
||||
for _, mod := range mods {
|
||||
mod(model)
|
||||
}
|
||||
return model
|
||||
}
|
||||
customDomainFixture := func(mods ...func(*cdn.CustomDomain)) *cdn.CustomDomain {
|
||||
distribution := &cdn.CustomDomain{
|
||||
Errors: &[]cdn.StatusError{},
|
||||
Name: cdn.PtrString("https://testdomain.com"),
|
||||
Status: cdn.DOMAINSTATUS_ACTIVE.Ptr(),
|
||||
}
|
||||
for _, mod := range mods {
|
||||
mod(distribution)
|
||||
}
|
||||
return distribution
|
||||
}
|
||||
tests := map[string]struct {
|
||||
Input *cdn.CustomDomain
|
||||
Expected *CustomDomainModel
|
||||
IsValid bool
|
||||
}{
|
||||
"happy_path": {
|
||||
Expected: expectedModel(),
|
||||
Input: customDomainFixture(),
|
||||
IsValid: true,
|
||||
},
|
||||
"happy_path_status_error": {
|
||||
Expected: expectedModel(func(m *CustomDomainModel) {
|
||||
m.Status = types.StringValue("ERROR")
|
||||
}),
|
||||
Input: customDomainFixture(func(d *cdn.CustomDomain) {
|
||||
d.Status = cdn.DOMAINSTATUS_ERROR.Ptr()
|
||||
}),
|
||||
IsValid: true,
|
||||
},
|
||||
"sad_path_custom_domain_nil": {
|
||||
Expected: expectedModel(),
|
||||
Input: nil,
|
||||
IsValid: false,
|
||||
},
|
||||
"sad_path_name_missing": {
|
||||
Expected: expectedModel(),
|
||||
Input: customDomainFixture(func(d *cdn.CustomDomain) {
|
||||
d.Name = nil
|
||||
}),
|
||||
IsValid: false,
|
||||
},
|
||||
}
|
||||
for tn, tc := range tests {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
model := &CustomDomainModel{}
|
||||
model.DistributionId = tc.Expected.DistributionId
|
||||
model.ProjectId = tc.Expected.ProjectId
|
||||
err := mapCustomDomainFields(tc.Input, model)
|
||||
if err != nil && tc.IsValid {
|
||||
t.Fatalf("Error mapping fields: %v", err)
|
||||
}
|
||||
if err == nil && !tc.IsValid {
|
||||
t.Fatalf("Should have failed")
|
||||
}
|
||||
if tc.IsValid {
|
||||
diff := cmp.Diff(model, tc.Expected)
|
||||
if diff != "" {
|
||||
t.Fatalf("Create Payload not as expected: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
|
|
@ -154,7 +155,7 @@ func (r *distributionResource) Schema(_ context.Context, _ resource.SchemaReques
|
|||
Computed: true,
|
||||
},
|
||||
"distribution_id": schema.StringAttribute{
|
||||
Description: schemaDescriptions["project_id"],
|
||||
Description: schemaDescriptions["distribution_id"],
|
||||
Computed: true,
|
||||
Validators: []validator.String{validate.UUID()},
|
||||
PlanModifiers: []planmodifier.String{
|
||||
|
|
@ -266,7 +267,7 @@ func (r *distributionResource) Create(ctx context.Context, req resource.CreateRe
|
|||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating CDN distribution", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
waitResp, err := wait.CreateDistributionPoolWaitHandler(ctx, r.client, projectId, *createResp.Distribution.Id).WaitWithContext(ctx)
|
||||
waitResp, err := wait.CreateDistributionPoolWaitHandler(ctx, r.client, projectId, *createResp.Distribution.Id).SetTimeout(5 * time.Minute).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating CDN distribution", fmt.Sprintf("Waiting for create: %v", err))
|
||||
return
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features"
|
||||
roleAssignements "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/authorization/roleassignments"
|
||||
cdnCustomDomain "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/cdn/customdomain"
|
||||
cdn "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/cdn/distribution"
|
||||
dnsRecordSet "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/dns/recordset"
|
||||
dnsZone "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/dns/zone"
|
||||
|
|
@ -476,6 +477,7 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
|
|||
return []func() datasource.DataSource{
|
||||
alertGroup.NewAlertGroupDataSource,
|
||||
cdn.NewDistributionDataSource,
|
||||
cdnCustomDomain.NewCustomDomainDataSource,
|
||||
dnsZone.NewZoneDataSource,
|
||||
dnsRecordSet.NewRecordSetDataSource,
|
||||
iaasAffinityGroup.NewAffinityGroupDatasource,
|
||||
|
|
@ -532,6 +534,7 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource {
|
|||
resources := []func() resource.Resource{
|
||||
alertGroup.NewAlertGroupResource,
|
||||
cdn.NewDistributionResource,
|
||||
cdnCustomDomain.NewCustomDomainResource,
|
||||
dnsZone.NewZoneResource,
|
||||
dnsRecordSet.NewRecordSetResource,
|
||||
iaasAffinityGroup.NewAffinityGroupResource,
|
||||
|
|
|
|||
60
templates/guides/stackit_cdn_with_custom_domain.md.tmpl
Normal file
60
templates/guides/stackit_cdn_with_custom_domain.md.tmpl
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
---
|
||||
page_title: "Using STACKIT CDN with your own domain"
|
||||
---
|
||||
# Using STACKIT CDN with your own domain
|
||||
|
||||
## Overview
|
||||
|
||||
This guide outlines the process of creating a STACKIT CDN distribution and configuring it to make use of an existing domain using STACKIT DNS.
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Create a STACKIT CDN and DNS Zone**
|
||||
|
||||
Create the CDN distribution and the DNS zone.
|
||||
|
||||
```terraform
|
||||
resource "stackit_cdn_distribution" "example_distribution" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
config = {
|
||||
backend = {
|
||||
type = "http"
|
||||
origin_url = "mybackend.onstackit.cloud"
|
||||
}
|
||||
regions = ["EU", "US", "ASIA", "AF", "SA"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "stackit_dns_zone" "example_zone" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
name = "My DNS zone"
|
||||
dns_name = "myapp.runs.onstackit.cloud"
|
||||
contact_email = "aa@bb.ccc"
|
||||
type = "primary"
|
||||
}
|
||||
```
|
||||
|
||||
2. **Add CNAME record to your DNS zone**
|
||||
|
||||
If you want to redirect your entire domain to the CDN, you can instead use an A record.
|
||||
```terraform
|
||||
resource "stackit_dns_record_set" "example" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
zone_id = stackit_dns_zone.example_zone.zone_id
|
||||
name = "cdn"
|
||||
type = "CNAME"
|
||||
records = ["${stackit_cdn_distribution.domains[0].name}."]
|
||||
}
|
||||
```
|
||||
|
||||
3. **Create a STACKIT CDN Custom Domain**
|
||||
```terraform
|
||||
# Create a CDN custom domain
|
||||
resource "stackit_cdn_custom_domain" "example" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
distribution_id = stackit_cdn_distribution.example_distribution.distribution_id
|
||||
name = "${stackit_dns_record_set.example.name}.${stackit_dns_zone.dns_name}"
|
||||
}
|
||||
```
|
||||
|
||||
Now, you can access your content on the url `cdn.myapp.runs.onstackit.cloud`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue