hammerkit 1.26.0 → 1.27.0-next.10
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 +62 -1
- package/dist/cache/backends/local-cache-backend.js +10 -2
- package/dist/cache/backends/registry-cache-backend.js +2 -2
- package/dist/cache/explain.js +13 -11
- package/dist/cli.js +3 -0
- package/dist/docker/item-network.js +471 -0
- package/dist/docker/pull.js +4 -2
- package/dist/docker/remove-orphaned-containers.js +13 -6
- package/dist/environment/replace-env-variables.js +8 -6
- package/dist/executer/await-completed-dependencies.js +2 -1
- package/dist/executer/check-readiness.js +2 -2
- package/dist/executer/docker-service.js +24 -13
- package/dist/executer/docker-task.js +18 -29
- package/dist/executer/event-cache.js +24 -2
- package/dist/executer/execute-command.js +18 -5
- package/dist/executer/execute-docker.js +4 -1
- package/dist/executer/execution-steps.js +33 -7
- package/dist/executer/get-docker-executor.js +14 -7
- package/dist/executer/scheduler/enqueue-next.js +17 -3
- package/dist/executer/watch-loop.js +2 -2
- package/dist/executer/watch-state-key.js +1 -1
- package/dist/file/within-root.js +42 -0
- package/dist/harness/connect.js +99 -0
- package/dist/harness/format.js +73 -0
- package/dist/harness/mcp-command.js +59 -0
- package/dist/harness/mcp.js +432 -0
- package/dist/harness/opencode.js +492 -0
- package/dist/harness/pi.js +217 -0
- package/dist/harness/sandbox-client.js +411 -0
- package/dist/index.js +33 -36
- package/dist/kubernetes/apply.js +42 -1
- package/dist/kubernetes/await-running-state.js +33 -1
- package/dist/kubernetes/claim-layout.js +71 -0
- package/dist/kubernetes/ensure-http-route.js +1 -1
- package/dist/kubernetes/ensure-kubernetes-deployment-exists.js +46 -28
- package/dist/kubernetes/ensure-kubernetes-persistent-volume-claim-exists.js +132 -5
- package/dist/kubernetes/ensure-kubernetes-service-exists.js +27 -11
- package/dist/kubernetes/ensure-persistent-data.js +75 -24
- package/dist/kubernetes/get-service-ip.js +16 -2
- package/dist/kubernetes/job-placement.js +99 -0
- package/dist/kubernetes/job-secrets.js +320 -0
- package/dist/kubernetes/kept-claims.js +203 -0
- package/dist/kubernetes/kubernetes-instance.js +10 -2
- package/dist/kubernetes/network-gate.js +71 -0
- package/dist/kubernetes/network-policy.js +583 -0
- package/dist/kubernetes/remove-persistent-data.js +3 -1
- package/dist/kubernetes/resources.js +37 -4
- package/dist/kubernetes/restore-kubernetes-data.js +28 -7
- package/dist/kubernetes/secrets.js +7 -9
- package/dist/kubernetes/store-kubernetes-data.js +1 -1
- package/dist/kubernetes/volumes.js +104 -12
- package/dist/log.js +1 -1
- package/dist/main.js +41 -0
- package/dist/network/egress-proxy.js +407 -0
- package/dist/network/port-relay.js +35 -0
- package/dist/optimizer/get-work-cache-stats.js +16 -0
- package/dist/optimizer/work-task-cache-description.js +5 -2
- package/dist/planner/service-definition.js +5 -0
- package/dist/planner/utils/append-work-dependencies.js +5 -2
- package/dist/planner/utils/append-work-environment.js +2 -0
- package/dist/planner/utils/append-work-service.js +5 -0
- package/dist/planner/utils/append-work-task.js +20 -1
- package/dist/planner/utils/assert-item-paths.js +50 -0
- package/dist/planner/utils/hosted-volumes.js +35 -0
- package/dist/planner/utils/task-arguments.js +3 -0
- package/dist/planner/work-network.js +193 -0
- package/dist/planner/work-runtime-docker.js +43 -1
- package/dist/planner/work-runtime-kubernetes.js +370 -111
- package/dist/planner/work-runtime-local.js +2 -1
- package/dist/program.js +33 -5
- package/dist/remote/annotations.js +115 -0
- package/dist/remote/bundle-text.js +24 -0
- package/dist/remote/bundle.js +9 -1
- package/dist/remote/remote-commands.js +15 -3
- package/dist/remote/remote-work-commands.js +272 -71
- package/dist/remote/server-client.js +18 -5
- package/dist/remote/source-manifest.js +15 -2
- package/dist/remote/token-source.js +67 -0
- package/dist/schema/build-file-base-task-schema.js +7 -0
- package/dist/schema/build-file-container-service-schema.js +8 -0
- package/dist/schema/build-file-container-task-schema.js +3 -0
- package/dist/schema/build-file-environment-schema.js +9 -0
- package/dist/schema/network-schema.js +78 -0
- package/dist/schema/reference-parser.js +2 -0
- package/dist/schema/schema-parser.js +16 -3
- package/dist/schema/work-scope-parser.js +5 -0
- package/dist/secrets/command-secret-provider.js +73 -10
- package/dist/secrets/github-app-token-command.js +26 -0
- package/dist/secrets/github-app-token.js +191 -0
- package/dist/server/accounts-api.js +9 -2
- package/dist/server/definitions-api.js +69 -47
- package/dist/server/definitions.js +76 -51
- package/dist/server/executor.js +156 -41
- package/dist/server/git-checkout.js +72 -8
- package/dist/server/kubernetes-backend.js +603 -0
- package/dist/server/kubernetes-sandbox-storage.js +997 -0
- package/dist/server/oauth.js +12 -0
- package/dist/server/plan.js +88 -2
- package/dist/server/policy.js +104 -15
- package/dist/server/proposals-api.js +24 -40
- package/dist/server/proposals.js +12 -25
- package/dist/server/repo-access.js +37 -6
- package/dist/server/run-logs.js +217 -0
- package/dist/server/run-plan.js +48 -2
- package/dist/server/run-request.js +29 -10
- package/dist/server/run-store.js +5 -0
- package/dist/server/run-worker-command.js +13 -1
- package/dist/server/run-worker.js +121 -24
- package/dist/server/runs.js +191 -10
- package/dist/server/sandbox-docker.js +62 -0
- package/dist/server/sandbox-files-api.js +269 -0
- package/dist/server/sandbox-files.js +1054 -0
- package/dist/server/sandbox-patch.js +481 -0
- package/dist/server/sandbox-pod-command.js +15 -0
- package/dist/server/sandbox-pod.js +146 -0
- package/dist/server/sandbox-storage.js +462 -0
- package/dist/server/sandboxes-api.js +216 -0
- package/dist/server/sandboxes.js +519 -0
- package/dist/server/server-command.js +39 -20
- package/dist/server/server-config.js +443 -22
- package/dist/server/server.js +219 -40
- package/dist/utils/abort-on-signals.js +46 -6
- package/dist/utils/run-id.js +12 -0
- package/dist/utils/tar.js +25 -0
- package/harness/opencode.ts +11 -0
- package/harness/pi.ts +7 -0
- package/package.json +8 -2
- package/dist/remote/text-diff.js +0 -89
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.evictKeptClaims = exports.sweepKeptClaims = exports.expireKeptClaims = exports.recordClaimState = exports.forgetClaimState = exports.useClaimState = exports.isKeptClaim = exports.getClaimName = exports.EXPIRES_ANNOTATION = exports.STATE_ANNOTATION = exports.KEPT_BY_LABEL = exports.VOLUME_SCOPE_LABEL = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
const apply_1 = require("./apply");
|
|
6
|
+
const sleep_1 = require("../utils/sleep");
|
|
7
|
+
const job_placement_1 = require("./job-placement");
|
|
8
|
+
const resources_1 = require("./resources");
|
|
9
|
+
// On a server with `backend.keepVolumes`, the claim of a task stays after the run, so the
|
|
10
|
+
// next run of the same source finds the outputs in it and mounts them without restoring
|
|
11
|
+
// them from the cache. A kept claim has no run label (the end of the run removes what has
|
|
12
|
+
// one), but these:
|
|
13
|
+
// the trust of the source of the runs that may mount it (see getVolumeScope)
|
|
14
|
+
exports.VOLUME_SCOPE_LABEL = 'hammerkit.dev/volume-scope';
|
|
15
|
+
// the run label of the run that mounted it last; while it runs, the claim does not expire
|
|
16
|
+
exports.KEPT_BY_LABEL = 'hammerkit.dev/kept-by';
|
|
17
|
+
// the state key of the outputs in it, set once the task completed or they were restored
|
|
18
|
+
exports.STATE_ANNOTATION = 'hammerkit.dev/state';
|
|
19
|
+
// when it goes, set by the end of the run that mounted it last
|
|
20
|
+
exports.EXPIRES_ANNOTATION = 'hammerkit.dev/expires';
|
|
21
|
+
// The claim of a work item: a task's on a server that keeps them carries the scope, so a
|
|
22
|
+
// run of another source never mounts it, and the runs of one scope go one at a time
|
|
23
|
+
// (KubernetesExecutor.exclusive). Any other claim on a server is the run's own, as the
|
|
24
|
+
// runs of a caller share their namespace. 63 characters at most, a pod's volume name.
|
|
25
|
+
function getClaimName(work, kubernetes) {
|
|
26
|
+
const scope = kubernetes.server?.volumeScope;
|
|
27
|
+
if (scope && isTask(work)) {
|
|
28
|
+
return `hammerkit-${work.id()}-${(0, crypto_1.createHash)('sha256').update(scope).digest('hex').slice(0, 12)}`;
|
|
29
|
+
}
|
|
30
|
+
return 'hammerkit-' + (0, resources_1.getItemKey)(work, kubernetes);
|
|
31
|
+
}
|
|
32
|
+
exports.getClaimName = getClaimName;
|
|
33
|
+
// whether the claim of the work item stays after the run
|
|
34
|
+
function isKeptClaim(work, kubernetes) {
|
|
35
|
+
return !!kubernetes.server?.volumeScope && isTask(work);
|
|
36
|
+
}
|
|
37
|
+
exports.isKeptClaim = isKeptClaim;
|
|
38
|
+
function isTask(work) {
|
|
39
|
+
return work.data.type === 'container-task';
|
|
40
|
+
}
|
|
41
|
+
async function readClaim(instance, namespace, name) {
|
|
42
|
+
try {
|
|
43
|
+
return await instance.coreApi.readNamespacedPersistentVolumeClaim({ name, namespace });
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
if ((0, apply_1.statusCodeOf)(e) === 404) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
throw e;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// The state key of the outputs a kept claim holds, null when it holds none that are whole.
|
|
53
|
+
// The claim is the run's from now: it does not expire or make way for another claim
|
|
54
|
+
// before the run ended, so outputs the run found in it are still there when it mounts them.
|
|
55
|
+
async function useClaimState(instance, kubernetes, name) {
|
|
56
|
+
let claim;
|
|
57
|
+
try {
|
|
58
|
+
claim = await instance.objectApi.patch({
|
|
59
|
+
apiVersion: 'v1',
|
|
60
|
+
kind: 'PersistentVolumeClaim',
|
|
61
|
+
metadata: {
|
|
62
|
+
name,
|
|
63
|
+
namespace: kubernetes.namespace,
|
|
64
|
+
labels: { [exports.KEPT_BY_LABEL]: (0, job_placement_1.getRunLabelValue)(kubernetes.server?.run ?? '') },
|
|
65
|
+
// null removes it
|
|
66
|
+
annotations: { [exports.EXPIRES_ANNOTATION]: null },
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
if ((0, apply_1.statusCodeOf)(e) === 404) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
throw e;
|
|
75
|
+
}
|
|
76
|
+
// one being removed holds nothing to mount, nor one of another source (ensured anyway
|
|
77
|
+
// by the hash of the source in its name)
|
|
78
|
+
if (claim.metadata?.deletionTimestamp ||
|
|
79
|
+
claim.metadata?.labels?.[exports.VOLUME_SCOPE_LABEL] !== kubernetes.server?.volumeScope) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
return claim.metadata?.annotations?.[exports.STATE_ANNOTATION] ?? null;
|
|
83
|
+
}
|
|
84
|
+
exports.useClaimState = useClaimState;
|
|
85
|
+
async function annotate(instance, namespace, name, annotations) {
|
|
86
|
+
try {
|
|
87
|
+
await instance.objectApi.patch({
|
|
88
|
+
apiVersion: 'v1',
|
|
89
|
+
kind: 'PersistentVolumeClaim',
|
|
90
|
+
metadata: { name, namespace, annotations: annotations },
|
|
91
|
+
});
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
if ((0, apply_1.statusCodeOf)(e) === 404) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
throw e;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Before a task writes to its kept claim: the outputs in it stop being those of a state.
|
|
102
|
+
// Whether the claim was there, with what an earlier run left in it.
|
|
103
|
+
function forgetClaimState(instance, kubernetes, name) {
|
|
104
|
+
return annotate(instance, kubernetes.namespace, name, { [exports.STATE_ANNOTATION]: null });
|
|
105
|
+
}
|
|
106
|
+
exports.forgetClaimState = forgetClaimState;
|
|
107
|
+
// Once the outputs of `stateKey` are whole in the kept claim
|
|
108
|
+
async function recordClaimState(instance, kubernetes, name, stateKey) {
|
|
109
|
+
await annotate(instance, kubernetes.namespace, name, { [exports.STATE_ANNOTATION]: stateKey });
|
|
110
|
+
}
|
|
111
|
+
exports.recordClaimState = recordClaimState;
|
|
112
|
+
// The kept claims in a namespace
|
|
113
|
+
async function listKeptClaims(instance, namespace, labelSelector = exports.VOLUME_SCOPE_LABEL) {
|
|
114
|
+
const claims = await instance.coreApi.listNamespacedPersistentVolumeClaim({ namespace, labelSelector });
|
|
115
|
+
return claims.items.filter((claim) => !claim.metadata?.deletionTimestamp);
|
|
116
|
+
}
|
|
117
|
+
async function removeClaims(instance, namespace, claims, timeout) {
|
|
118
|
+
const names = [];
|
|
119
|
+
for (const claim of claims) {
|
|
120
|
+
const name = claim.metadata?.name;
|
|
121
|
+
if (!name) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
// only as it was listed: one a run took for itself since (useClaimState) stays
|
|
126
|
+
await instance.coreApi.deleteNamespacedPersistentVolumeClaim({
|
|
127
|
+
name,
|
|
128
|
+
namespace,
|
|
129
|
+
body: { preconditions: { resourceVersion: claim.metadata?.resourceVersion } },
|
|
130
|
+
});
|
|
131
|
+
names.push(name);
|
|
132
|
+
}
|
|
133
|
+
catch (e) {
|
|
134
|
+
if ((0, apply_1.statusCodeOf)(e) === 404) {
|
|
135
|
+
names.push(name);
|
|
136
|
+
}
|
|
137
|
+
else if ((0, apply_1.statusCodeOf)(e) !== 409) {
|
|
138
|
+
throw e;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// a run mounts a claim of the same name again
|
|
143
|
+
const deadline = Date.now() + timeout;
|
|
144
|
+
for (const name of names) {
|
|
145
|
+
while (await readClaim(instance, namespace, name)) {
|
|
146
|
+
if (Date.now() > deadline) {
|
|
147
|
+
throw new Error(`the claim ${name} is still there after ${Math.round(timeout / 1000)}s`);
|
|
148
|
+
}
|
|
149
|
+
await (0, sleep_1.sleep)(1000);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return names;
|
|
153
|
+
}
|
|
154
|
+
// At the end of a run: the claims it kept go `keep` milliseconds from now, unless a run
|
|
155
|
+
// mounts them before
|
|
156
|
+
async function expireKeptClaims(instance, namespace, runLabel, expires) {
|
|
157
|
+
for (const claim of await listKeptClaims(instance, namespace, `${exports.KEPT_BY_LABEL}=${runLabel}`)) {
|
|
158
|
+
if (claim.metadata?.name) {
|
|
159
|
+
await annotate(instance, namespace, claim.metadata.name, { [exports.EXPIRES_ANNOTATION]: expires.toISOString() });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
exports.expireKeptClaims = expireKeptClaims;
|
|
164
|
+
// The kept claims that expired, and those a run left without an expiry because the server
|
|
165
|
+
// stopped before its end (all of them when the server keeps none any more), but those of
|
|
166
|
+
// the scopes `inUse`: a run of the scope executes and mounts them, or may in a moment.
|
|
167
|
+
// The names of those removed, and when the next of the others expires.
|
|
168
|
+
async function sweepKeptClaims(instance, namespace, options) {
|
|
169
|
+
const expired = [];
|
|
170
|
+
let next = null;
|
|
171
|
+
for (const claim of await listKeptClaims(instance, namespace)) {
|
|
172
|
+
if (options.inUse?.has(claim.metadata?.labels?.[exports.VOLUME_SCOPE_LABEL] ?? '')) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const expires = Date.parse(claim.metadata?.annotations?.[exports.EXPIRES_ANNOTATION] ?? '');
|
|
176
|
+
if (!options.keep || !(expires > options.now.getTime())) {
|
|
177
|
+
expired.push(claim);
|
|
178
|
+
}
|
|
179
|
+
else if (next === null || expires < next) {
|
|
180
|
+
next = expires;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const removed = await removeClaims(instance, namespace, expired, options.timeout);
|
|
184
|
+
return { removed, next: next === null ? null : new Date(next) };
|
|
185
|
+
}
|
|
186
|
+
exports.sweepKeptClaims = sweepKeptClaims;
|
|
187
|
+
// When a run needs room under the quota of its namespace: the kept claims it does not use
|
|
188
|
+
// go, those of the other sources and those of its own a task of this run did not take (one
|
|
189
|
+
// renamed or changed since). Of another scope, only one its last run released, which has an
|
|
190
|
+
// expiry: one without is mounted by a run of the caller that executes now (or was left by
|
|
191
|
+
// a server that stopped, which the next sweep removes). The names of those removed.
|
|
192
|
+
async function evictKeptClaims(instance, kubernetes, timeout) {
|
|
193
|
+
const server = kubernetes.server;
|
|
194
|
+
if (!server?.volumeScope) {
|
|
195
|
+
return [];
|
|
196
|
+
}
|
|
197
|
+
const unused = await listKeptClaims(instance, kubernetes.namespace, `${exports.VOLUME_SCOPE_LABEL},${exports.KEPT_BY_LABEL}!=${(0, job_placement_1.getRunLabelValue)(server.run)}`);
|
|
198
|
+
const released = unused.filter((claim) => claim.metadata?.labels?.[exports.VOLUME_SCOPE_LABEL] === server.volumeScope ||
|
|
199
|
+
!!claim.metadata?.annotations?.[exports.EXPIRES_ANNOTATION]);
|
|
200
|
+
return removeClaims(instance, kubernetes.namespace, released, timeout);
|
|
201
|
+
}
|
|
202
|
+
exports.evictKeptClaims = evictKeptClaims;
|
|
203
|
+
//# sourceMappingURL=kept-claims.js.map
|
|
@@ -12,23 +12,31 @@ function createKubernetesInstances(kubernetes) {
|
|
|
12
12
|
else {
|
|
13
13
|
kc.loadFromDefault();
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
if (kubernetes.context) {
|
|
16
|
+
kc.setCurrentContext(kubernetes.context);
|
|
17
|
+
}
|
|
16
18
|
const coreApi = kc.makeApiClient(client_node_1.CoreV1Api);
|
|
17
19
|
const batchApi = kc.makeApiClient(client_node_1.BatchV1Api);
|
|
18
20
|
const appsApi = kc.makeApiClient(client_node_1.AppsV1Api);
|
|
19
21
|
const objectApi = kc.makeApiClient(client_node_1.KubernetesObjectApi);
|
|
22
|
+
const authApi = kc.makeApiClient(client_node_1.AuthenticationV1Api);
|
|
20
23
|
const exec = new client_node_1.Exec(kc);
|
|
21
24
|
return {
|
|
22
25
|
objectApi,
|
|
26
|
+
log: new client_node_1.Log(kc),
|
|
23
27
|
exec,
|
|
24
28
|
appsApi,
|
|
25
29
|
coreApi,
|
|
26
30
|
batchApi,
|
|
31
|
+
authApi,
|
|
27
32
|
};
|
|
28
33
|
}
|
|
29
34
|
catch (e) {
|
|
30
35
|
if ((0, log_1.getErrorMessage)(e) === 'No active cluster!') {
|
|
31
|
-
if (kubernetes.
|
|
36
|
+
if (!kubernetes.context) {
|
|
37
|
+
throw new Error('No cluster found: no kubeconfig names a current context, and this is not a pod of a cluster');
|
|
38
|
+
}
|
|
39
|
+
else if (kubernetes.kubeConfig) {
|
|
32
40
|
throw new Error(`No cluster found for context ${kubernetes.context} in ${kubernetes.kubeConfig}`);
|
|
33
41
|
}
|
|
34
42
|
else {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getNetworkGateContainer = exports.HELPER_SECURITY_CONTEXT = exports.NETWORK_GATE_SCRIPT = exports.NETWORK_GATE_CONTAINER = exports.NETWORK_GATE_TIMEOUT_MS = void 0;
|
|
4
|
+
const egress_proxy_1 = require("../network/egress-proxy");
|
|
5
|
+
// Some network plugins (kind's kindnet among them) enforce a NetworkPolicy a
|
|
6
|
+
// moment after a pod started, so a limited pod's first connections could
|
|
7
|
+
// still get out. The gate is the pod's first init container: it waits until
|
|
8
|
+
// the pod no longer reaches the cluster DNS, which the policy of every limited
|
|
9
|
+
// item denies, so the item's own containers start only once the policy holds.
|
|
10
|
+
// A policy that never takes hold fails the pod instead of running it open.
|
|
11
|
+
// A script for `node -e` in the proxy's image, like the proxy.
|
|
12
|
+
exports.NETWORK_GATE_TIMEOUT_MS = 60000;
|
|
13
|
+
// the name of the gate's init container
|
|
14
|
+
exports.NETWORK_GATE_CONTAINER = 'hammerkit-network';
|
|
15
|
+
// Kept free of anything but Node's own modules.
|
|
16
|
+
exports.NETWORK_GATE_SCRIPT = `
|
|
17
|
+
const fs = require('fs')
|
|
18
|
+
const net = require('net')
|
|
19
|
+
const resolvConf = process.env.HAMMERKIT_GATE_RESOLV_CONF || '/etc/resolv.conf'
|
|
20
|
+
const port = parseInt(process.env.HAMMERKIT_GATE_PORT || '53', 10)
|
|
21
|
+
const deadline = Date.now() + parseInt(process.env.HAMMERKIT_GATE_TIMEOUT_MS || '${exports.NETWORK_GATE_TIMEOUT_MS}', 10)
|
|
22
|
+
function fail(message) {
|
|
23
|
+
process.stderr.write('hammerkit: ' + message + '\\n')
|
|
24
|
+
try {
|
|
25
|
+
fs.writeFileSync('/dev/termination-log', 'hammerkit: ' + message)
|
|
26
|
+
} catch (e) {}
|
|
27
|
+
process.exit(1)
|
|
28
|
+
}
|
|
29
|
+
let conf = ''
|
|
30
|
+
try {
|
|
31
|
+
conf = fs.readFileSync(resolvConf, 'utf8')
|
|
32
|
+
} catch (e) {}
|
|
33
|
+
const match = /^nameserver\\s+(\\S+)/m.exec(conf)
|
|
34
|
+
if (!match) {
|
|
35
|
+
fail('the pod has no cluster DNS, so its network policy can not be checked')
|
|
36
|
+
}
|
|
37
|
+
const nameserver = match[1]
|
|
38
|
+
function attempt() {
|
|
39
|
+
const socket = net.connect({ host: nameserver, port, timeout: 2000 })
|
|
40
|
+
socket.once('connect', () => {
|
|
41
|
+
socket.destroy()
|
|
42
|
+
if (Date.now() > deadline) {
|
|
43
|
+
fail('the network policy of this pod is not enforced, it still reaches ' + nameserver)
|
|
44
|
+
}
|
|
45
|
+
setTimeout(attempt, 500)
|
|
46
|
+
})
|
|
47
|
+
// dropped or refused: the policy holds
|
|
48
|
+
socket.once('timeout', () => process.exit(0))
|
|
49
|
+
socket.once('error', () => process.exit(0))
|
|
50
|
+
}
|
|
51
|
+
attempt()
|
|
52
|
+
`;
|
|
53
|
+
exports.HELPER_SECURITY_CONTEXT = {
|
|
54
|
+
runAsNonRoot: true,
|
|
55
|
+
// the image's `node` user
|
|
56
|
+
runAsUser: 1000,
|
|
57
|
+
allowPrivilegeEscalation: false,
|
|
58
|
+
readOnlyRootFilesystem: true,
|
|
59
|
+
capabilities: { drop: ['ALL'] },
|
|
60
|
+
};
|
|
61
|
+
function getNetworkGateContainer() {
|
|
62
|
+
return {
|
|
63
|
+
name: exports.NETWORK_GATE_CONTAINER,
|
|
64
|
+
image: egress_proxy_1.EGRESS_PROXY_IMAGE,
|
|
65
|
+
command: ['node', '-e', exports.NETWORK_GATE_SCRIPT],
|
|
66
|
+
securityContext: exports.HELPER_SECURITY_CONTEXT,
|
|
67
|
+
resources: { limits: { memory: '64Mi' } },
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
exports.getNetworkGateContainer = getNetworkGateContainer;
|
|
71
|
+
//# sourceMappingURL=network-gate.js.map
|