@@ -77,7 +77,9 @@ const HEX_RADIX = 16;
7777const SAMPLE_VALIDATOR_INDEX = 1 ;
7878const SAMPLE_RPC_INDEX = 2 ;
7979const SAMPLE_FAUCET_INDEX = 99 ;
80- const EXPECTED_CONFIGMAP_COUNT = 8 ;
80+ const EXPECTED_CONFIGMAP_COUNT = 9 ;
81+ const EXPECTED_SECRET_COUNT = 3 ;
82+ const HEX_PREFIX_PATTERN = / ^ 0 x / ;
8183const TEST_CHAIN_ID = 1 ;
8284const HTTP_CONFLICT_STATUS = 409 ;
8385const HTTP_INTERNAL_ERROR_STATUS = 500 ;
@@ -153,17 +155,23 @@ describe("outputResult", () => {
153155 await rm ( "out" , { recursive : true , force : true } ) ;
154156 } ) ;
155157
156- test ( "kubernetes output creates configmaps" , async ( ) => {
158+ test ( "kubernetes output creates configmaps and secrets " , async ( ) => {
157159 const originalLoad = ( KubeConfig . prototype as any ) . loadFromCluster ;
158160 const originalMake = ( KubeConfig . prototype as any ) . makeApiClient ;
159161 const originalFile = Bun . file ;
160162
161- const created : Array < {
163+ const createdConfigMaps : Array < {
162164 namespace : string ;
163165 name : string ;
164166 data : Record < string , string > ;
165167 } > = [ ] ;
166- const listedNamespaces : string [ ] = [ ] ;
168+ const createdSecrets : Array < {
169+ namespace : string ;
170+ name : string ;
171+ data : Record < string , string > ;
172+ } > = [ ] ;
173+ const listedConfigNamespaces : string [ ] = [ ] ;
174+ const listedSecretNamespaces : string [ ] = [ ] ;
167175
168176 try {
169177 ( KubeConfig . prototype as any ) . loadFromCluster =
@@ -174,7 +182,11 @@ describe("outputResult", () => {
174182 ( KubeConfig . prototype as any ) . makeApiClient = function makeApiClient ( ) {
175183 const client = {
176184 listNamespacedConfigMap : ( { namespace } : { namespace : string } ) => {
177- listedNamespaces . push ( namespace ) ;
185+ listedConfigNamespaces . push ( namespace ) ;
186+ return Promise . resolve ( ) ;
187+ } ,
188+ listNamespacedSecret : ( { namespace } : { namespace : string } ) => {
189+ listedSecretNamespaces . push ( namespace ) ;
178190 return Promise . resolve ( ) ;
179191 } ,
180192 createNamespacedConfigMap : ( {
@@ -184,13 +196,27 @@ describe("outputResult", () => {
184196 namespace : string ;
185197 body : any ;
186198 } ) => {
187- created . push ( {
199+ createdConfigMaps . push ( {
188200 namespace,
189201 name : body ?. metadata ?. name ?? "" ,
190202 data : body ?. data ?? { } ,
191203 } ) ;
192204 return Promise . resolve ( ) ;
193205 } ,
206+ createNamespacedSecret : ( {
207+ namespace,
208+ body,
209+ } : {
210+ namespace : string ;
211+ body : any ;
212+ } ) => {
213+ createdSecrets . push ( {
214+ namespace,
215+ name : body ?. metadata ?. name ?? "" ,
216+ data : body ?. stringData ?? { } ,
217+ } ) ;
218+ return Promise . resolve ( ) ;
219+ } ,
194220 } ;
195221 return client as unknown as CoreV1Api ;
196222 } ;
@@ -202,11 +228,26 @@ describe("outputResult", () => {
202228
203229 await outputResult ( "kubernetes" , samplePayload ) ;
204230
205- expect ( listedNamespaces ) . toEqual ( [ "test-namespace" ] ) ;
206- expect ( created ) . toHaveLength ( EXPECTED_CONFIGMAP_COUNT ) ;
207- const names = created . map ( ( entry ) => entry . name ) . sort ( ) ;
208- expect ( names ) . toContain ( "besu-node-validator-1-address" ) ;
209- expect ( names ) . toContain ( "besu-node-rpc-node-2-private-key" ) ;
231+ expect ( listedConfigNamespaces ) . toEqual ( [ "test-namespace" ] ) ;
232+ expect ( listedSecretNamespaces ) . toEqual ( [ "test-namespace" ] ) ;
233+ expect ( createdConfigMaps ) . toHaveLength ( EXPECTED_CONFIGMAP_COUNT ) ;
234+ expect ( createdSecrets ) . toHaveLength ( EXPECTED_SECRET_COUNT ) ;
235+ const mapNames = createdConfigMaps . map ( ( entry ) => entry . name ) . sort ( ) ;
236+ expect ( mapNames ) . toContain ( "besu-node-validator-1-address" ) ;
237+ expect ( mapNames ) . toContain ( "besu-genesis" ) ;
238+ expect ( mapNames ) . toContain ( "besu-faucet-address" ) ;
239+ expect ( mapNames ) . toContain ( "besu-faucet-pubkey" ) ;
240+ expect ( mapNames ) . not . toContain ( "besu-faucet-enode" ) ;
241+ const secretNames = createdSecrets . map ( ( entry ) => entry . name ) . sort ( ) ;
242+ expect ( secretNames ) . toEqual ( [
243+ "besu-faucet-private-key" ,
244+ "besu-node-rpc-node-2-private-key" ,
245+ "besu-node-validator-1-private-key" ,
246+ ] ) ;
247+ const privateKeySecret = createdSecrets . find ( ( entry ) =>
248+ entry . name . endsWith ( "validator-1-private-key" )
249+ ) ;
250+ expect ( privateKeySecret ?. data ?. privateKey ) . toMatch ( HEX_PREFIX_PATTERN ) ;
210251 } finally {
211252 ( KubeConfig . prototype as any ) . loadFromCluster = originalLoad ;
212253 ( KubeConfig . prototype as any ) . makeApiClient = originalMake ;
@@ -227,6 +268,7 @@ describe("outputResult", () => {
227268 ( KubeConfig . prototype as any ) . makeApiClient = function makeApiClient ( ) {
228269 const client = {
229270 listNamespacedConfigMap : ( ) => Promise . resolve ( ) ,
271+ listNamespacedSecret : ( ) => Promise . resolve ( ) ,
230272 createNamespacedConfigMap : ( ) => {
231273 const error = new Error ( "already exists" ) ;
232274 (
@@ -239,6 +281,7 @@ describe("outputResult", () => {
239281 } ;
240282 throw error ;
241283 } ,
284+ createNamespacedSecret : ( ) => Promise . resolve ( ) ,
242285 } ;
243286 return client as unknown as CoreV1Api ;
244287 } ;
@@ -258,6 +301,52 @@ describe("outputResult", () => {
258301 }
259302 } ) ;
260303
304+ test ( "kubernetes output surfaces secret conflict errors" , async ( ) => {
305+ const originalLoad = ( KubeConfig . prototype as any ) . loadFromCluster ;
306+ const originalMake = ( KubeConfig . prototype as any ) . makeApiClient ;
307+ const originalFile = Bun . file ;
308+
309+ try {
310+ ( KubeConfig . prototype as any ) . loadFromCluster =
311+ function loadFromCluster ( ) : void {
312+ /* no-op for tests */
313+ } ;
314+ ( KubeConfig . prototype as any ) . makeApiClient = function makeApiClient ( ) {
315+ const client = {
316+ listNamespacedConfigMap : ( ) => Promise . resolve ( ) ,
317+ listNamespacedSecret : ( ) => Promise . resolve ( ) ,
318+ createNamespacedConfigMap : ( ) => Promise . resolve ( ) ,
319+ createNamespacedSecret : ( ) => {
320+ const error = new Error ( "already exists" ) ;
321+ (
322+ error as {
323+ response ?: { statusCode : number ; body : { message : string } } ;
324+ }
325+ ) . response = {
326+ statusCode : HTTP_CONFLICT_STATUS ,
327+ body : { message : "already exists" } ,
328+ } ;
329+ throw error ;
330+ } ,
331+ } ;
332+ return client as unknown as CoreV1Api ;
333+ } ;
334+
335+ ( Bun as any ) . file = ( ) =>
336+ ( {
337+ text : ( ) => Promise . resolve ( "secret-conflict-namespace" ) ,
338+ } ) as unknown as ReturnType < typeof Bun . file > ;
339+
340+ await expect ( outputResult ( "kubernetes" , samplePayload ) ) . rejects . toThrow (
341+ "Secret besu-node-validator-1-private-key already exists. Delete it or choose a different output target."
342+ ) ;
343+ } finally {
344+ ( KubeConfig . prototype as any ) . loadFromCluster = originalLoad ;
345+ ( KubeConfig . prototype as any ) . makeApiClient = originalMake ;
346+ ( Bun as any ) . file = originalFile ;
347+ }
348+ } ) ;
349+
261350 test ( "kubernetes output fails without cluster credentials" , async ( ) => {
262351 const originalLoad = ( KubeConfig . prototype as any ) . loadFromCluster ;
263352 const originalFile = Bun . file ;
@@ -346,6 +435,7 @@ describe("outputResult", () => {
346435 ( KubeConfig . prototype as any ) . makeApiClient = function makeApiClient ( ) {
347436 const client = {
348437 listNamespacedConfigMap : ( ) => Promise . reject ( new Error ( "forbidden" ) ) ,
438+ listNamespacedSecret : ( ) => Promise . resolve ( ) ,
349439 } ;
350440 return client as unknown as CoreV1Api ;
351441 } ;
@@ -377,9 +467,11 @@ describe("outputResult", () => {
377467 ( KubeConfig . prototype as any ) . makeApiClient = function makeApiClient ( ) {
378468 const client = {
379469 listNamespacedConfigMap : ( ) => Promise . resolve ( ) ,
470+ listNamespacedSecret : ( ) => Promise . resolve ( ) ,
380471 createNamespacedConfigMap : ( ) => {
381472 throw new Error ( "boom" ) ;
382473 } ,
474+ createNamespacedSecret : ( ) => Promise . resolve ( ) ,
383475 } ;
384476 return client as unknown as CoreV1Api ;
385477 } ;
@@ -411,12 +503,14 @@ describe("outputResult", () => {
411503 ( KubeConfig . prototype as any ) . makeApiClient = function makeApiClient ( ) {
412504 const client = {
413505 listNamespacedConfigMap : ( ) => Promise . resolve ( ) ,
506+ listNamespacedSecret : ( ) => Promise . resolve ( ) ,
414507 createNamespacedConfigMap : ( ) => {
415508 const error = new Error ( "failed" ) ;
416509 ( error as { statusCode ?: number } ) . statusCode =
417510 HTTP_INTERNAL_ERROR_STATUS ;
418511 throw error ;
419512 } ,
513+ createNamespacedSecret : ( ) => Promise . resolve ( ) ,
420514 } ;
421515 return client as unknown as CoreV1Api ;
422516 } ;
@@ -448,6 +542,7 @@ describe("outputResult", () => {
448542 ( KubeConfig . prototype as any ) . makeApiClient = function makeApiClient ( ) {
449543 const client = {
450544 listNamespacedConfigMap : ( ) => Promise . resolve ( ) ,
545+ listNamespacedSecret : ( ) => Promise . resolve ( ) ,
451546 createNamespacedConfigMap : ( ) => {
452547 const error = new Error ( "response error" ) ;
453548 Object . defineProperty ( error , "message" , { value : undefined } ) ;
@@ -456,6 +551,7 @@ describe("outputResult", () => {
456551 } ;
457552 throw error ;
458553 } ,
554+ createNamespacedSecret : ( ) => Promise . resolve ( ) ,
459555 } ;
460556 return client as unknown as CoreV1Api ;
461557 } ;
@@ -487,6 +583,7 @@ describe("outputResult", () => {
487583 ( KubeConfig . prototype as any ) . makeApiClient = function makeApiClient ( ) {
488584 const client = {
489585 listNamespacedConfigMap : ( ) => Promise . resolve ( ) ,
586+ listNamespacedSecret : ( ) => Promise . resolve ( ) ,
490587 createNamespacedConfigMap : ( ) => {
491588 const error = new Error ( "body error" ) ;
492589 Object . defineProperty ( error , "message" , { value : undefined } ) ;
@@ -495,6 +592,7 @@ describe("outputResult", () => {
495592 } ;
496593 throw error ;
497594 } ,
595+ createNamespacedSecret : ( ) => Promise . resolve ( ) ,
498596 } ;
499597 return client as unknown as CoreV1Api ;
500598 } ;
0 commit comments