Fix generated Run component identity
This commit is contained in:
+45
-8
@@ -69,18 +69,19 @@ func Load() Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
workspaceRoot := envOrDefault("RUN_WORKSPACE_ROOT", filepath.Join(".", ".run-workspace"))
|
workspaceRoot := envOrDefault("RUN_WORKSPACE_ROOT", filepath.Join(".", ".run-workspace"))
|
||||||
|
identity := loadPackagedIdentity()
|
||||||
return Config{
|
return Config{
|
||||||
Mode: mode,
|
Mode: mode,
|
||||||
PlatformURL: platformURL,
|
PlatformURL: platformURL,
|
||||||
RunEndpointID: envOrDefault("RUN_ENDPOINT_ID", stringOrDefault(BuildRunEndpointID, DefaultEndpointID)),
|
RunEndpointID: identity.runEndpointID,
|
||||||
DisplayName: envOrDefault("RUN_DISPLAY_NAME", stringOrDefault(BuildDisplayName, DefaultDisplayName)),
|
DisplayName: envOrDefault("RUN_DISPLAY_NAME", stringOrDefault(BuildDisplayName, DefaultDisplayName)),
|
||||||
Version: envOrDefault("RUN_VERSION", BuildVersion),
|
Version: identity.version,
|
||||||
RegistrationToken: envOrDefault("RUN_REGISTRATION_TOKEN", stringOrDefault(BuildRegistrationToken, "local-registration")),
|
RegistrationToken: identity.registrationToken,
|
||||||
ServerInstanceID: envOrDefault("RUN_SERVER_INSTANCE_ID", BuildServerInstanceID),
|
ServerInstanceID: identity.serverInstanceID,
|
||||||
PluginID: envOrDefault("RUN_PLUGIN_ID", BuildPluginID),
|
PluginID: identity.pluginID,
|
||||||
ComponentKind: envOrDefault("RUN_COMPONENT_KIND", BuildComponentKind),
|
ComponentKind: identity.componentKind,
|
||||||
ComponentKey: envOrDefault("RUN_COMPONENT_KEY", BuildComponentKey),
|
ComponentKey: identity.componentKey,
|
||||||
KeyGeneration: intEnvOrDefault("RUN_KEY_GENERATION", intStringOrDefault(BuildKeyGeneration, 0)),
|
KeyGeneration: identity.keyGeneration,
|
||||||
WorkspaceSeed: envOrDefault("RUN_WORKSPACE_SEED", BuildWorkspaceSeed),
|
WorkspaceSeed: envOrDefault("RUN_WORKSPACE_SEED", BuildWorkspaceSeed),
|
||||||
WorkspaceRoot: workspaceRoot,
|
WorkspaceRoot: workspaceRoot,
|
||||||
BuildSourceRoot: envOrDefault("RUN_BUILD_SOURCE_ROOT", "."),
|
BuildSourceRoot: envOrDefault("RUN_BUILD_SOURCE_ROOT", "."),
|
||||||
@@ -98,6 +99,42 @@ func Load() Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type packagedIdentity struct {
|
||||||
|
runEndpointID string
|
||||||
|
version string
|
||||||
|
registrationToken string
|
||||||
|
serverInstanceID string
|
||||||
|
pluginID string
|
||||||
|
componentKind string
|
||||||
|
componentKey string
|
||||||
|
keyGeneration int
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadPackagedIdentity() packagedIdentity {
|
||||||
|
if BuildRegistrationToken != "" {
|
||||||
|
return packagedIdentity{
|
||||||
|
runEndpointID: stringOrDefault(BuildRunEndpointID, DefaultEndpointID),
|
||||||
|
version: stringOrDefault(BuildVersion, DefaultVersion),
|
||||||
|
registrationToken: BuildRegistrationToken,
|
||||||
|
serverInstanceID: BuildServerInstanceID,
|
||||||
|
pluginID: BuildPluginID,
|
||||||
|
componentKind: BuildComponentKind,
|
||||||
|
componentKey: BuildComponentKey,
|
||||||
|
keyGeneration: intStringOrDefault(BuildKeyGeneration, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return packagedIdentity{
|
||||||
|
runEndpointID: envOrDefault("RUN_ENDPOINT_ID", stringOrDefault(BuildRunEndpointID, DefaultEndpointID)),
|
||||||
|
version: envOrDefault("RUN_VERSION", BuildVersion),
|
||||||
|
registrationToken: envOrDefault("RUN_REGISTRATION_TOKEN", "local-registration"),
|
||||||
|
serverInstanceID: envOrDefault("RUN_SERVER_INSTANCE_ID", BuildServerInstanceID),
|
||||||
|
pluginID: envOrDefault("RUN_PLUGIN_ID", BuildPluginID),
|
||||||
|
componentKind: envOrDefault("RUN_COMPONENT_KIND", BuildComponentKind),
|
||||||
|
componentKey: envOrDefault("RUN_COMPONENT_KEY", BuildComponentKey),
|
||||||
|
keyGeneration: intEnvOrDefault("RUN_KEY_GENERATION", intStringOrDefault(BuildKeyGeneration, 0)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func stringOrDefault(value string, fallback string) string {
|
func stringOrDefault(value string, fallback string) string {
|
||||||
if value == "" {
|
if value == "" {
|
||||||
return fallback
|
return fallback
|
||||||
|
|||||||
+10
-3
@@ -53,7 +53,7 @@ func TestLoadUsesEnvironment(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadUsesBuildDefaultsWithEnvironmentOverride(t *testing.T) {
|
func TestLoadUsesPackagedIdentityOverEnvironment(t *testing.T) {
|
||||||
oldMode, oldPlatformURL, oldRunEndpointID, oldDisplayName := BuildMode, BuildPlatformURL, BuildRunEndpointID, BuildDisplayName
|
oldMode, oldPlatformURL, oldRunEndpointID, oldDisplayName := BuildMode, BuildPlatformURL, BuildRunEndpointID, BuildDisplayName
|
||||||
oldRegistrationToken, oldServerInstanceID, oldPluginID := BuildRegistrationToken, BuildServerInstanceID, BuildPluginID
|
oldRegistrationToken, oldServerInstanceID, oldPluginID := BuildRegistrationToken, BuildServerInstanceID, BuildPluginID
|
||||||
oldComponentKind, oldComponentKey, oldKeyGeneration, oldVersion, oldWorkspaceSeed := BuildComponentKind, BuildComponentKey, BuildKeyGeneration, BuildVersion, BuildWorkspaceSeed
|
oldComponentKind, oldComponentKey, oldKeyGeneration, oldVersion, oldWorkspaceSeed := BuildComponentKind, BuildComponentKey, BuildKeyGeneration, BuildVersion, BuildWorkspaceSeed
|
||||||
@@ -85,9 +85,16 @@ func TestLoadUsesBuildDefaultsWithEnvironmentOverride(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("RUN_PLATFORM_URL", "http://127.0.0.1:18080")
|
t.Setenv("RUN_PLATFORM_URL", "http://127.0.0.1:18080")
|
||||||
|
t.Setenv("RUN_ENDPOINT_ID", "stale-run")
|
||||||
|
t.Setenv("RUN_REGISTRATION_TOKEN", "stale-token")
|
||||||
|
t.Setenv("RUN_SERVER_INSTANCE_ID", "stale-server")
|
||||||
|
t.Setenv("RUN_PLUGIN_ID", "stale-plugin")
|
||||||
|
t.Setenv("RUN_COMPONENT_KIND", "client-manager")
|
||||||
|
t.Setenv("RUN_COMPONENT_KEY", "stale-profile")
|
||||||
t.Setenv("RUN_KEY_GENERATION", "7")
|
t.Setenv("RUN_KEY_GENERATION", "7")
|
||||||
|
t.Setenv("RUN_VERSION", "stale-version")
|
||||||
cfg = Load()
|
cfg = Load()
|
||||||
if cfg.PlatformURL != "http://127.0.0.1:18080" || cfg.KeyGeneration != 7 {
|
if cfg.PlatformURL != "http://127.0.0.1:18080" || cfg.RunEndpointID != "run-server-1" || cfg.RegistrationToken != "compiled-run-key" || cfg.ServerInstanceID != "server-1" || cfg.PluginID != "game.scum" || cfg.ComponentKind != "run" || cfg.ComponentKey != "" || cfg.KeyGeneration != 5 || cfg.Version != "run-dist-1" {
|
||||||
t.Fatalf("expected environment override, got %+v", cfg)
|
t.Fatalf("expected packaged identity with platform URL override, got %+v", cfg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ func ApplyPackageConfig(base Config, pkg PackageConfig) Config {
|
|||||||
base.ServerInstanceID = pkg.ServerInstanceID
|
base.ServerInstanceID = pkg.ServerInstanceID
|
||||||
base.PluginID = pkg.PluginID
|
base.PluginID = pkg.PluginID
|
||||||
base.ComponentKind = pkg.Kind
|
base.ComponentKind = pkg.Kind
|
||||||
base.ComponentKey = pkg.ProfileKey
|
base.ComponentKey = packageComponentKey(pkg)
|
||||||
base.KeyGeneration = pkg.KeyGeneration
|
base.KeyGeneration = pkg.KeyGeneration
|
||||||
base.SecretRef = pkg.SecretRef
|
base.SecretRef = pkg.SecretRef
|
||||||
if pkg.RunEndpointID != "" {
|
if pkg.RunEndpointID != "" {
|
||||||
@@ -184,7 +184,7 @@ func AuthenticatePackageGeneration(pkg PackageConfig, auth ComponentAuthResult)
|
|||||||
if err := ValidatePackageConfig(pkg); err != nil {
|
if err := ValidatePackageConfig(pkg); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if auth.ServerInstanceID != pkg.ServerInstanceID || auth.Kind != pkg.Kind || auth.ProfileKey != pkg.ProfileKey {
|
if auth.ServerInstanceID != pkg.ServerInstanceID || auth.Kind != pkg.Kind || auth.ProfileKey != packageComponentKey(pkg) {
|
||||||
return fmt.Errorf("component authentication scope does not match package")
|
return fmt.Errorf("component authentication scope does not match package")
|
||||||
}
|
}
|
||||||
if !auth.Allowed {
|
if !auth.Allowed {
|
||||||
@@ -196,6 +196,13 @@ func AuthenticatePackageGeneration(pkg PackageConfig, auth ComponentAuthResult)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func packageComponentKey(pkg PackageConfig) string {
|
||||||
|
if pkg.Kind == PackageComponentRun {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(pkg.ProfileKey)
|
||||||
|
}
|
||||||
|
|
||||||
func fingerprint(value string) string {
|
func fingerprint(value string) string {
|
||||||
sum := sha256.Sum256([]byte(value))
|
sum := sha256.Sum256([]byte(value))
|
||||||
return hex.EncodeToString(sum[:])[:12]
|
return hex.EncodeToString(sum[:])[:12]
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ func TestLoadPackageConfigAppliesServerScopedIdentity(t *testing.T) {
|
|||||||
ServerInstanceID: "server-1",
|
ServerInstanceID: "server-1",
|
||||||
PluginID: "game.minecraft",
|
PluginID: "game.minecraft",
|
||||||
RunEndpointID: "run-server-1",
|
RunEndpointID: "run-server-1",
|
||||||
|
ProfileKey: "run-local",
|
||||||
TargetOS: "linux",
|
TargetOS: "linux",
|
||||||
TargetArch: "amd64",
|
TargetArch: "amd64",
|
||||||
SecretRef: "secret://runtime-keys/server-1/run/current",
|
SecretRef: "secret://runtime-keys/server-1/run/current",
|
||||||
@@ -26,7 +27,7 @@ func TestLoadPackageConfigAppliesServerScopedIdentity(t *testing.T) {
|
|||||||
t.Fatalf("load package config: %v", err)
|
t.Fatalf("load package config: %v", err)
|
||||||
}
|
}
|
||||||
cfg := ApplyPackageConfig(Config{RunEndpointID: DefaultEndpointID, DisplayName: DefaultDisplayName}, pkg)
|
cfg := ApplyPackageConfig(Config{RunEndpointID: DefaultEndpointID, DisplayName: DefaultDisplayName}, pkg)
|
||||||
if cfg.RegistrationToken != "opaque-runtime-key" || cfg.RunEndpointID != "run-server-1" || cfg.ServerInstanceID != "server-1" || cfg.KeyGeneration != 3 {
|
if cfg.RegistrationToken != "opaque-runtime-key" || cfg.RunEndpointID != "run-server-1" || cfg.ServerInstanceID != "server-1" || cfg.ComponentKey != "" || cfg.KeyGeneration != 3 {
|
||||||
t.Fatalf("expected package identity to be applied, got %+v", cfg)
|
t.Fatalf("expected package identity to be applied, got %+v", cfg)
|
||||||
}
|
}
|
||||||
diagnostics := pkg.RedactedDiagnostics()
|
diagnostics := pkg.RedactedDiagnostics()
|
||||||
@@ -70,6 +71,7 @@ func TestAuthenticatePackageGenerationRejectsStalePackages(t *testing.T) {
|
|||||||
Kind: PackageComponentRun,
|
Kind: PackageComponentRun,
|
||||||
ServerInstanceID: "server-1",
|
ServerInstanceID: "server-1",
|
||||||
PluginID: "game.minecraft",
|
PluginID: "game.minecraft",
|
||||||
|
ProfileKey: "run-local",
|
||||||
TargetOS: "linux",
|
TargetOS: "linux",
|
||||||
TargetArch: "amd64",
|
TargetArch: "amd64",
|
||||||
SecretRef: "secret://runtime-keys/server-1/run/current",
|
SecretRef: "secret://runtime-keys/server-1/run/current",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package runtime
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
@@ -14,6 +15,54 @@ import (
|
|||||||
"browser.local/run/protocol"
|
"browser.local/run/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestMaterializeWorkspaceSeedUsesLifecycleProfileWhenRunComponentKeyIsEmpty(t *testing.T) {
|
||||||
|
cfg := workerTestConfig(t)
|
||||||
|
cfg.ServerInstanceID = "server-worker"
|
||||||
|
cfg.PluginID = "game.scum"
|
||||||
|
cfg.ComponentKind = "run"
|
||||||
|
cfg.ComponentKey = ""
|
||||||
|
plan := protocol.RunAutonomousLifecyclePlan{
|
||||||
|
SchemaVersion: "1",
|
||||||
|
ServerInstanceID: cfg.ServerInstanceID,
|
||||||
|
PluginID: "game.scum",
|
||||||
|
PluginVersion: "1.0.0",
|
||||||
|
RunEndpointID: cfg.RunEndpointID,
|
||||||
|
ProfileKey: "run-local",
|
||||||
|
TargetOS: runtime.GOOS,
|
||||||
|
TargetArch: runtime.GOARCH,
|
||||||
|
TargetRelease: "run-dist-test",
|
||||||
|
Bootstrap: &protocol.RunAutonomousLifecycleAction{Action: "start", Operation: "start", Capability: protocol.RunCapabilityProcessStart, TargetKey: "actions/start.json"},
|
||||||
|
}
|
||||||
|
planPayload, err := json.Marshal(plan)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("marshal plan: %v", err)
|
||||||
|
}
|
||||||
|
seedPayload, err := json.Marshal([]workspaceSeedFile{
|
||||||
|
{Path: "actions/start.json", Content: `{"version":1,"action":"start","mode":"supervised","executableKey":"bin/game-server"}`, Mode: 0o600},
|
||||||
|
{Path: "bin/game-server", Content: "plugin-owned executable", Mode: 0o700},
|
||||||
|
{Path: autonomousLifecyclePlanKey, Content: string(planPayload), Mode: 0o600},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("marshal seed: %v", err)
|
||||||
|
}
|
||||||
|
cfg.WorkspaceSeed = base64.StdEncoding.EncodeToString(seedPayload)
|
||||||
|
|
||||||
|
if err := MaterializeWorkspaceSeed(cfg); err != nil {
|
||||||
|
t.Fatalf("materialize workspace seed: %v", err)
|
||||||
|
}
|
||||||
|
profileScope, err := NewWorkspaceResolver(cfg.WorkspaceRoot).Scope(cfg.ServerInstanceID, "run-local")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("resolve profile scope: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(filepath.Join(profileScope, autonomousLifecyclePlanKey)); err != nil {
|
||||||
|
t.Fatalf("expected lifecycle plan under profile scope: %v", err)
|
||||||
|
}
|
||||||
|
loaded, scope, ok, err := LoadAutonomousLifecyclePlan(cfg)
|
||||||
|
if err != nil || !ok || loaded.ProfileKey != "run-local" || scope != profileScope {
|
||||||
|
t.Fatalf("expected lifecycle plan to load from profile scope, ok=%t scope=%q plan=%+v err=%v", ok, scope, loaded, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestWorkerRunsAutonomousBootstrapFromSeededPlan(t *testing.T) {
|
func TestWorkerRunsAutonomousBootstrapFromSeededPlan(t *testing.T) {
|
||||||
client := newFakeWorkerClient()
|
client := newFakeWorkerClient()
|
||||||
cfg := workerTestConfig(t)
|
cfg := workerTestConfig(t)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"browser.local/run/config"
|
||||||
"browser.local/run/protocol"
|
"browser.local/run/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -330,7 +331,7 @@ func buildRunLDFlags(input protocol.DistributionBuildInputResponse, platformURL
|
|||||||
"BuildServerInstanceID": input.ServerInstanceID,
|
"BuildServerInstanceID": input.ServerInstanceID,
|
||||||
"BuildPluginID": input.PluginID,
|
"BuildPluginID": input.PluginID,
|
||||||
"BuildComponentKind": input.ComponentKind,
|
"BuildComponentKind": input.ComponentKind,
|
||||||
"BuildComponentKey": input.ProfileKey,
|
"BuildComponentKey": runBuildComponentKey(input),
|
||||||
"BuildKeyGeneration": fmt.Sprint(input.KeyGeneration),
|
"BuildKeyGeneration": fmt.Sprint(input.KeyGeneration),
|
||||||
"BuildVersion": input.TargetRelease,
|
"BuildVersion": input.TargetRelease,
|
||||||
}
|
}
|
||||||
@@ -341,6 +342,13 @@ func buildRunLDFlags(input protocol.DistributionBuildInputResponse, platformURL
|
|||||||
return strings.Join(flags, " ")
|
return strings.Join(flags, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runBuildComponentKey(input protocol.DistributionBuildInputResponse) string {
|
||||||
|
if input.ComponentKind == config.PackageComponentRun {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(input.ProfileKey)
|
||||||
|
}
|
||||||
|
|
||||||
func (worker *Worker) uploadDistributionArtifact(ctx context.Context, assignment protocol.RunJobAssignment, artifactID string, payload []byte) error {
|
func (worker *Worker) uploadDistributionArtifact(ctx context.Context, assignment protocol.RunJobAssignment, artifactID string, payload []byte) error {
|
||||||
checksum := bytesChecksum(payload)
|
checksum := bytesChecksum(payload)
|
||||||
state, err := worker.registeredState()
|
state, err := worker.registeredState()
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ func TestDistributionBuildIsolationUsesPluginJobWorkspaceAndDistinctPackageState
|
|||||||
ServerInstanceID: firstAssignment.ServerInstanceID,
|
ServerInstanceID: firstAssignment.ServerInstanceID,
|
||||||
PluginID: "game.scum",
|
PluginID: "game.scum",
|
||||||
RunEndpointID: firstAssignment.RunEndpointID,
|
RunEndpointID: firstAssignment.RunEndpointID,
|
||||||
|
ProfileKey: "run-local",
|
||||||
TargetOS: "linux",
|
TargetOS: "linux",
|
||||||
TargetArch: "amd64",
|
TargetArch: "amd64",
|
||||||
PlatformURL: "https://scum.npc0.com",
|
PlatformURL: "https://scum.npc0.com",
|
||||||
@@ -166,6 +167,9 @@ func TestDistributionBuildIsolationUsesPluginJobWorkspaceAndDistinctPackageState
|
|||||||
if !strings.Contains(firstFlags, "BuildServerInstanceID=scum-alpha") || !strings.Contains(firstFlags, "BuildRegistrationToken=alpha-component-key") {
|
if !strings.Contains(firstFlags, "BuildServerInstanceID=scum-alpha") || !strings.Contains(firstFlags, "BuildRegistrationToken=alpha-component-key") {
|
||||||
t.Fatalf("expected first build flags to carry first server identity, got %q", firstFlags)
|
t.Fatalf("expected first build flags to carry first server identity, got %q", firstFlags)
|
||||||
}
|
}
|
||||||
|
if strings.Contains(firstFlags, "BuildComponentKey=run-local") || !strings.Contains(firstFlags, "BuildComponentKey=") {
|
||||||
|
t.Fatalf("expected run component identity to stay separate from lifecycle profile, got %q", firstFlags)
|
||||||
|
}
|
||||||
if !strings.Contains(secondFlags, "BuildServerInstanceID=scum-beta") || !strings.Contains(secondFlags, "BuildRegistrationToken=beta-component-key") {
|
if !strings.Contains(secondFlags, "BuildServerInstanceID=scum-beta") || !strings.Contains(secondFlags, "BuildRegistrationToken=beta-component-key") {
|
||||||
t.Fatalf("expected second build flags to carry second server identity, got %q", secondFlags)
|
t.Fatalf("expected second build flags to carry second server identity, got %q", secondFlags)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ func MaterializeWorkspaceSeed(cfg config.Config) error {
|
|||||||
log.Printf("RUN phase=workspace_seed status=failed reason=missing_server workspace=%s", safeOptional(cfg.WorkspaceRoot))
|
log.Printf("RUN phase=workspace_seed status=failed reason=missing_server workspace=%s", safeOptional(cfg.WorkspaceRoot))
|
||||||
return fmt.Errorf("workspace seed requires a server instance id")
|
return fmt.Errorf("workspace seed requires a server instance id")
|
||||||
}
|
}
|
||||||
scope, err := seededWorkspaceScope(cfg)
|
scope, err := seededWorkspaceScopeForFiles(cfg, files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("RUN phase=workspace_seed status=scope_failed workspace=%s server=%s componentKey=%s error=%s", safeOptional(cfg.WorkspaceRoot), safeOptional(cfg.ServerInstanceID), safeOptional(cfg.ComponentKey), RedactText(err.Error()))
|
log.Printf("RUN phase=workspace_seed status=scope_failed workspace=%s server=%s componentKey=%s error=%s", safeOptional(cfg.WorkspaceRoot), safeOptional(cfg.ServerInstanceID), safeOptional(cfg.ComponentKey), RedactText(err.Error()))
|
||||||
return err
|
return err
|
||||||
@@ -71,12 +71,69 @@ func MaterializeWorkspaceSeed(cfg config.Config) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func seededWorkspaceScope(cfg config.Config) (string, error) {
|
func seededWorkspaceScope(cfg config.Config) (string, error) {
|
||||||
|
return seededWorkspaceScopeForFiles(cfg, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func seededWorkspaceScopeForFiles(cfg config.Config, files []workspaceSeedFile) (string, error) {
|
||||||
if strings.TrimSpace(cfg.ComponentKey) != "" {
|
if strings.TrimSpace(cfg.ComponentKey) != "" {
|
||||||
return NewWorkspaceResolver(cfg.WorkspaceRoot).Scope(cfg.ServerInstanceID, cfg.ComponentKey)
|
return NewWorkspaceResolver(cfg.WorkspaceRoot).Scope(cfg.ServerInstanceID, cfg.ComponentKey)
|
||||||
}
|
}
|
||||||
|
profileKey, err := workspaceSeedProfileKey(cfg, files)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if profileKey != "" {
|
||||||
|
return NewWorkspaceResolver(cfg.WorkspaceRoot).Scope(cfg.ServerInstanceID, profileKey)
|
||||||
|
}
|
||||||
return scopedServerWorkspace(cfg.WorkspaceRoot, cfg.ServerInstanceID)
|
return scopedServerWorkspace(cfg.WorkspaceRoot, cfg.ServerInstanceID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func workspaceSeedProfileKey(cfg config.Config, files []workspaceSeedFile) (string, error) {
|
||||||
|
var err error
|
||||||
|
if len(files) == 0 && strings.TrimSpace(cfg.WorkspaceSeed) != "" {
|
||||||
|
files, err = decodeWorkspaceSeedFiles(cfg.WorkspaceSeed)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, file := range files {
|
||||||
|
if filepath.ToSlash(strings.TrimSpace(file.Path)) != autonomousLifecyclePlanKey {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
body, err := workspaceSeedFileContent(file)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var plan struct {
|
||||||
|
ProfileKey string `json:"profileKey,omitempty"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(body, &plan); err != nil {
|
||||||
|
return "", fmt.Errorf("decode workspace seed lifecycle profile: %w", err)
|
||||||
|
}
|
||||||
|
profileKey := strings.TrimSpace(plan.ProfileKey)
|
||||||
|
if profileKey == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
if !protocol.ValidLogicalFileKey(profileKey) {
|
||||||
|
return "", fmt.Errorf("workspace seed lifecycle profile is unsafe")
|
||||||
|
}
|
||||||
|
return profileKey, nil
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeWorkspaceSeedFiles(encoded string) ([]workspaceSeedFile, error) {
|
||||||
|
payload, err := base64.StdEncoding.DecodeString(strings.TrimSpace(encoded))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decode workspace seed: %w", err)
|
||||||
|
}
|
||||||
|
var files []workspaceSeedFile
|
||||||
|
if err := json.Unmarshal(payload, &files); err != nil {
|
||||||
|
return nil, fmt.Errorf("decode workspace seed manifest: %w", err)
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
func writeWorkspaceSeedFile(scope string, file workspaceSeedFile, index int, total int) (int, error) {
|
func writeWorkspaceSeedFile(scope string, file workspaceSeedFile, index int, total int) (int, error) {
|
||||||
target, err := workspaceSeedTarget(scope, file.Path)
|
target, err := workspaceSeedTarget(scope, file.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user