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>
72 lines
1.7 KiB
Go
72 lines
1.7 KiB
Go
// 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()
|
|
}
|