128 lines
3.6 KiB
Go
128 lines
3.6 KiB
Go
|
|
package plan
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"net/http"
|
||
|
|
"net/http/httptest"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"metis/pkg/inventory"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestFilesAndInjectWithSecretsAndOverlays(t *testing.T) {
|
||
|
|
vault := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
|
|
if r.URL.Path != "/v1/secret/data/nodes/titan-15" {
|
||
|
|
http.NotFound(w, r)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
||
|
|
"data": map[string]any{
|
||
|
|
"data": map[string]any{
|
||
|
|
"cloud_init": "#cloud-config\nmanage_etc_hosts: true\n",
|
||
|
|
"k3s_token": "secret-token",
|
||
|
|
"extra": map[string]string{"foo": "bar"},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
})
|
||
|
|
}))
|
||
|
|
defer vault.Close()
|
||
|
|
t.Setenv("VAULT_ADDR", vault.URL)
|
||
|
|
t.Setenv("VAULT_TOKEN", "tok")
|
||
|
|
|
||
|
|
dir := t.TempDir()
|
||
|
|
bootOverlay := filepath.Join(dir, "boot-overlay")
|
||
|
|
rootOverlay := filepath.Join(dir, "root-overlay")
|
||
|
|
if err := os.MkdirAll(filepath.Join(bootOverlay, "over"), 0o755); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if err := os.MkdirAll(filepath.Join(rootOverlay, "etc"), 0o755); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if err := os.WriteFile(filepath.Join(bootOverlay, "over", "cmdline.txt"), []byte("console=tty1"), 0o644); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if err := os.WriteFile(filepath.Join(rootOverlay, "etc", "issue"), []byte("hello"), 0o644); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
inv := &inventory.Inventory{
|
||
|
|
Classes: []inventory.NodeClass{{
|
||
|
|
Name: "c1",
|
||
|
|
Arch: "arm64",
|
||
|
|
OS: "linux",
|
||
|
|
Image: "file:///tmp/base.img",
|
||
|
|
BootOverlay: bootOverlay,
|
||
|
|
RootOverlay: rootOverlay,
|
||
|
|
}},
|
||
|
|
Nodes: []inventory.NodeSpec{{
|
||
|
|
Name: "titan-15",
|
||
|
|
Class: "c1",
|
||
|
|
Hostname: "titan-15",
|
||
|
|
IP: "192.168.22.43",
|
||
|
|
K3sRole: "agent",
|
||
|
|
SSHUser: "atlas",
|
||
|
|
SSHAuthorized: []string{"ssh-ed25519 AAA"},
|
||
|
|
}},
|
||
|
|
}
|
||
|
|
|
||
|
|
files, err := Files(inv, "titan-15")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("Files: %v", err)
|
||
|
|
}
|
||
|
|
var sawSecret, sawBootOverlay, sawRootOverlay, sawCloudInit bool
|
||
|
|
for _, f := range files {
|
||
|
|
switch {
|
||
|
|
case f.Path == "etc/metis/secrets.json":
|
||
|
|
sawSecret = true
|
||
|
|
case f.Path == "over/cmdline.txt":
|
||
|
|
sawBootOverlay = true
|
||
|
|
case f.Path == "etc/issue":
|
||
|
|
sawRootOverlay = true
|
||
|
|
case f.Path == "user-data":
|
||
|
|
sawCloudInit = strings.Contains(string(f.Content), "manage_etc_hosts: true")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if !sawSecret || !sawBootOverlay || !sawRootOverlay || !sawCloudInit {
|
||
|
|
t.Fatalf("missing generated files: secret=%v boot=%v root=%v cloudinit=%v", sawSecret, sawBootOverlay, sawRootOverlay, sawCloudInit)
|
||
|
|
}
|
||
|
|
|
||
|
|
bootDir := filepath.Join(dir, "boot")
|
||
|
|
rootDir := filepath.Join(dir, "root")
|
||
|
|
if err := os.MkdirAll(bootDir, 0o755); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if err := os.MkdirAll(rootDir, 0o755); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if err := Inject(inv, "titan-15", bootDir, rootDir); err != nil {
|
||
|
|
t.Fatalf("Inject: %v", err)
|
||
|
|
}
|
||
|
|
if _, err := os.Stat(filepath.Join(bootDir, "over", "cmdline.txt")); err != nil {
|
||
|
|
t.Fatalf("expected boot overlay file: %v", err)
|
||
|
|
}
|
||
|
|
if _, err := os.Stat(filepath.Join(rootDir, "etc/metis/node.json")); err != nil {
|
||
|
|
t.Fatalf("expected injected rootfs file: %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestNextRunStale(t *testing.T) {
|
||
|
|
if !NextRunStale(time.Now().Add(-2*time.Hour), time.Hour) {
|
||
|
|
t.Fatal("expected stale run")
|
||
|
|
}
|
||
|
|
if NextRunStale(time.Now(), time.Hour) {
|
||
|
|
t.Fatal("did not expect fresh run to be stale")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAllowK3sNodeLabelRules(t *testing.T) {
|
||
|
|
if allowK3sNodeLabel("agent", "node-role.kubernetes.io/worker") {
|
||
|
|
t.Fatal("agent should block node-role labels")
|
||
|
|
}
|
||
|
|
if !allowK3sNodeLabel("server", "node-role.kubernetes.io/worker") {
|
||
|
|
t.Fatal("server should allow node-role labels")
|
||
|
|
}
|
||
|
|
}
|