mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-06 16:16:26 +00:00
642 lines
18 KiB
Go
642 lines
18 KiB
Go
package models
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestStateClearAllDockerHosts(t *testing.T) {
|
|
state := &State{
|
|
DockerHosts: []DockerHost{
|
|
{ID: "h1"},
|
|
{ID: "h2"},
|
|
},
|
|
}
|
|
|
|
count := state.ClearAllDockerHosts()
|
|
if count != 2 {
|
|
t.Fatalf("count = %d, want 2", count)
|
|
}
|
|
if len(state.DockerHosts) != 0 {
|
|
t.Fatalf("DockerHosts = %#v, want empty", state.DockerHosts)
|
|
}
|
|
if state.DockerHosts == nil {
|
|
t.Fatal("DockerHosts should remain an initialized empty slice")
|
|
}
|
|
}
|
|
|
|
func TestStateKubernetesClusterLifecycle(t *testing.T) {
|
|
state := &State{}
|
|
|
|
initial := KubernetesCluster{
|
|
ID: "c1",
|
|
Name: "alpha",
|
|
CustomDisplayName: "keep",
|
|
Hidden: true,
|
|
PendingUninstall: true,
|
|
Status: "init",
|
|
}
|
|
state.UpsertKubernetesCluster(initial)
|
|
|
|
update := KubernetesCluster{
|
|
ID: "c1",
|
|
Name: "alpha",
|
|
CustomDisplayName: "",
|
|
Hidden: false,
|
|
PendingUninstall: false,
|
|
Status: "ready",
|
|
}
|
|
state.UpsertKubernetesCluster(update)
|
|
|
|
clusters := state.GetKubernetesClusters()
|
|
if len(clusters) != 1 {
|
|
t.Fatalf("clusters = %#v, want 1", clusters)
|
|
}
|
|
if clusters[0].CustomDisplayName != "keep" {
|
|
t.Fatalf("CustomDisplayName = %q, want keep", clusters[0].CustomDisplayName)
|
|
}
|
|
if !clusters[0].Hidden || !clusters[0].PendingUninstall {
|
|
t.Fatalf("expected Hidden and PendingUninstall preserved")
|
|
}
|
|
|
|
if ok := state.SetKubernetesClusterStatus("c1", "ok"); !ok {
|
|
t.Fatalf("SetKubernetesClusterStatus returned false")
|
|
}
|
|
if _, ok := state.SetKubernetesClusterHidden("c1", false); !ok {
|
|
t.Fatalf("SetKubernetesClusterHidden returned false")
|
|
}
|
|
if _, ok := state.SetKubernetesClusterPendingUninstall("c1", false); !ok {
|
|
t.Fatalf("SetKubernetesClusterPendingUninstall returned false")
|
|
}
|
|
if _, ok := state.SetKubernetesClusterCustomDisplayName("c1", "custom"); !ok {
|
|
t.Fatalf("SetKubernetesClusterCustomDisplayName returned false")
|
|
}
|
|
|
|
removed, ok := state.RemoveKubernetesCluster("c1")
|
|
if !ok || removed.ID != "c1" {
|
|
t.Fatalf("RemoveKubernetesCluster = (%v, %v), want c1", removed, ok)
|
|
}
|
|
if _, ok := state.RemoveKubernetesCluster("missing"); ok {
|
|
t.Fatalf("expected RemoveKubernetesCluster to fail for missing")
|
|
}
|
|
}
|
|
|
|
func TestStateRemovedKubernetesClusters(t *testing.T) {
|
|
state := &State{}
|
|
t1 := time.Date(2024, 1, 1, 1, 0, 0, 0, time.UTC)
|
|
t2 := time.Date(2024, 1, 2, 1, 0, 0, 0, time.UTC)
|
|
|
|
state.AddRemovedKubernetesCluster(RemovedKubernetesCluster{ID: "c1", RemovedAt: t1})
|
|
state.AddRemovedKubernetesCluster(RemovedKubernetesCluster{ID: "c2", RemovedAt: t2})
|
|
state.AddRemovedKubernetesCluster(RemovedKubernetesCluster{ID: "c1", DisplayName: "updated", RemovedAt: t1})
|
|
|
|
entries := state.GetRemovedKubernetesClusters()
|
|
if len(entries) != 2 {
|
|
t.Fatalf("entries = %#v, want 2", entries)
|
|
}
|
|
if entries[0].ID != "c2" {
|
|
t.Fatalf("entries[0].ID = %q, want c2", entries[0].ID)
|
|
}
|
|
|
|
state.RemoveRemovedKubernetesCluster("c1")
|
|
entries = state.GetRemovedKubernetesClusters()
|
|
if len(entries) != 1 || entries[0].ID != "c2" {
|
|
t.Fatalf("entries = %#v, want c2 only", entries)
|
|
}
|
|
}
|
|
|
|
func TestStateClearAllHosts(t *testing.T) {
|
|
state := &State{
|
|
Hosts: []Host{{ID: "h1"}, {ID: "h2"}},
|
|
}
|
|
|
|
count := state.ClearAllHosts()
|
|
if count != 2 {
|
|
t.Fatalf("count = %d, want 2", count)
|
|
}
|
|
if len(state.Hosts) != 0 {
|
|
t.Fatalf("Hosts = %#v, want empty", state.Hosts)
|
|
}
|
|
if state.Hosts == nil {
|
|
t.Fatal("Hosts should remain an initialized empty slice")
|
|
}
|
|
}
|
|
|
|
func TestStateWholeSliceUpdatesPreserveInitializedEmptySlices(t *testing.T) {
|
|
state := NewState()
|
|
|
|
state.UpdateRecentlyResolved([]ResolvedAlert{{Alert: Alert{ID: "resolved-1"}}})
|
|
state.UpdateNodes([]Node{{ID: "node-1"}})
|
|
state.UpdateVMs([]VM{{ID: "vm-1"}})
|
|
state.UpdateContainers([]Container{{ID: "ct-1"}})
|
|
state.UpdateStorage([]Storage{{ID: "storage-1"}})
|
|
state.UpdatePBSInstances([]PBSInstance{{ID: "pbs-1"}})
|
|
state.UpdatePMGInstances([]PMGInstance{{ID: "pmg-1"}})
|
|
|
|
state.UpdateRecentlyResolved(nil)
|
|
state.UpdateNodes(nil)
|
|
state.UpdateVMs(nil)
|
|
state.UpdateContainers(nil)
|
|
state.UpdateStorage(nil)
|
|
state.UpdatePBSInstances(nil)
|
|
state.UpdatePMGInstances(nil)
|
|
|
|
if state.RecentlyResolved == nil {
|
|
t.Fatal("RecentlyResolved should remain an initialized empty slice")
|
|
}
|
|
if len(state.RecentlyResolved) != 0 {
|
|
t.Fatalf("RecentlyResolved = %#v, want empty", state.RecentlyResolved)
|
|
}
|
|
if state.Nodes == nil {
|
|
t.Fatal("Nodes should remain an initialized empty slice")
|
|
}
|
|
if len(state.Nodes) != 0 {
|
|
t.Fatalf("Nodes = %#v, want empty", state.Nodes)
|
|
}
|
|
if state.VMs == nil {
|
|
t.Fatal("VMs should remain an initialized empty slice")
|
|
}
|
|
if len(state.VMs) != 0 {
|
|
t.Fatalf("VMs = %#v, want empty", state.VMs)
|
|
}
|
|
if state.Containers == nil {
|
|
t.Fatal("Containers should remain an initialized empty slice")
|
|
}
|
|
if len(state.Containers) != 0 {
|
|
t.Fatalf("Containers = %#v, want empty", state.Containers)
|
|
}
|
|
if state.Storage == nil {
|
|
t.Fatal("Storage should remain an initialized empty slice")
|
|
}
|
|
if len(state.Storage) != 0 {
|
|
t.Fatalf("Storage = %#v, want empty", state.Storage)
|
|
}
|
|
if state.PBSInstances == nil {
|
|
t.Fatal("PBSInstances should remain an initialized empty slice")
|
|
}
|
|
if len(state.PBSInstances) != 0 {
|
|
t.Fatalf("PBSInstances = %#v, want empty", state.PBSInstances)
|
|
}
|
|
if state.PMGInstances == nil {
|
|
t.Fatal("PMGInstances should remain an initialized empty slice")
|
|
}
|
|
if len(state.PMGInstances) != 0 {
|
|
t.Fatalf("PMGInstances = %#v, want empty", state.PMGInstances)
|
|
}
|
|
}
|
|
|
|
func TestStateLinkNodeToHostAgent(t *testing.T) {
|
|
state := &State{
|
|
Nodes: []Node{{ID: "n1"}},
|
|
}
|
|
|
|
if ok := state.LinkNodeToHostAgent("n1", "h1"); !ok {
|
|
t.Fatalf("LinkNodeToHostAgent returned false")
|
|
}
|
|
if state.Nodes[0].LinkedAgentID != "h1" {
|
|
t.Fatalf("LinkedAgentID = %q, want h1", state.Nodes[0].LinkedAgentID)
|
|
}
|
|
if ok := state.LinkNodeToHostAgent("missing", "h1"); ok {
|
|
t.Fatalf("expected false for missing node")
|
|
}
|
|
}
|
|
|
|
func TestStateUnlinkNodesFromHostAgent(t *testing.T) {
|
|
state := &State{
|
|
Nodes: []Node{
|
|
{ID: "n1", LinkedAgentID: "h1"},
|
|
{ID: "n2", LinkedAgentID: "h1"},
|
|
{ID: "n3", LinkedAgentID: "h2"},
|
|
},
|
|
}
|
|
|
|
count := state.UnlinkNodesFromHostAgent("h1")
|
|
if count != 2 {
|
|
t.Fatalf("count = %d, want 2", count)
|
|
}
|
|
for _, node := range state.Nodes[:2] {
|
|
if node.LinkedAgentID != "" {
|
|
t.Fatalf("expected LinkedAgentID cleared, got %q", node.LinkedAgentID)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStateUpdateNodesForInstanceSkipsAmbiguousHostAgentHostnameMatch(t *testing.T) {
|
|
state := &State{
|
|
Hosts: []Host{
|
|
{ID: "host-a", Hostname: "pve1"},
|
|
{ID: "host-b", Hostname: "pve1.local"},
|
|
},
|
|
}
|
|
|
|
state.UpdateNodesForInstance("cluster-a", []Node{
|
|
{ID: "node-1", Name: "pve1", Instance: "cluster-a"},
|
|
})
|
|
|
|
nodes := state.Nodes
|
|
if len(nodes) != 1 {
|
|
t.Fatalf("nodes = %#v, want 1", nodes)
|
|
}
|
|
if nodes[0].LinkedAgentID != "" {
|
|
t.Fatalf("expected no auto-link for ambiguous hostname, got %q", nodes[0].LinkedAgentID)
|
|
}
|
|
}
|
|
|
|
func TestStateUpdateNodesForInstanceLinksUniqueHostname(t *testing.T) {
|
|
state := &State{
|
|
Hosts: []Host{
|
|
{ID: "host-a", Hostname: "pve1.local"},
|
|
},
|
|
}
|
|
|
|
state.UpdateNodesForInstance("cluster-a", []Node{
|
|
{ID: "node-1", Name: "pve1", Instance: "cluster-a"},
|
|
})
|
|
|
|
nodes := state.Nodes
|
|
if len(nodes) != 1 {
|
|
t.Fatalf("nodes = %#v, want 1", nodes)
|
|
}
|
|
if nodes[0].LinkedAgentID != "host-a" {
|
|
t.Fatalf("LinkedAgentID = %q, want host-a", nodes[0].LinkedAgentID)
|
|
}
|
|
if state.Hosts[0].LinkedNodeID != "node-1" {
|
|
t.Fatalf("LinkedNodeID = %q, want node-1", state.Hosts[0].LinkedNodeID)
|
|
}
|
|
if state.Hosts[0].LinkedVMID != "" || state.Hosts[0].LinkedContainerID != "" {
|
|
t.Fatalf("expected guest links cleared when host links to node")
|
|
}
|
|
}
|
|
|
|
func TestUpdateNodesForInstancePreservesLinkWhenNodeIDChanges(t *testing.T) {
|
|
state := &State{
|
|
Hosts: []Host{
|
|
{ID: "host-1", Hostname: "pve01", LinkedNodeID: "cluster-a-pve01-old"},
|
|
},
|
|
Nodes: []Node{
|
|
{
|
|
ID: "cluster-a-pve01-old",
|
|
Name: "pve01",
|
|
Instance: "cluster-entry-a",
|
|
ClusterName: "cluster-a",
|
|
IsClusterMember: true,
|
|
LinkedAgentID: "host-1",
|
|
},
|
|
},
|
|
}
|
|
|
|
state.UpdateNodesForInstance("cluster-entry-a", []Node{
|
|
{
|
|
ID: "cluster-a-pve01-new",
|
|
Name: "pve01",
|
|
Instance: "cluster-entry-a",
|
|
ClusterName: "cluster-a",
|
|
IsClusterMember: true,
|
|
Status: "online",
|
|
},
|
|
})
|
|
|
|
if len(state.Nodes) != 1 {
|
|
t.Fatalf("nodes = %#v, want exactly 1 node", state.Nodes)
|
|
}
|
|
if state.Nodes[0].ID != "cluster-a-pve01-new" {
|
|
t.Fatalf("node id = %q, want cluster-a-pve01-new", state.Nodes[0].ID)
|
|
}
|
|
if state.Nodes[0].LinkedAgentID != "host-1" {
|
|
t.Fatalf("linkedAgentID = %q, want host-1", state.Nodes[0].LinkedAgentID)
|
|
}
|
|
if state.Hosts[0].LinkedNodeID != "cluster-a-pve01-new" {
|
|
t.Fatalf("host LinkedNodeID = %q, want cluster-a-pve01-new", state.Hosts[0].LinkedNodeID)
|
|
}
|
|
}
|
|
|
|
func TestUpdateNodesForInstanceMergesStandaloneNodeIntoClusterNodeByEndpoint(t *testing.T) {
|
|
state := &State{
|
|
Hosts: []Host{
|
|
{
|
|
ID: "host-1",
|
|
Hostname: "minipc.local",
|
|
ReportIP: "10.0.0.5",
|
|
LinkedNodeID: "cluster-homelab-minipc",
|
|
NetworkInterfaces: []HostNetworkInterface{{Name: "eth0", Addresses: []string{"10.0.0.5/24"}}},
|
|
},
|
|
},
|
|
Nodes: []Node{
|
|
{
|
|
ID: "cluster-homelab-minipc",
|
|
Name: "minipc",
|
|
Instance: "homelab-entry",
|
|
ClusterName: "homelab",
|
|
IsClusterMember: true,
|
|
Host: "https://10.0.0.5:8006",
|
|
LinkedAgentID: "host-1",
|
|
Status: "online",
|
|
},
|
|
},
|
|
}
|
|
|
|
state.UpdateNodesForInstance("minipc-standalone", []Node{
|
|
{
|
|
ID: "minipc-standalone-minipc",
|
|
Name: "minipc",
|
|
Instance: "minipc-standalone",
|
|
Host: "https://10.0.0.5:8006",
|
|
Status: "online",
|
|
},
|
|
})
|
|
|
|
if len(state.Nodes) != 1 {
|
|
t.Fatalf("nodes = %#v, want exactly 1 node", state.Nodes)
|
|
}
|
|
if state.Nodes[0].ID != "cluster-homelab-minipc" {
|
|
t.Fatalf("node ID = %q, want cluster-homelab-minipc", state.Nodes[0].ID)
|
|
}
|
|
if state.Nodes[0].LinkedAgentID != "host-1" {
|
|
t.Fatalf("LinkedAgentID = %q, want host-1", state.Nodes[0].LinkedAgentID)
|
|
}
|
|
if state.Hosts[0].LinkedNodeID != "cluster-homelab-minipc" {
|
|
t.Fatalf("host LinkedNodeID = %q, want cluster-homelab-minipc", state.Hosts[0].LinkedNodeID)
|
|
}
|
|
}
|
|
|
|
func TestStateUpdateNodesForInstancePrefersUniqueEndpointIPOverAmbiguousHostname(t *testing.T) {
|
|
state := &State{
|
|
Hosts: []Host{
|
|
{
|
|
ID: "host-a",
|
|
Hostname: "minipc.local",
|
|
ReportIP: "10.0.0.5",
|
|
NetworkInterfaces: []HostNetworkInterface{
|
|
{Name: "eth0", Addresses: []string{"10.0.0.5/24"}},
|
|
},
|
|
},
|
|
{
|
|
ID: "host-b",
|
|
Hostname: "minipc.lab",
|
|
ReportIP: "10.0.0.6",
|
|
NetworkInterfaces: []HostNetworkInterface{
|
|
{Name: "eth0", Addresses: []string{"10.0.0.6/24"}},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
state.UpdateNodesForInstance("cluster-a", []Node{
|
|
{
|
|
ID: "node-1",
|
|
Name: "minipc",
|
|
Instance: "cluster-a",
|
|
Host: "https://10.0.0.5:8006",
|
|
},
|
|
})
|
|
|
|
if len(state.Nodes) != 1 {
|
|
t.Fatalf("nodes = %#v, want 1", state.Nodes)
|
|
}
|
|
if state.Nodes[0].LinkedAgentID != "host-a" {
|
|
t.Fatalf("LinkedAgentID = %q, want host-a", state.Nodes[0].LinkedAgentID)
|
|
}
|
|
}
|
|
|
|
func TestUpdateNodesForInstanceMergesNodeViewsAcrossEndpointFormsWhenLinkedHostMatches(t *testing.T) {
|
|
state := &State{
|
|
Hosts: []Host{
|
|
{
|
|
ID: "host-1",
|
|
Hostname: "minipc.local",
|
|
ReportIP: "10.0.0.5",
|
|
LinkedNodeID: "minipc-ip-view",
|
|
NetworkInterfaces: []HostNetworkInterface{
|
|
{Name: "eth0", Addresses: []string{"10.0.0.5/24"}},
|
|
},
|
|
},
|
|
},
|
|
Nodes: []Node{
|
|
{
|
|
ID: "minipc-ip-view",
|
|
Name: "minipc",
|
|
Instance: "standalone-ip",
|
|
Host: "https://10.0.0.5:8006",
|
|
LinkedAgentID: "host-1",
|
|
Status: "online",
|
|
},
|
|
},
|
|
}
|
|
|
|
state.UpdateNodesForInstance("standalone-hostname", []Node{
|
|
{
|
|
ID: "minipc-hostname-view",
|
|
Name: "minipc",
|
|
Instance: "standalone-hostname",
|
|
Host: "https://minipc.local:8006",
|
|
Status: "online",
|
|
},
|
|
})
|
|
|
|
if len(state.Nodes) != 1 {
|
|
t.Fatalf("nodes = %#v, want exactly 1 node", state.Nodes)
|
|
}
|
|
if state.Nodes[0].ID != "minipc-ip-view" {
|
|
t.Fatalf("node ID = %q, want minipc-ip-view", state.Nodes[0].ID)
|
|
}
|
|
if state.Nodes[0].LinkedAgentID != "host-1" {
|
|
t.Fatalf("LinkedAgentID = %q, want host-1", state.Nodes[0].LinkedAgentID)
|
|
}
|
|
if state.Hosts[0].LinkedNodeID != "minipc-ip-view" {
|
|
t.Fatalf("host LinkedNodeID = %q, want minipc-ip-view", state.Hosts[0].LinkedNodeID)
|
|
}
|
|
}
|
|
|
|
func TestUpdateNodesForInstanceDeduplicatesLogicalNodeByClusterName(t *testing.T) {
|
|
state := NewState()
|
|
older := time.Now().Add(-2 * time.Minute)
|
|
newer := time.Now()
|
|
|
|
state.UpdateNodesForInstance("instance-a", []Node{
|
|
{
|
|
ID: "node-old",
|
|
Name: "pve01",
|
|
Instance: "instance-a",
|
|
ClusterName: "cluster-a",
|
|
IsClusterMember: true,
|
|
Status: "unknown",
|
|
ConnectionHealth: "unknown",
|
|
LastSeen: older,
|
|
},
|
|
{
|
|
ID: "node-new",
|
|
Name: "pve01",
|
|
Instance: "instance-a",
|
|
ClusterName: "cluster-a",
|
|
IsClusterMember: true,
|
|
Status: "online",
|
|
ConnectionHealth: "healthy",
|
|
LastSeen: newer,
|
|
},
|
|
})
|
|
|
|
if len(state.Nodes) != 1 {
|
|
t.Fatalf("expected one logical node after dedupe, got %d (%#v)", len(state.Nodes), state.Nodes)
|
|
}
|
|
if state.Nodes[0].ID != "node-new" {
|
|
t.Fatalf("node id = %q, want node-new", state.Nodes[0].ID)
|
|
}
|
|
if state.Nodes[0].Status != "online" {
|
|
t.Fatalf("status = %q, want online", state.Nodes[0].Status)
|
|
}
|
|
}
|
|
|
|
func TestUpdateNodesForInstancePrefersHealthyOnCrossInstanceClusterCollisionDifferentIDs(t *testing.T) {
|
|
now := time.Now()
|
|
state := &State{
|
|
Nodes: []Node{
|
|
{
|
|
ID: "node-from-b",
|
|
Name: "pve01",
|
|
Instance: "instance-b",
|
|
ClusterName: "cluster-a",
|
|
IsClusterMember: true,
|
|
Status: "online",
|
|
ConnectionHealth: "healthy",
|
|
LastSeen: now,
|
|
},
|
|
},
|
|
}
|
|
|
|
state.UpdateNodesForInstance("instance-a", []Node{
|
|
{
|
|
ID: "node-from-a",
|
|
Name: "pve01",
|
|
Instance: "instance-a",
|
|
ClusterName: "cluster-a",
|
|
IsClusterMember: true,
|
|
Status: "unknown",
|
|
ConnectionHealth: "unknown",
|
|
LastSeen: now.Add(-time.Minute),
|
|
},
|
|
})
|
|
|
|
if len(state.Nodes) != 1 {
|
|
t.Fatalf("nodes = %#v, want exactly one merged node", state.Nodes)
|
|
}
|
|
if state.Nodes[0].ID != "node-from-b" {
|
|
t.Fatalf("node id = %q, want node-from-b", state.Nodes[0].ID)
|
|
}
|
|
if state.Nodes[0].Instance != "instance-b" {
|
|
t.Fatalf("instance = %q, want instance-b", state.Nodes[0].Instance)
|
|
}
|
|
if state.Nodes[0].Status != "online" {
|
|
t.Fatalf("status = %q, want online", state.Nodes[0].Status)
|
|
}
|
|
}
|
|
|
|
func TestStateLinkHostAgentToNode(t *testing.T) {
|
|
state := &State{
|
|
Hosts: []Host{
|
|
{ID: "h1", LinkedNodeID: "n1"},
|
|
{ID: "h2", LinkedVMID: "vm1", LinkedContainerID: "ct1"},
|
|
},
|
|
Nodes: []Node{
|
|
{ID: "n1", LinkedAgentID: "h1"},
|
|
{ID: "n2"},
|
|
},
|
|
}
|
|
|
|
if err := state.LinkHostAgentToNode("h2", "n2"); err != nil {
|
|
t.Fatalf("LinkHostAgentToNode error: %v", err)
|
|
}
|
|
if state.Hosts[1].LinkedNodeID != "n2" {
|
|
t.Fatalf("LinkedNodeID = %q, want n2", state.Hosts[1].LinkedNodeID)
|
|
}
|
|
if state.Nodes[1].LinkedAgentID != "h2" {
|
|
t.Fatalf("LinkedAgentID = %q, want h2", state.Nodes[1].LinkedAgentID)
|
|
}
|
|
if state.Hosts[1].LinkedVMID != "" || state.Hosts[1].LinkedContainerID != "" {
|
|
t.Fatalf("expected VM/container links cleared")
|
|
}
|
|
|
|
if err := state.LinkHostAgentToNode("missing", "n2"); err == nil || !errors.Is(err, ErrHostAgentNotFound) || !strings.Contains(err.Error(), "missing") {
|
|
t.Fatalf("expected host not found error, got %v", err)
|
|
}
|
|
if err := state.LinkHostAgentToNode("h2", "missing"); err == nil || !errors.Is(err, ErrNodeNotFound) || !strings.Contains(err.Error(), "missing") {
|
|
t.Fatalf("expected node not found error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestStateUnlinkHostAgent(t *testing.T) {
|
|
state := &State{
|
|
Hosts: []Host{{ID: "h1", LinkedNodeID: "n1", LinkedVMID: "vm", LinkedContainerID: "ct"}},
|
|
Nodes: []Node{{ID: "n1", LinkedAgentID: "h1"}},
|
|
}
|
|
|
|
if ok := state.UnlinkHostAgent("h1"); !ok {
|
|
t.Fatalf("UnlinkHostAgent returned false")
|
|
}
|
|
if state.Hosts[0].LinkedNodeID != "" || state.Hosts[0].LinkedVMID != "" || state.Hosts[0].LinkedContainerID != "" {
|
|
t.Fatalf("expected host links cleared")
|
|
}
|
|
if state.Nodes[0].LinkedAgentID != "" {
|
|
t.Fatalf("expected node link cleared")
|
|
}
|
|
if ok := state.UnlinkHostAgent("missing"); ok {
|
|
t.Fatalf("expected false for missing host")
|
|
}
|
|
}
|
|
|
|
func TestStateUpsertCephCluster(t *testing.T) {
|
|
state := &State{}
|
|
state.UpsertCephCluster(CephCluster{ID: "c1", Name: "b"})
|
|
state.UpsertCephCluster(CephCluster{ID: "c2", Name: "a"})
|
|
state.UpsertCephCluster(CephCluster{ID: "c1", Name: "c"})
|
|
|
|
if len(state.CephClusters) != 2 {
|
|
t.Fatalf("clusters = %#v, want 2", state.CephClusters)
|
|
}
|
|
if state.CephClusters[0].Name != "a" || state.CephClusters[1].Name != "c" {
|
|
t.Fatalf("clusters order = %#v, want a then c", state.CephClusters)
|
|
}
|
|
}
|
|
|
|
func TestStateSetHostCommandsEnabled(t *testing.T) {
|
|
state := &State{
|
|
Hosts: []Host{{ID: "h1", CommandsEnabled: false}},
|
|
}
|
|
|
|
if ok := state.SetHostCommandsEnabled("h1", true); !ok {
|
|
t.Fatalf("SetHostCommandsEnabled returned false")
|
|
}
|
|
if !state.Hosts[0].CommandsEnabled {
|
|
t.Fatalf("CommandsEnabled not updated")
|
|
}
|
|
if ok := state.SetHostCommandsEnabled("missing", true); ok {
|
|
t.Fatalf("expected false for missing host")
|
|
}
|
|
}
|
|
|
|
func TestStateContainers(t *testing.T) {
|
|
now := time.Now()
|
|
state := &State{
|
|
Containers: []Container{{ID: "ct1"}},
|
|
}
|
|
|
|
containers := state.GetContainers()
|
|
if len(containers) != 1 || containers[0].ID != "ct1" {
|
|
t.Fatalf("containers = %#v, want ct1", containers)
|
|
}
|
|
containers[0].ID = "changed"
|
|
if state.Containers[0].ID != "ct1" {
|
|
t.Fatalf("state containers should not be modified by copy")
|
|
}
|
|
|
|
if ok := state.UpdateContainerDockerStatus("ct1", true, now); !ok {
|
|
t.Fatalf("UpdateContainerDockerStatus returned false")
|
|
}
|
|
if !state.Containers[0].HasDocker {
|
|
t.Fatalf("HasDocker not updated")
|
|
}
|
|
if ok := state.UpdateContainerDockerStatus("missing", true, now); ok {
|
|
t.Fatalf("expected false for missing container")
|
|
}
|
|
}
|