사용자별 credential 저장, lease, projection, runtime 전달과 OpenAI-compatible 계약 및 검증 근거를 함께 반영한다.
111 lines
4 KiB
Go
111 lines
4 KiB
Go
package bootstrap
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"iop/apps/edge/internal/configrefresh"
|
|
)
|
|
|
|
const legacyCredentialModeYAML = `
|
|
server:
|
|
listen: "127.0.0.1:9090"
|
|
credential_plane:
|
|
enabled: false
|
|
`
|
|
|
|
const managedCredentialModeYAML = `
|
|
server:
|
|
listen: "127.0.0.1:9090"
|
|
tls:
|
|
enabled: true
|
|
cert: "/cert"
|
|
key: "/key"
|
|
ca: "/ca"
|
|
peer_role: "node"
|
|
control_plane:
|
|
enabled: true
|
|
wire_addr: "control-plane:19081"
|
|
tls:
|
|
enabled: true
|
|
cert: "/cert"
|
|
key: "/key"
|
|
ca: "/ca"
|
|
server_name: "control-plane"
|
|
peer_role: "control-plane"
|
|
credential_plane:
|
|
enabled: true
|
|
lease_ttl_seconds: 30
|
|
lease_cache_size: 256
|
|
`
|
|
|
|
func writeCredentialModeConfig(t *testing.T, name, body string) string {
|
|
t.Helper()
|
|
path := filepath.Join(t.TempDir(), name)
|
|
if err := os.WriteFile(path, []byte(body), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return path
|
|
}
|
|
|
|
func TestCredentialModeRefreshAndRollbackRequireRestartWithoutPartialApply(t *testing.T) {
|
|
managedEdgeKeyChanged := strings.Replace(managedCredentialModeYAML, " key: \"/key\"\n ca: \"/ca\"\n peer_role: \"node\"", " key: \"/rotated-edge-key\"\n ca: \"/ca\"\n peer_role: \"node\"", 1)
|
|
managedControlPlaneKeyChanged := strings.Replace(managedCredentialModeYAML, " key: \"/key\"\n ca: \"/ca\"\n server_name:", " key: \"/rotated-control-plane-key\"\n ca: \"/ca\"\n server_name:", 1)
|
|
managedOpenAI := managedCredentialModeYAML + `
|
|
openai:
|
|
enabled: true
|
|
provider_id: "provider-fixture"
|
|
tls:
|
|
enabled: true
|
|
cert: "/openai-cert"
|
|
key: "/openai-key"
|
|
`
|
|
for _, tc := range []struct {
|
|
name string
|
|
currentBody string
|
|
candidateBody string
|
|
expectedPath string
|
|
}{
|
|
{name: "legacy to managed", currentBody: legacyCredentialModeYAML, candidateBody: managedCredentialModeYAML, expectedPath: "credential_plane"},
|
|
{name: "managed rollback to legacy", currentBody: managedCredentialModeYAML, candidateBody: legacyCredentialModeYAML, expectedPath: "credential_plane"},
|
|
{name: "legacy source change", currentBody: legacyCredentialModeYAML, candidateBody: legacyCredentialModeYAML + "\nopenai:\n bearer_token: legacy-fixture\n", expectedPath: "openai"},
|
|
{name: "managed lease setting change", currentBody: managedCredentialModeYAML, candidateBody: strings.Replace(managedCredentialModeYAML, "lease_ttl_seconds: 30", "lease_ttl_seconds: 31", 1), expectedPath: "credential_plane"},
|
|
{name: "managed edge TLS key rotation", currentBody: managedCredentialModeYAML, candidateBody: managedEdgeKeyChanged, expectedPath: "tls"},
|
|
{name: "managed Control Plane TLS key rotation", currentBody: managedCredentialModeYAML, candidateBody: managedControlPlaneKeyChanged, expectedPath: "control_plane"},
|
|
{name: "managed OpenAI TLS key rotation", currentBody: managedOpenAI, candidateBody: strings.Replace(managedOpenAI, "/openai-key", "/rotated-openai-key", 1), expectedPath: "openai"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
currentPath := writeCredentialModeConfig(t, "current.yaml", tc.currentBody)
|
|
candidatePath := writeCredentialModeConfig(t, "candidate.yaml", tc.candidateBody)
|
|
current, err := configrefresh.LoadCandidate(currentPath)
|
|
if err != nil {
|
|
t.Fatalf("load current: %v", err)
|
|
}
|
|
runtime := &Runtime{Cfg: current}
|
|
result, err := runtime.RefreshConfig(context.Background(), configrefresh.Request{
|
|
Mode: configrefresh.ModeApply, ConfigPath: candidatePath, RequestID: "credential-mode-transition",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("RefreshConfig: %v", err)
|
|
}
|
|
if result.Status != configrefresh.StatusRestartRequired {
|
|
t.Fatalf("status = %q, want restart_required: %+v", result.Status, result.Changes)
|
|
}
|
|
found := false
|
|
for _, change := range result.Changes {
|
|
if change.Path == tc.expectedPath && change.Class == configrefresh.StatusRestartRequired {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatalf("restart path %q missing: %+v", tc.expectedPath, result.Changes)
|
|
}
|
|
if runtime.Cfg != current {
|
|
t.Fatal("restart-required credential mode was partially applied")
|
|
}
|
|
})
|
|
}
|
|
}
|