mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 09:01:31 +00:00
refactor: establish node status contract
This commit is contained in:
@@ -10,10 +10,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
clusterv1 "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/mulgadc/spinifex/spinifex/admin"
|
||||
"github.com/mulgadc/spinifex/spinifex/config"
|
||||
"github.com/mulgadc/spinifex/spinifex/runtime/compute/gpu"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@@ -111,7 +111,7 @@ func init() {
|
||||
}
|
||||
|
||||
// gpuNodeStatus queries NATS and returns the NodeStatusResponse for the target node.
|
||||
func gpuNodeStatus(targetNode string) (*types.NodeStatusResponse, error) {
|
||||
func gpuNodeStatus(targetNode string) (*clusterv1.NodeStatusResponse, error) {
|
||||
cfg, nc, err := loadConfigAndConnect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -122,12 +122,12 @@ func gpuNodeStatus(targetNode string) (*types.NodeStatusResponse, error) {
|
||||
targetNode = cfg.Node
|
||||
}
|
||||
|
||||
responses, err := collectResponses(nc, "spinifex.node.status", 3*time.Second)
|
||||
responses, err := collectResponses(nc, clusterv1.NodeStatusSubject, 3*time.Second)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("collect node status: %w", err)
|
||||
}
|
||||
for _, raw := range responses {
|
||||
var resp types.NodeStatusResponse
|
||||
var resp clusterv1.NodeStatusResponse
|
||||
if err := json.Unmarshal(raw, &resp); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
|
||||
clusterv1 "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/mulgadc/spinifex/spinifex/config"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/mulgadc/spinifex/spinifex/utils"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/pterm/pterm"
|
||||
@@ -134,7 +133,7 @@ func formatUptime(seconds int64) string {
|
||||
return fmt.Sprintf("%dm", mins)
|
||||
}
|
||||
|
||||
func formatRoles(resp types.NodeStatusResponse) string {
|
||||
func formatRoles(resp clusterv1.NodeStatusResponse) string {
|
||||
var roles []string
|
||||
if resp.NATSRole != "" {
|
||||
roles = append(roles, "nats:"+resp.NATSRole)
|
||||
@@ -170,16 +169,16 @@ func runGetNodes(cmd *cobra.Command, args []string) {
|
||||
defer nc.Close()
|
||||
|
||||
timeout, _ := cmd.Flags().GetDuration("timeout")
|
||||
responses, err := collectResponses(nc, "spinifex.node.status", timeout)
|
||||
responses, err := collectResponses(nc, clusterv1.NodeStatusSubject, timeout)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Parse responses into a map by node name
|
||||
respondedNodes := make(map[string]types.NodeStatusResponse)
|
||||
respondedNodes := make(map[string]clusterv1.NodeStatusResponse)
|
||||
for _, data := range responses {
|
||||
var resp types.NodeStatusResponse
|
||||
var resp clusterv1.NodeStatusResponse
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package cmd
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
types "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
clusterv1 "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/pterm/pterm"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -43,15 +43,15 @@ func runTopNodes(cmd *cobra.Command, args []string) {
|
||||
defer nc.Close()
|
||||
|
||||
timeout, _ := cmd.Flags().GetDuration("timeout")
|
||||
responses, err := collectResponses(nc, "spinifex.node.status", timeout)
|
||||
responses, err := collectResponses(nc, clusterv1.NodeStatusSubject, timeout)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
respondedNodes := make(map[string]types.NodeStatusResponse)
|
||||
respondedNodes := make(map[string]clusterv1.NodeStatusResponse)
|
||||
for _, data := range responses {
|
||||
var resp types.NodeStatusResponse
|
||||
var resp clusterv1.NodeStatusResponse
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -20,3 +20,10 @@ by EKS host lookup. It is distinct from node status and capacity reporting:
|
||||
do not add scheduling or physical-GPU inventory merely because those callers
|
||||
also inspect nodes. Do not change its subject or JSON shape incompatibly; add
|
||||
a new version when a wire change is required.
|
||||
|
||||
`NodeStatusSubject` retains the deployed `spinifex.node.status` fan-out route.
|
||||
`NodeStatusResponse` carries daemon-reported status, schedulable capacity and
|
||||
physical-GPU inventory for the SPX operator CLI and EC2/EKS placement callers.
|
||||
It is an observation contract, not the owner of capacity, scheduling or GPU
|
||||
allocation policy. Do not change its subject or JSON shape incompatibly; add
|
||||
a new version when a wire change is required.
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
package types
|
||||
package clusterv1
|
||||
|
||||
const (
|
||||
// NodeStatusSubject fans out a request for each responding daemon's status
|
||||
// and schedulable-capacity snapshot. Its reply payload is NodeStatusResponse.
|
||||
NodeStatusSubject = "spinifex.node.status"
|
||||
)
|
||||
|
||||
// GPUSliceInfo describes a single MIG slice within a physical GPU.
|
||||
type GPUSliceInfo struct {
|
||||
@@ -22,8 +28,9 @@ type GPUInfo struct {
|
||||
Slices []GPUSliceInfo `json:"slices,omitempty"`
|
||||
}
|
||||
|
||||
// NodeStatusResponse is returned by the spinifex.node.status NATS topic (fan-out).
|
||||
// Schedulable capacity = TotalVCPU - ReservedVCPU - AllocVCPU (same for memory).
|
||||
// NodeStatusResponse is one daemon's reply on NodeStatusSubject. Schedulable
|
||||
// capacity = TotalVCPU - ReservedVCPU - AllocVCPU (and the same for memory).
|
||||
// The subject fans out, so callers collect one response per responding node.
|
||||
type NodeStatusResponse struct {
|
||||
Node string `json:"node"`
|
||||
Status string `json:"status"`
|
||||
@@ -0,0 +1,81 @@
|
||||
package clusterv1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNodeStatusResponseJSONContract(t *testing.T) {
|
||||
data, err := json.Marshal(NodeStatusResponse{
|
||||
Node: "node-a",
|
||||
Status: "Ready",
|
||||
Host: "10.0.0.1",
|
||||
Region: "ap-southeast-2",
|
||||
AZ: "ap-southeast-2a",
|
||||
Uptime: 42,
|
||||
Services: []string{"nats", "spinifex"},
|
||||
TotalVCPU: 16,
|
||||
TotalMemGB: 64,
|
||||
ReservedVCPU: 2,
|
||||
ReservedMemGB: 4,
|
||||
AllocVCPU: 4,
|
||||
AllocMemGB: 8,
|
||||
TotalGPUs: 1,
|
||||
AllocGPUs: 1,
|
||||
GPUCapable: true,
|
||||
GPUPassthrough: true,
|
||||
GPUModels: []string{"NVIDIA A10"},
|
||||
GPUs: []GPUInfo{{
|
||||
PCIAddress: "0000:01:00.0",
|
||||
Model: "NVIDIA A10",
|
||||
VRAMMiB: 23028,
|
||||
MIGEnabled: true,
|
||||
MIGProfile: "1g.6gb",
|
||||
InstanceID: "i-abc123",
|
||||
Slices: []GPUSliceInfo{{
|
||||
GIID: 1,
|
||||
Profile: "1g.6gb",
|
||||
VRAMMiB: 6144,
|
||||
MdevPath: "/sys/bus/mdev/devices/example",
|
||||
InstanceID: "i-abc123",
|
||||
}},
|
||||
}},
|
||||
VMCount: 1,
|
||||
InstanceTypes: []InstanceTypeCap{{
|
||||
Name: "g5.xlarge", VCPU: 4, MemoryGB: 16, Available: 2,
|
||||
}},
|
||||
NATSRole: "leader",
|
||||
PredastoreRole: "follower",
|
||||
OVNNBRole: "leader",
|
||||
OVNSBRole: "follower",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, `{
|
||||
"node":"node-a", "status":"Ready", "host":"10.0.0.1",
|
||||
"region":"ap-southeast-2", "az":"ap-southeast-2a", "uptime":42,
|
||||
"services":["nats","spinifex"],
|
||||
"total_vcpu":16, "total_mem_gb":64,
|
||||
"reserved_vcpu":2, "reserved_mem_gb":4,
|
||||
"alloc_vcpu":4, "alloc_mem_gb":8,
|
||||
"total_gpus":1, "alloc_gpus":1,
|
||||
"gpu_capable":true, "gpu_passthrough":true, "gpu_models":["NVIDIA A10"],
|
||||
"gpus":[{
|
||||
"pci_address":"0000:01:00.0", "model":"NVIDIA A10", "vram_mib":23028,
|
||||
"mig_enabled":true, "mig_profile":"1g.6gb", "instance_id":"i-abc123",
|
||||
"slices":[{
|
||||
"gi_id":1, "profile":"1g.6gb", "vram_mib":6144,
|
||||
"mdev_path":"/sys/bus/mdev/devices/example", "instance_id":"i-abc123"
|
||||
}]
|
||||
}],
|
||||
"vm_count":1,
|
||||
"instance_types":[{"name":"g5.xlarge", "vcpu":4, "memory_gb":16, "available":2}],
|
||||
"nats_role":"leader", "predastore_role":"follower",
|
||||
"ovn_nb_role":"leader", "ovn_sb_role":"follower"
|
||||
}`, string(data))
|
||||
}
|
||||
|
||||
func TestNodeStatusSubject(t *testing.T) {
|
||||
require.Equal(t, "spinifex.node.status", NodeStatusSubject)
|
||||
}
|
||||
@@ -187,6 +187,7 @@ while the branch is in flight; they do not describe the umbrella repository's
|
||||
| `6d6275e78` | `spinifex/types/cluster.go` (`NodeHealthResponse`) | `contracts/cluster/v1/health.go` | Node health is a versioned cluster control-plane representation shared by the per-node NATS route and daemon HTTPS `/health`; `health_test.go` pins both wire forms. |
|
||||
| `53a614717` | `spinifex/types/cluster.go` (`SharedClusterData`) | `spinifex/daemon/config_hash.go` | Shared-config hash input is daemon-local implementation data; focused config-hash tests preserve deterministic, shared-only semantics. |
|
||||
| `78fd017e0` | `spinifex/types/telemetry.go` (`GuestTelemetryMeta`) | `contracts/telemetry/v1/guest_meta.go` | QMP collector sidecar JSON is a versioned file contract between VM runtime and telemetry role; `guest_meta_test.go` pins its filename and payload. |
|
||||
| `4b35a2a33` | `spinifex/types/node.go` (`NodeVMsResponse`, `VMInfo`, `VMGPUInfo`) | `contracts/cluster/v1/node_vms.go` | VM inventory is a versioned cluster fan-out payload used by SPX CLI and EKS host lookup; `node_vms_test.go` pins its subject and JSON shape. |
|
||||
|
||||
The EC2 contract row is intentionally different from the directory moves: it creates
|
||||
a compatibility boundary. `ec2.cmd.*` retains its deployed one-token NATS
|
||||
|
||||
@@ -91,6 +91,7 @@ services:
|
||||
# An EIP association reaches whichever node holds the instance, which is
|
||||
# rarely the node that served the request (fan-out, no queue group).
|
||||
- "ec2.eni.public-ip-changed"
|
||||
# contracts/cluster/v1 owns the node status/capacity payload and subject.
|
||||
- "spinifex.node.status"
|
||||
# contracts/cluster/v1 owns the VM inventory payload and subject.
|
||||
- "spinifex.node.vms"
|
||||
|
||||
@@ -76,7 +76,6 @@ import (
|
||||
"github.com/mulgadc/spinifex/spinifex/runtime/compute/gpu"
|
||||
"github.com/mulgadc/spinifex/spinifex/runtime/compute/vm"
|
||||
"github.com/mulgadc/spinifex/spinifex/services/viperblockd/vbwire"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/mulgadc/spinifex/spinifex/utils"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/nats-io/nats.go/jetstream"
|
||||
@@ -666,7 +665,7 @@ func (rm *ResourceManager) GetSupportedInstanceTypeInfos() []*ec2.InstanceTypeIn
|
||||
|
||||
// GetResourceStats returns host resource figures, reservation, allocation, and
|
||||
// per-type capacity caps for the node status response.
|
||||
func (rm *ResourceManager) GetResourceStats() (totalVCPU int, totalMemGB float64, reservedVCPU int, reservedMemGB float64, allocVCPU int, allocMemGB float64, caps []types.InstanceTypeCap) {
|
||||
func (rm *ResourceManager) GetResourceStats() (totalVCPU int, totalMemGB float64, reservedVCPU int, reservedMemGB float64, allocVCPU int, allocMemGB float64, caps []clusterv1.InstanceTypeCap) {
|
||||
rm.mu.RLock()
|
||||
defer rm.mu.RUnlock()
|
||||
|
||||
@@ -1020,7 +1019,7 @@ func (d *Daemon) subscribeAll() error {
|
||||
{"ec2.DisableSerialConsoleAccess", handleNATSRequest(d.node, d.accountService.DisableSerialConsoleAccess), "spinifex-workers"},
|
||||
{clusterv1.NodeHealthSubject(d.node), d.handleHealthCheck, ""},
|
||||
{clusterv1.NodesDiscoverSubject, d.handleNodeDiscover, ""},
|
||||
{"spinifex.node.status", d.handleNodeStatus, ""},
|
||||
{clusterv1.NodeStatusSubject, d.handleNodeStatus, ""},
|
||||
{clusterv1.NodeVMsSubject, d.handleNodeVMs, ""},
|
||||
{operatorv1.StorageConfigSubject, d.handleStorageConfig, ""},
|
||||
{"spinifex.image.promote", d.handleSpinifexPromoteImage, "spinifex-workers"},
|
||||
|
||||
@@ -10,14 +10,13 @@ import (
|
||||
clusterv1 "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/mulgadc/spinifex/spinifex/runtime/compute/gpu"
|
||||
"github.com/mulgadc/spinifex/spinifex/runtime/compute/vm"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// gpuStatusRequest subscribes and requests handleNodeStatus on a fresh subject
|
||||
// so each test gets an isolated round trip.
|
||||
func gpuStatusRequest(t *testing.T, daemon *Daemon, subject string) types.NodeStatusResponse {
|
||||
func gpuStatusRequest(t *testing.T, daemon *Daemon, subject string) clusterv1.NodeStatusResponse {
|
||||
t.Helper()
|
||||
sub, err := daemon.natsConn.Subscribe(subject, asMsgHandler(daemon.handleNodeStatus))
|
||||
require.NoError(t, err)
|
||||
@@ -26,7 +25,7 @@ func gpuStatusRequest(t *testing.T, daemon *Daemon, subject string) types.NodeSt
|
||||
reply, err := daemon.natsConn.Request(subject, nil, 5*time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
var resp types.NodeStatusResponse
|
||||
var resp clusterv1.NodeStatusResponse
|
||||
require.NoError(t, json.Unmarshal(reply.Data, &resp))
|
||||
return resp
|
||||
}
|
||||
@@ -69,7 +68,7 @@ func TestHandleNodeStatus_WholeGPU(t *testing.T) {
|
||||
resp := gpuStatusRequest(t, daemon, "spinifex.node.status.wholegpu")
|
||||
|
||||
require.Len(t, resp.GPUs, 2)
|
||||
byPCI := make(map[string]types.GPUInfo, 2)
|
||||
byPCI := make(map[string]clusterv1.GPUInfo, 2)
|
||||
for _, g := range resp.GPUs {
|
||||
byPCI[g.PCIAddress] = g
|
||||
}
|
||||
@@ -123,7 +122,7 @@ func TestHandleNodeStatus_MIG(t *testing.T) {
|
||||
assert.Empty(t, g.InstanceID, "whole-GPU InstanceID is unused in MIG mode")
|
||||
require.Len(t, g.Slices, 2)
|
||||
|
||||
byGI := make(map[int]types.GPUSliceInfo, 2)
|
||||
byGI := make(map[int]clusterv1.GPUSliceInfo, 2)
|
||||
for _, s := range g.Slices {
|
||||
byGI[s.GIID] = s
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"github.com/mulgadc/spinifex/spinifex/runtime/compute/gpu"
|
||||
"github.com/mulgadc/spinifex/spinifex/runtime/compute/vm"
|
||||
"github.com/mulgadc/spinifex/spinifex/runtime/formation"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/mulgadc/spinifex/spinifex/utils"
|
||||
"github.com/nats-io/nats.go"
|
||||
"go.opentelemetry.io/otel"
|
||||
@@ -408,12 +407,12 @@ func (d *Daemon) handleNodeStatus(msg *nats.Msg) string {
|
||||
gpuModelNames = append(gpuModelNames, dev.Model)
|
||||
}
|
||||
|
||||
var gpuInventory []types.GPUInfo
|
||||
var gpuInventory []clusterv1.GPUInfo
|
||||
if d.gpuManager != nil {
|
||||
gpuInventory = buildGPUInventory(d.gpuManager.Snapshot())
|
||||
}
|
||||
|
||||
resp := types.NodeStatusResponse{
|
||||
resp := clusterv1.NodeStatusResponse{
|
||||
Node: d.node,
|
||||
Status: "Ready",
|
||||
Host: d.daemonIP(),
|
||||
@@ -518,14 +517,14 @@ func fetchNATSRole(url string, client *http.Client) string {
|
||||
// buildGPUInventory converts a pool snapshot into per-physical-GPU GPUInfo
|
||||
// records suitable for the NodeStatusResponse. Entries are ordered by first
|
||||
// appearance of each PCI address in the snapshot.
|
||||
func buildGPUInventory(snapshot []gpu.PoolEntry) []types.GPUInfo {
|
||||
byPCI := make(map[string]*types.GPUInfo, len(snapshot))
|
||||
func buildGPUInventory(snapshot []gpu.PoolEntry) []clusterv1.GPUInfo {
|
||||
byPCI := make(map[string]*clusterv1.GPUInfo, len(snapshot))
|
||||
var order []string
|
||||
|
||||
for _, e := range snapshot {
|
||||
pci := e.Device.PCIAddress
|
||||
if _, ok := byPCI[pci]; !ok {
|
||||
byPCI[pci] = &types.GPUInfo{
|
||||
byPCI[pci] = &clusterv1.GPUInfo{
|
||||
PCIAddress: pci,
|
||||
Model: e.Device.Model,
|
||||
VRAMMiB: e.Device.MemoryMiB,
|
||||
@@ -536,7 +535,7 @@ func buildGPUInventory(snapshot []gpu.PoolEntry) []types.GPUInfo {
|
||||
if e.MIGInstance != nil {
|
||||
g.MIGEnabled = true
|
||||
g.MIGProfile = e.MIGInstance.Profile.Name
|
||||
g.Slices = append(g.Slices, types.GPUSliceInfo{
|
||||
g.Slices = append(g.Slices, clusterv1.GPUSliceInfo{
|
||||
GIID: e.MIGInstance.GIID,
|
||||
Profile: e.MIGInstance.Profile.Name,
|
||||
VRAMMiB: e.MIGInstance.Profile.MemoryMiB,
|
||||
@@ -548,7 +547,7 @@ func buildGPUInventory(snapshot []gpu.PoolEntry) []types.GPUInfo {
|
||||
}
|
||||
}
|
||||
|
||||
gpus := make([]types.GPUInfo, 0, len(order))
|
||||
gpus := make([]clusterv1.GPUInfo, 0, len(order))
|
||||
for _, pci := range order {
|
||||
gpus = append(gpus, *byPCI[pci])
|
||||
}
|
||||
|
||||
@@ -2169,7 +2169,7 @@ func TestHandleNodeStatus(t *testing.T) {
|
||||
reply, err := daemon.natsConn.Request("spinifex.node.status.test", nil, 5*time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
var resp types.NodeStatusResponse
|
||||
var resp clusterv1.NodeStatusResponse
|
||||
err = json.Unmarshal(reply.Data, &resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -2199,7 +2199,7 @@ func TestHandleNodeStatus_NoVMs(t *testing.T) {
|
||||
reply, err := daemon.natsConn.Request("spinifex.node.status.empty", nil, 5*time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
var resp types.NodeStatusResponse
|
||||
var resp clusterv1.NodeStatusResponse
|
||||
err = json.Unmarshal(reply.Data, &resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
clusterv1 "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/mulgadc/spinifex/spinifex/foundation/aws/errors"
|
||||
"github.com/mulgadc/spinifex/spinifex/runtime/compute/gpu"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -203,7 +203,7 @@ func TestGetResourceStats_GPUTypesAreGatedOnGPUs(t *testing.T) {
|
||||
reservations: make(map[string]*capacityReservation),
|
||||
}
|
||||
|
||||
capFor := func() types.InstanceTypeCap {
|
||||
capFor := func() clusterv1.InstanceTypeCap {
|
||||
t.Helper()
|
||||
_, _, _, _, _, _, caps := rm.GetResourceStats()
|
||||
for _, c := range caps {
|
||||
@@ -212,7 +212,7 @@ func TestGetResourceStats_GPUTypesAreGatedOnGPUs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
t.Fatal("gpu.4x4c missing from the census")
|
||||
return types.InstanceTypeCap{}
|
||||
return clusterv1.InstanceTypeCap{}
|
||||
}
|
||||
|
||||
assert.Equal(t, 1, capFor().Available, "four GPUs back one four-GPU instance")
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
clusterv1 "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
)
|
||||
|
||||
// errInsufficientCapacity is returned by allocateForLaunch when MinCount
|
||||
@@ -237,7 +237,7 @@ func nbdkitChargeMiB(mainVols, auxVols, mainMiB, auxMiB int) int64 {
|
||||
|
||||
// resourceStatsForType computes the InstanceTypeCap for a single type given
|
||||
// remaining resources. Clamps negative capacity to zero without logging.
|
||||
func resourceStatsForType(remainVCPU int, remainMem float64, it *ec2.InstanceTypeInfo) types.InstanceTypeCap {
|
||||
func resourceStatsForType(remainVCPU int, remainMem float64, it *ec2.InstanceTypeInfo) clusterv1.InstanceTypeCap {
|
||||
vCPUs := instanceTypeVCPUs(it)
|
||||
memGB := float64(instanceTypeMemoryMiB(it)) / 1024.0
|
||||
|
||||
@@ -253,7 +253,7 @@ func resourceStatsForType(remainVCPU int, remainMem float64, it *ec2.InstanceTyp
|
||||
name = *it.InstanceType
|
||||
}
|
||||
|
||||
return types.InstanceTypeCap{
|
||||
return clusterv1.InstanceTypeCap{
|
||||
Name: name,
|
||||
VCPU: int(vCPUs),
|
||||
MemoryGB: memGB,
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/mulgadc/spinifex/spinifex/foundation/aws/errors"
|
||||
types "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/mulgadc/spinifex/internal/testkit"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/mulgadc/spinifex/spinifex/foundation/aws/errors"
|
||||
"github.com/mulgadc/spinifex/spinifex/utils"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
clusterv1 "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/mulgadc/spinifex/spinifex/utils"
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
@@ -32,7 +32,7 @@ func collectCensus(ctx context.Context, natsConn *nats.Conn, expectedNodes int,
|
||||
expectedNodes = 1
|
||||
}
|
||||
|
||||
frames, _, err := utils.Gather(ctx, natsConn, "spinifex.node.status", []byte("{}"),
|
||||
frames, _, err := utils.Gather(ctx, natsConn, clusterv1.NodeStatusSubject, []byte("{}"),
|
||||
utils.GatherOpts{Timeout: censusTimeout, ExpectedNodes: expectedNodes, AccountID: accountID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -41,7 +41,7 @@ func collectCensus(ctx context.Context, natsConn *nats.Conn, expectedNodes int,
|
||||
seen := make(map[string]struct{})
|
||||
var census []nodeCensus
|
||||
for _, frame := range frames {
|
||||
var status types.NodeStatusResponse
|
||||
var status clusterv1.NodeStatusResponse
|
||||
if err := json.Unmarshal(frame.Data, &status); err != nil {
|
||||
slog.DebugContext(ctx, "collectCensus: failed to unmarshal response", "err", err)
|
||||
continue
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
types "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/mulgadc/spinifex/internal/testkit"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
clusterv1 "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/mulgadc/spinifex/spinifex/foundation/aws/errors"
|
||||
handlers_ec2_placementgroup "github.com/mulgadc/spinifex/spinifex/handlers/ec2/placementgroup"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/mulgadc/spinifex/spinifex/utils"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
@@ -67,7 +67,7 @@ func distributeInstances(ctx context.Context, input *ec2.RunInstancesInput, nats
|
||||
// Early-exits once expectedNodes reply; on a degraded cluster it waits the full timeout
|
||||
// rather than placing on a partial view.
|
||||
func queryNodeCapacity(ctx context.Context, natsConn *nats.Conn, instanceType string, expectedNodes int, accountID string) ([]nodeAllocation, error) {
|
||||
frames, _, err := utils.Gather(ctx, natsConn, "spinifex.node.status", []byte("{}"),
|
||||
frames, _, err := utils.Gather(ctx, natsConn, clusterv1.NodeStatusSubject, []byte("{}"),
|
||||
utils.GatherOpts{Timeout: 3 * time.Second, ExpectedNodes: expectedNodes, AccountID: accountID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -75,7 +75,7 @@ func queryNodeCapacity(ctx context.Context, natsConn *nats.Conn, instanceType st
|
||||
|
||||
var nodes []nodeAllocation
|
||||
for _, frame := range frames {
|
||||
var status types.NodeStatusResponse
|
||||
var status clusterv1.NodeStatusResponse
|
||||
if err := json.Unmarshal(frame.Data, &status); err != nil {
|
||||
slog.DebugContext(ctx, "queryNodeCapacity: failed to unmarshal response", "err", err)
|
||||
continue
|
||||
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
types "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/mulgadc/spinifex/contracts/ec2/v1"
|
||||
"github.com/mulgadc/spinifex/spinifex/foundation/aws/errors"
|
||||
handlers_ec2_placementgroup "github.com/mulgadc/spinifex/spinifex/handlers/ec2/placementgroup"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/mulgadc/spinifex/spinifex/utils"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
types "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/mulgadc/spinifex/spinifex/foundation/aws/errors"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/mulgadc/spinifex/spinifex/utils"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
handlers_quota "github.com/mulgadc/spinifex/spinifex/handlers/quota"
|
||||
types "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/mulgadc/spinifex/internal/testkit"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
handlers_quota "github.com/mulgadc/spinifex/spinifex/handlers/quota"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/nats-io/nats.go/jetstream"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"time"
|
||||
|
||||
clusterv1 "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/mulgadc/spinifex/spinifex/utils"
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
@@ -41,21 +40,21 @@ func GetVersion(version, commit string) (*VersionOutput, error) {
|
||||
|
||||
// GetNodesOutput is the response for GetNodes.
|
||||
type GetNodesOutput struct {
|
||||
Nodes []types.NodeStatusResponse `json:"nodes"`
|
||||
ClusterMode string `json:"cluster_mode"`
|
||||
Nodes []clusterv1.NodeStatusResponse `json:"nodes"`
|
||||
ClusterMode string `json:"cluster_mode"`
|
||||
}
|
||||
|
||||
// GetNodes queries all daemon nodes via NATS fan-out and returns their status.
|
||||
func GetNodes(ctx context.Context, nc *nats.Conn, expectedNodes int) (*GetNodesOutput, error) {
|
||||
frames, _, err := utils.Gather(ctx, nc, "spinifex.node.status", []byte("{}"),
|
||||
frames, _, err := utils.Gather(ctx, nc, clusterv1.NodeStatusSubject, []byte("{}"),
|
||||
utils.GatherOpts{Timeout: 3 * time.Second, ExpectedNodes: expectedNodes})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodes := make([]types.NodeStatusResponse, 0, len(frames))
|
||||
nodes := make([]clusterv1.NodeStatusResponse, 0, len(frames))
|
||||
for _, frame := range frames {
|
||||
var node types.NodeStatusResponse
|
||||
var node clusterv1.NodeStatusResponse
|
||||
if json.Unmarshal(frame.Data, &node) == nil {
|
||||
nodes = append(nodes, node)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
clusterv1 "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/mulgadc/spinifex/internal/testkit"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/nats-io/nats-server/v2/server"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -54,8 +53,8 @@ func TestGetNodes_NoResponders(t *testing.T) {
|
||||
func TestGetNodes_SingleNode(t *testing.T) {
|
||||
_, nc := startEmbeddedNATS(t)
|
||||
|
||||
sub, err := nc.Subscribe("spinifex.node.status", func(msg *nats.Msg) {
|
||||
resp := types.NodeStatusResponse{
|
||||
sub, err := nc.Subscribe(clusterv1.NodeStatusSubject, func(msg *nats.Msg) {
|
||||
resp := clusterv1.NodeStatusResponse{
|
||||
Node: "node1",
|
||||
Status: "Ready",
|
||||
Host: "10.0.0.1",
|
||||
@@ -65,7 +64,7 @@ func TestGetNodes_SingleNode(t *testing.T) {
|
||||
TotalMemGB: 16.0,
|
||||
AllocVCPU: 2,
|
||||
AllocMemGB: 2.0,
|
||||
InstanceTypes: []types.InstanceTypeCap{
|
||||
InstanceTypes: []clusterv1.InstanceTypeCap{
|
||||
{Name: "t3.small", VCPU: 2, MemoryGB: 2.0, Available: 3},
|
||||
},
|
||||
}
|
||||
@@ -93,8 +92,8 @@ func TestGetNodes_MultiNode(t *testing.T) {
|
||||
|
||||
for _, name := range []string{"node1", "node2", "node3"} {
|
||||
nodeName := name
|
||||
sub, err := nc.Subscribe("spinifex.node.status", func(msg *nats.Msg) {
|
||||
resp := types.NodeStatusResponse{Node: nodeName, Status: "Ready"}
|
||||
sub, err := nc.Subscribe(clusterv1.NodeStatusSubject, func(msg *nats.Msg) {
|
||||
resp := clusterv1.NodeStatusResponse{Node: nodeName, Status: "Ready"}
|
||||
data, _ := json.Marshal(resp)
|
||||
msg.Respond(data)
|
||||
})
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/mulgadc/spinifex/spinifex/domains/ec2/instancetypes"
|
||||
"github.com/mulgadc/spinifex/spinifex/foundation/aws/errors"
|
||||
handlers_ec2_placementgroup "github.com/mulgadc/spinifex/spinifex/handlers/ec2/placementgroup"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/mulgadc/spinifex/spinifex/utils"
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
@@ -500,8 +499,8 @@ func (h *natsHostScheduler) SchedulableHosts(ctx context.Context, instanceType s
|
||||
|
||||
var hosts []azHost
|
||||
seen := make(map[string]bool)
|
||||
h.fanout(ctx, "spinifex.node.status", func(data []byte) {
|
||||
var st types.NodeStatusResponse
|
||||
h.fanout(ctx, clusterv1.NodeStatusSubject, func(data []byte) {
|
||||
var st clusterv1.NodeStatusResponse
|
||||
if json.Unmarshal(data, &st) != nil || st.Node == "" || seen[st.Node] {
|
||||
return
|
||||
}
|
||||
@@ -554,7 +553,7 @@ func spreadHostsByAZ(hosts []azHost) []string {
|
||||
|
||||
// nodeFitsCustomerInstance reports whether a node advertises at least one free
|
||||
// slot for the given customer instance type in its node.status capacity.
|
||||
func nodeFitsCustomerInstance(st types.NodeStatusResponse, instanceType string) bool {
|
||||
func nodeFitsCustomerInstance(st clusterv1.NodeStatusResponse, instanceType string) bool {
|
||||
for _, c := range st.InstanceTypes {
|
||||
if c.Name == instanceType && c.Available >= 1 {
|
||||
return true
|
||||
@@ -565,7 +564,7 @@ func nodeFitsCustomerInstance(st types.NodeStatusResponse, instanceType string)
|
||||
|
||||
// nodeFitsSystemInstance reports whether a node's headroom (Total - Reserved - Alloc)
|
||||
// fits at least one VM of the given vCPU/memory footprint.
|
||||
func nodeFitsSystemInstance(st types.NodeStatusResponse, vcpu int, memGB float64) bool {
|
||||
func nodeFitsSystemInstance(st clusterv1.NodeStatusResponse, vcpu int, memGB float64) bool {
|
||||
remainVCPU := st.TotalVCPU - st.ReservedVCPU - st.AllocVCPU
|
||||
remainMem := st.TotalMemGB - st.ReservedMemGB - st.AllocMemGB
|
||||
return remainVCPU >= vcpu && remainMem >= memGB
|
||||
|
||||
@@ -13,12 +13,12 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/mulgadc/spinifex/spinifex/foundation/aws/errors"
|
||||
types "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/mulgadc/spinifex/internal/testkit"
|
||||
"github.com/mulgadc/spinifex/spinifex/domains/ec2/instancetypes"
|
||||
"github.com/mulgadc/spinifex/spinifex/foundation/aws/errors"
|
||||
handlers_ec2_placementgroup "github.com/mulgadc/spinifex/spinifex/handlers/ec2/placementgroup"
|
||||
"github.com/mulgadc/spinifex/spinifex/handlers/sysinstance"
|
||||
"github.com/mulgadc/spinifex/internal/testkit"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go/service/ecr"
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/aws/aws-sdk-go/service/sts"
|
||||
clusterv1 "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/mulgadc/spinifex/spinifex/gateway"
|
||||
gateway_bedrock "github.com/mulgadc/spinifex/spinifex/gateway/bedrock"
|
||||
gateway_ecr "github.com/mulgadc/spinifex/spinifex/gateway/ecr"
|
||||
@@ -40,7 +41,6 @@ import (
|
||||
handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam"
|
||||
handlers_sts "github.com/mulgadc/spinifex/spinifex/handlers/sts"
|
||||
"github.com/mulgadc/spinifex/spinifex/objectstore"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/mulgadc/spinifex/spinifex/utils"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -202,7 +202,7 @@ func startGateway(t *testing.T, collector *conformanceCollector, opts ...Option)
|
||||
Config: cfg,
|
||||
}
|
||||
|
||||
nodeReply, err := json.Marshal(types.NodeDiscoverResponse{Node: "integration-test-node"})
|
||||
nodeReply, err := json.Marshal(clusterv1.NodeDiscoverResponse{Node: "integration-test-node"})
|
||||
require.NoError(t, err)
|
||||
gw.StubSubject(t, nodeDiscoverSubject, nodeReply)
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
types "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
types "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
types "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/aws/aws-sdk-go/service/sts"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
types "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
types "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/mulgadc/spinifex/spinifex/gateway"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
types "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
types "github.com/mulgadc/spinifex/contracts/cluster/v1"
|
||||
"github.com/mulgadc/spinifex/contracts/ec2/v1"
|
||||
"github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
Reference in New Issue
Block a user