Implement read-only Dynu Terraform provider skeleton

This commit is contained in:
beatz174-bit
2026-04-21 13:02:42 +10:00
parent 0889d83a0a
commit b268d7a6aa
16 changed files with 1041 additions and 0 deletions
+193
View File
@@ -0,0 +1,193 @@
package dynuclient
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strings"
"time"
)
const defaultBaseURL = "https://api.dynu.com/v2"
type Client struct {
apiKey string
baseURL string
httpClient *http.Client
}
type Option func(*Client)
func WithHTTPClient(httpClient *http.Client) Option {
return func(c *Client) {
c.httpClient = httpClient
}
}
func WithBaseURL(baseURL string) Option {
return func(c *Client) {
c.baseURL = strings.TrimRight(baseURL, "/")
}
}
func New(apiKey string, opts ...Option) *Client {
c := &Client{
apiKey: apiKey,
baseURL: defaultBaseURL,
httpClient: &http.Client{
Timeout: 30 * time.Second,
},
}
for _, opt := range opts {
opt(c)
}
return c
}
type apiException struct {
StatusCode int `json:"statusCode"`
Type string `json:"type"`
Message string `json:"message"`
}
type apiResponse struct {
StatusCode int `json:"statusCode"`
Exception *apiException `json:"exception"`
}
type Domain struct {
ID int64 `json:"id"`
Name string `json:"name"`
UnicodeName string `json:"unicodeName"`
Token string `json:"token"`
State string `json:"state"`
Group string `json:"group"`
IPv4Address string `json:"ipv4Address"`
IPv6Address string `json:"ipv6Address"`
TTL int64 `json:"ttl"`
IPv4 bool `json:"ipv4"`
IPv6 bool `json:"ipv6"`
IPv4WildcardAlias bool `json:"ipv4WildcardAlias"`
IPv6WildcardAlias bool `json:"ipv6WildcardAlias"`
AllowZoneTransfer bool `json:"allowZoneTransfer"`
DNSSEC bool `json:"dnssec"`
CreatedOn string `json:"createdOn"`
UpdatedOn string `json:"updatedOn"`
}
type DNSRecord struct {
ID int64 `json:"id"`
DomainID int64 `json:"domainId"`
DomainName string `json:"domainName"`
NodeName string `json:"nodeName"`
Hostname string `json:"hostname"`
RecordType string `json:"recordType"`
State bool `json:"state"`
TTL int64 `json:"ttl"`
Content string `json:"content"`
UpdatedOn string `json:"updatedOn"`
Group string `json:"group"`
Host string `json:"host"`
}
type listDomainsResponse struct {
apiResponse
Domains []Domain `json:"domains"`
}
type getDomainResponse struct {
apiResponse
Domain
}
type listDNSRecordsResponse struct {
apiResponse
DNSRecords []DNSRecord `json:"dnsRecords"`
}
type getRootResponse struct {
apiResponse
ID int64 `json:"id"`
Hostname string `json:"hostname"`
DomainName string `json:"domainName"`
Node string `json:"node"`
}
func (c *Client) ListDomains(ctx context.Context) ([]Domain, error) {
var resp listDomainsResponse
if err := c.doGET(ctx, "/dns", &resp); err != nil {
return nil, err
}
return resp.Domains, nil
}
func (c *Client) GetDomainByID(ctx context.Context, domainID int64) (*Domain, error) {
var resp getDomainResponse
if err := c.doGET(ctx, fmt.Sprintf("/dns/%d", domainID), &resp); err != nil {
return nil, err
}
return &resp.Domain, nil
}
func (c *Client) GetRootDomain(ctx context.Context, hostname string) (int64, string, error) {
var resp getRootResponse
if err := c.doGET(ctx, fmt.Sprintf("/dns/getroot/%s", hostname), &resp); err != nil {
return 0, "", err
}
if resp.ID == 0 || resp.DomainName == "" {
return 0, "", errors.New("dynu API returned an incomplete root domain response")
}
return resp.ID, resp.DomainName, nil
}
func (c *Client) ListDNSRecords(ctx context.Context, domainID int64) ([]DNSRecord, error) {
var resp listDNSRecordsResponse
if err := c.doGET(ctx, fmt.Sprintf("/dns/%d/record", domainID), &resp); err != nil {
return nil, err
}
return resp.DNSRecords, nil
}
func (c *Client) doGET(ctx context.Context, path string, target any) error {
url := c.baseURL + path
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return err
}
req.Header.Set("Accept", "application/json")
req.Header.Set("API-Key", c.apiKey)
res, err := c.httpClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
payload, err := io.ReadAll(res.Body)
if err != nil {
return err
}
if res.StatusCode < 200 || res.StatusCode >= 300 {
return fmt.Errorf("dynu API returned status %d: %s", res.StatusCode, strings.TrimSpace(string(payload)))
}
if err := json.Unmarshal(payload, target); err != nil {
return fmt.Errorf("failed to decode dynu API response: %w", err)
}
apiResult := apiResponse{}
if err := json.Unmarshal(payload, &apiResult); err == nil && apiResult.Exception != nil {
return fmt.Errorf("dynu API error %d (%s): %s", apiResult.Exception.StatusCode, apiResult.Exception.Type, apiResult.Exception.Message)
}
return nil
}
+48
View File
@@ -0,0 +1,48 @@
package dynuclient
import (
"context"
"net/http"
"net/http/httptest"
"testing"
)
func TestListDomains(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/dns" {
t.Fatalf("unexpected path: %s", r.URL.Path)
}
if got := r.Header.Get("API-Key"); got != "test-key" {
t.Fatalf("unexpected api key header: %s", got)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"statusCode":200,"domains":[{"id":1,"name":"example.com","state":"Complete"}]}`))
}))
defer ts.Close()
client := New("test-key", WithBaseURL(ts.URL), WithHTTPClient(ts.Client()))
domains, err := client.ListDomains(context.Background())
if err != nil {
t.Fatalf("ListDomains() error = %v", err)
}
if len(domains) != 1 {
t.Fatalf("expected 1 domain, got %d", len(domains))
}
if domains[0].Name != "example.com" {
t.Fatalf("unexpected domain name %q", domains[0].Name)
}
}
func TestDoGetErrorResponse(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
_, _ = w.Write([]byte(`{"statusCode":401,"exception":{"statusCode":401,"type":"Authentication Exception","message":"invalid"}}`))
}))
defer ts.Close()
client := New("test-key", WithBaseURL(ts.URL), WithHTTPClient(ts.Client()))
_, err := client.ListDomains(context.Background())
if err == nil {
t.Fatal("expected error, got nil")
}
}
@@ -0,0 +1,137 @@
package provider
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"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"
)
var (
_ datasource.DataSource = &dnsRecordsDataSource{}
_ datasource.DataSourceWithConfigure = &dnsRecordsDataSource{}
)
type dnsRecordsDataSource struct {
clientProvider *providerData
}
type dnsRecordsDataSourceModel struct {
Hostname types.String `tfsdk:"hostname"`
DomainID types.Int64 `tfsdk:"domain_id"`
DomainName types.String `tfsdk:"domain_name"`
Records []dnsRecordStateItem `tfsdk:"records"`
}
type dnsRecordStateItem struct {
ID types.Int64 `tfsdk:"id"`
DomainID types.Int64 `tfsdk:"domain_id"`
DomainName types.String `tfsdk:"domain_name"`
NodeName types.String `tfsdk:"node_name"`
Hostname types.String `tfsdk:"hostname"`
RecordType types.String `tfsdk:"record_type"`
TTL types.Int64 `tfsdk:"ttl"`
State types.Bool `tfsdk:"state"`
Content types.String `tfsdk:"content"`
UpdatedOn types.String `tfsdk:"updated_on"`
Group types.String `tfsdk:"group"`
Host types.String `tfsdk:"host"`
}
func NewDNSRecordsDataSource() datasource.DataSource {
return &dnsRecordsDataSource{}
}
func (d *dnsRecordsDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_dns_records"
}
func (d *dnsRecordsDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Get DNS records from Dynu for the domain resolved from a hostname.",
Attributes: map[string]schema.Attribute{
"hostname": schema.StringAttribute{
Required: true,
Description: "Any hostname under the target root domain.",
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
},
"domain_id": schema.Int64Attribute{Computed: true},
"domain_name": schema.StringAttribute{Computed: true},
"records": schema.ListNestedAttribute{
Computed: true,
NestedObject: schema.NestedAttributeObject{Attributes: map[string]schema.Attribute{
"id": schema.Int64Attribute{Computed: true},
"domain_id": schema.Int64Attribute{Computed: true},
"domain_name": schema.StringAttribute{Computed: true},
"node_name": schema.StringAttribute{Computed: true},
"hostname": schema.StringAttribute{Computed: true},
"record_type": schema.StringAttribute{Computed: true},
"ttl": schema.Int64Attribute{Computed: true},
"state": schema.BoolAttribute{Computed: true},
"content": schema.StringAttribute{Computed: true},
"updated_on": schema.StringAttribute{Computed: true},
"group": schema.StringAttribute{Computed: true},
"host": schema.StringAttribute{Computed: true},
}},
},
},
}
}
func (d *dnsRecordsDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
providerData, ok := req.ProviderData.(*providerData)
if !ok {
resp.Diagnostics.AddError("Unexpected data source configure type", fmt.Sprintf("Expected *providerData, got %T", req.ProviderData))
return
}
d.clientProvider = providerData
}
func (d *dnsRecordsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var state dnsRecordsDataSourceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
domainID, domainName, err := d.clientProvider.client.GetRootDomain(ctx, state.Hostname.ValueString())
if err != nil {
resp.Diagnostics.AddError("Unable to resolve Dynu domain from hostname", err.Error())
return
}
records, err := d.clientProvider.client.ListDNSRecords(ctx, domainID)
if err != nil {
resp.Diagnostics.AddError("Unable to list Dynu DNS records", err.Error())
return
}
state.DomainID = types.Int64Value(domainID)
state.DomainName = types.StringValue(domainName)
state.Records = make([]dnsRecordStateItem, 0, len(records))
for _, record := range records {
state.Records = append(state.Records, dnsRecordStateItem{
ID: types.Int64Value(record.ID),
DomainID: types.Int64Value(record.DomainID),
DomainName: mapString(record.DomainName),
NodeName: mapString(record.NodeName),
Hostname: mapString(record.Hostname),
RecordType: mapString(record.RecordType),
TTL: types.Int64Value(record.TTL),
State: types.BoolValue(record.State),
Content: mapString(record.Content),
UpdatedOn: mapString(record.UpdatedOn),
Group: mapString(record.Group),
Host: mapString(record.Host),
})
}
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}
+142
View File
@@ -0,0 +1,142 @@
package provider
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
)
var (
_ datasource.DataSource = &domainDataSource{}
_ datasource.DataSourceWithConfigure = &domainDataSource{}
)
type domainDataSource struct {
clientProvider *providerData
}
type domainDataSourceModel struct {
Hostname types.String `tfsdk:"hostname"`
Domain types.Object `tfsdk:"domain"`
}
func NewDomainDataSource() datasource.DataSource {
return &domainDataSource{}
}
func (d *domainDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_domain"
}
func (d *domainDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Get a Dynu DNS domain by hostname.",
Attributes: map[string]schema.Attribute{
"hostname": schema.StringAttribute{
Required: true,
Description: "Hostname to resolve to a root domain.",
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
},
"domain": schema.SingleNestedAttribute{
Computed: true,
Description: "Resolved Dynu DNS domain details.",
Attributes: domainAttributes(),
},
},
}
}
func (d *domainDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
providerData, ok := req.ProviderData.(*providerData)
if !ok {
resp.Diagnostics.AddError("Unexpected data source configure type", fmt.Sprintf("Expected *providerData, got %T", req.ProviderData))
return
}
d.clientProvider = providerData
}
func (d *domainDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var state domainDataSourceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
if state.Hostname.IsUnknown() || state.Hostname.IsNull() {
resp.Diagnostics.AddAttributeError(path.Root("hostname"), "Invalid hostname", "hostname must be known and non-null.")
return
}
domainID, _, err := d.clientProvider.client.GetRootDomain(ctx, state.Hostname.ValueString())
if err != nil {
resp.Diagnostics.AddError("Unable to resolve Dynu domain from hostname", err.Error())
return
}
domain, err := d.clientProvider.client.GetDomainByID(ctx, domainID)
if err != nil {
resp.Diagnostics.AddError("Unable to get Dynu domain", err.Error())
return
}
domainValue := mapDomain(*domain)
domainObject, diags := types.ObjectValue(
map[string]attr.Type{
"id": types.Int64Type,
"name": types.StringType,
"unicode_name": types.StringType,
"token": types.StringType,
"state": types.StringType,
"group": types.StringType,
"ipv4_address": types.StringType,
"ipv6_address": types.StringType,
"ttl": types.Int64Type,
"ipv4": types.BoolType,
"ipv6": types.BoolType,
"ipv4_wildcard_alias": types.BoolType,
"ipv6_wildcard_alias": types.BoolType,
"allow_zone_transfer": types.BoolType,
"dnssec": types.BoolType,
"created_on": types.StringType,
"updated_on": types.StringType,
},
map[string]attr.Value{
"id": domainValue.ID,
"name": domainValue.Name,
"unicode_name": domainValue.UnicodeName,
"token": domainValue.Token,
"state": domainValue.State,
"group": domainValue.Group,
"ipv4_address": domainValue.IPv4Address,
"ipv6_address": domainValue.IPv6Address,
"ttl": domainValue.TTL,
"ipv4": domainValue.IPv4,
"ipv6": domainValue.IPv6,
"ipv4_wildcard_alias": domainValue.IPv4WildcardAlias,
"ipv6_wildcard_alias": domainValue.IPv6WildcardAlias,
"allow_zone_transfer": domainValue.AllowZoneTransfer,
"dnssec": domainValue.DNSSEC,
"created_on": domainValue.CreatedOn,
"updated_on": domainValue.UpdatedOn,
},
)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
state.Domain = domainObject
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}
+115
View File
@@ -0,0 +1,115 @@
package provider
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
)
var (
_ datasource.DataSource = &domainsDataSource{}
_ datasource.DataSourceWithConfigure = &domainsDataSource{}
)
type domainsDataSource struct {
clientProvider *providerData
}
type domainsDataSourceModel struct {
Domains []domainModel `tfsdk:"domains"`
}
type domainModel struct {
ID types.Int64 `tfsdk:"id"`
Name types.String `tfsdk:"name"`
UnicodeName types.String `tfsdk:"unicode_name"`
Token types.String `tfsdk:"token"`
State types.String `tfsdk:"state"`
Group types.String `tfsdk:"group"`
IPv4Address types.String `tfsdk:"ipv4_address"`
IPv6Address types.String `tfsdk:"ipv6_address"`
TTL types.Int64 `tfsdk:"ttl"`
IPv4 types.Bool `tfsdk:"ipv4"`
IPv6 types.Bool `tfsdk:"ipv6"`
IPv4WildcardAlias types.Bool `tfsdk:"ipv4_wildcard_alias"`
IPv6WildcardAlias types.Bool `tfsdk:"ipv6_wildcard_alias"`
AllowZoneTransfer types.Bool `tfsdk:"allow_zone_transfer"`
DNSSEC types.Bool `tfsdk:"dnssec"`
CreatedOn types.String `tfsdk:"created_on"`
UpdatedOn types.String `tfsdk:"updated_on"`
}
func NewDomainsDataSource() datasource.DataSource {
return &domainsDataSource{}
}
func (d *domainsDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_domains"
}
func (d *domainsDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "List DNS domains from Dynu.",
Attributes: map[string]schema.Attribute{
"domains": schema.ListNestedAttribute{
Computed: true,
Description: "List of Dynu DNS domains.",
NestedObject: schema.NestedAttributeObject{Attributes: domainAttributes()},
},
},
}
}
func (d *domainsDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
providerData, ok := req.ProviderData.(*providerData)
if !ok {
resp.Diagnostics.AddError("Unexpected data source configure type", fmt.Sprintf("Expected *providerData, got %T", req.ProviderData))
return
}
d.clientProvider = providerData
}
func (d *domainsDataSource) Read(ctx context.Context, _ datasource.ReadRequest, resp *datasource.ReadResponse) {
domains, err := d.clientProvider.client.ListDomains(ctx)
if err != nil {
resp.Diagnostics.AddError("Unable to list Dynu domains", err.Error())
return
}
state := domainsDataSourceModel{Domains: make([]domainModel, 0, len(domains))}
for _, domain := range domains {
state.Domains = append(state.Domains, mapDomain(domain))
}
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}
func domainAttributes() map[string]schema.Attribute {
return map[string]schema.Attribute{
"id": schema.Int64Attribute{Computed: true},
"name": schema.StringAttribute{Computed: true},
"unicode_name": schema.StringAttribute{Computed: true},
"token": schema.StringAttribute{Computed: true, Sensitive: true},
"state": schema.StringAttribute{Computed: true},
"group": schema.StringAttribute{Computed: true},
"ipv4_address": schema.StringAttribute{Computed: true},
"ipv6_address": schema.StringAttribute{Computed: true},
"ttl": schema.Int64Attribute{Computed: true},
"ipv4": schema.BoolAttribute{Computed: true},
"ipv6": schema.BoolAttribute{Computed: true},
"ipv4_wildcard_alias": schema.BoolAttribute{Computed: true},
"ipv6_wildcard_alias": schema.BoolAttribute{Computed: true},
"allow_zone_transfer": schema.BoolAttribute{Computed: true},
"dnssec": schema.BoolAttribute{Computed: true},
"created_on": schema.StringAttribute{Computed: true},
"updated_on": schema.StringAttribute{Computed: true},
}
}
+36
View File
@@ -0,0 +1,36 @@
package provider
import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/dynu/terraform-provider-dynu/internal/dynuclient"
)
func mapDomain(domain dynuclient.Domain) domainModel {
return domainModel{
ID: types.Int64Value(domain.ID),
Name: types.StringValue(domain.Name),
UnicodeName: types.StringValue(domain.UnicodeName),
Token: types.StringValue(domain.Token),
State: types.StringValue(domain.State),
Group: types.StringValue(domain.Group),
IPv4Address: types.StringValue(domain.IPv4Address),
IPv6Address: types.StringValue(domain.IPv6Address),
TTL: types.Int64Value(domain.TTL),
IPv4: types.BoolValue(domain.IPv4),
IPv6: types.BoolValue(domain.IPv6),
IPv4WildcardAlias: types.BoolValue(domain.IPv4WildcardAlias),
IPv6WildcardAlias: types.BoolValue(domain.IPv6WildcardAlias),
AllowZoneTransfer: types.BoolValue(domain.AllowZoneTransfer),
DNSSEC: types.BoolValue(domain.DNSSEC),
CreatedOn: types.StringValue(domain.CreatedOn),
UpdatedOn: types.StringValue(domain.UpdatedOn),
}
}
func mapString(in string) types.String {
if in == "" {
return types.StringNull()
}
return types.StringValue(in)
}
+92
View File
@@ -0,0 +1,92 @@
package provider
import (
"context"
"os"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/dynu/terraform-provider-dynu/internal/dynuclient"
)
var _ provider.Provider = &dynuProvider{}
type dynuProvider struct {
version string
}
type dynuProviderModel struct {
APIKey types.String `tfsdk:"api_key"`
}
type providerData struct {
client *dynuclient.Client
}
func New(version string) func() provider.Provider {
return func() provider.Provider {
return &dynuProvider{version: version}
}
}
func (p *dynuProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) {
resp.TypeName = "dynu"
resp.Version = p.version
}
func (p *dynuProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Terraform provider for Dynu DNS read-only operations.",
Attributes: map[string]schema.Attribute{
"api_key": schema.StringAttribute{
Optional: true,
Sensitive: true,
Description: "Dynu API key. Can also be provided using DYNU_API_KEY.",
},
},
}
}
func (p *dynuProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
var data dynuProviderModel
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
apiKey := os.Getenv("DYNU_API_KEY")
if !data.APIKey.IsNull() {
apiKey = data.APIKey.ValueString()
}
if apiKey == "" {
resp.Diagnostics.AddAttributeError(
path.Root("api_key"),
"Missing Dynu API key",
"Set api_key in the provider configuration or DYNU_API_KEY in the environment.",
)
return
}
providerData := &providerData{client: dynuclient.New(apiKey)}
resp.DataSourceData = providerData
resp.ResourceData = nil
}
func (p *dynuProvider) DataSources(_ context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
NewDomainsDataSource,
NewDomainDataSource,
NewDNSRecordsDataSource,
}
}
func (p *dynuProvider) Resources(_ context.Context) []func() resource.Resource {
return nil
}
+15
View File
@@ -0,0 +1,15 @@
package provider
import (
"os"
"testing"
)
func TestAccScaffold(t *testing.T) {
if os.Getenv("TF_ACC") == "" || os.Getenv("DYNU_API_KEY") == "" {
t.Skip("set TF_ACC=1 and DYNU_API_KEY to enable acceptance tests")
}
// Acceptance tests for read-only data sources are intentionally scaffolded in phase 1.
// Add terraform-plugin-testing based test cases in a follow-up with stable fixtures.
}