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>
188 lines
5.8 KiB
Go
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,
|
|
}
|
|
}
|
|
}
|