feat: move distribution builds to platform Docker builder

This commit is contained in:
npc0-hue
2026-07-30 19:25:50 +08:00
parent 1e004dc9ec
commit e614a17fe3
45 changed files with 4492 additions and 294 deletions
+62 -104
View File
@@ -100,7 +100,7 @@ func TestCoreServiceGeneratesRunDistributionWithEncryptedSingletonKey(t *testing
}
}
func TestCoreServiceBuildsDedicatedRunOnDeploymentTarget(t *testing.T) {
func TestCoreServiceBuildsDedicatedRunInPlatformBuilder(t *testing.T) {
svc, session, instance := newDistributionTestFixture(t)
targetID := instance.RunEndpointID
instance.State = domain.ServerInstanceStateDraft
@@ -115,14 +115,14 @@ func TestCoreServiceBuildsDedicatedRunOnDeploymentTarget(t *testing.T) {
t.Fatalf("generate dedicated Run: %v", err)
}
job, err := svc.GetJob(distribution.BuildJobID)
if err != nil || job.RunEndpointID != targetID || distribution.RunEndpointID != instance.RunEndpointID {
t.Fatalf("expected build on target %q for dedicated Run %q, job=%+v distribution=%+v err=%v", targetID, instance.RunEndpointID, job, distribution, err)
if err != nil || job.RunEndpointID != platformDistributionBuilderEndpointID || distribution.RunEndpointID != instance.RunEndpointID {
t.Fatalf("expected platform build for dedicated Run %q, job=%+v distribution=%+v err=%v", instance.RunEndpointID, job, distribution, err)
}
}
func TestCoreServicePromotesLegacyRunBindingBeforeDistributionBuild(t *testing.T) {
svc, session, instance := newDistributionTestFixture(t)
builderID := instance.RunEndpointID
legacyEndpointID := instance.RunEndpointID
instance.State = domain.ServerInstanceStateFailed
if err := svc.store.ServerInstances().Update(instance); err != nil {
t.Fatalf("mark legacy server failed: %v", err)
@@ -136,36 +136,37 @@ func TestCoreServicePromotesLegacyRunBindingBeforeDistributionBuild(t *testing.T
if err != nil {
t.Fatalf("get migrated server: %v", err)
}
if migrated.DeploymentTargetID != builderID || migrated.RunEndpointID != "server-run-"+instance.ID {
if migrated.DeploymentTargetID != legacyEndpointID || migrated.RunEndpointID != "server-run-"+instance.ID {
t.Fatalf("expected legacy binding promotion, got %+v", migrated)
}
job, err := svc.GetJob(distribution.BuildJobID)
if err != nil || job.RunEndpointID != builderID || distribution.RunEndpointID != migrated.RunEndpointID {
t.Fatalf("expected build target %q and dedicated package endpoint %q, job=%+v distribution=%+v err=%v", builderID, migrated.RunEndpointID, job, distribution, err)
if err != nil || job.RunEndpointID != platformDistributionBuilderEndpointID || distribution.RunEndpointID != migrated.RunEndpointID {
t.Fatalf("expected platform build and dedicated package endpoint %q, job=%+v distribution=%+v err=%v", migrated.RunEndpointID, job, distribution, err)
}
}
func TestCoreServiceRejectsDistributionBuildForStaleRunEndpoint(t *testing.T) {
func TestCoreServiceDistributionBuildIgnoresStaleRunEndpoint(t *testing.T) {
svc, session, instance := newDistributionTestFixture(t)
svc.now = func() time.Time { return fixedTime.Add(capacityHeartbeatStaleAfter + time.Second) }
_, err := svc.GenerateRunDistributionForSession(session, domain.RunDistributionGenerateRequest{
distribution, err := svc.GenerateRunDistributionForSession(session, domain.RunDistributionGenerateRequest{
ServerInstanceID: instance.ID,
TargetOS: "linux",
TargetArch: "amd64",
IdempotencyKey: "idem-stale-run-endpoint",
})
if err == nil || !strings.Contains(err.Error(), "heartbeat is stale") {
t.Fatalf("expected stale Run endpoint rejection, got %v", err)
if err != nil {
t.Fatalf("platform build must ignore stale Run endpoint: %v", err)
}
completeDistributionBuild(t, svc, distribution, nil)
actions, err := svc.GetServerRuntimeActionsForSession(session, instance.ID)
if err != nil {
t.Fatalf("get runtime actions: %v", err)
}
for _, action := range actions.Actions {
if action.Key == "generate-run" && action.Available {
t.Fatalf("stale Run endpoint must not expose generate-run as available: %+v", action)
if action.Key == "generate-run" && !action.Available {
t.Fatalf("stale Run endpoint must not gate platform build availability: %+v", action)
}
}
}
@@ -206,14 +207,13 @@ func TestCoreServiceRuntimeActionsGateDependenciesOnPluginPermission(t *testing.
}
}
func TestCoreServiceDistributionBuildRejectsPrematureSuccessAndCanRetryAfterUpload(t *testing.T) {
t.Setenv("PLATFORM_RUN_RELEASE_URL", "https://scum.npc0.com")
func TestCoreServiceMachineEndpointCannotClaimPlatformDistributionBuild(t *testing.T) {
svc, session, instance := newDistributionTestFixture(t)
distribution, err := svc.GenerateRunDistributionForSession(session, domain.RunDistributionGenerateRequest{
ServerInstanceID: instance.ID,
TargetOS: "linux",
TargetArch: "amd64",
IdempotencyKey: "idem-premature-result",
IdempotencyKey: "idem-platform-build-ownership",
})
if err != nil {
t.Fatalf("generate run distribution: %v", err)
@@ -223,7 +223,7 @@ func TestCoreServiceDistributionBuildRejectsPrematureSuccessAndCanRetryAfterUplo
helloRequest.CapabilityReport.Capabilities = append(helloRequest.CapabilityReport.Capabilities, domain.JobCapabilityDistributionBuild)
hello, err := svc.RegisterRunHello(helloRequest)
if err != nil {
t.Fatalf("register build worker: %v", err)
t.Fatalf("register machine endpoint: %v", err)
}
claim, err := svc.ClaimRunJob(domain.RunJobClaim{
RunEndpointID: instance.RunEndpointID,
@@ -231,50 +231,21 @@ func TestCoreServiceDistributionBuildRejectsPrematureSuccessAndCanRetryAfterUplo
Capabilities: []string{domain.JobCapabilityDistributionBuild},
Capacity: domain.RunCapacity{MaxJobs: 1},
})
if err != nil || !claim.HasJob || claim.Job.JobID != distribution.BuildJobID {
t.Fatalf("claim distribution build job: claim=%+v err=%v", claim, err)
}
buildInput, err := svc.GetDistributionBuildInput(domain.DistributionBuildInputRequest{
RunEndpointID: claim.Job.RunEndpointID,
SessionToken: hello.SessionToken,
JobID: claim.Job.JobID,
LeaseToken: claim.Job.LeaseToken,
Attempt: claim.Job.Attempt,
})
if err != nil {
t.Fatalf("get distribution build input: %v", err)
t.Fatalf("claim machine-side build: %v", err)
}
if buildInput.PlatformURL != "https://scum.npc0.com" || buildInput.PackageFormat != "raw-executable" || buildInput.AuthKey == "" || len(buildInput.AuthKey) < 80 {
t.Fatalf("expected raw executable build input with release URL and long key, got %+v", buildInput)
}
result := domain.RunJobResult{
RunEndpointID: instance.RunEndpointID,
SessionToken: hello.SessionToken,
JobID: claim.Job.JobID,
LeaseToken: claim.Job.LeaseToken,
Attempt: claim.Job.Attempt,
State: domain.JobStateSucceeded,
Progress: domain.RunJobProgressReport{Percent: 100, Message: "package_finalize: done"},
ResultRef: "artifact://" + distribution.ArtifactID,
Message: "done",
}
if _, err := svc.CompleteRunJob(result); err == nil {
t.Fatal("expected premature success without uploaded artifact to be rejected")
}
stored, err := svc.GetJob(distribution.BuildJobID)
if err != nil || stored.State != domain.JobStateAccepted {
t.Fatalf("premature success must not make the job terminal, job=%+v err=%v", stored, err)
if claim.HasJob {
t.Fatalf("machine-side endpoint must not receive platform build job: %+v", claim)
}
if _, err := svc.createPlatformArtifactPayload(distribution.ArtifactID, domain.ArtifactOwnerKindJob, distribution.BuildJobID, []byte("actual compiled archive")); err != nil {
t.Fatalf("publish uploaded build output: %v", err)
distribution = completeDistributionBuild(t, svc, distribution, nil)
job, err := svc.GetJob(distribution.BuildJobID)
if err != nil || job.State != domain.JobStateSucceeded || job.RunEndpointID != platformDistributionBuilderEndpointID {
t.Fatalf("expected completed platform build job, job=%+v err=%v", job, err)
}
if _, err := svc.CompleteRunJob(result); err != nil {
t.Fatalf("retry success after artifact upload: %v", err)
}
stored, err = svc.GetJob(distribution.BuildJobID)
if err != nil || stored.State != domain.JobStateSucceeded {
t.Fatalf("expected terminal success after upload, job=%+v err=%v", stored, err)
artifact, err := svc.GetArtifact(distribution.ArtifactID)
if err != nil || artifact.OwnerKind != domain.ArtifactOwnerKindJob || artifact.OwnerID != job.ID {
t.Fatalf("expected build job-owned artifact, artifact=%+v err=%v", artifact, err)
}
}
@@ -348,6 +319,8 @@ func TestCoreServiceRunDistributionRetryReusesPartialArtifact(t *testing.T) {
func TestCoreServicePushRunUpdateReusesExistingUpdateJob(t *testing.T) {
svc, session, instance := newDistributionTestFixture(t)
payload := []byte("compiled run archive")
svc.ConfigureDistributionBuilder(staticDistributionBuilder{payload: payload})
distribution, err := svc.GenerateRunDistributionForSession(session, domain.RunDistributionGenerateRequest{
ServerInstanceID: instance.ID,
TargetOS: "linux",
@@ -357,7 +330,7 @@ func TestCoreServicePushRunUpdateReusesExistingUpdateJob(t *testing.T) {
if err != nil {
t.Fatalf("generate run distribution: %v", err)
}
distribution = completeDistributionBuild(t, svc, distribution, []byte("compiled run archive"))
distribution = completeDistributionBuild(t, svc, distribution, payload)
request := domain.RunUpdateRequest{
ServerInstanceID: instance.ID,
@@ -624,7 +597,6 @@ func newDistributionTestFixture(t *testing.T) (*CoreService, string, domain.Serv
t.Fatalf("update plugin fixture: %v", err)
}
endpoint.Capabilities = append(endpoint.Capabilities,
domain.JobCapabilityDistributionBuild,
domain.JobCapabilityRunSelfUpdate,
domain.JobCapabilityDependenciesCheck,
domain.JobCapabilityDependenciesInstall,
@@ -704,58 +676,44 @@ func readGeneratedPackageConfig(t *testing.T, svc *CoreService, session string,
return generatedPackageConfig{}
}
func completeDistributionBuild(t *testing.T, svc *CoreService, distribution domain.RunDistribution, payload []byte) domain.RunDistribution {
func completeDistributionBuild(t *testing.T, svc *CoreService, distribution domain.RunDistribution, _ []byte) domain.RunDistribution {
t.Helper()
artifact, err := svc.createPlatformArtifactPayload(distribution.ArtifactID, domain.ArtifactOwnerKindJob, distribution.BuildJobID, payload)
if err != nil {
t.Fatalf("publish run build artifact: %v", err)
deadline := time.Now().Add(time.Second)
for time.Now().Before(deadline) {
updated, err := svc.store.RunDistributions().Get(distribution.ID)
if err != nil {
t.Fatalf("get run distribution: %v", err)
}
if updated.Status == domain.DistributionStatusAvailable {
return updated
}
if updated.Status == domain.DistributionStatusFailed {
t.Fatalf("platform run build failed: %+v", updated)
}
time.Sleep(time.Millisecond)
}
job, err := svc.GetJob(distribution.BuildJobID)
if err != nil {
t.Fatalf("get run build job: %v", err)
}
job.State = domain.JobStateSucceeded
job.Progress = domain.JobProgress{Percent: 100, Message: "package_finalize: build artifact available"}
job.ResultRef = "artifact://" + artifact.ID
job.UpdatedAt = svc.now()
if err := svc.store.Jobs().Update(job); err != nil {
t.Fatalf("update run build job: %v", err)
}
if err := svc.projectDistributionBuildResult(job, svc.now()); err != nil {
t.Fatalf("project run build: %v", err)
}
updated, err := svc.store.RunDistributions().Get(distribution.ID)
if err != nil {
t.Fatalf("get completed run distribution: %v", err)
}
return updated
t.Fatalf("platform run build did not complete")
return domain.RunDistribution{}
}
func completeClientDistributionBuild(t *testing.T, svc *CoreService, distribution domain.ClientManagerDistribution, payload []byte) domain.ClientManagerDistribution {
func completeClientDistributionBuild(t *testing.T, svc *CoreService, distribution domain.ClientManagerDistribution, _ []byte) domain.ClientManagerDistribution {
t.Helper()
artifact, err := svc.createPlatformArtifactPayload(distribution.ArtifactID, domain.ArtifactOwnerKindJob, distribution.BuildJobID, payload)
if err != nil {
t.Fatalf("publish client build artifact: %v", err)
deadline := time.Now().Add(time.Second)
for time.Now().Before(deadline) {
updated, err := svc.store.ClientManagerDistributions().Get(distribution.ID)
if err != nil {
t.Fatalf("get client distribution: %v", err)
}
if updated.Status == domain.DistributionStatusAvailable {
return updated
}
if updated.Status == domain.DistributionStatusFailed {
t.Fatalf("platform client build failed: %+v", updated)
}
time.Sleep(time.Millisecond)
}
job, err := svc.GetJob(distribution.BuildJobID)
if err != nil {
t.Fatalf("get client build job: %v", err)
}
job.State = domain.JobStateSucceeded
job.Progress = domain.JobProgress{Percent: 100, Message: "package_finalize: build artifact available"}
job.ResultRef = "artifact://" + artifact.ID
job.UpdatedAt = svc.now()
if err := svc.store.Jobs().Update(job); err != nil {
t.Fatalf("update client build job: %v", err)
}
if err := svc.projectDistributionBuildResult(job, svc.now()); err != nil {
t.Fatalf("project client build: %v", err)
}
updated, err := svc.store.ClientManagerDistributions().Get(distribution.ID)
if err != nil {
t.Fatalf("get completed client distribution: %v", err)
}
return updated
t.Fatalf("platform client build did not complete")
return domain.ClientManagerDistribution{}
}
func TestCoreServiceDeniesRunDistributionWithoutPluginDeclaration(t *testing.T) {