freestyle-sandboxes 0.1.39 → 0.1.41
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/index.cjs +412 -116
- package/index.d.cts +33 -4
- package/index.d.mts +33 -4
- package/index.mjs +412 -116
- package/package.json +1 -1
package/index.cjs
CHANGED
|
@@ -6009,6 +6009,64 @@ function composeVmSpecs(specs) {
|
|
|
6009
6009
|
}
|
|
6010
6010
|
|
|
6011
6011
|
const DEFAULT_CONFIGURE_BASE_IMAGE = "FROM debian:trixie-slim";
|
|
6012
|
+
const RUN_COMMANDS_SYSTEMD_SERVICE_PREFIX = "freestyle-run-command";
|
|
6013
|
+
const WAIT_FOR_SYSTEMD_SERVICE_PREFIX = "freestyle-wait-for";
|
|
6014
|
+
function escapeRegExp(value) {
|
|
6015
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6016
|
+
}
|
|
6017
|
+
function getGeneratedServiceState(services, prefix) {
|
|
6018
|
+
const generatedPrefixes = [
|
|
6019
|
+
RUN_COMMANDS_SYSTEMD_SERVICE_PREFIX,
|
|
6020
|
+
WAIT_FOR_SYSTEMD_SERVICE_PREFIX
|
|
6021
|
+
];
|
|
6022
|
+
let maxIndex = 0;
|
|
6023
|
+
let lastGeneratedServiceName;
|
|
6024
|
+
for (const service of services) {
|
|
6025
|
+
if (!service.name) {
|
|
6026
|
+
continue;
|
|
6027
|
+
}
|
|
6028
|
+
for (const generatedPrefix of generatedPrefixes) {
|
|
6029
|
+
const match = service.name.match(
|
|
6030
|
+
new RegExp(`^${escapeRegExp(generatedPrefix)}-(\\d+)$`)
|
|
6031
|
+
);
|
|
6032
|
+
if (!match) {
|
|
6033
|
+
continue;
|
|
6034
|
+
}
|
|
6035
|
+
if (generatedPrefix === prefix) {
|
|
6036
|
+
const index = Number.parseInt(match[1] ?? "0", 10);
|
|
6037
|
+
if (index > maxIndex) {
|
|
6038
|
+
maxIndex = index;
|
|
6039
|
+
}
|
|
6040
|
+
}
|
|
6041
|
+
lastGeneratedServiceName = service.name;
|
|
6042
|
+
break;
|
|
6043
|
+
}
|
|
6044
|
+
}
|
|
6045
|
+
return { maxIndex, lastGeneratedServiceName };
|
|
6046
|
+
}
|
|
6047
|
+
function appendServiceDependency(dependencies, dependency) {
|
|
6048
|
+
if (!dependency) {
|
|
6049
|
+
return dependencies ?? void 0;
|
|
6050
|
+
}
|
|
6051
|
+
const values = [...dependencies ?? []];
|
|
6052
|
+
if (!values.includes(dependency)) {
|
|
6053
|
+
values.push(dependency);
|
|
6054
|
+
}
|
|
6055
|
+
return values;
|
|
6056
|
+
}
|
|
6057
|
+
function buildWaitForScript(command, intervalSeconds) {
|
|
6058
|
+
const body = command.trim().split("\n").map((line) => ` ${line}`).join("\n");
|
|
6059
|
+
return [
|
|
6060
|
+
"while true; do",
|
|
6061
|
+
" if (",
|
|
6062
|
+
body,
|
|
6063
|
+
" ); then",
|
|
6064
|
+
" exit 0",
|
|
6065
|
+
" fi",
|
|
6066
|
+
` sleep ${intervalSeconds}`,
|
|
6067
|
+
"done"
|
|
6068
|
+
].join("\n");
|
|
6069
|
+
}
|
|
6012
6070
|
function processSystemdServices(services, existingFiles = {}) {
|
|
6013
6071
|
const additionalFiles = { ...existingFiles };
|
|
6014
6072
|
const processedServices = [];
|
|
@@ -6043,6 +6101,63 @@ ${bash}`;
|
|
|
6043
6101
|
}
|
|
6044
6102
|
return { services: processedServices, additionalFiles };
|
|
6045
6103
|
}
|
|
6104
|
+
function normalizeSystemdServices(services) {
|
|
6105
|
+
return services.map((service) => ({
|
|
6106
|
+
...service,
|
|
6107
|
+
after: service.after?.map((s) => s.includes(".") ? s : `${s}.service`),
|
|
6108
|
+
requires: service.requires?.map(
|
|
6109
|
+
(s) => s.includes(".") ? s : `${s}.service`
|
|
6110
|
+
),
|
|
6111
|
+
wantedBy: service.wantedBy?.map(
|
|
6112
|
+
(s) => s.includes(".") ? s : `${s}.service`
|
|
6113
|
+
)
|
|
6114
|
+
}));
|
|
6115
|
+
}
|
|
6116
|
+
function normalizePatchedServices(services) {
|
|
6117
|
+
return services.map((service) => ({
|
|
6118
|
+
...service,
|
|
6119
|
+
after: service.after?.map((s) => s.includes(".") ? s : `${s}.service`),
|
|
6120
|
+
requires: service.requires?.map(
|
|
6121
|
+
(s) => s.includes(".") ? s : `${s}.service`
|
|
6122
|
+
),
|
|
6123
|
+
wantedBy: service.wantedBy?.map(
|
|
6124
|
+
(s) => s.includes(".") ? s : `${s}.service`
|
|
6125
|
+
)
|
|
6126
|
+
}));
|
|
6127
|
+
}
|
|
6128
|
+
function normalizeTemplateForRequest(template) {
|
|
6129
|
+
const normalizedTemplate = {
|
|
6130
|
+
...template ?? {}
|
|
6131
|
+
};
|
|
6132
|
+
if (normalizedTemplate.template) {
|
|
6133
|
+
normalizedTemplate.template = normalizeTemplateForRequest(
|
|
6134
|
+
normalizedTemplate.template
|
|
6135
|
+
);
|
|
6136
|
+
}
|
|
6137
|
+
if (normalizedTemplate.baseImage) {
|
|
6138
|
+
normalizedTemplate.baseImage = normalizeBaseImage(
|
|
6139
|
+
normalizedTemplate.baseImage
|
|
6140
|
+
)?.toRaw();
|
|
6141
|
+
}
|
|
6142
|
+
normalizedTemplate.git = normalizeGitOptions(normalizedTemplate.git);
|
|
6143
|
+
if (normalizedTemplate.systemd?.services) {
|
|
6144
|
+
const normalizedServices = normalizeSystemdServices(
|
|
6145
|
+
normalizedTemplate.systemd.services
|
|
6146
|
+
);
|
|
6147
|
+
const { services: processedServices, additionalFiles: bashFiles } = processSystemdServices(
|
|
6148
|
+
normalizedServices,
|
|
6149
|
+
normalizedTemplate.additionalFiles ?? {}
|
|
6150
|
+
);
|
|
6151
|
+
normalizedTemplate.systemd.services = processedServices;
|
|
6152
|
+
normalizedTemplate.additionalFiles = bashFiles;
|
|
6153
|
+
}
|
|
6154
|
+
if (normalizedTemplate.systemd?.patchedServices) {
|
|
6155
|
+
normalizedTemplate.systemd.patchedServices = normalizePatchedServices(
|
|
6156
|
+
normalizedTemplate.systemd.patchedServices
|
|
6157
|
+
);
|
|
6158
|
+
}
|
|
6159
|
+
return normalizedTemplate;
|
|
6160
|
+
}
|
|
6046
6161
|
function normalizeGitOptions(git) {
|
|
6047
6162
|
if (!git) return git;
|
|
6048
6163
|
return {
|
|
@@ -6238,10 +6353,12 @@ class Vm {
|
|
|
6238
6353
|
params: { vm_id: this.vmId },
|
|
6239
6354
|
body: options
|
|
6240
6355
|
});
|
|
6241
|
-
const vmId = response.id;
|
|
6242
6356
|
return {
|
|
6243
|
-
|
|
6244
|
-
|
|
6357
|
+
forks: response.forks.map((fork) => ({
|
|
6358
|
+
vmId: fork.id,
|
|
6359
|
+
vm: this._freestyle.vms.ref({ vmId: fork.id })
|
|
6360
|
+
// Create a new Vm instance for the forked VM
|
|
6361
|
+
}))
|
|
6245
6362
|
};
|
|
6246
6363
|
}
|
|
6247
6364
|
/**
|
|
@@ -6390,10 +6507,14 @@ class VmSpec {
|
|
|
6390
6507
|
return { ...this.withDiscriminators };
|
|
6391
6508
|
}
|
|
6392
6509
|
mergeRaw(options) {
|
|
6510
|
+
const existingSnapshot = this.raw.snapshot;
|
|
6393
6511
|
this.raw = composeCreateVmOptions([
|
|
6394
6512
|
this.raw,
|
|
6395
6513
|
options
|
|
6396
6514
|
]);
|
|
6515
|
+
if (existingSnapshot !== void 0 && this.raw.snapshot === void 0) {
|
|
6516
|
+
this.raw.snapshot = existingSnapshot;
|
|
6517
|
+
}
|
|
6397
6518
|
return this;
|
|
6398
6519
|
}
|
|
6399
6520
|
clearBuilders() {
|
|
@@ -6477,9 +6598,74 @@ class VmSpec {
|
|
|
6477
6598
|
return this;
|
|
6478
6599
|
}
|
|
6479
6600
|
runCommands(...commands) {
|
|
6480
|
-
const
|
|
6481
|
-
|
|
6482
|
-
|
|
6601
|
+
const normalizedCommands = commands.map((command) => command.trim()).filter((command) => command.length > 0);
|
|
6602
|
+
if (normalizedCommands.length === 0) {
|
|
6603
|
+
return this;
|
|
6604
|
+
}
|
|
6605
|
+
const existingSystemd = this.raw.systemd ?? {};
|
|
6606
|
+
const existingServices = [...existingSystemd.services ?? []];
|
|
6607
|
+
let { maxIndex: maxRunCommandIndex, lastGeneratedServiceName } = getGeneratedServiceState(
|
|
6608
|
+
existingServices,
|
|
6609
|
+
RUN_COMMANDS_SYSTEMD_SERVICE_PREFIX
|
|
6610
|
+
);
|
|
6611
|
+
for (const command of normalizedCommands) {
|
|
6612
|
+
const nextIndex = ++maxRunCommandIndex;
|
|
6613
|
+
const serviceName = `${RUN_COMMANDS_SYSTEMD_SERVICE_PREFIX}-${nextIndex}`;
|
|
6614
|
+
existingServices.push({
|
|
6615
|
+
name: serviceName,
|
|
6616
|
+
mode: "oneshot",
|
|
6617
|
+
deleteAfterSuccess: true,
|
|
6618
|
+
bash: command,
|
|
6619
|
+
...lastGeneratedServiceName ? {
|
|
6620
|
+
after: [lastGeneratedServiceName],
|
|
6621
|
+
requires: [lastGeneratedServiceName]
|
|
6622
|
+
} : {}
|
|
6623
|
+
});
|
|
6624
|
+
lastGeneratedServiceName = serviceName;
|
|
6625
|
+
}
|
|
6626
|
+
this.raw.systemd = {
|
|
6627
|
+
...existingSystemd,
|
|
6628
|
+
services: existingServices
|
|
6629
|
+
};
|
|
6630
|
+
return this;
|
|
6631
|
+
}
|
|
6632
|
+
waitFor(command, config = {}) {
|
|
6633
|
+
const trimmedCommand = command.trim();
|
|
6634
|
+
if (!trimmedCommand) {
|
|
6635
|
+
throw new Error("VmSpec.waitFor requires a non-empty command");
|
|
6636
|
+
}
|
|
6637
|
+
const intervalSeconds = config.intervalSeconds ?? 2;
|
|
6638
|
+
if (!Number.isFinite(intervalSeconds) || intervalSeconds <= 0) {
|
|
6639
|
+
throw new Error("VmSpec.waitFor intervalSeconds must be greater than 0");
|
|
6640
|
+
}
|
|
6641
|
+
const existingSystemd = this.raw.systemd ?? {};
|
|
6642
|
+
const existingServices = [...existingSystemd.services ?? []];
|
|
6643
|
+
const { maxIndex, lastGeneratedServiceName } = getGeneratedServiceState(
|
|
6644
|
+
existingServices,
|
|
6645
|
+
WAIT_FOR_SYSTEMD_SERVICE_PREFIX
|
|
6646
|
+
);
|
|
6647
|
+
const {
|
|
6648
|
+
intervalSeconds: _intervalSeconds,
|
|
6649
|
+
name,
|
|
6650
|
+
after,
|
|
6651
|
+
requires,
|
|
6652
|
+
timeoutSec,
|
|
6653
|
+
...rest
|
|
6654
|
+
} = config;
|
|
6655
|
+
existingServices.push({
|
|
6656
|
+
...rest,
|
|
6657
|
+
name: name ?? `${WAIT_FOR_SYSTEMD_SERVICE_PREFIX}-${maxIndex + 1}`,
|
|
6658
|
+
mode: "oneshot",
|
|
6659
|
+
// deleteAfterSuccess: true,
|
|
6660
|
+
timeoutSec: timeoutSec ?? 0,
|
|
6661
|
+
after: appendServiceDependency(after, lastGeneratedServiceName),
|
|
6662
|
+
requires: appendServiceDependency(requires, lastGeneratedServiceName),
|
|
6663
|
+
bash: buildWaitForScript(trimmedCommand, intervalSeconds)
|
|
6664
|
+
});
|
|
6665
|
+
this.raw.systemd = {
|
|
6666
|
+
...existingSystemd,
|
|
6667
|
+
services: existingServices
|
|
6668
|
+
};
|
|
6483
6669
|
return this;
|
|
6484
6670
|
}
|
|
6485
6671
|
repo(repo, path) {
|
|
@@ -6506,9 +6692,76 @@ class VmSpec {
|
|
|
6506
6692
|
function isVmSpecLike$1(value) {
|
|
6507
6693
|
return !!value && typeof value === "object" && "raw" in value && "with" in value;
|
|
6508
6694
|
}
|
|
6509
|
-
function isVmTemplateLike(value) {
|
|
6695
|
+
function isVmTemplateLike$1(value) {
|
|
6510
6696
|
return !!value && typeof value === "object" && "raw" in value && "with" in value;
|
|
6511
6697
|
}
|
|
6698
|
+
function cloneVmSpecTree(spec) {
|
|
6699
|
+
const clonedRaw = cloneVmSpecRaw$1(spec.raw);
|
|
6700
|
+
return new VmSpec({
|
|
6701
|
+
...clonedRaw,
|
|
6702
|
+
with: { ...spec.builders },
|
|
6703
|
+
__withDiscriminators: spec.getBuilderDiscriminators()
|
|
6704
|
+
});
|
|
6705
|
+
}
|
|
6706
|
+
function isVmSpecInstance(value) {
|
|
6707
|
+
return value instanceof VmSpec;
|
|
6708
|
+
}
|
|
6709
|
+
function isVmTemplateInstance(value) {
|
|
6710
|
+
return value instanceof VmTemplate;
|
|
6711
|
+
}
|
|
6712
|
+
function cloneVmTemplateTree(template) {
|
|
6713
|
+
const clonedRaw = cloneVmTemplateRaw$1(template.raw);
|
|
6714
|
+
return new VmTemplate({
|
|
6715
|
+
...clonedRaw,
|
|
6716
|
+
with: { ...template.with }
|
|
6717
|
+
});
|
|
6718
|
+
}
|
|
6719
|
+
function cloneVmSpecRaw$1(raw) {
|
|
6720
|
+
const cloned = cloneVmTemplateRaw$1(raw);
|
|
6721
|
+
if (isVmSpecInstance(raw.snapshot)) {
|
|
6722
|
+
cloned.snapshot = cloneVmSpecTree(raw.snapshot);
|
|
6723
|
+
} else if (raw.snapshot !== void 0) {
|
|
6724
|
+
cloned.snapshot = cloneVmValue(raw.snapshot);
|
|
6725
|
+
}
|
|
6726
|
+
return cloned;
|
|
6727
|
+
}
|
|
6728
|
+
function cloneVmTemplateRaw$1(raw) {
|
|
6729
|
+
const cloned = cloneVmPlainObject(raw);
|
|
6730
|
+
if (isVmTemplateInstance(raw.template)) {
|
|
6731
|
+
cloned.template = cloneVmTemplateTree(raw.template);
|
|
6732
|
+
} else if (raw.template !== void 0) {
|
|
6733
|
+
cloned.template = cloneVmValue(raw.template);
|
|
6734
|
+
}
|
|
6735
|
+
return cloned;
|
|
6736
|
+
}
|
|
6737
|
+
function cloneVmPlainObject(value) {
|
|
6738
|
+
if (!value || typeof value !== "object") {
|
|
6739
|
+
return value;
|
|
6740
|
+
}
|
|
6741
|
+
const cloned = {};
|
|
6742
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
6743
|
+
cloned[key] = cloneVmValue(entry);
|
|
6744
|
+
}
|
|
6745
|
+
return cloned;
|
|
6746
|
+
}
|
|
6747
|
+
function cloneVmValue(value) {
|
|
6748
|
+
if (value instanceof VmBaseImage) {
|
|
6749
|
+
return new VmBaseImage(value.toRaw());
|
|
6750
|
+
}
|
|
6751
|
+
if (isVmSpecInstance(value)) {
|
|
6752
|
+
return cloneVmSpecTree(value);
|
|
6753
|
+
}
|
|
6754
|
+
if (isVmTemplateInstance(value)) {
|
|
6755
|
+
return cloneVmTemplateTree(value);
|
|
6756
|
+
}
|
|
6757
|
+
if (Array.isArray(value)) {
|
|
6758
|
+
return value.map((entry) => cloneVmValue(entry));
|
|
6759
|
+
}
|
|
6760
|
+
if (value && typeof value === "object") {
|
|
6761
|
+
return cloneVmPlainObject(value);
|
|
6762
|
+
}
|
|
6763
|
+
return value;
|
|
6764
|
+
}
|
|
6512
6765
|
async function convertSpecSnapshotsToTemplates(spec, processBuilders = true) {
|
|
6513
6766
|
if (!isVmSpecLike$1(spec.raw.snapshot)) {
|
|
6514
6767
|
return void 0;
|
|
@@ -6620,7 +6873,17 @@ class VmsNamespace {
|
|
|
6620
6873
|
snapshots;
|
|
6621
6874
|
async create(options = {}) {
|
|
6622
6875
|
if (isVmSpecLike$1(options)) {
|
|
6623
|
-
options = { spec: options };
|
|
6876
|
+
options = { spec: cloneVmSpecTree(options) };
|
|
6877
|
+
} else {
|
|
6878
|
+
if (isVmSpecLike$1(options.spec)) {
|
|
6879
|
+
options.spec = cloneVmSpecTree(options.spec);
|
|
6880
|
+
}
|
|
6881
|
+
if (isVmSpecLike$1(options.snapshot)) {
|
|
6882
|
+
options.snapshot = cloneVmSpecTree(options.snapshot);
|
|
6883
|
+
}
|
|
6884
|
+
if (isVmTemplateLike$1(options.template)) {
|
|
6885
|
+
options.template = cloneVmTemplateTree(options.template);
|
|
6886
|
+
}
|
|
6624
6887
|
}
|
|
6625
6888
|
if (isVmSpecLike$1(options.snapshot)) {
|
|
6626
6889
|
if (isVmSpecLike$1(options.spec)) {
|
|
@@ -6632,21 +6895,8 @@ class VmsNamespace {
|
|
|
6632
6895
|
}
|
|
6633
6896
|
}
|
|
6634
6897
|
if (options.systemd?.services) {
|
|
6635
|
-
const normalizedServices =
|
|
6636
|
-
|
|
6637
|
-
return {
|
|
6638
|
-
...service,
|
|
6639
|
-
after: service.after?.map(
|
|
6640
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
6641
|
-
),
|
|
6642
|
-
requires: service.requires?.map(
|
|
6643
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
6644
|
-
),
|
|
6645
|
-
wantedBy: service.wantedBy?.map(
|
|
6646
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
6647
|
-
)
|
|
6648
|
-
};
|
|
6649
|
-
}
|
|
6898
|
+
const normalizedServices = normalizeSystemdServices(
|
|
6899
|
+
options.systemd.services
|
|
6650
6900
|
);
|
|
6651
6901
|
const { services: processedServices, additionalFiles: bashFiles } = processSystemdServices(
|
|
6652
6902
|
normalizedServices,
|
|
@@ -6656,19 +6906,8 @@ class VmsNamespace {
|
|
|
6656
6906
|
options.additionalFiles = bashFiles;
|
|
6657
6907
|
}
|
|
6658
6908
|
if (options.systemd?.patchedServices) {
|
|
6659
|
-
options.systemd.patchedServices =
|
|
6660
|
-
|
|
6661
|
-
service.after = service.after?.map(
|
|
6662
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
6663
|
-
);
|
|
6664
|
-
service.requires = service.requires?.map(
|
|
6665
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
6666
|
-
);
|
|
6667
|
-
service.wantedBy = service.wantedBy?.map(
|
|
6668
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
6669
|
-
);
|
|
6670
|
-
return service;
|
|
6671
|
-
}
|
|
6909
|
+
options.systemd.patchedServices = normalizePatchedServices(
|
|
6910
|
+
options.systemd.patchedServices
|
|
6672
6911
|
);
|
|
6673
6912
|
}
|
|
6674
6913
|
if ("snapshot" in options) {
|
|
@@ -6701,7 +6940,7 @@ class VmsNamespace {
|
|
|
6701
6940
|
const specBuilders = isVmSpecLike$1(
|
|
6702
6941
|
options.spec
|
|
6703
6942
|
) ? collectSpecBuilders(options.spec) : void 0;
|
|
6704
|
-
const templateBuilders = isVmTemplateLike(options.template) ? options.template.with : void 0;
|
|
6943
|
+
const templateBuilders = isVmTemplateLike$1(options.template) ? options.template.with : void 0;
|
|
6705
6944
|
const builders = {
|
|
6706
6945
|
...templateBuilders || {},
|
|
6707
6946
|
...specBuilders || {},
|
|
@@ -6709,10 +6948,10 @@ class VmsNamespace {
|
|
|
6709
6948
|
};
|
|
6710
6949
|
const { with: _, spec: _spec, ...baseConfig } = options;
|
|
6711
6950
|
let config = baseConfig;
|
|
6712
|
-
if (isVmTemplateLike(config.template)) {
|
|
6951
|
+
if (isVmTemplateLike$1(config.template)) {
|
|
6713
6952
|
config.template = await processTemplateTree(config.template);
|
|
6714
6953
|
}
|
|
6715
|
-
if (isVmTemplateLike(config.template)) {
|
|
6954
|
+
if (isVmTemplateLike$1(config.template)) {
|
|
6716
6955
|
config.template = await ensureNestedTemplates(
|
|
6717
6956
|
config.template,
|
|
6718
6957
|
this.snapshots
|
|
@@ -6726,18 +6965,9 @@ class VmsNamespace {
|
|
|
6726
6965
|
}
|
|
6727
6966
|
}
|
|
6728
6967
|
if (config.systemd?.services) {
|
|
6729
|
-
const normalizedServices =
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
6733
|
-
),
|
|
6734
|
-
requires: service.requires?.map(
|
|
6735
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
6736
|
-
),
|
|
6737
|
-
wantedBy: service.wantedBy?.map(
|
|
6738
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
6739
|
-
)
|
|
6740
|
-
}));
|
|
6968
|
+
const normalizedServices = normalizeSystemdServices(
|
|
6969
|
+
config.systemd.services
|
|
6970
|
+
);
|
|
6741
6971
|
const { services: processedServices, additionalFiles: bashFiles } = processSystemdServices(
|
|
6742
6972
|
normalizedServices,
|
|
6743
6973
|
config.additionalFiles ?? {}
|
|
@@ -6746,28 +6976,20 @@ class VmsNamespace {
|
|
|
6746
6976
|
config.additionalFiles = bashFiles;
|
|
6747
6977
|
}
|
|
6748
6978
|
if (config.systemd?.patchedServices) {
|
|
6749
|
-
config.systemd.patchedServices =
|
|
6750
|
-
|
|
6751
|
-
service.after = service.after?.map(
|
|
6752
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
6753
|
-
);
|
|
6754
|
-
service.requires = service.requires?.map(
|
|
6755
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
6756
|
-
);
|
|
6757
|
-
service.wantedBy = service.wantedBy?.map(
|
|
6758
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
6759
|
-
);
|
|
6760
|
-
return service;
|
|
6761
|
-
}
|
|
6979
|
+
config.systemd.patchedServices = normalizePatchedServices(
|
|
6980
|
+
config.systemd.patchedServices
|
|
6762
6981
|
);
|
|
6763
6982
|
}
|
|
6764
6983
|
config.git = normalizeGitOptions(config.git);
|
|
6765
6984
|
const serializedBaseImage = normalizeBaseImage(config.baseImage)?.toRaw();
|
|
6766
|
-
const rawTemplate = isVmTemplateLike(config.template) ? config.template.raw : config.template;
|
|
6985
|
+
const rawTemplate = isVmTemplateLike$1(config.template) ? config.template.raw : config.template;
|
|
6767
6986
|
const requestTemplate = serializedBaseImage ? {
|
|
6768
6987
|
...rawTemplate ?? {},
|
|
6769
6988
|
baseImage: serializedBaseImage
|
|
6770
6989
|
} : rawTemplate;
|
|
6990
|
+
const normalizedRequestTemplate = requestTemplate ? normalizeTemplateForRequest(
|
|
6991
|
+
requestTemplate
|
|
6992
|
+
) : requestTemplate;
|
|
6771
6993
|
const {
|
|
6772
6994
|
baseImage: _baseImage,
|
|
6773
6995
|
template: _template,
|
|
@@ -6776,7 +6998,7 @@ class VmsNamespace {
|
|
|
6776
6998
|
const response = await this.freestyle._apiClient.post("/v1/vms", {
|
|
6777
6999
|
body: {
|
|
6778
7000
|
...requestConfig,
|
|
6779
|
-
template:
|
|
7001
|
+
template: normalizedRequestTemplate,
|
|
6780
7002
|
// Cast systemd since we've processed SystemdServiceInput[] to RawSystemdService[]
|
|
6781
7003
|
systemd: config.systemd,
|
|
6782
7004
|
// Normalize git options - default config to {}
|
|
@@ -6871,6 +7093,35 @@ function enhanceError(e) {
|
|
|
6871
7093
|
if (!e.message.includes("create --snapshot")) {
|
|
6872
7094
|
e.message = `${e.message}. Hint: use \`npx freestyle-sandboxes@latest vm create --snapshot ${e.body.snapshotId} --ssh --delete\` to debug.`;
|
|
6873
7095
|
}
|
|
7096
|
+
if (e.body.diagnostics) {
|
|
7097
|
+
const d = e.body.diagnostics;
|
|
7098
|
+
const parts = [];
|
|
7099
|
+
if (d.serviceStates.length > 0) {
|
|
7100
|
+
parts.push("Service states:");
|
|
7101
|
+
for (const g of d.serviceStates) {
|
|
7102
|
+
parts.push(
|
|
7103
|
+
` ${g.activeState}(${g.subState}): ${g.services.join(", ")}`
|
|
7104
|
+
);
|
|
7105
|
+
}
|
|
7106
|
+
}
|
|
7107
|
+
const allLogs = [
|
|
7108
|
+
...d.failedServiceLogs ?? [],
|
|
7109
|
+
...d.additionalFailedServiceLogs ?? []
|
|
7110
|
+
];
|
|
7111
|
+
if (allLogs.length > 0) {
|
|
7112
|
+
parts.push("Failed service logs:");
|
|
7113
|
+
for (const l of allLogs) {
|
|
7114
|
+
parts.push(` --- ${l.unitName} ---`);
|
|
7115
|
+
parts.push(` ${l.log}`);
|
|
7116
|
+
}
|
|
7117
|
+
}
|
|
7118
|
+
if (parts.length > 0) {
|
|
7119
|
+
e.message = `${e.message}
|
|
7120
|
+
|
|
7121
|
+
Diagnostics:
|
|
7122
|
+
${parts.join("\n")}`;
|
|
7123
|
+
}
|
|
7124
|
+
}
|
|
6874
7125
|
}
|
|
6875
7126
|
return e;
|
|
6876
7127
|
}
|
|
@@ -6909,7 +7160,12 @@ class VmSnapshotsNamespace {
|
|
|
6909
7160
|
this.apiClient = apiClient;
|
|
6910
7161
|
}
|
|
6911
7162
|
async ensure(options) {
|
|
6912
|
-
let requestOptions =
|
|
7163
|
+
let requestOptions = {
|
|
7164
|
+
...options,
|
|
7165
|
+
spec: isVmSpecLike$1(options.spec) ? cloneVmSpecTree(options.spec) : void 0,
|
|
7166
|
+
snapshot: isVmSpecLike$1(options.snapshot) ? cloneVmSpecTree(options.snapshot) : void 0,
|
|
7167
|
+
template: isVmTemplateLike$1(options.template) ? cloneVmTemplateTree(options.template) : options.template
|
|
7168
|
+
};
|
|
6913
7169
|
if (isVmSpecLike$1(requestOptions.snapshot)) {
|
|
6914
7170
|
if (isVmSpecLike$1(requestOptions.spec)) {
|
|
6915
7171
|
if (!requestOptions.spec.raw.snapshot) {
|
|
@@ -6938,12 +7194,12 @@ class VmSnapshotsNamespace {
|
|
|
6938
7194
|
const { spec: _spec, ...rest } = requestOptions;
|
|
6939
7195
|
requestOptions = rest;
|
|
6940
7196
|
}
|
|
6941
|
-
if (isVmTemplateLike(requestOptions.template)) {
|
|
7197
|
+
if (isVmTemplateLike$1(requestOptions.template)) {
|
|
6942
7198
|
const processedTemplate = await processTemplateTree(
|
|
6943
7199
|
requestOptions.template
|
|
6944
7200
|
);
|
|
6945
7201
|
requestOptions.template = processedTemplate;
|
|
6946
|
-
if (isVmTemplateLike(processedTemplate.raw.template)) {
|
|
7202
|
+
if (isVmTemplateLike$1(processedTemplate.raw.template)) {
|
|
6947
7203
|
requestOptions.template = await ensureNestedTemplates(
|
|
6948
7204
|
processedTemplate,
|
|
6949
7205
|
this
|
|
@@ -6958,7 +7214,9 @@ class VmSnapshotsNamespace {
|
|
|
6958
7214
|
return this.apiClient.post("/v1/vms/snapshots", {
|
|
6959
7215
|
body: {
|
|
6960
7216
|
...requestOptions,
|
|
6961
|
-
template:
|
|
7217
|
+
template: normalizeTemplateForRequest(
|
|
7218
|
+
isVmTemplateLike$1(requestOptions.template) ? requestOptions.template.raw : requestOptions.template
|
|
7219
|
+
)
|
|
6962
7220
|
}
|
|
6963
7221
|
}).catch((e) => {
|
|
6964
7222
|
enhanceError(e);
|
|
@@ -6978,22 +7236,9 @@ async function processTemplateTree(template) {
|
|
|
6978
7236
|
);
|
|
6979
7237
|
}
|
|
6980
7238
|
if (template.raw.systemd?.services) {
|
|
6981
|
-
|
|
6982
|
-
|
|
6983
|
-
after: service.after?.map((s) => s.includes(".") ? s : `${s}.service`),
|
|
6984
|
-
requires: service.requires?.map(
|
|
6985
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
6986
|
-
),
|
|
6987
|
-
wantedBy: service.wantedBy?.map(
|
|
6988
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
6989
|
-
)
|
|
6990
|
-
}));
|
|
6991
|
-
const { services: processedServices, additionalFiles: bashFiles } = processSystemdServices(
|
|
6992
|
-
normalizedServices,
|
|
6993
|
-
template.raw.additionalFiles ?? {}
|
|
7239
|
+
template.raw.systemd.services = normalizeSystemdServices(
|
|
7240
|
+
template.raw.systemd.services
|
|
6994
7241
|
);
|
|
6995
|
-
template.raw.systemd.services = processedServices;
|
|
6996
|
-
template.raw.additionalFiles = bashFiles;
|
|
6997
7242
|
}
|
|
6998
7243
|
for (const key in template.with) {
|
|
6999
7244
|
const builder = template.with[key];
|
|
@@ -7010,7 +7255,7 @@ async function processTemplateTree(template) {
|
|
|
7010
7255
|
}
|
|
7011
7256
|
if (builder.configureNestedTemplate) {
|
|
7012
7257
|
let nestedTemplate;
|
|
7013
|
-
if (isVmTemplateLike(template.raw.template)) {
|
|
7258
|
+
if (isVmTemplateLike$1(template.raw.template)) {
|
|
7014
7259
|
nestedTemplate = template.raw.template;
|
|
7015
7260
|
} else {
|
|
7016
7261
|
nestedTemplate = new VmTemplate({});
|
|
@@ -7033,38 +7278,16 @@ async function processTemplateTree(template) {
|
|
|
7033
7278
|
);
|
|
7034
7279
|
}
|
|
7035
7280
|
if (template.raw.systemd?.services) {
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
after: service.after?.map((s) => s.includes(".") ? s : `${s}.service`),
|
|
7039
|
-
requires: service.requires?.map(
|
|
7040
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
7041
|
-
),
|
|
7042
|
-
wantedBy: service.wantedBy?.map(
|
|
7043
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
7044
|
-
)
|
|
7045
|
-
}));
|
|
7046
|
-
const { services: processedServices, additionalFiles: bashFiles } = processSystemdServices(
|
|
7047
|
-
normalizedServices,
|
|
7048
|
-
template.raw.additionalFiles ?? {}
|
|
7281
|
+
template.raw.systemd.services = normalizeSystemdServices(
|
|
7282
|
+
template.raw.systemd.services
|
|
7049
7283
|
);
|
|
7050
|
-
template.raw.systemd.services = processedServices;
|
|
7051
|
-
template.raw.additionalFiles = bashFiles;
|
|
7052
7284
|
}
|
|
7053
7285
|
if (template.raw.systemd?.patchedServices) {
|
|
7054
|
-
template.raw.systemd.patchedServices =
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
7058
|
-
),
|
|
7059
|
-
requires: service.requires?.map(
|
|
7060
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
7061
|
-
),
|
|
7062
|
-
wantedBy: service.wantedBy?.map(
|
|
7063
|
-
(s) => s.includes(".") ? s : `${s}.service`
|
|
7064
|
-
)
|
|
7065
|
-
}));
|
|
7286
|
+
template.raw.systemd.patchedServices = normalizePatchedServices(
|
|
7287
|
+
template.raw.systemd.patchedServices
|
|
7288
|
+
);
|
|
7066
7289
|
}
|
|
7067
|
-
if (isVmTemplateLike(template.raw.template)) {
|
|
7290
|
+
if (isVmTemplateLike$1(template.raw.template)) {
|
|
7068
7291
|
template.raw.template = await processTemplateTree(
|
|
7069
7292
|
template.raw.template
|
|
7070
7293
|
);
|
|
@@ -7073,7 +7296,7 @@ async function processTemplateTree(template) {
|
|
|
7073
7296
|
}
|
|
7074
7297
|
async function ensureNestedTemplates(template, snapshots) {
|
|
7075
7298
|
const templates = [template];
|
|
7076
|
-
while (isVmTemplateLike(templates.at(-1)?.raw.template)) {
|
|
7299
|
+
while (isVmTemplateLike$1(templates.at(-1)?.raw.template)) {
|
|
7077
7300
|
const innerTemplate = templates.at(-1).raw.template;
|
|
7078
7301
|
templates.at(-1).raw.template = void 0;
|
|
7079
7302
|
templates.push(innerTemplate);
|
|
@@ -7258,7 +7481,7 @@ async function readFiles(directory) {
|
|
|
7258
7481
|
}
|
|
7259
7482
|
|
|
7260
7483
|
async function debugCreateRequests(freestyle, optionsOrSpec = {}) {
|
|
7261
|
-
const options = isVmSpecLike(optionsOrSpec) ? { spec: optionsOrSpec } : optionsOrSpec;
|
|
7484
|
+
const options = isVmSpecLike(optionsOrSpec) ? { spec: cloneVmSpec(optionsOrSpec) } : cloneCreateOptions(optionsOrSpec);
|
|
7262
7485
|
const requests = [];
|
|
7263
7486
|
let snapshotCounter = 0;
|
|
7264
7487
|
let vmCounter = 0;
|
|
@@ -7305,6 +7528,79 @@ async function debugCreateRequests(freestyle, optionsOrSpec = {}) {
|
|
|
7305
7528
|
function isVmSpecLike(value) {
|
|
7306
7529
|
return !!value && typeof value === "object" && "raw" in value && "with" in value;
|
|
7307
7530
|
}
|
|
7531
|
+
function isVmTemplateLike(value) {
|
|
7532
|
+
return !!value && typeof value === "object" && "raw" in value && "with" in value;
|
|
7533
|
+
}
|
|
7534
|
+
function cloneCreateOptions(options) {
|
|
7535
|
+
return {
|
|
7536
|
+
...clonePlainObject(options),
|
|
7537
|
+
with: options.with,
|
|
7538
|
+
template: isVmTemplateLike(options.template) ? cloneVmTemplate(options.template) : cloneValue(options.template),
|
|
7539
|
+
spec: isVmSpecLike(options.spec) ? cloneVmSpec(options.spec) : void 0,
|
|
7540
|
+
snapshot: isVmSpecLike(options.snapshot) ? cloneVmSpec(options.snapshot) : void 0
|
|
7541
|
+
};
|
|
7542
|
+
}
|
|
7543
|
+
function cloneVmSpec(spec) {
|
|
7544
|
+
const clonedRaw = cloneVmSpecRaw(spec.raw);
|
|
7545
|
+
return new VmSpec({
|
|
7546
|
+
...clonedRaw,
|
|
7547
|
+
with: { ...spec.builders },
|
|
7548
|
+
__withDiscriminators: typeof spec.getBuilderDiscriminators === "function" ? spec.getBuilderDiscriminators() : void 0
|
|
7549
|
+
});
|
|
7550
|
+
}
|
|
7551
|
+
function cloneVmTemplate(template) {
|
|
7552
|
+
const clonedRaw = cloneVmTemplateRaw(template.raw);
|
|
7553
|
+
return new VmTemplate({
|
|
7554
|
+
...clonedRaw,
|
|
7555
|
+
with: { ...template.with }
|
|
7556
|
+
});
|
|
7557
|
+
}
|
|
7558
|
+
function cloneVmSpecRaw(raw) {
|
|
7559
|
+
const cloned = cloneVmTemplateRaw(raw);
|
|
7560
|
+
if (isVmSpecLike(raw.snapshot)) {
|
|
7561
|
+
cloned.snapshot = cloneVmSpec(raw.snapshot);
|
|
7562
|
+
} else if (raw.snapshot !== void 0) {
|
|
7563
|
+
cloned.snapshot = cloneValue(raw.snapshot);
|
|
7564
|
+
}
|
|
7565
|
+
return cloned;
|
|
7566
|
+
}
|
|
7567
|
+
function cloneVmTemplateRaw(raw) {
|
|
7568
|
+
const cloned = clonePlainObject(raw);
|
|
7569
|
+
if (isVmTemplateLike(raw.template)) {
|
|
7570
|
+
cloned.template = cloneVmTemplate(raw.template);
|
|
7571
|
+
} else if (raw.template !== void 0) {
|
|
7572
|
+
cloned.template = cloneValue(raw.template);
|
|
7573
|
+
}
|
|
7574
|
+
return cloned;
|
|
7575
|
+
}
|
|
7576
|
+
function clonePlainObject(value) {
|
|
7577
|
+
if (!value || typeof value !== "object") {
|
|
7578
|
+
return value;
|
|
7579
|
+
}
|
|
7580
|
+
const cloned = {};
|
|
7581
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
7582
|
+
cloned[key] = cloneValue(entry);
|
|
7583
|
+
}
|
|
7584
|
+
return cloned;
|
|
7585
|
+
}
|
|
7586
|
+
function cloneValue(value) {
|
|
7587
|
+
if (value instanceof VmBaseImage) {
|
|
7588
|
+
return new VmBaseImage(value.toRaw());
|
|
7589
|
+
}
|
|
7590
|
+
if (isVmSpecLike(value)) {
|
|
7591
|
+
return cloneVmSpec(value);
|
|
7592
|
+
}
|
|
7593
|
+
if (isVmTemplateLike(value)) {
|
|
7594
|
+
return cloneVmTemplate(value);
|
|
7595
|
+
}
|
|
7596
|
+
if (Array.isArray(value)) {
|
|
7597
|
+
return value.map((entry) => cloneValue(entry));
|
|
7598
|
+
}
|
|
7599
|
+
if (value && typeof value === "object") {
|
|
7600
|
+
return clonePlainObject(value);
|
|
7601
|
+
}
|
|
7602
|
+
return value;
|
|
7603
|
+
}
|
|
7308
7604
|
|
|
7309
7605
|
class Freestyle {
|
|
7310
7606
|
/**
|