hammerkit 1.27.0-next.8 → 2.0.0-next.1
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/build.schema.json +176 -6
- package/dist/cache/backends/local-cache-backend.js +6 -0
- package/dist/cache/backends/registry/registry-client.js +1 -0
- package/dist/cache/cache-sync.js +1 -1
- package/dist/cache/explain.js +17 -0
- package/dist/cli.js +2 -0
- package/dist/dashboard/assets/index-CjnKm5-3.css +2 -0
- package/dist/dashboard/assets/index-zPXa4klZ.js +13 -0
- package/dist/dashboard/favicon.svg +1 -0
- package/dist/dashboard/index.html +15 -0
- package/dist/docker/item-network.js +13 -5
- package/dist/executer/archive-task-entry.js +4 -2
- package/dist/executer/deadline.js +17 -3
- package/dist/executer/deferred-restore.js +36 -0
- package/dist/executer/docker-service.js +30 -8
- package/dist/executer/docker-task.js +21 -1
- package/dist/executer/execute-work-service.js +2 -0
- package/dist/executer/execute-work-task.js +11 -4
- package/dist/executer/execution-steps.js +25 -4
- package/dist/executer/reset-work-tree.js +4 -0
- package/dist/executer/scheduler/enqueue-next.js +57 -6
- package/dist/executer/scheduler/state-key.js +42 -5
- package/dist/executer/watch-loop.js +6 -3
- package/dist/harness/connect.js +20 -6
- package/dist/harness/mcp-command.js +22 -6
- package/dist/harness/mcp.js +63 -114
- package/dist/harness/opencode.js +158 -50
- package/dist/harness/pi.js +29 -0
- package/dist/harness/sandbox-client.js +194 -23
- package/dist/harness/service-tools.js +141 -0
- package/dist/harness/task-tools.js +221 -0
- package/dist/kubernetes/apply.js +76 -17
- package/dist/kubernetes/await-running-state.js +172 -13
- package/dist/kubernetes/cluster-state.js +245 -0
- package/dist/kubernetes/ensure-http-route.js +1 -1
- package/dist/kubernetes/ensure-kubernetes-deployment-exists.js +138 -18
- package/dist/kubernetes/ensure-kubernetes-service-exists.js +6 -9
- package/dist/kubernetes/ensure-persistent-data.js +212 -10
- package/dist/kubernetes/follow-pod-logs.js +182 -0
- package/dist/kubernetes/get-service-ip.js +2 -2
- package/dist/kubernetes/job-placement.js +36 -12
- package/dist/kubernetes/job-secrets.js +9 -6
- package/dist/kubernetes/kept-claims.js +22 -10
- package/dist/kubernetes/kubernetes-instance.js +1 -0
- package/dist/kubernetes/network-policy.js +16 -8
- package/dist/kubernetes/remove-persistent-data.js +3 -1
- package/dist/kubernetes/remove-stale-items.js +46 -0
- package/dist/kubernetes/resources.js +25 -3
- package/dist/kubernetes/restore-kubernetes-data.js +9 -5
- package/dist/kubernetes/sandbox-runner.js +628 -0
- package/dist/kubernetes/secrets.js +7 -11
- package/dist/kubernetes/service-rollouts.js +74 -0
- package/dist/kubernetes/store-kubernetes-data.js +5 -2
- package/dist/kubernetes/volumes.js +96 -11
- package/dist/main.js +2 -0
- package/dist/optimizer/calculate-checksum.js +3 -2
- package/dist/optimizer/check-key.js +94 -0
- package/dist/optimizer/check-keys.js +64 -0
- package/dist/optimizer/get-work-cache-stats.js +17 -3
- package/dist/optimizer/work-task-cache-description.js +49 -1
- package/dist/planner/service-definition.js +9 -4
- package/dist/planner/utils/append-work-dependencies.js +5 -3
- package/dist/planner/utils/append-work-service.js +18 -1
- package/dist/planner/utils/append-work-task.js +13 -3
- package/dist/planner/utils/get-work-runtime.js +6 -1
- package/dist/planner/utils/normalize-path.js +19 -1
- package/dist/planner/utils/parse-work-mount.js +7 -1
- package/dist/planner/utils/parse-work-port.js +4 -1
- package/dist/planner/validate.js +22 -0
- package/dist/planner/work-network.js +3 -1
- package/dist/planner/work-runtime-aggregate.js +32 -0
- package/dist/planner/work-runtime-kubernetes.js +153 -26
- package/dist/planner/work-task.js +6 -1
- package/dist/program.js +27 -9
- package/dist/remote/device-login.js +7 -2
- package/dist/remote/host-config.js +69 -3
- package/dist/remote/remote-commands.js +33 -7
- package/dist/remote/remote-status.js +112 -0
- package/dist/remote/remote-work-commands.js +562 -45
- package/dist/remote/server-client.js +15 -2
- package/dist/remote/token-source.js +64 -26
- package/dist/run-program.js +9 -0
- package/dist/schema/build-file-base-task-schema.js +3 -3
- package/dist/schema/build-file-container-service-schema.js +10 -0
- package/dist/schema/build-file-container-task-schema.js +16 -1
- package/dist/schema/build-file-include-schema.js +18 -4
- package/dist/schema/build-file-kubernetes-service-schema.js +2 -0
- package/dist/schema/build-file-task-schema.js +5 -1
- package/dist/schema/capabilities-schema.js +56 -0
- package/dist/schema/reference-parser.js +119 -11
- package/dist/schema/removed-properties.js +20 -0
- package/dist/schema/schema-parser.js +94 -41
- package/dist/schema/work-scope-parser.js +33 -10
- package/dist/secrets/command-secret-provider.js +3 -1
- package/dist/secrets/github-app-token-command.js +3 -1
- package/dist/secrets/secret-catalog.js +43 -19
- package/dist/server/accounts-api.js +4 -1
- package/dist/server/approvals-api.js +1 -1
- package/dist/server/capacity-format.js +36 -0
- package/dist/server/capacity.js +300 -0
- package/dist/server/checks-api.js +83 -0
- package/dist/server/checks.js +116 -0
- package/dist/server/dashboard.js +196 -0
- package/dist/server/definitions-api.js +66 -3
- package/dist/server/endpoints.js +794 -0
- package/dist/server/executor.js +164 -9
- package/dist/server/kubernetes-backend.js +478 -70
- package/dist/server/kubernetes-sandbox-storage.js +46 -2
- package/dist/server/overview.js +271 -0
- package/dist/server/plan.js +170 -121
- package/dist/server/policy.js +34 -2
- package/dist/server/proposals-api.js +17 -8
- package/dist/server/repo-access.js +5 -0
- package/dist/server/run-logs.js +4 -0
- package/dist/server/run-plan.js +38 -4
- package/dist/server/run-request.js +58 -3
- package/dist/server/run-worker.js +262 -13
- package/dist/server/runs.js +969 -36
- package/dist/server/sandbox-pod.js +28 -0
- package/dist/server/sandbox-storage.js +23 -0
- package/dist/server/sandboxes.js +8 -0
- package/dist/server/server-config.js +246 -10
- package/dist/server/server.js +429 -32
- package/dist/server/service-runs.js +82 -0
- package/dist/server/timings.js +132 -0
- package/dist/server/wait-reason.js +21 -0
- package/dist/utils/tar.js +28 -1
- package/dist/utils/terminal-prompt.js +53 -0
- package/package.json +8 -2
|
@@ -17,7 +17,7 @@ const sleep_1 = require("../utils/sleep");
|
|
|
17
17
|
const network_gate_1 = require("./network-gate");
|
|
18
18
|
const job_placement_1 = require("./job-placement");
|
|
19
19
|
Object.defineProperty(exports, "RUN_LABEL", { enumerable: true, get: function () { return job_placement_1.RUN_LABEL; } });
|
|
20
|
-
const
|
|
20
|
+
const resources_1 = require("./resources");
|
|
21
21
|
const PROXY_LABEL = 'hammerkit.dev/proxy-for';
|
|
22
22
|
const NETWORK_OF_LABEL = 'hammerkit.dev/network-of';
|
|
23
23
|
// on the proxy of a task on a sandbox: the sandbox, whose pod of files it stays with
|
|
@@ -86,7 +86,7 @@ function getNetworkPolicyName(item, scope) {
|
|
|
86
86
|
exports.getNetworkPolicyName = getNetworkPolicyName;
|
|
87
87
|
// the pods of the item, of the run when the scope has one
|
|
88
88
|
function getItemPodLabels(item, scope) {
|
|
89
|
-
return { [ID_LABEL]: item.id(), ...(scope.run ? { [job_placement_1.RUN_LABEL]: scope.run } : {}) };
|
|
89
|
+
return { [resources_1.ID_LABEL]: item.id(), ...(scope.run ? { [job_placement_1.RUN_LABEL]: scope.run } : {}) };
|
|
90
90
|
}
|
|
91
91
|
function toSelector(labels) {
|
|
92
92
|
return Object.entries(labels)
|
|
@@ -107,7 +107,12 @@ function getIngress(env, item) {
|
|
|
107
107
|
if (ids.length === 0) {
|
|
108
108
|
return [];
|
|
109
109
|
}
|
|
110
|
-
|
|
110
|
+
// on a server, of this run: the runs of a caller share their namespace
|
|
111
|
+
const run = (0, job_placement_1.getRunLabels)(env);
|
|
112
|
+
const from = {
|
|
113
|
+
matchExpressions: [{ key: resources_1.ID_LABEL, operator: 'In', values: ids }],
|
|
114
|
+
...(Object.keys(run).length > 0 ? { matchLabels: run } : {}),
|
|
115
|
+
};
|
|
111
116
|
// the client's name for `from`, a reserved word
|
|
112
117
|
return [{ _from: [{ podSelector: from }] }];
|
|
113
118
|
}
|
|
@@ -124,7 +129,8 @@ function buildNetworkPolicy(env, item, network, scope, proxy = getRunProxy(item,
|
|
|
124
129
|
namespace: env.namespace,
|
|
125
130
|
name: getNetworkPolicyName(item, scope),
|
|
126
131
|
annotations: { 'hammerkit.dev/version': (0, version_1.getVersion)() },
|
|
127
|
-
labels: { [NETWORK_OF_LABEL]: item.id(), ...(0, job_placement_1.getRunLabels)(env) },
|
|
132
|
+
labels: { [NETWORK_OF_LABEL]: item.id(), ...(scope.owners ? {} : (0, job_placement_1.getRunLabels)(env)) },
|
|
133
|
+
...(scope.owners ? { ownerReferences: scope.owners } : {}),
|
|
128
134
|
},
|
|
129
135
|
spec: {
|
|
130
136
|
podSelector: { matchLabels: getItemPodLabels(item, scope) },
|
|
@@ -132,7 +138,7 @@ function buildNetworkPolicy(env, item, network, scope, proxy = getRunProxy(item,
|
|
|
132
138
|
ingress: getIngress(env, item),
|
|
133
139
|
egress: [
|
|
134
140
|
...(needs.length > 0
|
|
135
|
-
? [{ to: needs.map((need) => ({ podSelector: { matchLabels:
|
|
141
|
+
? [{ to: needs.map((need) => ({ podSelector: { matchLabels: (0, resources_1.getItemLabels)(need.service, env) } })) }]
|
|
136
142
|
: []),
|
|
137
143
|
...(proxied
|
|
138
144
|
? [
|
|
@@ -306,12 +312,14 @@ async function releaseKubernetesItemNetwork(instance, env, item, scope) {
|
|
|
306
312
|
}
|
|
307
313
|
exports.releaseKubernetesItemNetwork = releaseKubernetesItemNetwork;
|
|
308
314
|
// Every policy and proxy of the item, of any run, once none of its pods runs:
|
|
309
|
-
// what `down` and `clean` remove.
|
|
315
|
+
// what `down` and `clean` remove. On a server, those of the server's run only: the
|
|
316
|
+
// runs of a caller share their namespace, and the pods of another run of the same item
|
|
317
|
+
// would run unlimited without their policy.
|
|
310
318
|
async function removeAllKubernetesItemNetworks(instance, env, item) {
|
|
311
|
-
if (!(await awaitPodsEnded(instance, env,
|
|
319
|
+
if (!(await awaitPodsEnded(instance, env, (0, resources_1.getItemLabels)(item, env)))) {
|
|
312
320
|
throw new Error(`pods of ${item.name} are still running, so its network policy stays`);
|
|
313
321
|
}
|
|
314
|
-
const labelSelector =
|
|
322
|
+
const labelSelector = toSelector({ [NETWORK_OF_LABEL]: item.id(), ...(0, job_placement_1.getRunLabels)(env) });
|
|
315
323
|
let policies;
|
|
316
324
|
try {
|
|
317
325
|
const list = await instance.objectApi.list('networking.k8s.io/v1', 'NetworkPolicy', env.namespace, undefined, undefined, undefined, undefined, labelSelector);
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.removePersistentData = void 0;
|
|
4
|
+
const resources_1 = require("./resources");
|
|
4
5
|
async function removePersistentData(instance, kubernetes, task) {
|
|
5
|
-
|
|
6
|
+
// on a server, the run's: a claim it kept has no run label and goes once it expired
|
|
7
|
+
const labelSelector = (0, resources_1.getItemSelector)(task, kubernetes);
|
|
6
8
|
const configmaps = await instance.coreApi.listNamespacedConfigMap({ namespace: kubernetes.namespace, labelSelector });
|
|
7
9
|
for (const configMap of configmaps.items) {
|
|
8
10
|
if (configMap.metadata?.name) {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getStaleItemSelector = exports.removeStaleItems = void 0;
|
|
4
|
+
const job_placement_1 = require("./job-placement");
|
|
5
|
+
const resources_1 = require("./resources");
|
|
6
|
+
const apply_1 = require("./apply");
|
|
7
|
+
/**
|
|
8
|
+
* What a service run made for items it has no more, once it moved to another commit: the
|
|
9
|
+
* Deployment, Service, Secret, policies and Jobs of a service or task whose id changed (a new
|
|
10
|
+
* image, other volumes). Their claims stay, with the namespace of the run.
|
|
11
|
+
*/
|
|
12
|
+
async function removeStaleItems(instance, env, ids) {
|
|
13
|
+
if (!env.server) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const namespace = env.namespace;
|
|
17
|
+
const labelSelector = getStaleItemSelector(env.server.run, ids);
|
|
18
|
+
const selector = { namespace, labelSelector };
|
|
19
|
+
await instance.appsApi.deleteCollectionNamespacedDeployment({ ...selector, propagationPolicy: 'Background' });
|
|
20
|
+
await instance.batchApi.deleteCollectionNamespacedJob({ ...selector, propagationPolicy: 'Background' });
|
|
21
|
+
await instance.coreApi.deleteCollectionNamespacedService(selector);
|
|
22
|
+
await instance.coreApi.deleteCollectionNamespacedSecret(selector);
|
|
23
|
+
const policies = await instance.objectApi.list('networking.k8s.io/v1', 'NetworkPolicy', namespace, undefined, undefined, undefined, undefined, labelSelector);
|
|
24
|
+
for (const policy of policies.items) {
|
|
25
|
+
const metadata = { name: policy.metadata?.name, namespace };
|
|
26
|
+
await instance.objectApi
|
|
27
|
+
.delete({ apiVersion: 'networking.k8s.io/v1', kind: 'NetworkPolicy', metadata })
|
|
28
|
+
.catch((e) => {
|
|
29
|
+
if ((0, apply_1.statusCodeOf)(e) !== 404) {
|
|
30
|
+
throw e;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.removeStaleItems = removeStaleItems;
|
|
36
|
+
// the objects of the run's items but those of `ids`
|
|
37
|
+
function getStaleItemSelector(run, ids) {
|
|
38
|
+
const current = [...new Set(ids)].sort();
|
|
39
|
+
return [
|
|
40
|
+
`${job_placement_1.RUN_LABEL}=${(0, job_placement_1.getRunLabelValue)(run)}`,
|
|
41
|
+
resources_1.ID_LABEL,
|
|
42
|
+
...(current.length > 0 ? [`${resources_1.ID_LABEL} notin (${current.join(',')})`] : []),
|
|
43
|
+
].join(',');
|
|
44
|
+
}
|
|
45
|
+
exports.getStaleItemSelector = getStaleItemSelector;
|
|
46
|
+
//# sourceMappingURL=remove-stale-items.js.map
|
|
@@ -1,12 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.removeInvalidCharacters = exports.getResourceName = void 0;
|
|
3
|
+
exports.removeInvalidCharacters = exports.getResourceName = exports.getItemSelector = exports.getItemLabels = exports.getItemKey = exports.ID_LABEL = void 0;
|
|
4
|
+
const job_placement_1 = require("./job-placement");
|
|
4
5
|
// An object name is a DNS label: lowercase letters, digits and `-`, at most 63
|
|
5
6
|
// characters, starting and ending with a letter or digit. The id makes a name
|
|
6
7
|
// unique; the item's name is there for people and is cut to fit.
|
|
7
8
|
const MAX_NAME = 63;
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
exports.ID_LABEL = 'hammerkit.dev/id';
|
|
10
|
+
// What tells the objects of an item apart in its namespace: its id, and on a server the
|
|
11
|
+
// run's label too. The runs of a caller execute side by side in their namespace, and two
|
|
12
|
+
// of them often run the same task (the same command on two sandboxes of a repository),
|
|
13
|
+
// so each has objects of its own, which nothing of the other run finds or removes. The
|
|
14
|
+
// first 8 digits of the run label tell runs apart, as they do for a run's proxies.
|
|
15
|
+
function getItemKey(item, env) {
|
|
16
|
+
return env.server ? `${item.id().substring(0, 32)}-${(0, job_placement_1.getRunLabelValue)(env.server.run).substring(0, 8)}` : item.id();
|
|
17
|
+
}
|
|
18
|
+
exports.getItemKey = getItemKey;
|
|
19
|
+
// The labels the objects of an item carry, by which they are found again
|
|
20
|
+
function getItemLabels(item, env) {
|
|
21
|
+
return { [exports.ID_LABEL]: item.id(), ...(0, job_placement_1.getRunLabels)(env) };
|
|
22
|
+
}
|
|
23
|
+
exports.getItemLabels = getItemLabels;
|
|
24
|
+
function getItemSelector(item, env) {
|
|
25
|
+
return Object.entries(getItemLabels(item, env))
|
|
26
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
27
|
+
.join(',');
|
|
28
|
+
}
|
|
29
|
+
exports.getItemSelector = getItemSelector;
|
|
30
|
+
function getResourceName(item, env, suffix) {
|
|
31
|
+
const tail = `-${getItemKey(item, env)}${suffix ?? ''}`;
|
|
10
32
|
const name = removeInvalidCharacters(item.name)
|
|
11
33
|
.slice(0, Math.max(0, MAX_NAME - tail.length))
|
|
12
34
|
.replace(/-+$/, '');
|
|
@@ -8,14 +8,18 @@ const ensure_persistent_data_1 = require("./ensure-persistent-data");
|
|
|
8
8
|
const claim_layout_1 = require("./claim-layout");
|
|
9
9
|
// Where the upload pod (which mounts the work's claim at /dev/hammerkit/<id>) unpacks the
|
|
10
10
|
// archive of an output: the output's place in the claim, which its mount reads
|
|
11
|
-
function getRestoreTarget(work, path
|
|
12
|
-
|
|
11
|
+
function getRestoreTarget(work, path,
|
|
12
|
+
// the folder of the state on a service run, see getStateFilesDir
|
|
13
|
+
stateDir) {
|
|
14
|
+
return `/dev/hammerkit/${work.id()}/${stateDir ? `${stateDir}/` : ''}${(0, claim_layout_1.getClaimSubPath)(work, path)}`;
|
|
13
15
|
}
|
|
14
16
|
exports.getRestoreTarget = getRestoreTarget;
|
|
15
17
|
async function restoreKubernetesData(work, kubernetes, instance, environment, path,
|
|
16
|
-
// the kept claim of the work holds what an earlier run left, which goes first
|
|
18
|
+
// the kept claim of the work holds what an earlier run left, which goes first; on a
|
|
19
|
+
// service run, the outputs go into the folder of `stateKey` (see getStateFilesDir)
|
|
17
20
|
options = {}) {
|
|
18
|
-
const persistence = await (0, volumes_1.getKubernetesPersistence)(work, kubernetes);
|
|
21
|
+
const persistence = await (0, volumes_1.getKubernetesPersistence)(work, kubernetes, options.stateKey);
|
|
22
|
+
const stateDir = kubernetes.server?.serviceRun ? (0, volumes_1.getItemFilesDir)(work, options.stateKey) : undefined;
|
|
19
23
|
await (0, ensure_persistent_data_1.getPodForPersistence)(instance, kubernetes, work, persistence, 'write', async (name) => {
|
|
20
24
|
if (options.reset) {
|
|
21
25
|
await (0, ensure_persistent_data_1.clearClaim)(instance, kubernetes, work, name);
|
|
@@ -24,7 +28,7 @@ options = {}) {
|
|
|
24
28
|
if (!(await environment.file.exists(generatedArchive.filename))) {
|
|
25
29
|
continue;
|
|
26
30
|
}
|
|
27
|
-
const targetPath = getRestoreTarget(work, generatedArchive.path);
|
|
31
|
+
const targetPath = getRestoreTarget(work, generatedArchive.path, stateDir);
|
|
28
32
|
// a claim made for this run (a server removes the claims of a run when it
|
|
29
33
|
// ends) does not have the directory of the output yet
|
|
30
34
|
let failure = null;
|