Hello,
I am developing a provider using terraform-plugin-framework v0.9.0. I started with the terraform-provider-scaffolding-framework.
go install
, terraform init
, and terraform plan
all work as expected. However, when doing a terraform apply I am getting the following error, which I have been unable to successfully troubleshoot.
Error: Error parsing plan
hostname_resource::Create - An unexpected error was encountered parsing the plan. This is always a bug in the provider.
[DEBUG] provider.stdio: received EOF, stopping recv loop: err=“rpc error: code = Unavailable desc = transport is closing”
Any pointers in how I can troubleshoot this would be much appreciated.
Provider.go and relevant resource code are below.
Thanks,
Don
provider.go
package provider
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-log/tflog"
antclient "terraform-provider-ant/internal/client"
)
// Ensure provider defined types fully satisfy framework interfaces
var _ tfsdk.Provider = &provider{}
type provider struct {
client *antclient.Client
configured bool
version string
}
type providerData struct {
}
func (p *provider) Configure(ctx context.Context, req tfsdk.ConfigureProviderRequest, resp *tfsdk.ConfigureProviderResponse) {
var data providerData
diags := req.Config.Get(ctx, &data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
client, err := antclient.NewClient()
if err != nil {
resp.Diagnostics.AddError("Error creating ANT client", fmt.Sprintf("An unexpected error was encountered creating an API client.\n\nDetails: %s", err.Error()))
return
}
p.client = client // provider client is now available
p.version = "dev"
p.configured = true
tflog.Debug(ctx, "provider::Configure - provider is configured")
}
func (p *provider) GetResources(ctx context.Context) (map[string]tfsdk.ResourceType, diag.Diagnostics) {
var logout = map[string]tfsdk.ResourceType{
"ant_hostname": hostnameResourceType{},
}
return map[string]tfsdk.ResourceType{
"ant_hostname": hostnameResourceType{},
}, nil
}
func (p *provider) GetDataSources(ctx context.Context) (map[string]tfsdk.DataSourceType, diag.Diagnostics) {
map[string]tfsdk.DataSourceType{},\n")
return map[string]tfsdk.DataSourceType{}, nil
}
func (p *provider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
return tfsdk.Schema{}, nil
}
func New(version string) func() tfsdk.Provider {
var diags diag.Diagnostics
diags.AddWarning("\nprovider::New::", "103 - tfsdk.Provider{},,\n")
return func() tfsdk.Provider {
return &provider{
version: version,
}
}
}
func convertProviderType(in tfsdk.Provider) (provider, diag.Diagnostics) {
var diags diag.Diagnostics
p, ok := in.(*provider)
if !ok {
diags.AddError(
"Unexpected Provider Instance Type",
fmt.Sprintf("While creating the data source or resource, an unexpected provider type (%T) was received. This is always a bug in the provider code and should be reported to the provider developers.", p),
)
return provider{}, diags
}
if p == nil {
diags.AddError(
"Unexpected Provider Instance Type",
"While creating the data source or resource, an unexpected empty provider instance was received. This is always a bug in the provider code and should be reported to the provider developers.",
)
return provider{}, diags
}
return *p, diags
}
hostname_resource.go
package provider
import (
"context"
antclient "terraform-provider-ant/internal/client"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-go/tftypes"
"github.com/hashicorp/terraform-plugin-log/tflog"
)
// Ensure provider defined types fully satisfy framework interfaces
var _ tfsdk.ResourceType = hostnameResourceType{}
type hostnameResourceType struct{}
func (t hostnameResourceType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
return tfsdk.Schema{
// This description is used by the documentation generator and the language server.
MarkdownDescription: "Hostname resource",
Attributes: map[string]tfsdk.Attribute{
"location": {
MarkdownDescription: "Location: where the VM exists. Use the appropriate **Code** from https://hostnametool.1dc.com/hostnametool/main#/antparameters/assetLocation",
Required: true,
Type: types.StringType,
},
"os": {
Required: true,
MarkdownDescription: "Operating System: \"l\" for Linux, \"w\" for Windows",
Type: types.StringType,
},
"environment": {
Required: true,
MarkdownDescription: "Environment: lifecycle stage the asset implements. Use the appropriate **Code** from https://hostnametool.1dc.com/hostnametool/main#/antparameters/assetEnvironment",
Type: types.StringType,
},
"infratype": {
// Computed: true,
// Required: false,
// Optional: false,
Required: true,
MarkdownDescription: "Infrastructure Type: \"2\" for Virtual Compute Asset ",
Type: types.StringType,
},
"assetContext": {
Required: true,
MarkdownDescription: "Asset Context: the Fiserv Service the VM belongs to. Use the appropriate **Code** from https://hostnametool.1dc.com/hostnametool/main#/antparameters/assetContext",
Type: types.StringType,
},
"assetPurpose": {
Required: true,
MarkdownDescription: "Asset Purpose: the function, or purpose, the VM performs. Use the appropriate **Code** from https://hostnametool.1dc.com/hostnametool/main#/antparameters/assetPurpose",
Type: types.StringType,
},
"description": {
Required: true,
MarkdownDescription: "Description: Arbitrary text to describe the VM",
Type: types.StringType,
},
"contact": {
Required: true,
MarkdownDescription: "Email address of person responsible for the VM.",
Type: types.StringType,
},
},
}, nil
}
type hostnameResourceData struct {
Location types.String `tfsdk:"location"`
OS types.String `tfsdk:"os"`
Environment types.String `tfsdk:"environment"`
Infratype types.String `tfsdk:"infratype"`
AssetContext types.String `tfsdk:"assetContext"`
AssetPurpose types.String `tfsdk:"assetPurpose"`
Description types.String `tfsdk:"description"`
Contact types.String `tfsdk:"contact"`
}
type hostnameResourceData1 struct {
// Location types.String `tfsdk:"location"`
// OS types.String `tfsdk:"os"`
// Environment types.String `tfsdk:"environment"`
// Infratype types.String `tfsdk:"infratype"`
// AssetContext types.String `tfsdk:"assetContext"`
// AssetPurpose types.String `tfsdk:"assetPurpose"`
// Description types.String `tfsdk:"description"`
// Contact types.String `tfsdk:"contact"`
Name types.String `tfsdk:"name"`
}
func (t hostnameResourceType) NewResource(ctx context.Context, in tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
provider, diags := convertProviderType(in)
return hostnameResource{
provider: provider,
}, diags
}
type hostnameResource struct {
provider provider
}
// The provider uses the Create function to create a new resource based on the schema data.
func (r hostnameResource) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
var data hostnameResourceData
// Retrieves values from the plan. The function will attempt to retrieve values from the plan and convert it to the hostnameResourceData struct.
if r.provider.configured {
tflog.Debug(ctx, "hostname_resource::Create::141 - The Provider is Configured.")
} else {
tflog.Error(ctx, "hostname_resource::Create::143 - The Provider is NOT Configured.")
resp.Diagnostics.AddError("Provider is NOT Configured.", "hostname_resource::Create::143 The Provider is NOT Configured.")
return
}
planErr := req.Plan.Get(ctx, &data)
if planErr != nil {
resp.Diagnostics.AddError("Error parsing plan", "hostname_resource::Create - An unexpected error was encountered parsing the plan. This is always a bug in the provider.\n\nDetail: ")
resp.Diagnostics.Append(resp.Diagnostics...)
return
}