bda568b25c
Go service for Proxmox homelab cluster provisioning. Handles PXE boot, Proxmox autoinstall (answer file generation), cluster join via SSH, and Infrastructure API registration. - Host state machine (registered → pxe_ready → installing → ready) - dnsmasq supervisor with MAC-based allowlist - iPXE script and Proxmox answer file generation - First-boot phone-home → cluster join → infra registration - Operation locking with expiry (409 on conflict) - SSE event hub for real-time dashboard updates - Admin dashboard (host grid, detail, registration form) - Config-driven server types with hot-reload - Docker deployment (multi-stage fat image) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
93 lines
2.2 KiB
Go
93 lines
2.2 KiB
Go
package infra
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
type Client struct {
|
|
BaseURL string
|
|
HTTPClient *http.Client
|
|
}
|
|
|
|
func NewClient(baseURL string, timeout time.Duration) *Client {
|
|
return &Client{
|
|
BaseURL: baseURL,
|
|
HTTPClient: &http.Client{Timeout: timeout},
|
|
}
|
|
}
|
|
|
|
type CreateHostRequest struct {
|
|
HardwareID string `json:"hardware_id"`
|
|
Hostname string `json:"hostname"`
|
|
AssetID string `json:"asset_id"`
|
|
RoomID int `json:"room_id"`
|
|
ServerTypeID int `json:"server_type_id"`
|
|
}
|
|
|
|
type CreateHostResponse struct {
|
|
ID int64 `json:"id"`
|
|
}
|
|
|
|
func (c *Client) CreateHost(ctx context.Context, req CreateHostRequest) (int64, error) {
|
|
body, err := json.Marshal(req)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, c.BaseURL+"/api/hosts", bytes.NewReader(body))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
httpReq.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := c.HTTPClient.Do(httpReq)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("infra: create host: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusCreated {
|
|
return 0, fmt.Errorf("infra: create host: status %d", resp.StatusCode)
|
|
}
|
|
|
|
var result CreateHostResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
|
return 0, fmt.Errorf("infra: decode response: %w", err)
|
|
}
|
|
return result.ID, nil
|
|
}
|
|
|
|
type CreateInterfaceRequest struct {
|
|
HostID int `json:"host_id"`
|
|
Name string `json:"name"`
|
|
MACAddress string `json:"mac_address"`
|
|
IPAddress string `json:"ip_address"`
|
|
}
|
|
|
|
func (c *Client) CreateInterface(ctx context.Context, req CreateInterfaceRequest) error {
|
|
body, err := json.Marshal(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, c.BaseURL+"/api/interfaces", bytes.NewReader(body))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
httpReq.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := c.HTTPClient.Do(httpReq)
|
|
if err != nil {
|
|
return fmt.Errorf("infra: create interface: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusCreated {
|
|
return fmt.Errorf("infra: create interface: status %d", resp.StatusCode)
|
|
}
|
|
return nil
|
|
}
|