Restore durable log ingest and typed plugin projections

This commit is contained in:
npc0-hue
2026-09-02 10:20:30 +08:00
parent 40ac46ba17
commit 6018d8f0fc
61 changed files with 809 additions and 3157 deletions
@@ -188,60 +188,29 @@ func (svc *CoreService) platformDistributionBuildInput(job domain.Job) (domain.D
if err != nil {
return domain.DistributionBuildInput{}, err
}
profile, err := svc.clientManagerDistributionBuildProfile(distribution)
if err != nil {
return domain.DistributionBuildInput{}, err
}
return domain.DistributionBuildInput{
JobID: job.ID,
ComponentKind: domain.DistributionComponentClientManager,
ServerInstanceID: distribution.ServerInstanceID,
PluginID: distribution.PluginID,
RunEndpointID: job.RunEndpointID,
ProfileKey: distribution.ProfileKey,
TargetOS: distribution.TargetOS,
TargetArch: distribution.TargetArch,
PlatformURL: runReleasePlatformURL(),
PackageFormat: packageFormatForTarget(distribution.TargetOS),
RepositoryURL: distribution.RepositoryURL,
SourceRevision: distribution.SourceRevision,
WorkspaceRef: profile.WorkspaceRef,
EntryRef: profile.EntryRef,
ConfigTemplateRef: clientManagerBuildConfigTemplateRef(profile),
ArtifactID: distribution.ArtifactID,
OutputFilename: clientManagerOutputName(distribution.ProfileKey, distribution.TargetOS),
SecretRef: distribution.SecretRef,
KeyGeneration: distribution.KeyGeneration,
AuthKey: plainKey,
JobID: job.ID,
ComponentKind: domain.DistributionComponentClientManager,
ServerInstanceID: distribution.ServerInstanceID,
PluginID: distribution.PluginID,
RunEndpointID: job.RunEndpointID,
ProfileKey: distribution.ProfileKey,
TargetOS: distribution.TargetOS,
TargetArch: distribution.TargetArch,
PlatformURL: runReleasePlatformURL(),
PackageFormat: packageFormatForTarget(distribution.TargetOS),
RepositoryURL: distribution.RepositoryURL,
SourceRevision: distribution.SourceRevision,
ArtifactID: distribution.ArtifactID,
OutputFilename: clientManagerOutputName(distribution.ProfileKey, distribution.TargetOS),
SecretRef: distribution.SecretRef,
KeyGeneration: distribution.KeyGeneration,
AuthKey: plainKey,
}, nil
}
return domain.DistributionBuildInput{}, repo.ErrNotFound
}
func (svc *CoreService) clientManagerDistributionBuildProfile(distribution domain.ClientManagerDistribution) (domain.RuntimeClientManagerProfile, error) {
plugin, err := svc.store.GamePlugins().Get(distribution.PluginID)
if err != nil {
return domain.RuntimeClientManagerProfile{}, err
}
profile, err := findRuntimeClientManagerProfile(plugin, distribution.ProfileKey)
if err != nil {
return domain.RuntimeClientManagerProfile{}, err
}
if profile.RepositoryURL != distribution.RepositoryURL || !clientManagerProfileAllowsRevision(profile, distribution.SourceRevision) || !clientManagerProfileSupportsTarget(profile, distribution.TargetOS, distribution.TargetArch) {
return domain.RuntimeClientManagerProfile{}, validationError("client-manager build no longer matches the declared profile")
}
return profile, nil
}
func clientManagerBuildConfigTemplateRef(profile domain.RuntimeClientManagerProfile) string {
for _, candidate := range profile.ConfigTemplates {
if candidate.OutputRef == "config.yaml" && strings.TrimSpace(candidate.TemplateRef) != "" {
return candidate.TemplateRef
}
}
return "config.yaml.example"
}
type runDistributionPackageContext struct {
profileKey string
workspaceSeed string
@@ -277,39 +246,8 @@ func (svc *CoreService) runDistributionPackageContext(distribution domain.RunDis
return runDistributionPackageContext{profileKey: profileKey, workspaceSeed: seed, autonomousLifecycle: plan}, nil
}
func defaultRunDistributionProfileKey(plugin domain.GamePlugin, profileKey string) string {
profileKey = strings.TrimSpace(profileKey)
if profileKey != "" {
if profile, ok := runtimeLifecycleProfileForKey(plugin.RuntimeProfiles, profileKey); ok {
return profile.Key
}
return profileKey
}
return firstRuntimeLifecycleProfileKey(plugin)
}
func lifecycleDefaultProfileKey(instance domain.ServerInstance, plugin domain.GamePlugin, profileKey string) string {
profileKey = strings.TrimSpace(profileKey)
if profileKey != "" {
if profile, ok := runtimeLifecycleProfileForKey(plugin.RuntimeProfiles, profileKey); ok {
return profile.Key
}
return profileKey
}
if instance.RunEndpointID != "" && instance.RunEndpointID == generatedRunEndpointID(instance.ID) {
return firstRuntimeLifecycleProfileKey(plugin)
}
return ""
}
func firstRuntimeLifecycleProfileKey(plugin domain.GamePlugin) string {
if len(plugin.RuntimeProfiles.LifecycleProfiles) == 0 {
return ""
}
return plugin.RuntimeProfiles.LifecycleProfiles[0].Key
}
func runAutonomousLifecyclePlan(distribution domain.RunDistribution, instance domain.ServerInstance, plugin domain.GamePlugin, profileKey string, bindings map[string]string) (*domain.RunAutonomousLifecyclePlan, error) {
privateBindings := autonomousDataTargetBindings(bindings, instance, plugin.RuntimeProfiles.DataTargets)
plan := &domain.RunAutonomousLifecyclePlan{
SchemaVersion: "1",
ServerInstanceID: instance.ID,
@@ -321,8 +259,8 @@ func runAutonomousLifecyclePlan(distribution domain.RunDistribution, instance do
TargetArch: distribution.TargetArch,
TargetRelease: distribution.ID,
DeploymentRevision: instance.Deployment.Revision,
RuntimeBindings: domain.CopyStringMap(bindings),
Deployment: autonomousDeploymentFromDefinition(instance.Deployment, profileKey, bindings),
RuntimeBindings: privateBindings,
Deployment: autonomousDeploymentFromDefinition(instance.Deployment, profileKey, privateBindings),
}
profile, hasProfile := runtimeLifecycleProfileForKey(plugin.RuntimeProfiles, profileKey)
for _, action := range []domain.ServerLifecycleAction{domain.ServerLifecycleActionCreate, domain.ServerLifecycleActionStart, domain.ServerLifecycleActionStop, domain.ServerLifecycleActionStatus} {
@@ -351,6 +289,11 @@ func runAutonomousLifecyclePlan(distribution domain.RunDistribution, instance do
plan.LogSources = append(plan.LogSources, autonomousLogSource(source))
}
}
for _, target := range plugin.RuntimeProfiles.DataTargets {
if runtimePlatformsContain(target.Platforms, distribution.TargetOS) {
plan.DataTargets = append(plan.DataTargets, autonomousDataTarget(target))
}
}
if hasProfile && len(profile.DLLExtensionRefs) > 0 {
endpoint := domain.RunEndpoint{ID: distribution.RunEndpointID, Platform: distribution.TargetOS, Architecture: distribution.TargetArch}
extensions, err := lifecycleDLLExtensionPlans(plugin.RuntimeProfiles, profile, endpoint)
@@ -364,6 +307,19 @@ func runAutonomousLifecyclePlan(distribution domain.RunDistribution, instance do
return domain.CopyRunAutonomousLifecyclePlanPtr(plan), nil
}
func autonomousDataTargetBindings(bindings map[string]string, instance domain.ServerInstance, targets []domain.RuntimeDataTarget) map[string]string {
result := domain.CopyStringMap(bindings)
if result == nil {
result = map[string]string{}
}
for _, target := range targets {
if result[target.SourceRootKey] == "" && target.SourceRootKey == "server-root" && instance.Deployment.ServerRoot != "" {
result[target.SourceRootKey] = instance.Deployment.ServerRoot
}
}
return result
}
func autonomousLifecycleAction(plugin domain.GamePlugin, profile domain.RuntimeLifecycleProfile, hasProfile bool, action domain.ServerLifecycleAction) domain.RunAutonomousLifecycleAction {
targetKey := ""
if hasProfile {
@@ -409,6 +365,10 @@ func autonomousDLLExtension(extension domain.RuntimeDLLExtensionPlan) domain.Run
return domain.RunAutonomousDLLExtension{Key: extension.Key, Version: extension.Version, ReleaseURL: extension.ReleaseURL, Checksum: extension.Checksum, SizeBytes: extension.SizeBytes, TargetKey: extension.TargetKey, ModKey: extension.ModKey, DLLRef: extension.DLLRef, SCUMExecutableChecksum: extension.SCUMExecutableChecksum, UE4SSABI: extension.UE4SSABI, RCONPort: extension.RCONPort}
}
func autonomousDataTarget(target domain.RuntimeDataTarget) domain.RunAutonomousDataTarget {
return domain.RunAutonomousDataTarget{Key: target.Key, Kind: target.Kind, TransportKey: target.TransportKey, SourceRootKey: target.SourceRootKey, SourcePath: target.SourcePath, WorkspaceKey: target.WorkspaceKey, RefreshPolicy: target.RefreshPolicy, MaxBytes: target.MaxBytes, Platforms: domain.CopyStringSlice(target.Platforms)}
}
func autonomousDeploymentFromDefinition(definition domain.ServerDeploymentDefinition, profileKey string, bindings map[string]string) *domain.RunAutonomousDeployment {
if definition.Mode == "" {
return nil