Files
CLICD/backend/internal/config/store_sqlite_test.go
T
MengMengCode 38debab1aa Add custom image handling and access policy management
- Implement tests for custom KVM and LXC image creation, ensuring invalid sources and architecture mismatches are rejected.
- Introduce access policy management in CLI, allowing configuration of allowed sources and trusted proxies.
- Add NAT network configuration with validation for RFC1918 compliance and subnet parsing.
- Create panel access policy management, including normalization and evaluation of access decisions based on client IPs and forwarded headers.
- Develop middleware for enforcing access policies in the server, returning appropriate responses for allowed and denied requests.
- Enhance custom image downloading and validation, ensuring integrity and security of downloaded root filesystem archives.
- Include comprehensive tests for all new functionalities to ensure reliability and correctness.
2026-07-26 04:04:45 +08:00

186 lines
5.7 KiB
Go

package config
import (
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
)
func TestSQLiteConfigMigratesLegacyJSONAndPersists(t *testing.T) {
resetConfigStoreForTest(t)
dir := t.TempDir()
t.Cleanup(func() {
resetConfigStoreForTest(t)
})
legacyPath := filepath.Join(dir, "config.json")
SetConfigPath(legacyPath)
legacy := ClicdConfig{
AdminUser: "admin",
AdminPassHash: "hash",
JWTSecret: "secret",
Port: 8999,
DataDir: dir,
NextContainerID: 2,
NextVNCPort: 5900,
NextSSHPort: 22000,
Containers: []Container{{
ID: 1,
UUID: "uuid-1",
Name: "ct1",
Virtualization: "lxc",
Template: "debian-12",
Status: "running",
PortMappingLimit: 2,
SnapshotLimit: 3,
PortMappings: []PortMapping{{
ContainerPort: 22,
HostPort: 22001,
Protocol: "tcp",
Description: "SSH",
}},
}},
AuditLogs: []AuditLog{{
Time: "2026-06-07 17:29:00",
Action: "security_horizontal_scan",
Target: "ct1",
Detail: "[medium] 可疑横向探测",
User: "system",
}},
LoginLogs: []SavedLoginLog{{
Time: "2026-06-07 17:29:01 CST",
Username: "admin",
IP: "127.0.0.1",
UserAgent: "test",
Success: true,
}},
Tasks: []SavedTask{{
ID: "task-1",
Type: "create",
ContainerName: "ct2",
Status: "pending",
CreatedAt: "2026-06-07 17:29:02",
Config: `{"name":"ct2","template_id":"debian-12","vcpu":1,"ram_mb":512,"disk_gb":5,"extra_ports":[80,443],"nat_port_mappings":[{"host_port":30080,"container_port":80,"protocol":"tcp","description":"HTTP"}],"management_port":30022,"assign_ipv6":true}`,
}},
EnabledImages: []string{"debian-12"},
CustomKVMImages: []CustomKVMImage{{
ID: "custom-kvm-test",
Name: "Test Cloud Image",
Description: "third-party image",
Distro: "ubuntu",
Release: "noble",
Arch: "amd64",
URL: "https://images.example.test/ubuntu.qcow2",
Provisioner: KVMProvisionerLinuxCloudInit,
SHA256: strings.Repeat("a", 64),
CreatedAt: "2026-07-26 10:00:00",
}},
CustomLXCImages: []CustomLXCImage{{
ID: "custom-lxc-test",
Name: "Test Rootfs",
Description: "third-party LXC image",
Distro: "alpine",
Release: "3.21",
Arch: "amd64",
URL: "https://images.example.test/alpine-rootfs.tar.xz",
SHA256: strings.Repeat("b", 64),
CreatedAt: "2026-07-26 10:00:00",
}},
PanelAccessPolicy: PanelAccessPolicy{
Enabled: true,
AllowedSources: []string{"192.0.2.0/24"},
TrustedProxies: []string{"127.0.0.1"},
},
Snapshots: []Snapshot{{
ID: "snap-1",
ContainerID: 1,
ContainerName: "ct1",
LXCName: "ct-1",
CreatedAt: "2026-06-07 17:30:00",
Path: filepath.Join(dir, "snap-1"),
}},
}
data, err := json.Marshal(legacy)
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(legacyPath, data, 0600); err != nil {
t.Fatal(err)
}
cfg, err := InitConfig()
if err != nil {
t.Fatal(err)
}
if len(cfg.Containers) != 1 || len(cfg.Containers[0].PortMappings) != 1 {
t.Fatalf("legacy config was not migrated: %+v", cfg.Containers)
}
if len(cfg.Tasks) != 1 || !strings.Contains(cfg.Tasks[0].Config, `"extra_ports":[80,443]`) {
t.Fatalf("task config was not restored from sqlite columns: %+v", cfg.Tasks)
}
if !strings.Contains(cfg.Tasks[0].Config, `"nat_port_mappings":[{"host_port":30080,"container_port":80`) {
t.Fatalf("task NAT mappings were not restored from sqlite: %+v", cfg.Tasks)
}
if !strings.Contains(cfg.Tasks[0].Config, `"management_port":30022`) {
t.Fatalf("task management port was not restored from sqlite: %+v", cfg.Tasks)
}
if cfg.TaskConcurrency != DefaultTaskConcurrency {
t.Fatalf("legacy task concurrency = %d, want default %d", cfg.TaskConcurrency, DefaultTaskConcurrency)
}
if !cfg.PanelAccessPolicy.Enabled || len(cfg.PanelAccessPolicy.AllowedSources) != 1 {
t.Fatalf("legacy panel access policy was not migrated: %+v", cfg.PanelAccessPolicy)
}
if len(cfg.CustomKVMImages) != 1 || cfg.CustomKVMImages[0].ID != "custom-kvm-test" {
t.Fatalf("legacy custom KVM images were not migrated: %+v", cfg.CustomKVMImages)
}
if len(cfg.CustomLXCImages) != 1 || cfg.CustomLXCImages[0].ID != "custom-lxc-test" {
t.Fatalf("legacy custom LXC images were not migrated: %+v", cfg.CustomLXCImages)
}
if _, err := os.Stat(filepath.Join(dir, "config.db")); err != nil {
t.Fatalf("sqlite database was not created: %v", err)
}
cfg.Containers[0].Status = "stopped"
cfg.TaskConcurrency = 6
if err := SaveConfig(); err != nil {
t.Fatal(err)
}
resetConfigStoreForTest(t)
SetConfigPath(legacyPath)
cfg, err = InitConfig()
if err != nil {
t.Fatal(err)
}
if got := cfg.Containers[0].Status; got != "stopped" {
t.Fatalf("expected sqlite value to win after migration, got %q", got)
}
if got := cfg.TaskConcurrency; got != 6 {
t.Fatalf("persisted task concurrency = %d, want 6", got)
}
if !cfg.PanelAccessPolicy.Enabled || cfg.PanelAccessPolicy.AllowedSources[0] != "192.0.2.0/24" {
t.Fatalf("persisted panel access policy = %+v", cfg.PanelAccessPolicy)
}
if len(cfg.CustomKVMImages) != 1 || cfg.CustomKVMImages[0].SHA256 != strings.Repeat("a", 64) {
t.Fatalf("persisted custom KVM images = %+v", cfg.CustomKVMImages)
}
if len(cfg.CustomLXCImages) != 1 || cfg.CustomLXCImages[0].SHA256 != strings.Repeat("b", 64) {
t.Fatalf("persisted custom LXC images = %+v", cfg.CustomLXCImages)
}
}
func resetConfigStoreForTest(t *testing.T) {
t.Helper()
if db != nil {
if err := db.Close(); err != nil {
t.Fatal(err)
}
db = nil
}
AppConfig = nil
configPath = ""
}