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< 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 }