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