This commit is contained in:
@@ -5,6 +5,7 @@ import { db } from '../db/index.js';
|
||||
import type { NativeInstance } from '../backends/native.js';
|
||||
import type { LaunchRecipePlan } from '../fleet/launch-recipes.js';
|
||||
import type { PlacementOptions } from '../hosts/capacity.js';
|
||||
import type { ModerationConfiguration } from '../moderation/configuration.js';
|
||||
|
||||
export interface NativeLaunchInput {
|
||||
game: 't5' | 't6';
|
||||
@@ -14,6 +15,7 @@ export interface NativeLaunchInput {
|
||||
serverKey: string;
|
||||
rconPassword: string;
|
||||
bankToken: string;
|
||||
moderation?: ModerationConfiguration;
|
||||
rebuild: boolean;
|
||||
safetyOverride?: boolean;
|
||||
unhealthyRecovery?: boolean;
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import { sha256Buffer } from '../fleet/files.js';
|
||||
import type { NativeLaunchInput } from './launch-preparation.js';
|
||||
import { choosePlacement, type PlacementOptions } from '../hosts/capacity.js';
|
||||
import { isPrivateConfigPath } from '../fleet/private-config.js';
|
||||
|
||||
parentPort!.on(
|
||||
'message',
|
||||
@@ -79,12 +80,16 @@ parentPort!.on(
|
||||
'electric_cherry_bank.cfg'
|
||||
);
|
||||
if (
|
||||
recipe.files.some(
|
||||
(file) =>
|
||||
posix.join(relDestForKind(file.kind, recipe.game), file.path) === bankPath
|
||||
recipe.files.some((file) =>
|
||||
isPrivateConfigPath(
|
||||
posix.join(relDestForKind(file.kind, recipe.game), file.path),
|
||||
recipe.game
|
||||
)
|
||||
)
|
||||
)
|
||||
throw new Error('electric_cherry_bank.cfg is reserved for instance credentials.');
|
||||
throw new Error(
|
||||
'Private configuration files are reserved for instance credentials.'
|
||||
);
|
||||
const payload = {
|
||||
...(input.activationId ? { stageId: input.activationId } : {}),
|
||||
name: instance.server,
|
||||
@@ -95,13 +100,14 @@ parentPort!.on(
|
||||
bindAddress: instance.bind_address,
|
||||
rconPassword: password,
|
||||
bank: { token: input.bankToken, configPath: bankPath },
|
||||
...(input.moderation ? { moderation: input.moderation } : {}),
|
||||
env: {
|
||||
PLUTAINER_SERVER_NAME: instance.server,
|
||||
PLUTAINER_PORT: String(instance.port),
|
||||
PLUTAINER_GAME: recipe.game === 't6' ? 't6zm' : 't5sp',
|
||||
PLUTAINER_CONFIG_FILE: recipe.configFile,
|
||||
PLUTAINER_MAP_ROTATE: 'false',
|
||||
PLUTAINER_EXTRA_ARGS: `+exec electric_cherry_map.cfg +exec electric_cherry_bank.cfg +set tws_server_id ${instance.server} +map_rotate`,
|
||||
PLUTAINER_EXTRA_ARGS: `+exec electric_cherry_map.cfg +exec electric_cherry_bank.cfg${input.moderation ? ' +exec electric_cherry_moderation.cfg' : ''} +set tws_server_id ${instance.server} +map_rotate`,
|
||||
PLUTO_SERVER_KEY: input.serverKey
|
||||
},
|
||||
files: recipe.files.map((file) => ({
|
||||
|
||||
@@ -14,6 +14,11 @@ import { setImmediate as yieldToController } from 'node:timers/promises';
|
||||
import { choosePlacement, availableHostPort, type PlacementOptions } from '../hosts/capacity.js';
|
||||
import { randomBytes, randomUUID } from 'node:crypto';
|
||||
import { bankCredential } from '../auth/bank.js';
|
||||
import { moderationCredential } from '../moderation/credentials.js';
|
||||
import {
|
||||
moderationConfiguration,
|
||||
moderationConfigureCapability
|
||||
} from '../moderation/configuration.js';
|
||||
import { posix } from 'node:path';
|
||||
import { db } from '../db/index.js';
|
||||
import { unsettledCancelledCommand } from './cancellation.js';
|
||||
@@ -1996,6 +2001,7 @@ async function processJob(job: Job, eligibility: (job: Job) => string | null) {
|
||||
let prepared: PreparedNativeLaunch | undefined;
|
||||
let preparedKey: string | null = null;
|
||||
let preparedPassword: string | undefined;
|
||||
let preparedModeration = false;
|
||||
try {
|
||||
if (job.action === 'create' || job.action === 'rebuild') {
|
||||
if (
|
||||
@@ -2027,6 +2033,11 @@ async function processJob(job: Job, eligibility: (job: Job) => string | null) {
|
||||
(files.length ? runtimePasswordForServer(job.server) : undefined) ||
|
||||
config.rcon.password ||
|
||||
randomBytes(32).toString('hex');
|
||||
const rebuilding = job.action === 'rebuild' && !migration;
|
||||
const generation = instance.generation + (rebuilding ? 1 : 0);
|
||||
preparedModeration =
|
||||
!!config.publicUrl &&
|
||||
!!host.inventory.capabilities?.includes(moderationConfigureCapability);
|
||||
prepared = await prepareNativeLaunch({
|
||||
game: prior.game,
|
||||
mapSlug: prior.mapSlug,
|
||||
@@ -2035,7 +2046,15 @@ async function processJob(job: Job, eligibility: (job: Job) => string | null) {
|
||||
serverKey: preparedKey,
|
||||
rconPassword: preparedPassword,
|
||||
bankToken: bankCredential(instance.id),
|
||||
rebuild: job.action === 'rebuild' && !migration,
|
||||
...(preparedModeration
|
||||
? {
|
||||
moderation: moderationConfiguration(
|
||||
moderationCredential(instance.id, generation),
|
||||
generation
|
||||
)
|
||||
}
|
||||
: {}),
|
||||
rebuild: rebuilding,
|
||||
safetyOverride: overrideSafety,
|
||||
unhealthyRecovery:
|
||||
!files.length &&
|
||||
@@ -2124,6 +2143,16 @@ async function processJob(job: Job, eligibility: (job: Job) => string | null) {
|
||||
return;
|
||||
}
|
||||
if (prepared) {
|
||||
if (
|
||||
preparedModeration &&
|
||||
!liveHost.inventory.capabilities?.includes(moderationConfigureCapability)
|
||||
) {
|
||||
hold(
|
||||
job,
|
||||
'Host moderation support changed during preparation; preparing the current selection next pass.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Compare the same runtime config bytes while rechecking current promoted files.
|
||||
const expected =
|
||||
migration && isRebalanceMigration(job.id)
|
||||
|
||||
Reference in New Issue
Block a user