Files
Sankofa/crossplane-provider-proxmox/pkg/proxmox/client.go
defiQUG 6f28146ac3 Initial Phoenix Sankofa Cloud setup
- Complete project structure with Next.js frontend
- GraphQL API backend with Apollo Server
- Portal application with NextAuth
- Crossplane Proxmox provider
- GitOps configurations
- CI/CD pipelines
- Testing infrastructure (Vitest, Jest, Go tests)
- Error handling and monitoring
- Security hardening
- UI component library
- Documentation
2025-11-28 12:54:33 -08:00

263 lines
5.6 KiB
Go

package proxmox
import (
"context"
"fmt"
"time"
"github.com/pkg/errors"
)
// Client represents a Proxmox API client
type Client struct {
endpoint string
username string
password string
token string
}
// NewClient creates a new Proxmox API client
func NewClient(endpoint, username, password string) *Client {
return &Client{
endpoint: endpoint,
username: username,
password: password,
}
}
// RetryConfig defines retry behavior
type RetryConfig struct {
MaxRetries int
BaseDelay time.Duration
MaxDelay time.Duration
}
// DefaultRetryConfig returns default retry configuration
func DefaultRetryConfig() RetryConfig {
return RetryConfig{
MaxRetries: 3,
BaseDelay: time.Second,
MaxDelay: 30 * time.Second,
}
}
// RetryableError indicates an error that should be retried
type RetryableError struct {
Err error
RetryAfter time.Duration
}
func (e *RetryableError) Error() string {
return e.Err.Error()
}
// IsRetryable checks if an error is retryable
func IsRetryable(err error) bool {
if err == nil {
return false
}
_, ok := err.(*RetryableError)
return ok
}
// Retry executes a function with retry logic
func Retry(ctx context.Context, fn func() error, config RetryConfig) error {
var lastErr error
for attempt := 0; attempt <= config.MaxRetries; attempt++ {
if attempt > 0 {
delay := config.BaseDelay * time.Duration(1<<uint(attempt-1))
if delay > config.MaxDelay {
delay = config.MaxDelay
}
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(delay):
}
}
err := fn()
if err == nil {
return nil
}
lastErr = err
if !IsRetryable(err) {
return err
}
if attempt < config.MaxRetries {
if retryErr, ok := err.(*RetryableError); ok && retryErr.RetryAfter > 0 {
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(retryErr.RetryAfter):
}
}
}
}
return errors.Wrapf(lastErr, "failed after %d retries", config.MaxRetries)
}
// CreateVM creates a virtual machine
func (c *Client) CreateVM(ctx context.Context, spec VMSpec) (*VM, error) {
config := DefaultRetryConfig()
var vm *VM
err := Retry(ctx, func() error {
var retryErr error
vm, retryErr = c.createVM(ctx, spec)
if retryErr != nil {
// Check if error is retryable (network errors, temporary failures)
if isNetworkError(retryErr) || isTemporaryError(retryErr) {
return &RetryableError{Err: retryErr}
}
return retryErr
}
return nil
}, config)
return vm, err
}
// createVM performs the actual VM creation
func (c *Client) createVM(ctx context.Context, spec VMSpec) (*VM, error) {
// TODO: Implement actual Proxmox API call
return nil, fmt.Errorf("not implemented")
}
// UpdateVM updates a virtual machine
func (c *Client) UpdateVM(ctx context.Context, vmID int, spec VMSpec) (*VM, error) {
config := DefaultRetryConfig()
var vm *VM
err := Retry(ctx, func() error {
var retryErr error
vm, retryErr = c.updateVM(ctx, vmID, spec)
if retryErr != nil {
if isNetworkError(retryErr) || isTemporaryError(retryErr) {
return &RetryableError{Err: retryErr}
}
return retryErr
}
return nil
}, config)
return vm, err
}
func (c *Client) updateVM(ctx context.Context, vmID int, spec VMSpec) (*VM, error) {
// TODO: Implement actual Proxmox API call
return nil, fmt.Errorf("not implemented")
}
// DeleteVM deletes a virtual machine
func (c *Client) DeleteVM(ctx context.Context, vmID int) error {
config := DefaultRetryConfig()
return Retry(ctx, func() error {
err := c.deleteVM(ctx, vmID)
if err != nil {
if isNetworkError(err) || isTemporaryError(err) {
return &RetryableError{Err: err}
}
return err
}
return nil
}, config)
}
func (c *Client) deleteVM(ctx context.Context, vmID int) error {
// TODO: Implement actual Proxmox API call
return fmt.Errorf("not implemented")
}
// GetVMStatus gets the status of a virtual machine
func (c *Client) GetVMStatus(ctx context.Context, vmID int) (*VMStatus, error) {
config := DefaultRetryConfig()
var status *VMStatus
err := Retry(ctx, func() error {
var retryErr error
status, retryErr = c.getVMStatus(ctx, vmID)
if retryErr != nil {
if isNetworkError(retryErr) || isTemporaryError(retryErr) {
return &RetryableError{Err: retryErr}
}
return retryErr
}
return nil
}, config)
return status, err
}
func (c *Client) getVMStatus(ctx context.Context, vmID int) (*VMStatus, error) {
// TODO: Implement actual Proxmox API call
return nil, fmt.Errorf("not implemented")
}
// Helper functions
func isNetworkError(err error) bool {
if err == nil {
return false
}
errStr := err.Error()
return contains(errStr, "network") || contains(errStr, "timeout") || contains(errStr, "connection")
}
func isTemporaryError(err error) bool {
if err == nil {
return false
}
errStr := err.Error()
return contains(errStr, "temporary") || contains(errStr, "503") || contains(errStr, "502")
}
func contains(s, substr string) bool {
return len(s) >= len(substr) && (s == substr || len(substr) == 0 || indexOfSubstring(s, substr) >= 0)
}
func indexOfSubstring(s, substr string) int {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return i
}
}
return -1
}
// VMSpec represents VM specification
type VMSpec struct {
Node string
Name string
CPU int
Memory string
Disk string
Storage string
Network string
Image string
}
// VM represents a virtual machine
type VM struct {
ID int
Name string
Status string
IP string
Node string
Created time.Time
}
// VMStatus represents VM status
type VMStatus struct {
State string
IPAddress string
CPU float64
Memory int64
}