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>
This commit is contained in:
71
internal/client/client.go
Normal file
71
internal/client/client.go
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) EZSCALE.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Client is the VirtFusion API client.
|
||||
type Client struct {
|
||||
BaseURL string
|
||||
Token string
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// New creates a new VirtFusion API client.
|
||||
// The endpoint can be a hostname (e.g. "cp.example.com") or a full URL
|
||||
// (e.g. "https://cp.example.com" or "https://cp.example.com/api/v1").
|
||||
func New(endpoint, token string) (*Client, error) {
|
||||
if endpoint == "" {
|
||||
return nil, fmt.Errorf("endpoint is required")
|
||||
}
|
||||
if token == "" {
|
||||
return nil, fmt.Errorf("api_token is required")
|
||||
}
|
||||
|
||||
baseURL := normalizeEndpoint(endpoint)
|
||||
|
||||
return &Client{
|
||||
BaseURL: baseURL,
|
||||
Token: token,
|
||||
HTTPClient: &http.Client{
|
||||
Timeout: 60 * time.Second,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// normalizeEndpoint takes a user-provided endpoint and returns a full base URL
|
||||
// ending with /api/v1. Supports:
|
||||
// - "cp.example.com" → "https://cp.example.com/api/v1"
|
||||
// - "https://cp.example.com" → "https://cp.example.com/api/v1"
|
||||
// - "https://cp.example.com/api/v1" → "https://cp.example.com/api/v1"
|
||||
func normalizeEndpoint(endpoint string) string {
|
||||
endpoint = strings.TrimRight(endpoint, "/")
|
||||
|
||||
// If no scheme, add https://
|
||||
if !strings.Contains(endpoint, "://") {
|
||||
endpoint = "https://" + endpoint
|
||||
}
|
||||
|
||||
// Parse to validate
|
||||
u, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
// Fall back to simple construction
|
||||
return endpoint + "/api/v1"
|
||||
}
|
||||
|
||||
// If path already ends with /api/v1, use as-is
|
||||
if strings.HasSuffix(u.Path, "/api/v1") {
|
||||
return u.String()
|
||||
}
|
||||
|
||||
// Otherwise append /api/v1
|
||||
u.Path = strings.TrimRight(u.Path, "/") + "/api/v1"
|
||||
return u.String()
|
||||
}
|
||||
Reference in New Issue
Block a user