162 lines
6.3 KiB
Go
162 lines
6.3 KiB
Go
package bootstrap
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/Pingu-Studio/MaidnCLI/internal/config"
|
|
)
|
|
|
|
func TestRenderCiliumConfig(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "values.yaml")
|
|
if err := os.WriteFile(path, []byte("host: ${CILIUM_K8S_SERVICE_HOST}\ninterface: ${CILIUM_TRAFFIC_INTERFACE}\nstart: ${CILIUM_LB_START}\nend: ${CILIUM_LB_END}\n"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cfg := config.Config{Talos: config.TalosConfig{KubeconfigEndpoint: "192.168.45.3"}, Cilium: config.CiliumConfig{TrafficInterface: "eth1", LoadBalancerStart: "192.168.45.19", LoadBalancerEnd: "192.168.45.30"}}
|
|
if err := renderCiliumConfig(dir, cfg); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
content, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(string(content), "${") || !strings.Contains(string(content), "192.168.45.30") {
|
|
t.Fatalf("Cilium configuration was not rendered: %s", content)
|
|
}
|
|
}
|
|
|
|
func TestRenderDeliveryConfig(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "webhook.yaml")
|
|
if err := os.WriteFile(path, []byte("host: ${WEBHOOK_HOSTNAME}\npath: ${WEBHOOK_PATH}\n"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cfg := config.Config{Delivery: config.DeliveryConfig{WebhookHostname: "tekton.example.test", WebhookPath: "/hooks/forgejo"}}
|
|
if err := renderDeliveryConfig(dir, cfg); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
content, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(string(content), "${") || !strings.Contains(string(content), "/hooks/forgejo") {
|
|
t.Fatalf("delivery configuration was not rendered: %s", content)
|
|
}
|
|
}
|
|
|
|
func TestRenderDemocraticCSISecret(t *testing.T) {
|
|
secret, err := renderDemocraticCSISecret(config.DemocraticCSIConfig{TrueNASAPIKey: "api-key", TrueNASHost: "truenas.example.test", TargetPortal: "truenas.example.test:3260", ShareHost: "truenas.example.test", DatasetParentNFS: "pool/kubernetes/nfs/v", DatasetSnapshotsNFS: "pool/kubernetes/nfs/s", AllowedNetworks: "192.168.45.0/24", NameSuffix: "-test", PortalGroup: "1", InitiatorGroup: "1"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(string(secret), "name: democratic-csi-secrets") || !strings.Contains(string(secret), "dataset-parent-nfs: pool/kubernetes/nfs/v") {
|
|
t.Fatalf("Democratic CSI secret was not rendered: %s", secret)
|
|
}
|
|
}
|
|
|
|
func TestWriteDemocraticCSISecretEncryptsValues(t *testing.T) {
|
|
if _, err := exec.LookPath("age-keygen"); err != nil {
|
|
t.Skip("age-keygen is required for bootstrap encryption")
|
|
}
|
|
if _, err := exec.LookPath("sops"); err != nil {
|
|
t.Skip("sops is required for bootstrap encryption")
|
|
}
|
|
dir := t.TempDir()
|
|
identity := filepath.Join(dir, "age-key.txt")
|
|
if err := exec.Command("age-keygen", "-o", identity).Run(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
secretPath := filepath.Join(dir, "secret.sops.yaml")
|
|
csi := config.DemocraticCSIConfig{TrueNASAPIKey: "test-api-key", TrueNASHost: "truenas.example.test", TargetPortal: "truenas.example.test:3260", ShareHost: "truenas.example.test", DatasetParentNFS: "pool/kubernetes/nfs/v", DatasetSnapshotsNFS: "pool/kubernetes/nfs/s", AllowedNetworks: "192.168.45.0/24", NameSuffix: "-test", PortalGroup: "1", InitiatorGroup: "1"}
|
|
if err := writeDemocraticCSISecret(secretPath, csi, identity); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
encrypted, err := os.ReadFile(secretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(string(encrypted), csi.TrueNASAPIKey) || !strings.Contains(string(encrypted), "sops:") {
|
|
t.Fatal("Democratic CSI secret was not SOPS encrypted")
|
|
}
|
|
if err := writeDemocraticCSISecret(secretPath, csi, identity); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
unchanged, err := os.ReadFile(secretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(encrypted) != string(unchanged) {
|
|
t.Fatal("Democratic CSI secret was re-encrypted without a configuration change")
|
|
}
|
|
}
|
|
|
|
func TestNewWebhookAuthorization(t *testing.T) {
|
|
authorization, err := NewWebhookAuthorization()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.HasPrefix(authorization, "Bearer ") || len(authorization) <= len("Bearer ") {
|
|
t.Fatalf("invalid webhook authorization")
|
|
}
|
|
}
|
|
|
|
func TestCopyDirSkipsGitDirectory(t *testing.T) {
|
|
source := t.TempDir()
|
|
destination := t.TempDir()
|
|
if err := os.Mkdir(filepath.Join(source, ".git"), 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(source, ".git", "config"), []byte("private"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(source, "README.md"), []byte("template"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := copyDir(source, destination, false); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(destination, ".git")); !os.IsNotExist(err) {
|
|
t.Fatal("copied template must not contain its Git metadata")
|
|
}
|
|
content, err := os.ReadFile(filepath.Join(destination, "README.md"))
|
|
if err != nil || string(content) != "template" {
|
|
t.Fatalf("template content was not copied: %q, %v", content, err)
|
|
}
|
|
}
|
|
|
|
func TestEnsureManifestsKustomizations(t *testing.T) {
|
|
dir := t.TempDir()
|
|
for _, environment := range []string{"previews", "staging", "production"} {
|
|
if err := os.MkdirAll(filepath.Join(dir, "apps", environment), 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err := ensureManifestsKustomizations(dir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
content, err := os.ReadFile(filepath.Join(dir, "apps", "previews", "kustomization.yaml"))
|
|
if err != nil || !strings.Contains(string(content), "resources:") {
|
|
t.Fatalf("preview Kustomization was not created: %q, %v", content, err)
|
|
}
|
|
}
|
|
|
|
func TestRenderTerraformTFVarsIsStableAndRedactsToken(t *testing.T) {
|
|
cfg := config.Config{
|
|
ClusterID: "test",
|
|
Talos: config.TalosConfig{
|
|
Proxmox: config.TalosProxmoxConfig{APIURL: "https://proxmox.test", APITokenID: "id", APITokenSecret: "secret", NodeInterfaces: map[string]string{"b": "eno2", "a": "eno1"}},
|
|
Cluster: config.TalosClusterConfig{Name: "test", Domain: "test", DiskStorage: "local", AdditionalStorage: "local"},
|
|
Image: config.TalosImageConfig{TalosVersion: "v1.13.6", KubernetesVersion: "v1.33.4", SchematicID: "abcdefghijkl", Architecture: "amd64"},
|
|
},
|
|
}
|
|
first := renderTerraformTFVars(cfg)
|
|
second := renderTerraformTFVars(cfg)
|
|
if first != second || strings.Contains(first, "secret") || strings.Index(first, `"a"`) > strings.Index(first, `"b"`) {
|
|
t.Fatalf("Terraform rendering is not deterministic or redacted: %s", first)
|
|
}
|
|
}
|