agents.default and agents.nodes.<id> in the cluster's settings hold everything agent.yaml can say but the agent's id, control planes, address and certificate, which stay in the file and are refused there. The control plane sends each node its settings on register and, a second apart, when they change; an agent whose settings changed checks they load beside its file, keeps them and starts again on them, engines serving throughout. An edited agent.yaml that loads restarts the agent too. History, audit, ctl config, the console and the portal's settings page cover them as the rest of the settings. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
80 lines
2.9 KiB
Go
80 lines
2.9 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestANodesAgentSettingsAreTheDefaultWithItsOwnOver(t *testing.T) {
|
|
a := AgentSettings{
|
|
Default: map[string]any{"heartbeat_interval": "5s", "supervisor": map[string]any{"work_dir": "/srv/w", "x": 1}},
|
|
Nodes: map[string]map[string]any{
|
|
"cuda01": {"supervisor": map[string]any{"work_dir": "/fast/w"}, "labels": map[string]any{"gpu": "rtx6000"}},
|
|
},
|
|
}
|
|
got := a.For("cuda01")
|
|
sup := got["supervisor"].(map[string]any)
|
|
if sup["work_dir"] != "/fast/w" || sup["x"] != 1 || got["heartbeat_interval"] != "5s" || got["labels"] == nil {
|
|
t.Errorf("cuda01: %v", got)
|
|
}
|
|
if got := a.For("node01"); got["labels"] != nil || got["supervisor"].(map[string]any)["work_dir"] != "/srv/w" {
|
|
t.Errorf("node01: %v", got)
|
|
}
|
|
if got := (AgentSettings{}).For("x"); len(got) != 0 {
|
|
t.Errorf("none: %v", got)
|
|
}
|
|
}
|
|
|
|
func TestAgentSettingsKeepBootstrapKeysInTheFileAndMustLoad(t *testing.T) {
|
|
for _, a := range []AgentSettings{
|
|
{Default: map[string]any{"servers": []any{"x:7443"}}},
|
|
{Nodes: map[string]map[string]any{"n1": {"node_id": "other"}}},
|
|
{Nodes: map[string]map[string]any{"n1": {"tls": map[string]any{"ca": "/x"}}}},
|
|
} {
|
|
if err := a.Check(); err == nil || !strings.Contains(err.Error(), "stays in each node's agent.yaml") {
|
|
t.Errorf("%+v: %v", a, err)
|
|
}
|
|
}
|
|
// A fabric with no adapters would not load on the node it names.
|
|
bad := AgentSettings{Nodes: map[string]map[string]any{"n1": {"fabrics": []any{map[string]any{"name": "ib-a"}}}}}
|
|
if err := bad.Check(); err == nil || !strings.Contains(err.Error(), "agents.nodes.n1") || !strings.Contains(err.Error(), "no HCAs") {
|
|
t.Errorf("a fabric without HCAs: %v", err)
|
|
}
|
|
if err := (AgentSettings{Default: map[string]any{"heartbeat_interval": "3s"}}).Check(); err != nil {
|
|
t.Errorf("a plain setting: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestTheClusterSettingsCarryAgentsAndRefuseBadOnes(t *testing.T) {
|
|
good, err := Parse([]byte("agents:\n default:\n heartbeat_interval: 3s\n nodes:\n cuda01:\n labels: {gpu: rtx6000}\n"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cfg, err := FromMap(good)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if cfg.Agents.For("cuda01")["labels"] == nil {
|
|
t.Errorf("agents not read: %+v", cfg.Agents)
|
|
}
|
|
bad, _ := Parse([]byte("agents:\n default:\n mgmt_addr: 10.0.0.1\n"))
|
|
if _, err := FromMap(bad); err == nil {
|
|
t.Error("a bootstrap key in the cluster's settings was taken")
|
|
}
|
|
}
|
|
|
|
func TestTheSettingsAreLaidOverTheAgentsFile(t *testing.T) {
|
|
p := filepath.Join(t.TempDir(), "agent.yaml")
|
|
_ = os.WriteFile(p, []byte("node_id: n1\nserver: cp:7443\nheartbeat_interval: 9s\nlabels: {rack: r1}\n"), 0o600)
|
|
cfg, err := LoadAgentWith(p, []byte("heartbeat_interval: 2s\nlabels: {gpu: a100}\n"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if cfg.NodeID != "n1" || cfg.HeartbeatInterval != 2*time.Second || cfg.Labels["rack"] != "r1" || cfg.Labels["gpu"] != "a100" {
|
|
t.Errorf("loaded %+v", cfg)
|
|
}
|
|
}
|