Skip to content

Commit d27fe70

Browse files
authored
feat: parameterize bootstrap artifacts (#10)
## Summary - prompt for static artifact naming in the CLI and pipe defaults from helm values - ensure chart templates consume global naming overrides for genesis/static-nodes secrets - standardize output artifact names across file and kubernetes targets ## Testing - bun run typecheck - bun run check - bun run test ## Summary by Sourcery Parameterize bootstrap artifact naming by adding user-configurable options in the CLI, propagating these parameters through the bootstrap and output code, and aligning Helm chart templates and documentation to consume global overrides. New Features: - Add CLI flags and prompts to customize bootstrap artifact names for static node service, pod prefix, genesis and static-nodes ConfigMaps, and faucet prefixes Enhancements: - Carry custom artifact naming parameters through the bootstrap logic into file and Kubernetes output modules - Introduce a generic promptForText helper for text-based user input Documentation: - Extend Helm chart values, templates, and READMEs to support global naming overrides for bootstrap artifacts Tests: - Update and add tests to verify custom artifact naming in CLI prompts, file outputs, and Kubernetes ConfigMaps/Secrets
1 parent 3e9ca82 commit d27fe70

14 files changed

Lines changed: 625 additions & 77 deletions

File tree

charts/network/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,14 @@ A Helm chart for a blockchain network on Kubernetes
1717
| | network-bootstrapper | * |
1818
| | network-nodes | * |
1919

20+
## Values
21+
22+
| Key | Type | Default | Description |
23+
|-----|------|---------|-------------|
24+
| global | object | `{"networkNodes":{"faucetArtifactPrefix":"besu-faucet","genesisConfigMapName":"besu-genesis","podPrefix":"","serviceName":"","staticNodesConfigMapName":"besu-static-nodes"}}` | Global configuration shared across subcharts. |
25+
| global.networkNodes | object | `{"faucetArtifactPrefix":"besu-faucet","genesisConfigMapName":"besu-genesis","podPrefix":"","serviceName":"","staticNodesConfigMapName":"besu-static-nodes"}` | Defaults consumed by Besu network node workloads. |
26+
| global.networkNodes.faucetArtifactPrefix | string | `"besu-faucet"` | Prefix used for faucet ConfigMaps and Secrets. |
27+
| global.networkNodes.genesisConfigMapName | string | `"besu-genesis"` | ConfigMap name storing the generated genesis.json artifact. |
28+
| global.networkNodes.podPrefix | string | `""` | StatefulSet prefix used for validator pod hostnames. |
29+
| global.networkNodes.serviceName | string | `""` | Kubernetes Service name fronting validator pods to align bootstrapper static-nodes output. |
30+
| global.networkNodes.staticNodesConfigMapName | string | `"besu-static-nodes"` | ConfigMap name storing static-nodes.json entries. |

charts/network/charts/network-bootstrapper/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,19 @@ A Helm chart for Kubernetes
4848
| settings.defaultStaticNodeDiscoveryPort | int | `30303` | Default UDP discovery port used in generated enode URIs when no override is provided. |
4949
| settings.defaultStaticNodePort | int | `30303` | Default TCP P2P port used in generated enode URIs when no override is provided. |
5050
| settings.evmStackSize | int | `nil` | Maximum EVM stack size permitted for contract execution. |
51+
| settings.faucetArtifactPrefix | string | `nil` | Prefix used for faucet ConfigMaps and Secrets. |
5152
| settings.gasLimit | int | `nil` | Genesis block gas limit expressed as a decimal number. |
5253
| settings.gasPrice | int | `nil` | Base gas price in wei applied across the network. |
54+
| settings.genesisConfigMapName | string | `nil` | ConfigMap name storing the generated genesis.json payload. |
5355
| settings.outputType | string | `"kubernetes"` | Output target for generated artefacts: screen, file, or kubernetes. |
5456
| settings.secondsPerBlock | int | `nil` | Block interval in seconds for the genesis configuration. |
5557
| settings.staticNodeDiscoveryPort | int | `nil` | UDP discovery port embedded in static node enode URIs. |
5658
| settings.staticNodeDomain | string | `nil` | DNS suffix appended to generated static node hostnames. |
5759
| settings.staticNodeNamespace | string | `nil` | Namespace component inserted between service name and domain in static node hostnames. |
60+
| settings.staticNodePodPrefix | string | `nil` | StatefulSet prefix applied to validator pod hostnames. |
5861
| settings.staticNodePort | int | `nil` | TCP P2P port embedded in static node enode URIs. |
62+
| settings.staticNodeServiceName | string | `nil` | Headless Service name used when constructing static node hostnames. |
63+
| settings.staticNodesConfigMapName | string | `nil` | ConfigMap name storing the generated static-nodes.json payload. |
5964
| settings.validators | int | `nil` | Number of validator identities to generate (default 4). |
6065
| tolerations | list | `[]` | Tolerations allowing the bootstrapper pod onto tainted nodes. |
6166
| volumeMounts | list | `[]` | Extra volume mounts applied to the bootstrapper container. |

charts/network/charts/network-bootstrapper/templates/job.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,20 @@ spec:
5151
{{- $resolvedStaticDomain := default $clusterDomain .Values.settings.staticNodeDomain }}
5252
{{- $resolvedStaticPort := default $defaultStaticPort .Values.settings.staticNodePort }}
5353
{{- $resolvedStaticDiscovery := default $defaultStaticDiscovery .Values.settings.staticNodeDiscoveryPort }}
54+
{{- $globalNodes := default (dict) .Values.global.networkNodes }}
55+
{{- $autoNames := dict "service" "besu-node" "podPrefix" "besu-node-validator" }}
56+
{{- with (index $.Subcharts "network-nodes") }}
57+
{{- $service := include "nodes.fullname" . }}
58+
{{- $_ := set $autoNames "service" $service }}
59+
{{- $_ := set $autoNames "podPrefix" (printf "%s-validator" $service) }}
60+
{{- end }}
61+
{{- $serviceOverride := coalesce .Values.settings.staticNodeServiceName (get $globalNodes "serviceName") }}
62+
{{- $resolvedServiceName := default (index $autoNames "service") $serviceOverride }}
63+
{{- $podOverride := coalesce .Values.settings.staticNodePodPrefix (get $globalNodes "podPrefix") }}
64+
{{- $resolvedPodPrefix := default (index $autoNames "podPrefix") $podOverride }}
65+
{{- $resolvedGenesisName := default "besu-genesis" (default (get $globalNodes "genesisConfigMapName") .Values.settings.genesisConfigMapName) }}
66+
{{- $resolvedStaticNodesName := default "besu-static-nodes" (default (get $globalNodes "staticNodesConfigMapName") .Values.settings.staticNodesConfigMapName) }}
67+
{{- $resolvedFaucetPrefix := default "besu-faucet" (default (get $globalNodes "faucetArtifactPrefix") .Values.settings.faucetArtifactPrefix) }}
5468
{{- with .Values.settings.validators }}
5569
- --validators={{ . }}
5670
{{- end }}
@@ -60,6 +74,11 @@ spec:
6074
{{- end }}
6175
- --static-node-port={{ $resolvedStaticPort }}
6276
- --static-node-discovery-port={{ $resolvedStaticDiscovery }}
77+
- --static-node-service-name={{ $resolvedServiceName }}
78+
- --static-node-pod-prefix={{ $resolvedPodPrefix }}
79+
- --genesis-configmap-name={{ $resolvedGenesisName }}
80+
- --static-nodes-configmap-name={{ $resolvedStaticNodesName }}
81+
- --faucet-artifact-prefix={{ $resolvedFaucetPrefix }}
6382
{{- with .Values.settings.allocations }}
6483
- --allocations={{ . }}
6584
{{- end }}

charts/network/charts/network-bootstrapper/values.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,16 @@ settings:
134134
staticNodePort:
135135
# -- (int) UDP discovery port embedded in static node enode URIs.
136136
staticNodeDiscoveryPort:
137+
# -- (string) Headless Service name used when constructing static node hostnames.
138+
staticNodeServiceName:
139+
# -- (string) StatefulSet prefix applied to validator pod hostnames.
140+
staticNodePodPrefix:
141+
# -- (string) ConfigMap name storing the generated genesis.json payload.
142+
genesisConfigMapName:
143+
# -- (string) ConfigMap name storing the generated static-nodes.json payload.
144+
staticNodesConfigMapName:
145+
# -- (string) Prefix used for faucet ConfigMaps and Secrets.
146+
faucetArtifactPrefix:
137147
# -- (string) Path to a JSON allocations file providing pre-funded accounts.
138148
allocations:
139149
# -- (string) Output target for generated artefacts: screen, file, or kubernetes.

charts/network/charts/network-nodes/templates/statefulset-rpc.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ metadata:
77
app.kubernetes.io/component: rpc
88
spec:
99
{{- $root := . }}
10+
{{- $globalNodes := .Values.global.networkNodes | default (dict) }}
11+
{{- $genesisConfigMapName := default "besu-genesis" (get $globalNodes "genesisConfigMapName") }}
12+
{{- $staticNodesConfigMapName := default "besu-static-nodes" (get $globalNodes "staticNodesConfigMapName") }}
1013
{{- $persistence := .Values.persistence | default (dict) }}
1114
{{- $persistenceEnabled := default false (get $persistence "enabled") }}
1215
{{- $existingClaim := default "" (get $persistence "existingClaim") }}
@@ -177,10 +180,10 @@ spec:
177180
name: {{ include "nodes.fullname" . }}-config
178181
- name: besu-genesis
179182
configMap:
180-
name: besu-genesis
183+
name: {{ $genesisConfigMapName }}
181184
- name: besu-static-nodes
182185
configMap:
183-
name: besu-static-nodes
186+
name: {{ $staticNodesConfigMapName }}
184187
{{- if $useExistingClaim }}
185188
- name: {{ $volumeName }}
186189
persistentVolumeClaim:

charts/network/charts/network-nodes/templates/statefulset-validator.yaml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ spec:
2727
{{- $useClaimTemplate := and $persistenceEnabled (not $useExistingClaim) }}
2828
{{- $privateKeyFilename := default "privateKey" .Values.config.privateKeyFilename }}
2929
{{- $shouldMountPersistence := $persistenceEnabled }}
30+
{{- $globalNodes := .Values.global.networkNodes | default (dict) }}
31+
{{- $genesisConfigMapName := default "besu-genesis" (get $globalNodes "genesisConfigMapName") }}
32+
{{- $staticNodesConfigMapName := default "besu-static-nodes" (get $globalNodes "staticNodesConfigMapName") }}
33+
{{- $validatorPodPrefix := default "besu-node-validator" (get $globalNodes "podPrefix") }}
3034
{{- $validatorReplicaBudget := (include "nodes.validatorReplicaCount" . | int) }}
3135
{{- $initContainers := .Values.initContainers | default (dict) }}
3236
{{- $sharedInitContainers := get $initContainers "shared" }}
@@ -180,17 +184,17 @@ spec:
180184
name: {{ include "nodes.fullname" . }}-config
181185
- name: besu-genesis
182186
configMap:
183-
name: besu-genesis
187+
name: {{ $genesisConfigMapName }}
184188
- name: besu-static-nodes
185189
configMap:
186-
name: besu-static-nodes
190+
name: {{ $staticNodesConfigMapName }}
187191
- name: besu-private-key
188192
projected:
189193
{{- if gt $validatorReplicaBudget 0 }}
190194
sources:
191195
{{- range $i, $_ := until $validatorReplicaBudget }}
192196
- secret:
193-
name: {{ printf "besu-node-validator-%d-private-key" $i }}
197+
name: {{ printf "%s-%d-private-key" $validatorPodPrefix $i }}
194198
items:
195199
- key: privateKey
196200
path: {{ printf "%s-validator-%d/privateKey" (include "nodes.fullname" $root) $i }}

charts/network/values.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,16 @@
11
# This parent chart does not define additional values; override subchart defaults here when packaging composite releases.
2+
3+
# -- (object) Global configuration shared across subcharts.
4+
global:
5+
# -- (object) Defaults consumed by Besu network node workloads.
6+
networkNodes:
7+
# -- (string) Kubernetes Service name fronting validator pods to align bootstrapper static-nodes output.
8+
serviceName: ""
9+
# -- (string) StatefulSet prefix used for validator pod hostnames.
10+
podPrefix: ""
11+
# -- (string) ConfigMap name storing the generated genesis.json artifact.
12+
genesisConfigMapName: besu-genesis
13+
# -- (string) ConfigMap name storing static-nodes.json entries.
14+
staticNodesConfigMapName: besu-static-nodes
15+
# -- (string) Prefix used for faucet ConfigMaps and Secrets.
16+
faucetArtifactPrefix: besu-faucet

src/cli/build-command.test.ts

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
22

33
import { getAddress } from "viem";
4+
import { ARTIFACT_DEFAULTS } from "../constants/artifact-defaults.ts";
45
import type { BesuAllocAccount } from "../genesis/besu-genesis.service.ts";
56
import { ALGORITHM } from "../genesis/besu-genesis.service.ts";
67
import type { GeneratedNodeKey } from "../keys/node-key-factory.ts";
@@ -16,6 +17,13 @@ const EXPECTED_DEFAULT_VALIDATOR = 4;
1617
const DEFAULT_STATIC_NODE_PORT = 30_303;
1718
const CUSTOM_STATIC_NODE_PORT = 40_000;
1819
const LEADING_DOT_REGEX = /^\./u;
20+
const {
21+
staticNodeServiceName: DEFAULT_SERVICE_NAME,
22+
staticNodePodPrefix: DEFAULT_POD_PREFIX,
23+
genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME,
24+
staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME,
25+
faucetArtifactPrefix: DEFAULT_FAUCET_PREFIX,
26+
} = ARTIFACT_DEFAULTS;
1927
const UNCOMPRESSED_PUBLIC_KEY_PREFIX = "04";
2028
const UNCOMPRESSED_PUBLIC_KEY_LENGTH = 130;
2129
const HEX_RADIX = 16;
@@ -46,6 +54,10 @@ const createFactoryStub = () => {
4654
};
4755
};
4856

57+
const passthroughTextPrompt: BootstrapDependencies["promptForText"] = ({
58+
defaultValue,
59+
}) => Promise.resolve(defaultValue);
60+
4961
const expectedAddress = (index: number) => {
5062
const pattern = index.toString(HEX_RADIX).padStart(PAD_WIDTH, PAD_CHAR);
5163
return getAddress(`0x${pattern.repeat(ADDRESS_REPEAT)}`);
@@ -61,7 +73,9 @@ const expectedStaticNodeUri = (
6173
domain?: string,
6274
port: number = DEFAULT_STATIC_NODE_PORT,
6375
discoveryPort: number = DEFAULT_STATIC_NODE_PORT,
64-
namespace?: string
76+
namespace?: string,
77+
serviceName: string = DEFAULT_SERVICE_NAME,
78+
podPrefix: string = DEFAULT_POD_PREFIX
6579
): string => {
6680
const normalizedDomain =
6781
domain === undefined || domain.trim().length === 0
@@ -72,8 +86,7 @@ const expectedStaticNodeUri = (
7286
? undefined
7387
: namespace.trim();
7488
const ordinal = index - 1;
75-
const podName = `besu-node-validator-${ordinal}`;
76-
const serviceName = "besu-node";
89+
const podName = `${podPrefix}-${ordinal}`;
7790
const segments = [podName, serviceName];
7891
if (normalizedNamespace) {
7992
segments.push(normalizedNamespace);
@@ -121,6 +134,7 @@ describe("CLI command bootstrap", () => {
121134
test("runBootstrap orchestrates prompts and writes genesis", async () => {
122135
const factory = createFactoryStub();
123136
const promptCalls: [string, number | undefined, number][] = [];
137+
const textPromptCalls: [string, string][] = [];
124138
let loadAllocationsPath: string | undefined;
125139
let outputInvocation:
126140
| {
@@ -174,6 +188,10 @@ describe("CLI command bootstrap", () => {
174188
genesis: { config: {}, extraData: "0xextra" } as any,
175189
});
176190
},
191+
promptForText: ({ labelText, defaultValue }) => {
192+
textPromptCalls.push([labelText, defaultValue]);
193+
return Promise.resolve(defaultValue);
194+
},
177195
service: {} as any,
178196
loadAllocations: (path: string) => {
179197
loadAllocationsPath = path;
@@ -194,6 +212,13 @@ describe("CLI command bootstrap", () => {
194212
expect(promptCalls).toEqual([
195213
[VALIDATOR_LABEL, undefined, EXPECTED_DEFAULT_VALIDATOR],
196214
]);
215+
expect(textPromptCalls).toEqual([
216+
["Static node service name", DEFAULT_SERVICE_NAME],
217+
["Static node pod prefix", DEFAULT_POD_PREFIX],
218+
["Genesis ConfigMap name", DEFAULT_GENESIS_CONFIGMAP_NAME],
219+
["Static nodes ConfigMap name", DEFAULT_STATIC_NODES_CONFIGMAP_NAME],
220+
["Faucet artifact prefix", DEFAULT_FAUCET_PREFIX],
221+
]);
197222
const output = stdout.read();
198223
expect(output).toContain("Genesis");
199224
expect(output).toContain("Validator Nodes");
@@ -205,6 +230,12 @@ describe("CLI command bootstrap", () => {
205230
expectedStaticNodeUri(FIRST_VALIDATOR_INDEX),
206231
expectedStaticNodeUri(SECOND_VALIDATOR_INDEX),
207232
]);
233+
expect(outputInvocation?.payload.artifactNames).toEqual({
234+
faucetPrefix: DEFAULT_FAUCET_PREFIX,
235+
validatorPrefix: DEFAULT_POD_PREFIX,
236+
genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME,
237+
staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME,
238+
});
208239
});
209240

210241
test("createCliCommand wires metadata", () => {
@@ -243,6 +274,7 @@ describe("CLI command bootstrap", () => {
243274
genesis: { config: {}, extraData: "0xextra" } as any,
244275
});
245276
},
277+
promptForText: passthroughTextPrompt,
246278
service: {} as any,
247279
loadAllocations: () =>
248280
Promise.resolve({} satisfies Record<string, BesuAllocAccount>),
@@ -309,6 +341,7 @@ describe("CLI command bootstrap", () => {
309341
},
310342
genesis: { config: {}, extraData: "0xextra" } as any,
311343
}),
344+
promptForText: passthroughTextPrompt,
312345
service: {} as any,
313346
loadAllocations: () =>
314347
Promise.resolve({} satisfies Record<string, BesuAllocAccount>),
@@ -334,6 +367,16 @@ describe("CLI command bootstrap", () => {
334367
"40000",
335368
"--static-node-discovery-port",
336369
"0",
370+
"--static-node-service-name",
371+
"custom-service",
372+
"--static-node-pod-prefix",
373+
"custom-validator",
374+
"--genesis-configmap-name",
375+
"custom-genesis",
376+
"--static-nodes-configmap-name",
377+
"custom-static-nodes",
378+
"--faucet-artifact-prefix",
379+
"custom-faucet",
337380
],
338381
{ from: "node" }
339382
);
@@ -344,9 +387,17 @@ describe("CLI command bootstrap", () => {
344387
"svc.cluster.local",
345388
CUSTOM_STATIC_NODE_PORT,
346389
0,
347-
"network"
390+
"network",
391+
"custom-service",
392+
"custom-validator"
348393
),
349394
]);
395+
expect(capturedPayload?.artifactNames).toEqual({
396+
faucetPrefix: "custom-faucet",
397+
validatorPrefix: "custom-validator",
398+
genesisConfigMapName: "custom-genesis",
399+
staticNodesConfigMapName: "custom-static-nodes",
400+
});
350401
});
351402

352403
test("runBootstrap builds static nodes with domain and custom ports", async () => {
@@ -371,6 +422,7 @@ describe("CLI command bootstrap", () => {
371422
genesis: { config: {}, extraData: "0xextra" } as any,
372423
});
373424
},
425+
promptForText: passthroughTextPrompt,
374426
service: {} as any,
375427
loadAllocations: () =>
376428
Promise.resolve({} satisfies Record<string, BesuAllocAccount>),
@@ -438,6 +490,7 @@ describe("CLI command bootstrap", () => {
438490
genesis: { config: {}, extraData: "0xextra" } as any,
439491
});
440492
},
493+
promptForText: passthroughTextPrompt,
441494
service: {} as any,
442495
loadAllocations: () =>
443496
Promise.resolve({} satisfies Record<string, BesuAllocAccount>),
@@ -519,6 +572,7 @@ describe("CLI command bootstrap", () => {
519572
},
520573
genesis: { config: {}, extraData: "0x" } as any,
521574
}),
575+
promptForText: passthroughTextPrompt,
522576
service: {} as any,
523577
loadAllocations: () =>
524578
Promise.resolve({} satisfies Record<string, BesuAllocAccount>),
@@ -602,13 +656,20 @@ describe("CLI command bootstrap", () => {
602656
genesis: { config: {}, extraData: "0xextra" } as any,
603657
});
604658
},
659+
promptForText: passthroughTextPrompt,
605660
service: {} as any,
606661
loadAllocations: () => {
607662
loadAllocationsInvoked = true;
608663
return Promise.resolve({} as Record<string, BesuAllocAccount>);
609664
},
610665
outputResult: (_type, payload) => {
611666
expect(payload.validators).toHaveLength(EXPECTED_DEFAULT_VALIDATOR);
667+
expect(payload.artifactNames).toEqual({
668+
faucetPrefix: DEFAULT_FAUCET_PREFIX,
669+
validatorPrefix: DEFAULT_POD_PREFIX,
670+
genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME,
671+
staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME,
672+
});
612673
return Promise.resolve();
613674
},
614675
};

0 commit comments

Comments
 (0)