Fix generated Run component identity
This commit is contained in:
@@ -2,6 +2,7 @@ package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
@@ -14,6 +15,54 @@ import (
|
||||
"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) {
|
||||
client := newFakeWorkerClient()
|
||||
cfg := workerTestConfig(t)
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"browser.local/run/config"
|
||||
"browser.local/run/protocol"
|
||||
)
|
||||
|
||||
@@ -330,7 +331,7 @@ func buildRunLDFlags(input protocol.DistributionBuildInputResponse, platformURL
|
||||
"BuildServerInstanceID": input.ServerInstanceID,
|
||||
"BuildPluginID": input.PluginID,
|
||||
"BuildComponentKind": input.ComponentKind,
|
||||
"BuildComponentKey": input.ProfileKey,
|
||||
"BuildComponentKey": runBuildComponentKey(input),
|
||||
"BuildKeyGeneration": fmt.Sprint(input.KeyGeneration),
|
||||
"BuildVersion": input.TargetRelease,
|
||||
}
|
||||
@@ -341,6 +342,13 @@ func buildRunLDFlags(input protocol.DistributionBuildInputResponse, platformURL
|
||||
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 {
|
||||
checksum := bytesChecksum(payload)
|
||||
state, err := worker.registeredState()
|
||||
|
||||
@@ -136,6 +136,7 @@ func TestDistributionBuildIsolationUsesPluginJobWorkspaceAndDistinctPackageState
|
||||
ServerInstanceID: firstAssignment.ServerInstanceID,
|
||||
PluginID: "game.scum",
|
||||
RunEndpointID: firstAssignment.RunEndpointID,
|
||||
ProfileKey: "run-local",
|
||||
TargetOS: "linux",
|
||||
TargetArch: "amd64",
|
||||
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") {
|
||||
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") {
|
||||
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))
|
||||
return fmt.Errorf("workspace seed requires a server instance id")
|
||||
}
|
||||
scope, err := seededWorkspaceScope(cfg)
|
||||
scope, err := seededWorkspaceScopeForFiles(cfg, files)
|
||||
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()))
|
||||
return err
|
||||
@@ -71,12 +71,69 @@ func MaterializeWorkspaceSeed(cfg config.Config) 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) != "" {
|
||||
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)
|
||||
}
|
||||
|
||||
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) {
|
||||
target, err := workspaceSeedTarget(scope, file.Path)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user