Files
terraform-provider-virtfusion/internal/provider/provider.go
Andrew 6b7430b67b Overhaul VirtFusion provider: 20 resources, 30 data sources, multipage pagination
Complete rewrite of the VirtFusion Terraform provider with full API coverage:

- 20 managed resources (server, build, SSH key, user, firewall, IP blocks, etc.)
- 30 data sources (hypervisors, packages, servers, IP blocks, self-service, etc.)
- New HTTP client with proper error handling, query parameter support, and
  automatic multipage pagination via GetAllPages (fetches all pages from
  Laravel-style paginated endpoints and merges into a single response)
- Fixed type mismatches against live API: ServerData.Suspended (int→bool),
  IPBlockData.Type (string→int), PackageData json tags (primaryStorage, etc.),
  ServerData nested CPU/Settings/Resources structure, HypervisorGroupResources
  array response
- Configurable results-per-page (default 300) on all list data sources
- Migrated CI from GitHub Actions to Gitea Actions
- Updated goreleaser config, go.mod dependencies, and examples

Verified against live VirtFusion instance at cp.vps.ezscale.tech:
all data sources return correct data with full pagination support.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 02:01:16 -04:00

188 lines
5.8 KiB
Go

// Copyright (c) EZSCALE.
// SPDX-License-Identifier: MPL-2.0
package provider
import (
"context"
"fmt"
"os"
"terraform-provider-virtfusion/internal/client"
"github.com/hashicorp/terraform-plugin-framework/datasource"
dsschema "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"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"
)
const defaultResultsPerPage int64 = 300
// resultsSchemaAttribute returns the standard "results" schema attribute for list data sources.
func resultsSchemaAttribute() dsschema.Int64Attribute {
return dsschema.Int64Attribute{
MarkdownDescription: "Maximum number of results to return. Defaults to 300.",
Optional: true,
}
}
// resultsQueryParam returns the query parameter string for the results limit.
// If results is null/unknown, defaults to defaultResultsPerPage.
func resultsQueryParam(results types.Int64) string {
n := defaultResultsPerPage
if !results.IsNull() && !results.IsUnknown() {
n = results.ValueInt64()
}
return fmt.Sprintf("results=%d", n)
}
var _ provider.Provider = &VirtfusionProvider{}
// VirtfusionProvider defines the provider implementation.
type VirtfusionProvider struct {
version string
}
// VirtfusionProviderModel describes the provider data model.
type VirtfusionProviderModel struct {
Endpoint types.String `tfsdk:"endpoint"`
ApiToken types.String `tfsdk:"api_token"`
}
func (p *VirtfusionProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) {
resp.TypeName = "virtfusion"
resp.Version = p.version
}
func (p *VirtfusionProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "The VirtFusion provider allows managing VirtFusion virtualization platform resources.",
Attributes: map[string]schema.Attribute{
"endpoint": schema.StringAttribute{
MarkdownDescription: "The VirtFusion API endpoint. Can be a hostname (e.g. `cp.example.com`) or full URL (e.g. `https://cp.example.com/api/v1`). Can also be set with the `VIRTFUSION_ENDPOINT` environment variable.",
Optional: true,
},
"api_token": schema.StringAttribute{
MarkdownDescription: "The API token for authentication. Can also be set with the `VIRTFUSION_API_TOKEN` environment variable.",
Optional: true,
Sensitive: true,
},
},
}
}
func (p *VirtfusionProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
var data VirtfusionProviderModel
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Environment variables as fallback
endpoint := os.Getenv("VIRTFUSION_ENDPOINT")
apiToken := os.Getenv("VIRTFUSION_API_TOKEN")
// Config values override env vars
if !data.Endpoint.IsNull() && data.Endpoint.ValueString() != "" {
endpoint = data.Endpoint.ValueString()
}
if !data.ApiToken.IsNull() && data.ApiToken.ValueString() != "" {
apiToken = data.ApiToken.ValueString()
}
if endpoint == "" {
resp.Diagnostics.AddError(
"Missing Endpoint Configuration",
"The VirtFusion endpoint was not found in the VIRTFUSION_ENDPOINT environment variable or provider configuration block endpoint attribute.",
)
}
if apiToken == "" {
resp.Diagnostics.AddError(
"Missing API Token Configuration",
"The API token was not found in the VIRTFUSION_API_TOKEN environment variable or provider configuration block api_token attribute.",
)
}
if resp.Diagnostics.HasError() {
return
}
c, err := client.New(endpoint, apiToken)
if err != nil {
resp.Diagnostics.AddError("Failed to Create Client", err.Error())
return
}
resp.DataSourceData = c
resp.ResourceData = c
}
func (p *VirtfusionProvider) Resources(_ context.Context) []func() resource.Resource {
return []func() resource.Resource{
NewServerResource,
NewServerBuildResource,
NewSSHKeyResource,
NewUserResource,
NewServerFirewallResource,
NewServerNetworkWhitelistResource,
NewServerIPv4Resource,
NewServerTrafficBlockResource,
NewIPBlockRangeResource,
NewSelfServiceCreditResource,
NewSelfServiceResourcePackResource,
NewSelfServiceHourlyGroupProfileResource,
NewSelfServiceResourceGroupProfileResource,
NewServerPowerActionResource,
NewServerPasswordResetResource,
NewUserPasswordResetResource,
NewUserAuthTokenResource,
NewUserServerAuthTokenResource,
NewSelfServicePackServersActionResource,
NewSelfServiceHourlyResourcePackResource,
}
}
func (p *VirtfusionProvider) DataSources(_ context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
NewHypervisorDataSource,
NewHypervisorsDataSource,
NewHypervisorGroupDataSource,
NewHypervisorGroupsDataSource,
NewHypervisorGroupResourcesDataSource,
NewServerDataSource,
NewServersDataSource,
NewServersByUserDataSource,
NewServerTemplatesDataSource,
NewServerTrafficDataSource,
NewServerTrafficBlocksDataSource,
NewServerVNCDataSource,
NewServerBackupsDataSource,
NewServerFirewallDataSource,
NewPackageDataSource,
NewPackagesDataSource,
NewPackageTemplatesDataSource,
NewIPBlockDataSource,
NewIPBlocksDataSource,
NewSSHKeyDataSource,
NewSSHKeysByUserDataSource,
NewUserDataSource,
NewDNSServiceDataSource,
NewISODataSource,
NewQueueItemDataSource,
NewSelfServiceCurrenciesDataSource,
NewSelfServiceResourcePackDataSource,
NewSelfServiceHourlyStatsDataSource,
NewSelfServiceReportDataSource,
NewSelfServiceUsageDataSource,
}
}
func New(version string) func() provider.Provider {
return func() provider.Provider {
return &VirtfusionProvider{
version: version,
}
}
}