alchemy-docker-k3s 0.1.4 → 0.1.5

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/dist/index.d.mts CHANGED
@@ -42,6 +42,29 @@ interface K3sDefinition {
42
42
  };
43
43
  /** K3s' built-in Flannel data plane. @default "vxlan" */
44
44
  flannelBackend?: "vxlan" | "wireguard-native";
45
+ /**
46
+ * Kubelet settings applied to every node. Changing them re-runs the K3s installer
47
+ * on each existing node in place, one node at a time; running containers keep running.
48
+ */
49
+ kubelet?: KubeletSettings;
50
+ }
51
+ interface KubeletSettings {
52
+ /**
53
+ * Image garbage collection. The kubelet starts deleting unused images when the
54
+ * image filesystem is `highThresholdPercent` full and stops at `lowThresholdPercent`.
55
+ * Unset keeps the kubelet defaults (85 % and 80 %).
56
+ */
57
+ imageGc?: {
58
+ /** Integer percentage, 1–100, above `lowThresholdPercent`. */
59
+ highThresholdPercent: number;
60
+ /** Integer percentage, 0–99, below `highThresholdPercent`. */
61
+ lowThresholdPercent: number;
62
+ };
63
+ /**
64
+ * Further kubelet flags as `name=value`, passed as `--kubelet-arg`. Flags the package
65
+ * manages itself (`cloud-provider`, `provider-id`, `image-gc-*`) are rejected.
66
+ */
67
+ extraArgs?: string[];
45
68
  }
46
69
  interface NormalizedK3sDefinition {
47
70
  channel: `v1.${number}`;
@@ -55,6 +78,8 @@ interface NormalizedK3sDefinition {
55
78
  traefikValues: Record<string, unknown>;
56
79
  };
57
80
  flannelBackend: "vxlan" | "wireguard-native";
81
+ /** Validated kubelet flags as `name=value`, without the `--kubelet-arg` prefix. */
82
+ kubeletArgs: string[];
58
83
  }
59
84
  interface ClusterVersion {
60
85
  node: string;
@@ -108,4 +133,4 @@ declare const Cluster: (id: string, props: ClusterProps) => Effect.Effect<Cluste
108
133
  //#region src/cluster-state.d.ts
109
134
  declare const ClusterState: import("alchemy").ResourceClass<ClusterResource>;
110
135
  //#endregion
111
- export { Cluster, type ClusterAttributes, type Cluster$1 as ClusterInstance, type ClusterProps, ClusterState, type K3sDefinition, type PortMapping, Providers, providers };
136
+ export { Cluster, type ClusterAttributes, type Cluster$1 as ClusterInstance, type ClusterProps, ClusterState, type K3sDefinition, type KubeletSettings, type PortMapping, Providers, providers };
package/dist/index.mjs CHANGED
@@ -51,6 +51,24 @@ const validateUpdateWindow = (window) => {
51
51
  }
52
52
  return window;
53
53
  };
54
+ const MANAGED_KUBELET_FLAGS = /^(cloud-provider|provider-id|image-gc-[a-z-]+)=/;
55
+ const KUBELET_FLAG = /^[a-z][a-z0-9-]*=\S*$/;
56
+ /** Validates kubelet settings and renders them as `name=value` kubelet flags. */
57
+ const kubeletArguments = (settings = {}) => {
58
+ const args = [];
59
+ if (settings.imageGc !== void 0) {
60
+ const { highThresholdPercent: high, lowThresholdPercent: low } = settings.imageGc;
61
+ const percent = (value) => Number.isInteger(value) && value >= 0 && value <= 100;
62
+ if (!percent(high) || !percent(low) || low >= high) throw new Error(`kubelet.imageGc thresholds must be integer percentages with lowThresholdPercent < highThresholdPercent; received ${JSON.stringify(settings.imageGc)}`);
63
+ args.push(`image-gc-high-threshold=${high}`, `image-gc-low-threshold=${low}`);
64
+ }
65
+ for (const arg of settings.extraArgs ?? []) {
66
+ if (!KUBELET_FLAG.test(arg)) throw new Error(`kubelet.extraArgs entries must be name=value without a leading --; received ${JSON.stringify(arg)}`);
67
+ if (MANAGED_KUBELET_FLAGS.test(arg)) throw new Error(`kubelet.extraArgs must not set ${JSON.stringify(arg.split("=")[0])}, which the package manages`);
68
+ args.push(arg);
69
+ }
70
+ return args;
71
+ };
54
72
  const normalizeK3sDefinition = (definition) => {
55
73
  const normalized = {
56
74
  channel: validateChannel(definition.channel),
@@ -63,7 +81,8 @@ const normalizeK3sDefinition = (definition) => {
63
81
  metricsServer: definition.addons?.metricsServer ?? true,
64
82
  traefikValues: definition.addons?.traefikValues ?? {}
65
83
  },
66
- flannelBackend: definition.flannelBackend ?? "vxlan"
84
+ flannelBackend: definition.flannelBackend ?? "vxlan",
85
+ kubeletArgs: kubeletArguments(definition.kubelet)
67
86
  };
68
87
  if (normalized.flannelBackend !== "vxlan" && normalized.flannelBackend !== "wireguard-native") throw new Error(`flannelBackend must be "vxlan" or "wireguard-native"; received ${JSON.stringify(normalized.flannelBackend)}`);
69
88
  const traefikValues = normalized.addons.traefikValues;
@@ -241,7 +260,8 @@ const buildCreateArgs = (props, version, token) => {
241
260
  `--cluster-cidr=${props.k3s.clusterCidr}`,
242
261
  `--service-cidr=${props.k3s.serviceCidr}`,
243
262
  `--cluster-dns=${props.k3s.clusterDns}`,
244
- `--flannel-backend=${props.k3s.flannelBackend}`
263
+ `--flannel-backend=${props.k3s.flannelBackend}`,
264
+ ...props.k3s.kubeletArgs.map((arg) => `--kubelet-arg=${arg}`)
245
265
  ]) args.push("--k3s-arg", `${value}@server:0`);
246
266
  if (!props.k3s.addons.traefik) args.push("--k3s-arg", "--disable=traefik@server:0");
247
267
  if (!props.k3s.addons.metricsServer) args.push("--k3s-arg", "--disable=metrics-server@server:0");
@@ -425,7 +445,8 @@ const Cluster = (id, props) => Effect.gen(function* () {
425
445
  clusterCidr: k3s.clusterCidr,
426
446
  serviceCidr: k3s.serviceCidr,
427
447
  clusterDns: k3s.clusterDns,
428
- addons: k3s.addons
448
+ addons: k3s.addons,
449
+ kubeletArgs: k3s.kubeletArgs
429
450
  })
430
451
  });
431
452
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alchemy-docker-k3s",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Persistent local k3d clusters for Alchemy",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",