Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
Go
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443package config
import ( "context" "testing" "time"
"tangled.org/core/spindle/quota")
func TestLoadAllowsUnconfiguredMicroVMEngine(t *testing.T) { t.Setenv("SPINDLE_SERVER_HOSTNAME", "spindle.example.com") t.Setenv("SPINDLE_SERVER_OWNER", "did:web:spindle.example.com") t.Setenv("SPINDLE_MICROVM_PIPELINES_IMAGE_DIR", "")
cfg, err := Load(context.Background()) if err != nil { t.Fatal(err) } if cfg.MicroVMPipelines.ImageDir != "" { t.Fatalf("image directory = %q, want empty", cfg.MicroVMPipelines.ImageDir) } if cfg.Mill.DrainTimeout != 20*time.Minute { t.Fatalf("drain timeout = %s, want 20m", cfg.Mill.DrainTimeout) }}
func TestLoadRequiresOwnerForCoordinatorRoles(t *testing.T) { t.Setenv("SPINDLE_SERVER_HOSTNAME", "spindle.example.com") t.Setenv("SPINDLE_SERVER_OWNER", "")
for _, role := range []string{"standalone", "mill"} { t.Run(role, func(t *testing.T) { t.Setenv("SPINDLE_ROLE", role) if _, err := Load(context.Background()); err == nil { t.Fatalf("Load accepted SPINDLE_ROLE=%s without an owner", role) } }) }}
func TestLoadRequiresTheOIDCFieldsTogether(t *testing.T) { t.Setenv("SPINDLE_SERVER_HOSTNAME", "spindle.example.com") t.Setenv("SPINDLE_SERVER_OWNER", "did:web:spindle.example.com")
t.Setenv("SPINDLE_OIDC_KEY_PATH", "") t.Setenv("SPINDLE_OIDC_KEY_ID", "spindle-oidc") if _, err := Load(context.Background()); err == nil { t.Fatal("Load accepted a key id without a key path") }
t.Setenv("SPINDLE_OIDC_KEY_PATH", "/keys/oidc.pem") t.Setenv("SPINDLE_OIDC_ISSUER", "") if _, err := Load(context.Background()); err == nil { t.Fatal("Load accepted a key path without an issuer") }
t.Setenv("SPINDLE_OIDC_ISSUER", "https://tokens.spindle.tangled.sh") cfg, err := Load(context.Background()) if err != nil { t.Fatal(err) } if cfg.OIDC.KeyPath != "/keys/oidc.pem" || cfg.OIDC.KeyID != "spindle-oidc" { t.Fatalf("oidc config = %+v, want the configured key path and key id", cfg.OIDC) }}
func TestLoadAllowsExecutorWithoutOwner(t *testing.T) { t.Setenv("SPINDLE_ROLE", "executor") t.Setenv("SPINDLE_SERVER_HOSTNAME", "worker-1") t.Setenv("SPINDLE_MILL_URL", "wss://spindle.example.com/mill") t.Setenv("SPINDLE_MILL_SHARED_SECRET", "secret")
cfg, err := Load(context.Background()) if err != nil { t.Fatal(err) } if cfg.Server.Owner != "" { t.Fatalf("owner = %q, want empty", cfg.Server.Owner) }}
func TestLoadRejectsNonPositiveDrainTimeout(t *testing.T) { t.Setenv("SPINDLE_SERVER_HOSTNAME", "spindle.example.com") t.Setenv("SPINDLE_SERVER_OWNER", "did:web:spindle.example.com")
for _, timeout := range []string{"0s", "-1s"} { t.Run(timeout, func(t *testing.T) { t.Setenv("SPINDLE_MILL_DRAIN_TIMEOUT", timeout) if _, err := Load(context.Background()); err == nil { t.Fatalf("Load accepted drain timeout %s", timeout) } }) }}
func TestLoadRejectsInvalidScheduleLimits(t *testing.T) { t.Setenv("SPINDLE_SERVER_HOSTNAME", "spindle.example.com") t.Setenv("SPINDLE_SERVER_OWNER", "did:web:spindle.example.com") for _, tc := range []struct{ name, key, value string }{ {"disabled interval", "SPINDLE_SCHEDULE_MINIMUM_INTERVAL", "0s"}, {"sub-minute interval", "SPINDLE_SCHEDULE_MINIMUM_INTERVAL", "30s"}, {"fractional minute", "SPINDLE_SCHEDULE_MINIMUM_INTERVAL", "90s"}, {"no workers", "SPINDLE_SCHEDULE_CONCURRENCY", "0"}, {"negative workers", "SPINDLE_SCHEDULE_CONCURRENCY", "-1"}, } { t.Run(tc.name, func(t *testing.T) { t.Setenv(tc.key, tc.value) if _, err := Load(context.Background()); err == nil { t.Fatalf("Load accepted %s=%s", tc.key, tc.value) } }) }}
func TestLoadRequiresJumpHostKey(t *testing.T) { t.Setenv("SPINDLE_SERVER_HOSTNAME", "spindle.example.com") t.Setenv("SPINDLE_SERVER_OWNER", "did:web:spindle.example.com") t.Setenv("SPINDLE_ROLE", "mill") t.Setenv("SPINDLE_MILL_JUMP_LISTEN_ADDR", "0.0.0.0:22")
if _, err := Load(context.Background()); err == nil { t.Fatal("Load accepted a jump listener without a host key path") }}
func TestLoadRejectsJumpListenerOutsideMill(t *testing.T) { t.Setenv("SPINDLE_SERVER_HOSTNAME", "spindle.example.com") t.Setenv("SPINDLE_SERVER_OWNER", "did:web:spindle.example.com") t.Setenv("SPINDLE_ROLE", "standalone") t.Setenv("SPINDLE_MILL_JUMP_LISTEN_ADDR", "0.0.0.0:22") t.Setenv("SPINDLE_MILL_JUMP_HOST_KEY_PATH", "/tmp/jump-host-key")
if _, err := Load(context.Background()); err == nil { t.Fatal("Load accepted a mill jump listener in standalone mode") }}
func TestLoadValidatesMaxJumpConnections(t *testing.T) { t.Setenv("SPINDLE_SERVER_HOSTNAME", "spindle.example.com") t.Setenv("SPINDLE_SERVER_OWNER", "did:web:spindle.example.com") t.Setenv("SPINDLE_ROLE", "mill") t.Setenv("SPINDLE_MILL_JUMP_LISTEN_ADDR", "0.0.0.0:22") t.Setenv("SPINDLE_MILL_JUMP_HOST_KEY_PATH", "/tmp/jump-host-key") t.Setenv("SPINDLE_MILL_MAX_JUMP_CONNECTIONS", "0")
if _, err := Load(context.Background()); err == nil { t.Fatal("Load accepted a non-positive jump connection limit") }
t.Setenv("SPINDLE_MILL_MAX_JUMP_CONNECTIONS", "17") cfg, err := Load(context.Background()) if err != nil { t.Fatal(err) } if cfg.Mill.MaxJumpConnections != 17 { t.Fatalf("max jump connections = %d, want 17", cfg.Mill.MaxJumpConnections) }}
func TestLoadMetricsListenAddr(t *testing.T) { t.Setenv("SPINDLE_SERVER_HOSTNAME", "spindle.example.com") t.Setenv("SPINDLE_SERVER_OWNER", "did:web:spindle.example.com")
cfg, err := Load(context.Background()) if err != nil { t.Fatal(err) } if cfg.Server.MetricsListenAddr != "127.0.0.1:9091" { t.Errorf("default MetricsListenAddr = %q, want 127.0.0.1:9091", cfg.Server.MetricsListenAddr) }
t.Setenv("SPINDLE_SERVER_METRICS_LISTEN_ADDR", "0.0.0.0:9999") cfg, err = Load(context.Background()) if err != nil { t.Fatal(err) } if cfg.Server.MetricsListenAddr != "0.0.0.0:9999" { t.Errorf("overridden MetricsListenAddr = %q, want 0.0.0.0:9999", cfg.Server.MetricsListenAddr) }}
func TestLoadTracingConfig(t *testing.T) { t.Setenv("SPINDLE_SERVER_HOSTNAME", "spindle.example.com") t.Setenv("SPINDLE_SERVER_OWNER", "did:web:spindle.example.com") t.Setenv("SPINDLE_TRACING_ENDPOINT", "tempo:4318") t.Setenv("SPINDLE_TRACING_SERVICE_NAME", "spindle-test") t.Setenv("SPINDLE_TRACING_SAMPLE_RATIO", "0.5") t.Setenv("SPINDLE_TRACING_INSECURE", "true")
cfg, err := Load(context.Background()) if err != nil { t.Fatal(err) }
if cfg.Tracing.Endpoint != "tempo:4318" { t.Errorf("Endpoint = %q, want tempo:4318", cfg.Tracing.Endpoint) } if cfg.Tracing.ServiceName != "spindle-test" { t.Errorf("ServiceName = %q, want spindle-test", cfg.Tracing.ServiceName) } if cfg.Tracing.SampleRatio != 0.5 { t.Errorf("SampleRatio = %f, want 0.5", cfg.Tracing.SampleRatio) } if !cfg.Tracing.Insecure { t.Errorf("Insecure = %t, want true", cfg.Tracing.Insecure) }}
func TestLoadTracingConfigValidation(t *testing.T) { t.Setenv("SPINDLE_SERVER_HOSTNAME", "spindle.example.com") t.Setenv("SPINDLE_SERVER_OWNER", "did:web:spindle.example.com")
t.Setenv("SPINDLE_TRACING_SAMPLE_RATIO", "1.5") if _, err := Load(context.Background()); err == nil { t.Fatal("expected error for SampleRatio > 1") }
t.Setenv("SPINDLE_TRACING_SAMPLE_RATIO", "-0.1") if _, err := Load(context.Background()); err == nil { t.Fatal("expected error for SampleRatio < 0") }
t.Setenv("SPINDLE_TRACING_SAMPLE_RATIO", "NaN") if _, err := Load(context.Background()); err == nil { t.Fatal("expected error for NaN SampleRatio") }}
func TestLoadQuotaLimitsValidation(t *testing.T) { tests := []struct { name string envs map[string]string wantErr bool }{ { name: "valid defaults", envs: map[string]string{ "SPINDLE_QUOTA_USER_CACHE_STORAGE_MIB": "0", "SPINDLE_QUOTA_USER_WORKFLOWS": "0", "SPINDLE_QUOTA_USER_VCPUS": "0", "SPINDLE_QUOTA_USER_MEMORY_MIB": "0", "SPINDLE_QUOTA_USER_DISK_MIB": "0", "SPINDLE_QUOTA_REPO_CACHE_STORAGE_MIB": "0", "SPINDLE_QUOTA_REPO_WORKFLOWS": "0", "SPINDLE_QUOTA_REPO_VCPUS": "0", "SPINDLE_QUOTA_REPO_MEMORY_MIB": "0", "SPINDLE_QUOTA_REPO_DISK_MIB": "0", }, wantErr: false, }, { name: "valid positive values", envs: map[string]string{ "SPINDLE_QUOTA_USER_CACHE_STORAGE_MIB": "100", "SPINDLE_QUOTA_USER_WORKFLOWS": "5", "SPINDLE_QUOTA_USER_VCPUS": "4", "SPINDLE_QUOTA_USER_MEMORY_MIB": "4096", "SPINDLE_QUOTA_USER_DISK_MIB": "10240", }, wantErr: false, }, { name: "negative user cache storage", envs: map[string]string{ "SPINDLE_QUOTA_USER_CACHE_STORAGE_MIB": "-1", }, wantErr: true, }, { name: "negative user workflows", envs: map[string]string{ "SPINDLE_QUOTA_USER_WORKFLOWS": "-1", }, wantErr: true, }, { name: "negative user vcpus", envs: map[string]string{ "SPINDLE_QUOTA_USER_VCPUS": "-1", }, wantErr: true, }, { name: "negative user memory", envs: map[string]string{ "SPINDLE_QUOTA_USER_MEMORY_MIB": "-1", }, wantErr: true, }, { name: "negative user disk", envs: map[string]string{ "SPINDLE_QUOTA_USER_DISK_MIB": "-1", }, wantErr: true, }, { name: "negative repo cache storage", envs: map[string]string{ "SPINDLE_QUOTA_REPO_CACHE_STORAGE_MIB": "-1", }, wantErr: true, }, { name: "negative repo workflows", envs: map[string]string{ "SPINDLE_QUOTA_REPO_WORKFLOWS": "-1", }, wantErr: true, }, { name: "negative repo vcpus", envs: map[string]string{ "SPINDLE_QUOTA_REPO_VCPUS": "-1", }, wantErr: true, }, { name: "negative repo memory", envs: map[string]string{ "SPINDLE_QUOTA_REPO_MEMORY_MIB": "-1", }, wantErr: true, }, { name: "negative repo disk", envs: map[string]string{ "SPINDLE_QUOTA_REPO_DISK_MIB": "-1", }, wantErr: true, }, { name: "user cache storage overflow", envs: map[string]string{ "SPINDLE_QUOTA_USER_CACHE_STORAGE_MIB": "9223372036854775807", }, wantErr: true, }, { name: "repo cache storage overflow", envs: map[string]string{ "SPINDLE_QUOTA_REPO_CACHE_STORAGE_MIB": "9223372036854775807", }, wantErr: true, }, }
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Setenv("SPINDLE_QUOTA_USER_CACHE_STORAGE_MIB", "") t.Setenv("SPINDLE_QUOTA_USER_WORKFLOWS", "") t.Setenv("SPINDLE_QUOTA_USER_VCPUS", "") t.Setenv("SPINDLE_QUOTA_USER_MEMORY_MIB", "") t.Setenv("SPINDLE_QUOTA_USER_DISK_MIB", "") t.Setenv("SPINDLE_QUOTA_REPO_CACHE_STORAGE_MIB", "") t.Setenv("SPINDLE_QUOTA_REPO_WORKFLOWS", "") t.Setenv("SPINDLE_QUOTA_REPO_VCPUS", "") t.Setenv("SPINDLE_QUOTA_REPO_MEMORY_MIB", "") t.Setenv("SPINDLE_QUOTA_REPO_DISK_MIB", "")
t.Setenv("SPINDLE_SERVER_HOSTNAME", "spindle.example.com") t.Setenv("SPINDLE_SERVER_OWNER", "did:web:spindle.example.com") t.Setenv("SPINDLE_TRACING_SAMPLE_RATIO", "1.0")
for k, v := range tt.envs { t.Setenv(k, v) }
_, err := Load(context.Background()) if (err != nil) != tt.wantErr { t.Errorf("Load() error = %v, wantErr = %v", err, tt.wantErr) } }) }}
func TestLoadLoggingConfig(t *testing.T) { t.Setenv("SPINDLE_SERVER_HOSTNAME", "spindle.example.com") t.Setenv("SPINDLE_SERVER_OWNER", "did:web:spindle.example.com") t.Setenv("SPINDLE_LOGGING_ENDPOINT", "loki:4318") t.Setenv("SPINDLE_LOGGING_SERVICE_NAME", "spindle-log-test") t.Setenv("SPINDLE_LOGGING_INSECURE", "true")
cfg, err := Load(context.Background()) if err != nil { t.Fatal(err) }
if cfg.Logging.Endpoint != "loki:4318" { t.Errorf("Endpoint = %q, want loki:4318", cfg.Logging.Endpoint) } if cfg.Logging.ServiceName != "spindle-log-test" { t.Errorf("ServiceName = %q, want spindle-log-test", cfg.Logging.ServiceName) } if !cfg.Logging.Insecure { t.Errorf("Insecure = %t, want true", cfg.Logging.Insecure) }}
func TestLoadLoggingConfigValidation(t *testing.T) { t.Setenv("SPINDLE_SERVER_HOSTNAME", "spindle.example.com") t.Setenv("SPINDLE_SERVER_OWNER", "did:web:spindle.example.com") t.Setenv("SPINDLE_LOGGING_ENDPOINT", "loki:4318") t.Setenv("SPINDLE_LOGGING_SERVICE_NAME", "")
if _, err := Load(context.Background()); err == nil { t.Fatal("expected error for empty ServiceName when logging endpoint is configured") }}
func TestLoadQuotaDefaultsDoesNotRequireServerConfiguration(t *testing.T) { t.Setenv("SPINDLE_QUOTA_USER_WORKFLOWS", "3") defaults, err := LoadQuotaDefaults(context.Background()) if err != nil { t.Fatal(err) } if got := defaults[quota.ScopeUser][quota.ResourceWorkflows]; got != 3 { t.Fatalf("user workflow limit = %d, want 3", got) }}
func TestLoadQuotaDefaultsRejectsNegativeLimits(t *testing.T) { t.Setenv("SPINDLE_QUOTA_REPO_VCPUS", "-1") if _, err := LoadQuotaDefaults(context.Background()); err == nil { t.Fatal("negative quota limit accepted") }}
func TestLoadCacheStoreID(t *testing.T) { t.Setenv("SPINDLE_SERVER_HOSTNAME", "spindle.example.com") t.Setenv("SPINDLE_SERVER_OWNER", "did:web:spindle.example.com") t.Setenv("SPINDLE_CACHE_STORE_ID", "shared-cache")
cfg, err := Load(context.Background()) if err != nil { t.Fatal(err) } if cfg.Cache.StoreID != "shared-cache" { t.Fatalf("cache store ID = %q, want shared-cache", cfg.Cache.StoreID) }}