querysub 0.518.0 → 0.520.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/-f-node-discovery/LatencyTracking.ts +116 -0
- package/src/-f-node-discovery/NodeDiscovery.ts +9 -1
- package/src/-f-node-discovery/TrafficTracking.ts +159 -0
- package/src/0-path-value-core/PathRouter.ts +53 -5
- package/src/0-path-value-core/PathValueController.ts +3 -0
- package/src/0-path-value-core/pathValueArchives.ts +2 -1
- package/src/0-path-value-core/startupAuthority.ts +2 -2
- package/src/3-path-functions/PathFunctionRunner.ts +2 -0
- package/src/4-querysub/FunctionRunnerTracking.ts +5 -8
- package/src/4-querysub/Querysub.ts +4 -2
- package/src/4-querysub/QuerysubController.ts +2 -0
- package/src/4-querysub/querysubPrediction.ts +12 -11
- package/src/deployManager/components/MachineDetailPage.tsx +2 -2
- package/src/deployManager/components/ServiceDetailPage.tsx +60 -9
- package/src/deployManager/components/ServicesListPage.tsx +2 -2
- package/src/deployManager/components/Tools.tsx +6 -10
- package/src/deployManager/machineApplyMainCode.ts +13 -11
- package/src/deployManager/machineSchema.ts +50 -6
- package/src/diagnostics/managementPages.tsx +3 -3
- package/src/diagnostics/misc-pages/RoutingTablePage.tsx +324 -0
- package/src/diagnostics/pathAuditer.ts +75 -40
- package/src/library-components/LatencyGraph.tsx +1450 -0
- package/src/src.d.ts +3 -1
- package/src/diagnostics/misc-pages/AuthoritySpecPage.tsx +0 -146
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SocketFunction } from "socket-function/SocketFunction";
|
|
2
2
|
import { qreact } from "../../4-dom/qreact";
|
|
3
|
-
import { DEFAULT_OVERLAP_TIME, MACHINE_RESYNC_INTERVAL, MachineServiceController, ServiceConfig, ServiceParameters, getLiveServiceParameters, stripReleaseState } from "../machineSchema";
|
|
3
|
+
import { DEFAULT_OVERLAP_TIME, MACHINE_RESYNC_INTERVAL, MachineServiceController, ServiceConfig, ServiceParameters, applyCommandTemplate, getCommandTemplateVariables, getLiveServiceParameters, getMachineTargets, setMachineTargets, stripReleaseState } from "../machineSchema";
|
|
4
4
|
import { css } from "typesafecss";
|
|
5
5
|
import { t } from "../../2-proxy/schema2";
|
|
6
6
|
import { Querysub } from "../../4-querysub/Querysub";
|
|
@@ -165,6 +165,47 @@ export class ServiceDetailPage extends qreact.Component {
|
|
|
165
165
|
});
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
// Shows the `${name}` variables found in the command, and lets each picked machine entry set its own values for them (which converts machineIds to the { machineId, variables } form).
|
|
169
|
+
private renderTemplateVariables(config: ServiceConfig) {
|
|
170
|
+
let templateVariables = getCommandTemplateVariables(config.parameters.command);
|
|
171
|
+
let targets = getMachineTargets(config.parameters);
|
|
172
|
+
let anyVariablesSet = targets.some(target => Object.keys(target.variables).length > 0);
|
|
173
|
+
if (templateVariables.length === 0 && !anyVariablesSet) return undefined;
|
|
174
|
+
|
|
175
|
+
const setVariable = (entryIndex: number, name: string, value: string) => {
|
|
176
|
+
let updated = deepCloneJSON(config);
|
|
177
|
+
let updatedTargets = getMachineTargets(updated.parameters);
|
|
178
|
+
if (value) {
|
|
179
|
+
updatedTargets[entryIndex].variables[name] = value;
|
|
180
|
+
} else {
|
|
181
|
+
delete updatedTargets[entryIndex].variables[name];
|
|
182
|
+
}
|
|
183
|
+
setMachineTargets(updated.parameters, updatedTargets);
|
|
184
|
+
this.updateEditorState(updated);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
let dupIndexes = new Map<string, number>();
|
|
188
|
+
return <div className={css.vbox(10).fillWidth.pad2(12).bord2(0, 0, 20)}>
|
|
189
|
+
{targets.map((target, entryIndex) => {
|
|
190
|
+
let index = dupIndexes.get(target.machineId) || 0;
|
|
191
|
+
dupIndexes.set(target.machineId, index + 1);
|
|
192
|
+
// Variables set on the entry but no longer in the command are still shown (flagged), so stale values can be cleared.
|
|
193
|
+
let variableNames = [...new Set([...templateVariables, ...Object.keys(target.variables)])];
|
|
194
|
+
return <div className={css.hbox(12).wrap.alignItems("center").fillWidth}>
|
|
195
|
+
<div className={css.boldStyle}>{getScreenName({ serviceKey: config.parameters.key, index })} ({target.machineId})</div>
|
|
196
|
+
{variableNames.map(name => {
|
|
197
|
+
let inCommand = templateVariables.includes(name);
|
|
198
|
+
return <InputLabel
|
|
199
|
+
label={inCommand && name || `${name} (not in command)`}
|
|
200
|
+
value={target.variables[name] || ""}
|
|
201
|
+
onChangeValue={value => setVariable(entryIndex, name, value)}
|
|
202
|
+
/>;
|
|
203
|
+
})}
|
|
204
|
+
</div>;
|
|
205
|
+
})}
|
|
206
|
+
</div>;
|
|
207
|
+
}
|
|
208
|
+
|
|
168
209
|
render() {
|
|
169
210
|
const selectedServiceId = selectedServiceIdParam.value;
|
|
170
211
|
if (!selectedServiceId) return <div>No service selected</div>;
|
|
@@ -199,8 +240,9 @@ export class ServiceDetailPage extends qreact.Component {
|
|
|
199
240
|
|
|
200
241
|
// Sort machines by status and heartbeat
|
|
201
242
|
let nextIndexes = new Map<string, number>();
|
|
202
|
-
let
|
|
203
|
-
let
|
|
243
|
+
let machineTargets = getMachineTargets(config.parameters);
|
|
244
|
+
let machineIds = machineTargets.map(x => x.machineId);
|
|
245
|
+
let machineStatuses = machineTargets.map(({ machineId, variables }) => {
|
|
204
246
|
let index = nextIndexes.get(machineId) || 0;
|
|
205
247
|
nextIndexes.set(machineId, index + 1);
|
|
206
248
|
let machineInfo = controller.getMachineInfo(machineId);
|
|
@@ -212,6 +254,7 @@ export class ServiceDetailPage extends qreact.Component {
|
|
|
212
254
|
|
|
213
255
|
return {
|
|
214
256
|
machineId,
|
|
257
|
+
variables,
|
|
215
258
|
machineInfo,
|
|
216
259
|
serviceInfo,
|
|
217
260
|
isMachineDead,
|
|
@@ -297,17 +340,17 @@ export class ServiceDetailPage extends qreact.Component {
|
|
|
297
340
|
picked={machineIds}
|
|
298
341
|
addPicked={machineId => {
|
|
299
342
|
let updated = deepCloneJSON(config);
|
|
300
|
-
updated.parameters
|
|
343
|
+
setMachineTargets(updated.parameters, [...getMachineTargets(updated.parameters), { machineId, variables: {} }]);
|
|
301
344
|
this.updateEditorState(updated);
|
|
302
345
|
}}
|
|
303
346
|
removePicked={machineId => {
|
|
304
347
|
let updated = deepCloneJSON(config);
|
|
305
|
-
let
|
|
306
|
-
let index =
|
|
348
|
+
let targets = getMachineTargets(updated.parameters);
|
|
349
|
+
let index = targets.findIndex(x => x.machineId === machineId);
|
|
307
350
|
if (index !== -1) {
|
|
308
|
-
|
|
351
|
+
targets.splice(index, 1);
|
|
309
352
|
}
|
|
310
|
-
updated.parameters
|
|
353
|
+
setMachineTargets(updated.parameters, targets);
|
|
311
354
|
this.updateEditorState(updated);
|
|
312
355
|
}}
|
|
313
356
|
/>
|
|
@@ -325,7 +368,7 @@ export class ServiceDetailPage extends qreact.Component {
|
|
|
325
368
|
{config.parameters.deploy && <div className={css.vbox(8).fillWidth}>
|
|
326
369
|
<h3>Deployed Machines ({machineIds.length})</h3>
|
|
327
370
|
<div className={css.vbox(4).fillWidth}>
|
|
328
|
-
{machineStatuses.map(({ machineId, machineInfo, serviceInfo, isMachineDead, hasError, isDisabled, index }) => {
|
|
371
|
+
{machineStatuses.map(({ machineId, variables, machineInfo, serviceInfo, isMachineDead, hasError, isDisabled, index }) => {
|
|
329
372
|
if (!machineInfo) return <div key={machineId}>Loading {machineId}...</div>;
|
|
330
373
|
|
|
331
374
|
let backgroundColor = css.hsl(0, 0, 100); // Default: white
|
|
@@ -358,6 +401,12 @@ export class ServiceDetailPage extends qreact.Component {
|
|
|
358
401
|
<div>
|
|
359
402
|
{serviceInfo?.nodeId || machineId} ({machineInfo.info["getExternalIP"]})
|
|
360
403
|
</div>
|
|
404
|
+
{Object.keys(variables).length > 0 && <div
|
|
405
|
+
className={css.fontFamily("monospace").colorhsl(210, 60, 35)}
|
|
406
|
+
title={`Runs: ${applyCommandTemplate(config.parameters.command, variables)}`}
|
|
407
|
+
>
|
|
408
|
+
{Object.entries(variables).map(([name, value]) => `${name}=${value}`).join(" ")}
|
|
409
|
+
</div>}
|
|
361
410
|
</div>
|
|
362
411
|
{isDisabled && (
|
|
363
412
|
<div className={css.colorhsl(0, 0, 30)}>
|
|
@@ -708,6 +757,8 @@ export class ServiceDetailPage extends qreact.Component {
|
|
|
708
757
|
</div>;
|
|
709
758
|
})()}
|
|
710
759
|
|
|
760
|
+
{this.renderTemplateVariables(config)}
|
|
761
|
+
|
|
711
762
|
{this.state.saveError && (
|
|
712
763
|
<div className={css.pad2(12).bord2(0, 80, 50).hsl(0, 80, 95).colorhsl(0, 80, 30)}>
|
|
713
764
|
⚠️ Error: {this.state.saveError}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SocketFunction } from "socket-function/SocketFunction";
|
|
2
2
|
import { qreact } from "../../4-dom/qreact";
|
|
3
|
-
import { DEFAULT_OVERLAP_TIME, MachineServiceController, ServiceConfig, getLiveServiceParameters } from "../machineSchema";
|
|
3
|
+
import { DEFAULT_OVERLAP_TIME, MachineServiceController, ServiceConfig, getLiveServiceParameters, getMachineIdList } from "../machineSchema";
|
|
4
4
|
import { css } from "typesafecss";
|
|
5
5
|
import { t } from "../../2-proxy/schema2";
|
|
6
6
|
import { Querysub } from "../../4-querysub/Querysub";
|
|
@@ -82,7 +82,7 @@ export class ServicesListPage extends qreact.Component {
|
|
|
82
82
|
{services.map(([serviceId, config]) => {
|
|
83
83
|
if (!config) return <div key={serviceId}>Config is broken? Missing value for service? Is the file corrupted?</div>;
|
|
84
84
|
|
|
85
|
-
let machineIds = config.parameters
|
|
85
|
+
let machineIds = getMachineIdList(config.parameters);
|
|
86
86
|
let disabledMachines = machineIds.filter(machineId => {
|
|
87
87
|
let machineConfig = getMachineConfig(machineId);
|
|
88
88
|
return machineConfig?.disabled;
|
|
@@ -4,7 +4,7 @@ import { qreact } from "../../4-dom/qreact";
|
|
|
4
4
|
import { css } from "typesafecss";
|
|
5
5
|
import { t } from "../../2-proxy/schema2";
|
|
6
6
|
import { Querysub } from "../../4-querysub/Querysub";
|
|
7
|
-
import { MachineServiceController, ServiceConfig } from "../machineSchema";
|
|
7
|
+
import { MachineServiceController, ServiceConfig, getMachineTargets, setMachineTargets } from "../machineSchema";
|
|
8
8
|
import { MachinePicker } from "./MachinePicker";
|
|
9
9
|
import { isDefined } from "../../misc";
|
|
10
10
|
|
|
@@ -125,15 +125,11 @@ class RenameMachine extends qreact.Component {
|
|
|
125
125
|
);
|
|
126
126
|
let toUpdate: ServiceConfig[] = [];
|
|
127
127
|
for (let config of configs.filter(isDefined)) {
|
|
128
|
-
let
|
|
129
|
-
if (!
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
...config.parameters,
|
|
134
|
-
machineIds: machineIds.map(m => m === from ? to : m),
|
|
135
|
-
},
|
|
136
|
-
});
|
|
128
|
+
let targets = getMachineTargets(config.parameters);
|
|
129
|
+
if (!targets.some(target => target.machineId === from)) continue;
|
|
130
|
+
let updated = { ...config, parameters: { ...config.parameters } };
|
|
131
|
+
setMachineTargets(updated.parameters, targets.map(target => target.machineId === from ? { ...target, machineId: to } : target));
|
|
132
|
+
toUpdate.push(updated);
|
|
137
133
|
}
|
|
138
134
|
if (toUpdate.length === 0) {
|
|
139
135
|
Querysub.commit(() => {
|
|
@@ -4,7 +4,7 @@ import { measureWrap } from "socket-function/src/profiling/measure";
|
|
|
4
4
|
import { getOwnMachineId } from "sliftutils/misc/https/certs";
|
|
5
5
|
import { forceRemoveNode, getOurNodeId, getOurNodeIdAssert } from "../-f-node-discovery/NodeDiscovery";
|
|
6
6
|
import { Querysub } from "../4-querysub/Querysub";
|
|
7
|
-
import { MACHINE_RESYNC_INTERVAL, MachineServiceControllerBase, MachineInfo, ServiceConfig, ServiceParameters, SERVICE_FOLDER, machineInfos, SERVICE_NODE_FILE_NAME, getEffectiveServiceConfigs, getLiveServiceParameters, restartParametersKey, recordLaunch, DEFAULT_OVERLAP_TIME } from "./machineSchema";
|
|
7
|
+
import { MACHINE_RESYNC_INTERVAL, MachineServiceControllerBase, MachineInfo, ServiceConfig, ServiceParameters, SERVICE_FOLDER, machineInfos, SERVICE_NODE_FILE_NAME, getEffectiveServiceConfigs, getLiveServiceParameters, restartParametersKey, recordLaunch, DEFAULT_OVERLAP_TIME, getMachineTargets, getMachineIdList, applyCommandTemplate } from "./machineSchema";
|
|
8
8
|
import { runPromise } from "../functional/runCommand";
|
|
9
9
|
import { getExternalIP } from "socket-function/src/networking";
|
|
10
10
|
import { errorToUndefined, errorToUndefinedSilent } from "../errors";
|
|
@@ -656,7 +656,7 @@ const resyncServicesBase = runInSerial(measureWrap(async function resyncServices
|
|
|
656
656
|
let machineId = getOwnMachineId(getDomain());
|
|
657
657
|
let allConfigs = await getEffectiveServiceConfigs();
|
|
658
658
|
let relevantConfigs = allConfigs
|
|
659
|
-
.filter(config => (getLiveServiceParameters(config)
|
|
659
|
+
.filter(config => getMachineIdList(getLiveServiceParameters(config)).includes(machineId) || getMachineIdList(config.parameters).includes(machineId))
|
|
660
660
|
.filter(x => getLiveServiceParameters(x).deploy || x.parameters.deploy);
|
|
661
661
|
|
|
662
662
|
let machineInfo = await getLiveMachineInfo();
|
|
@@ -676,11 +676,11 @@ const resyncServicesBase = runInSerial(measureWrap(async function resyncServices
|
|
|
676
676
|
console.log(magenta(`Release in flight for ${record.serviceId} (${record.parameters.key}): new instances start at ${new Date(releaseTime - overlapTime).toLocaleString()}${Date.now() >= releaseTime - overlapTime && " (overlap running)" || ""}, old instances shut down at ${new Date(releaseTime).toLocaleString()}`));
|
|
677
677
|
// The release-overlap instances follow the NEW parameters' machineIds, so machines that are only in the new parameters still start their instances at overlap time (their canonical instances start once the release passes)
|
|
678
678
|
if (Date.now() >= releaseTime - overlapTime && record.parameters.deploy) {
|
|
679
|
-
let
|
|
680
|
-
for (let i = 0; i <
|
|
679
|
+
let nextTargets = getMachineTargets(record.parameters).filter(target => target.machineId === machineId);
|
|
680
|
+
for (let i = 0; i < nextTargets.length; i++) {
|
|
681
681
|
try {
|
|
682
682
|
await ensureNextInstance({
|
|
683
|
-
next: record.parameters,
|
|
683
|
+
next: { ...record.parameters, command: applyCommandTemplate(record.parameters.command, nextTargets[i].variables) },
|
|
684
684
|
index: i,
|
|
685
685
|
screenNamesUsed,
|
|
686
686
|
screenStateMap,
|
|
@@ -692,8 +692,10 @@ const resyncServicesBase = runInSerial(measureWrap(async function resyncServices
|
|
|
692
692
|
}
|
|
693
693
|
}
|
|
694
694
|
if (!config.parameters.deploy) continue;
|
|
695
|
-
let
|
|
696
|
-
for (let i = 0; i <
|
|
695
|
+
let matchedTargets = getMachineTargets(config.parameters).filter(target => target.machineId === machineId);
|
|
696
|
+
for (let i = 0; i < matchedTargets.length; i++) {
|
|
697
|
+
// Each instance runs the command with its own entry's template variables resolved, so the per-instance parameters (written to parameters.json and compared for restarts) carry the resolved command.
|
|
698
|
+
let instanceParameters = { ...config.parameters, command: applyCommandTemplate(config.parameters.command, matchedTargets[i].variables) };
|
|
697
699
|
let screenName = getScreenName({
|
|
698
700
|
serviceKey: config.parameters.key,
|
|
699
701
|
index: i,
|
|
@@ -741,9 +743,9 @@ const resyncServicesBase = runInSerial(measureWrap(async function resyncServices
|
|
|
741
743
|
if (await fsExistsAsync(parameterPath)) {
|
|
742
744
|
prevParameters = await fs.promises.readFile(parameterPath, "utf8");
|
|
743
745
|
}
|
|
744
|
-
let newParametersString = JSON.stringify(
|
|
746
|
+
let newParametersString = JSON.stringify(instanceParameters);
|
|
745
747
|
|
|
746
|
-
let sameParameters = sameRestartParameters(prevParameters,
|
|
748
|
+
let sameParameters = sameRestartParameters(prevParameters, instanceParameters);
|
|
747
749
|
let screenIsRunning = screenStateMap.get(screenName)?.isProcessRunning;
|
|
748
750
|
|
|
749
751
|
let nodePathId = folder + SERVICE_NODE_FILE_NAME;
|
|
@@ -760,7 +762,7 @@ const resyncServicesBase = runInSerial(measureWrap(async function resyncServices
|
|
|
760
762
|
continue;
|
|
761
763
|
}
|
|
762
764
|
|
|
763
|
-
console.log(`Resyncing service ${magenta(screenName)}, with ${
|
|
765
|
+
console.log(`Resyncing service ${magenta(screenName)}, with ${newParametersString}, isRunning = ${screenIsRunning}, sameParameters = ${sameParameters}`);
|
|
764
766
|
|
|
765
767
|
await fs.promises.writeFile(parameterPath, newParametersString);
|
|
766
768
|
|
|
@@ -781,7 +783,7 @@ const resyncServicesBase = runInSerial(measureWrap(async function resyncServices
|
|
|
781
783
|
|
|
782
784
|
await runScreenCommand({
|
|
783
785
|
screenName,
|
|
784
|
-
command:
|
|
786
|
+
command: instanceParameters.command,
|
|
785
787
|
});
|
|
786
788
|
await delay(2000);
|
|
787
789
|
let newScreens = await getScreenState(false);
|
|
@@ -66,15 +66,22 @@ export type MachineInfo = {
|
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
|
|
69
|
+
export type MachineTarget = {
|
|
70
|
+
machineId: string;
|
|
71
|
+
/** Substituted into the command's `${name}` template placeholders for this machine entry (see applyCommandTemplate). */
|
|
72
|
+
variables: Record<string, string>;
|
|
73
|
+
};
|
|
74
|
+
|
|
69
75
|
export type ServiceParameters = {
|
|
70
76
|
/** MUST be unique, and clean enough to be used as the screen/tmux name, and folder name */
|
|
71
77
|
key: string;
|
|
72
78
|
|
|
73
|
-
/** There can be duplicate machine IDs which just means we apply multiple times on that machine, as `${key}-${index}-dply`, each with their own screen and folder. Part of the parameters so machine changes are released on the same schedule as everything else (but they never restart already-running instances, see restartParametersKey). Optional, as already deployed configs predate the field (treat missing as []). */
|
|
74
|
-
machineIds?: string[];
|
|
79
|
+
/** There can be duplicate machine IDs which just means we apply multiple times on that machine, as `${key}-${index}-dply`, each with their own screen and folder. Part of the parameters so machine changes are released on the same schedule as everything else (but they never restart already-running instances, see restartParametersKey). Optional, as already deployed configs predate the field (treat missing as []). Entries are either bare machine ids, or objects carrying per-entry template variables for the command. Use getMachineTargets / setMachineTargets instead of touching this directly. */
|
|
80
|
+
machineIds?: string[] | MachineTarget[];
|
|
75
81
|
|
|
76
82
|
repoUrl: string;
|
|
77
83
|
gitRef: string;
|
|
84
|
+
/** May contain `${name}` template placeholders, filled per machine entry from its variables. Placeholders with no matching variable are left untouched, so bash's own `${ENV}` references keep working. */
|
|
78
85
|
command: string;
|
|
79
86
|
/** Allows forcing an update */
|
|
80
87
|
poke?: number;
|
|
@@ -106,6 +113,40 @@ export type ServiceConfig = {
|
|
|
106
113
|
|
|
107
114
|
export const DEFAULT_OVERLAP_TIME = timeInMinute * 10;
|
|
108
115
|
|
|
116
|
+
const TEMPLATE_VARIABLE_REGEX = /\$\{([a-zA-Z0-9_-]+)\}/g;
|
|
117
|
+
/** The `${name}` placeholder names in a command template, in order of first appearance. */
|
|
118
|
+
export function getCommandTemplateVariables(command: string): string[] {
|
|
119
|
+
let names = new Set<string>();
|
|
120
|
+
for (let match of command.matchAll(TEMPLATE_VARIABLE_REGEX)) {
|
|
121
|
+
names.add(match[1]);
|
|
122
|
+
}
|
|
123
|
+
return [...names];
|
|
124
|
+
}
|
|
125
|
+
export function applyCommandTemplate(command: string, variables: Record<string, string>): string {
|
|
126
|
+
return command.replace(TEMPLATE_VARIABLE_REGEX, (placeholder, name) => name in variables ? variables[name] : placeholder);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** The machineIds entries, normalized to the object form. */
|
|
130
|
+
export function getMachineTargets(parameters: ServiceParameters): MachineTarget[] {
|
|
131
|
+
return ((parameters.machineIds || []) as (string | MachineTarget)[]).map(entry =>
|
|
132
|
+
typeof entry === "string"
|
|
133
|
+
? { machineId: entry, variables: {} }
|
|
134
|
+
// Hand-edited configs (ex, via the raw config editor) may omit variables
|
|
135
|
+
: { machineId: entry.machineId, variables: entry.variables || {} }
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
export function getMachineIdList(parameters: ServiceParameters): string[] {
|
|
139
|
+
return getMachineTargets(parameters).map(x => x.machineId);
|
|
140
|
+
}
|
|
141
|
+
/** Stores targets back onto parameters, collapsing to the plain string[] form when no entry has variables (keeps configs without templates identical to how they always looked). */
|
|
142
|
+
export function setMachineTargets(parameters: ServiceParameters, targets: MachineTarget[]): void {
|
|
143
|
+
if (targets.every(target => Object.keys(target.variables).length === 0)) {
|
|
144
|
+
parameters.machineIds = targets.map(x => x.machineId);
|
|
145
|
+
} else {
|
|
146
|
+
parameters.machineIds = targets;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
109
150
|
/** The parameters machines should actually be running right now. */
|
|
110
151
|
export function getLiveServiceParameters(config: ServiceConfig): ServiceParameters {
|
|
111
152
|
let releaseTime = config.parameters.releaseTime;
|
|
@@ -121,12 +162,15 @@ export function getLiveServiceConfig(config: ServiceConfig): ServiceConfig {
|
|
|
121
162
|
export function stripReleaseState(config: ServiceConfig): ServiceConfig {
|
|
122
163
|
return { ...config, oldParameters: undefined, parameters: { ...config.parameters, releaseTime: undefined } };
|
|
123
164
|
}
|
|
124
|
-
/** The parts of the parameters that a running instance actually depends on. machineIds / releaseTime / overlapTime don't change what an instance runs, so changes to only them must never restart already-running instances (ex, adding a machine must not restart the existing machines). */
|
|
165
|
+
/** The parts of the parameters that a running instance actually depends on. machineIds / releaseTime / overlapTime don't change what an instance runs, so changes to only them must never restart already-running instances (ex, adding a machine must not restart the existing machines). Per-entry template variables DO change what an instance runs, but that is captured by the resolved command in the per-instance parameters the apply code compares (so a variable change restarts only that machine's instance). */
|
|
125
166
|
export function restartParametersKey(parameters: ServiceParameters): string {
|
|
126
167
|
return JSON.stringify({ ...parameters, machineIds: undefined, releaseTime: undefined, overlapTime: undefined });
|
|
127
168
|
}
|
|
128
169
|
function getConfigMachineIds(config: ServiceConfig): string[] {
|
|
129
|
-
return [...new Set([
|
|
170
|
+
return [...new Set([
|
|
171
|
+
...getMachineIdList(config.parameters),
|
|
172
|
+
...(config.oldParameters ? getMachineIdList(config.oldParameters) : []),
|
|
173
|
+
])];
|
|
130
174
|
}
|
|
131
175
|
// Migrates records that stored machineIds at the top level of ServiceConfig (it now lives in parameters)
|
|
132
176
|
function normalizeServiceConfig(config: ServiceConfig): ServiceConfig {
|
|
@@ -562,9 +606,9 @@ export async function getEffectiveServiceConfigs(): Promise<ServiceConfig[]> {
|
|
|
562
606
|
|
|
563
607
|
return configs.map(config => {
|
|
564
608
|
normalizeServiceConfig(config);
|
|
565
|
-
config.parameters
|
|
609
|
+
setMachineTargets(config.parameters, getMachineTargets(config.parameters).filter(target => !disabledMachineIds.has(target.machineId)));
|
|
566
610
|
if (config.oldParameters) {
|
|
567
|
-
config.oldParameters
|
|
611
|
+
setMachineTargets(config.oldParameters, getMachineTargets(config.oldParameters).filter(target => !disabledMachineIds.has(target.machineId)));
|
|
568
612
|
}
|
|
569
613
|
return config;
|
|
570
614
|
});
|
|
@@ -82,9 +82,9 @@ export async function registerManagementPages2(config: {
|
|
|
82
82
|
|
|
83
83
|
inputPages.push({
|
|
84
84
|
title: "Routing Table",
|
|
85
|
-
componentName: "
|
|
86
|
-
controllerName: "
|
|
87
|
-
getModule: () => import("./misc-pages/
|
|
85
|
+
componentName: "RoutingTablePage",
|
|
86
|
+
controllerName: "RoutingTablePageController",
|
|
87
|
+
getModule: () => import("./misc-pages/RoutingTablePage"),
|
|
88
88
|
});
|
|
89
89
|
inputPages.push({
|
|
90
90
|
title: "LOG VIEWER",
|