From 88abbbc7133f8b27c7c63f3fed097e1d0cd7f707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eligio=20Mari=C3=B1o?= <22875166+gmeligio@users.noreply.github.com> Date: Thu, 4 Apr 2024 23:43:19 +0200 Subject: [PATCH] feat: add validate and update methods in domain data source --- .../provider-install-verification/main.tf | 13 +++ go.mod | 2 +- internal/provider/domain.go | 91 ++++++++++++++++ internal/provider/domain_data_source.go | 76 +++++++++++++ internal/provider/example_data_source.go | 102 ------------------ internal/provider/provider.go | 18 +--- 6 files changed, 182 insertions(+), 120 deletions(-) create mode 100644 examples/provider-install-verification/main.tf create mode 100644 internal/provider/domain.go create mode 100644 internal/provider/domain_data_source.go delete mode 100644 internal/provider/example_data_source.go diff --git a/examples/provider-install-verification/main.tf b/examples/provider-install-verification/main.tf new file mode 100644 index 0000000..de5f936 --- /dev/null +++ b/examples/provider-install-verification/main.tf @@ -0,0 +1,13 @@ +terraform { + required_providers { + publicsuffix = { + source = "registry.terraform.io/gmeligio/publicsuffix" + } + } +} + +provider "publicsuffix" {} + +data "publicsuffix_domain" "example" { + # domain = "www.example.com" +} diff --git a/go.mod b/go.mod index 820d75f..8393691 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/hashicorp/terraform-plugin-go v0.22.1 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-testing v1.7.0 + golang.org/x/net v0.21.0 ) require ( @@ -68,7 +69,6 @@ require ( golang.org/x/crypto v0.21.0 // indirect golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 // indirect golang.org/x/mod v0.15.0 // indirect - golang.org/x/net v0.21.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.13.0 // indirect diff --git a/internal/provider/domain.go b/internal/provider/domain.go new file mode 100644 index 0000000..6f4ecae --- /dev/null +++ b/internal/provider/domain.go @@ -0,0 +1,91 @@ +package provider + +import ( + "context" + "strings" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types" + "golang.org/x/net/publicsuffix" +) + +// domainDataSourceModel describes the data source data model. +type domainDataSourceModel struct { + Domain types.String `tfsdk:"domain"` +} + +func (d domainDataSourceModel) validate(_ context.Context) diag.Diagnostics { + var diags diag.Diagnostics + + if d.Domain.IsUnknown() || d.Domain.IsNull() { + return diags + } + + domain := d.Domain.ValueString() + + eTLD, icann := publicsuffix.PublicSuffix(domain) + + manager := "Unmanaged" + if icann { + manager = "ICANN Managed" + } else if strings.IndexByte(eTLD, '.') >= 0 { + manager = "Privately Managed" + } + + if manager == "Unmanaged" { + diags.AddAttributeError( + path.Root("domain"), + "Invalid Attribute Configuration", + "Expected domain to be either ICANN managede or privately managed.", + ) + } + + return diags +} + +// func (d *domainDataSourceModel) update(ctx context.Context) diag.Diagnostics { +// var buffer bytes.Buffer +// var diags diag.Diagnostics +// var err error + +// // cloudinit Provider 'v2.2.0' doesn't actually set default values in state properly, so we need to make sure +// // that we don't use any known empty values from previous versions of state +// diags.Append(d.setDefaults(ctx)...) +// if diags.HasError() { +// return diags +// } + +// var configParts []configPartModel +// diags.Append(d.Parts.ElementsAs(ctx, &configParts, false)...) +// if diags.HasError() { +// return diags +// } + +// if d.Gzip.ValueBool() { +// gzipWriter := gzip.NewWriter(&buffer) + +// err = renderPartsToWriter(ctx, d.Boundary.ValueString(), configParts, gzipWriter) + +// gzipWriter.Close() +// } else { +// err = renderPartsToWriter(ctx, d.Boundary.ValueString(), configParts, &buffer) +// } + +// if err != nil { +// diags.AddError("Unable to render cloudinit config to MIME multi-part file", err.Error()) +// return diags +// } + +// output := "" +// if d.Base64Encode.ValueBool() { +// output = base64.StdEncoding.EncodeToString(buffer.Bytes()) +// } else { +// output = buffer.String() +// } + +// d.ID = types.StringValue(strconv.Itoa(hashcode.String(output))) +// d.Rendered = types.StringValue(output) + +// return diags +// } diff --git a/internal/provider/domain_data_source.go b/internal/provider/domain_data_source.go new file mode 100644 index 0000000..80d7106 --- /dev/null +++ b/internal/provider/domain_data_source.go @@ -0,0 +1,76 @@ +package provider + +import ( + "context" + "net/http" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +// Ensure provider defined types fully satisfy framework interfaces. +var _ datasource.DataSource = &domainDataSource{} + +func NewDomainDataSource() datasource.DataSource { + return &domainDataSource{} +} + +// domainDataSource defines the data source implementation. +type domainDataSource struct { + client *http.Client +} + +func (d *domainDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_domain" +} + +func (d *domainDataSource) ValidateConfig(ctx context.Context, req datasource.ValidateConfigRequest, resp *datasource.ValidateConfigResponse) { + var data domainDataSourceModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(data.validate(ctx)...) +} + +func (d *domainDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + // This description is used by the documentation generator and the language server. + MarkdownDescription: "Parses Public Suffix List properties from a domain", + + Attributes: map[string]schema.Attribute{ + "domain": schema.StringAttribute{ + MarkdownDescription: "The domain is the Second Level Domain (SLD) + Top Level Domain (TLD). For example: example.domain.org", + }, + }, + } +} + +func (d *domainDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data domainDataSourceModel + + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // For the purposes of this example code, hardcoding a response value to + // save into the Terraform state. + data.Domain = types.StringValue("foo.example.com") + + // Write logs using the tflog package + // Documentation: https://terraform.io/plugin/log + tflog.Trace(ctx, "read a data source") + + // resp.Diagnostics.Append(data.update(ctx)...) + + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/internal/provider/example_data_source.go b/internal/provider/example_data_source.go deleted file mode 100644 index d2645d4..0000000 --- a/internal/provider/example_data_source.go +++ /dev/null @@ -1,102 +0,0 @@ -package provider - -import ( - "context" - "fmt" - "net/http" - - "github.com/hashicorp/terraform-plugin-framework/datasource" - "github.com/hashicorp/terraform-plugin-framework/datasource/schema" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-log/tflog" -) - -// Ensure provider defined types fully satisfy framework interfaces. -var _ datasource.DataSource = &ExampleDataSource{} - -func NewExampleDataSource() datasource.DataSource { - return &ExampleDataSource{} -} - -// ExampleDataSource defines the data source implementation. -type ExampleDataSource struct { - client *http.Client -} - -// ExampleDataSourceModel describes the data source data model. -type ExampleDataSourceModel struct { - ConfigurableAttribute types.String `tfsdk:"configurable_attribute"` - Id types.String `tfsdk:"id"` -} - -func (d *ExampleDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_example" -} - -func (d *ExampleDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { - resp.Schema = schema.Schema{ - // This description is used by the documentation generator and the language server. - MarkdownDescription: "Example data source", - - Attributes: map[string]schema.Attribute{ - "configurable_attribute": schema.StringAttribute{ - MarkdownDescription: "Example configurable attribute", - Optional: true, - }, - "id": schema.StringAttribute{ - MarkdownDescription: "Example identifier", - Computed: true, - }, - }, - } -} - -func (d *ExampleDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { - // Prevent panic if the provider has not been configured. - if req.ProviderData == nil { - return - } - - client, ok := req.ProviderData.(*http.Client) - - if !ok { - resp.Diagnostics.AddError( - "Unexpected Data Source Configure Type", - fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), - ) - - return - } - - d.client = client -} - -func (d *ExampleDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - var data ExampleDataSourceModel - - // Read Terraform configuration data into the model - resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) - - if resp.Diagnostics.HasError() { - return - } - - // If applicable, this is a great opportunity to initialize any necessary - // provider client data and make a call using it. - // httpResp, err := d.client.Do(httpReq) - // if err != nil { - // resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read example, got error: %s", err)) - // return - // } - - // For the purposes of this example code, hardcoding a response value to - // save into the Terraform state. - data.Id = types.StringValue("example-id") - - // Write logs using the tflog package - // Documentation: https://terraform.io/plugin/log - tflog.Trace(ctx, "read a data source") - - // Save data into Terraform state - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) -} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 1bc57b8..7463204 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -2,7 +2,6 @@ package provider import ( "context" - "net/http" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/function" @@ -39,21 +38,6 @@ func (p *PublicsuffixProvider) Schema(ctx context.Context, req provider.SchemaRe } func (p *PublicsuffixProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { - var data PublicsuffixProviderModel - - resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) - - if resp.Diagnostics.HasError() { - return - } - - // Configuration values are now available. - // if data.Endpoint.IsNull() { /* ... */ } - - // Example client configuration for data sources and resources - client := http.DefaultClient - resp.DataSourceData = client - resp.ResourceData = client } func (p *PublicsuffixProvider) Resources(ctx context.Context) []func() resource.Resource { @@ -64,7 +48,7 @@ func (p *PublicsuffixProvider) Resources(ctx context.Context) []func() resource. func (p *PublicsuffixProvider) DataSources(ctx context.Context) []func() datasource.DataSource { return []func() datasource.DataSource{ - NewExampleDataSource, + NewDomainDataSource, } }