22/* eslint-disable @typescript-eslint/camelcase */
33import child_process from "child_process" ;
44import commander from "commander" ;
5- import { writeFileSync } from "fs" ;
5+ import fs from "fs" ;
66import yaml from "js-yaml" ;
77import path from "path" ;
88import process from "process" ;
99import shelljs , { TestOptions } from "shelljs" ;
1010import { Bedrock } from "../../config" ;
1111import { assertIsStringWithContent } from "../../lib/assertions" ;
12+ import * as bedrock from "../../lib/bedrockYaml" ;
1213import { build as buildCmd , exit as exitCmd } from "../../lib/commandBuilder" ;
1314import { generateAccessYaml } from "../../lib/fileutils" ;
1415import { tryGetGitOrigin } from "../../lib/gitutils" ;
1516import * as dns from "../../lib/net/dns" ;
16- import { TraefikIngressRoute } from "../../lib/traefik/ingress-route" ;
17- import { TraefikMiddleware } from "../../lib/traefik/middleware" ;
17+ import * as ingressRoute from "../../lib/traefik/ingress-route" ;
18+ import * as middleware from "../../lib/traefik/middleware" ;
1819import { logger } from "../../logger" ;
1920import { BedrockFile , BedrockServiceConfig } from "../../types" ;
2021import decorator from "./reconcile.decorator.json" ;
@@ -57,7 +58,7 @@ const exec = async (cmd: string, pipeIO = false): Promise<ExecResult> => {
5758
5859export interface ReconcileDependencies {
5960 exec : typeof execAndLog ;
60- writeFile : typeof writeFileSync ;
61+ writeFile : typeof fs . writeFileSync ;
6162 getGitOrigin : typeof tryGetGitOrigin ;
6263 generateAccessYaml : typeof generateAccessYaml ;
6364 createAccessYaml : typeof createAccessYaml ;
@@ -105,6 +106,7 @@ export const execute = async (
105106 ) ;
106107
107108 const bedrockConfig = Bedrock ( absBedrockPath ) ;
109+ bedrock . validateRings ( bedrockConfig ) ;
108110
109111 logger . info (
110112 `Attempting to reconcile HLD with services tracked in bedrock.yaml`
@@ -123,7 +125,7 @@ export const execute = async (
123125 exec : execAndLog ,
124126 generateAccessYaml,
125127 getGitOrigin : tryGetGitOrigin ,
126- writeFile : writeFileSync
128+ writeFile : fs . writeFileSync
127129 } ;
128130
129131 await reconcileHld (
@@ -238,22 +240,23 @@ export const reconcileHld = async (
238240 normalizedSvcName
239241 ) ;
240242
241- const ringsToCreate = Object . keys ( managedRings ) . map ( ring => {
242- const normalizedRingName = normalizedName ( ring ) ;
243- return {
244- normalizedRingName,
245- normalizedRingPathInHld : path . join (
246- normalizedSvcPathInHld ,
247- normalizedRingName
248- )
249- } ;
250- } ) ;
243+ const ringsToCreate = Object . entries ( managedRings ) . map (
244+ ( [ ring , { isDefault } ] ) => {
245+ const normalizedRingName = normalizedName ( ring ) ;
246+ return {
247+ isDefault : ! ! isDefault ,
248+ normalizedRingName,
249+ normalizedRingPathInHld : path . join (
250+ normalizedSvcPathInHld ,
251+ normalizedRingName
252+ )
253+ } ;
254+ }
255+ ) ;
251256
252257 // Will only loop over _existing_ rings in bedrock.yaml - does not cover the deletion case, ie: i remove a ring from bedrock.yaml
253- for ( const {
254- normalizedRingName,
255- normalizedRingPathInHld
256- } of ringsToCreate ) {
258+ for ( const ring of ringsToCreate ) {
259+ const { normalizedRingName, normalizedRingPathInHld, isDefault } = ring ;
257260 // Create the ring in the service.
258261 await dependencies . createRingComponent (
259262 dependencies . exec ,
@@ -302,7 +305,8 @@ export const reconcileHld = async (
302305 normalizedRingPathInHld ,
303306 normalizedSvcName ,
304307 normalizedRingName ,
305- ingressVersionAndPath
308+ ingressVersionAndPath ,
309+ isDefault
306310 ) ;
307311
308312 // Create Ingress Route.
@@ -312,7 +316,8 @@ export const reconcileHld = async (
312316 serviceConfig ,
313317 middlewares ,
314318 normalizedRingName ,
315- ingressVersionAndPath
319+ ingressVersionAndPath ,
320+ isDefault
316321 ) ;
317322 }
318323 }
@@ -369,68 +374,120 @@ export const createAccessYaml = async (
369374 writeAccessYaml ( absRepositoryPathInHldPath , originUrl ) ;
370375} ;
371376
372- const createIngressRouteForRing = (
377+ type MiddlewareMap < T = Partial < ReturnType < typeof middleware . create > > > = {
378+ ringed : T ;
379+ default ?: T ;
380+ } ;
381+
382+ export const createIngressRouteForRing = (
373383 ringPathInHld : string ,
374384 serviceName : string ,
375385 serviceConfig : BedrockServiceConfig ,
376- middlewares : TraefikMiddleware ,
386+ middlewares : MiddlewareMap ,
377387 ring : string ,
378- ingressVersionAndPath : string
379- ) : void => {
388+ ingressVersionAndPath : string ,
389+ ringIsDefault = false
390+ ) : ReturnType < typeof ingressRoute . create > [ ] => {
380391 const staticComponentPathInRing = path . join ( ringPathInHld , "static" ) ;
381392 const ingressRoutePathInStaticComponent = path . join (
382393 staticComponentPathInRing ,
383394 "ingress-route.yaml"
384395 ) ;
385- const ingressRoute = TraefikIngressRoute (
396+
397+ // Push the default ingress route with ring header
398+ const ingressRoutes = [ ] ;
399+ const ringedRoute = ingressRoute . create (
386400 serviceName ,
387401 ring ,
388402 serviceConfig . k8sBackendPort ,
389403 ingressVersionAndPath ,
390404 {
405+ isDefault : false ,
391406 k8sBackend : serviceConfig . k8sBackend ,
392407 middlewares : [
393- middlewares . metadata . name ,
408+ middlewares . ringed . metadata ? .name ,
394409 ...( serviceConfig . middlewares ?? [ ] )
395- ]
410+ ] . filter ( ( e ) : e is NonNullable < typeof e > => ! ! e )
396411 }
397412 ) ;
413+ ingressRoutes . push ( ringedRoute ) ;
414+
415+ // If ring isDefault, scaffold an additional ingress route without the ring
416+ // header -- i.e with an empty string ring name
417+ if ( ringIsDefault ) {
418+ const defaultRingRoute = ingressRoute . create (
419+ serviceName ,
420+ ring ,
421+ serviceConfig . k8sBackendPort ,
422+ ingressVersionAndPath ,
423+ {
424+ isDefault : ringIsDefault ,
425+ k8sBackend : serviceConfig . k8sBackend ,
426+ middlewares : [
427+ middlewares . default ?. metadata ?. name ,
428+ ...( serviceConfig . middlewares ?? [ ] )
429+ ] . filter ( ( e ) : e is NonNullable < typeof e > => ! ! e )
430+ }
431+ ) ;
432+ ingressRoutes . push ( defaultRingRoute ) ;
433+ }
398434
399- const routeYaml = yaml . safeDump ( ingressRoute , {
400- lineWidth : Number . MAX_SAFE_INTEGER
401- } ) ;
435+ // serialize to routes to yaml separately and join them with `---` to specify
436+ // multiple yaml documents in a single string
437+ const routeYaml = ingressRoutes
438+ . map ( str => {
439+ return yaml . safeDump ( str , {
440+ lineWidth : Number . MAX_SAFE_INTEGER
441+ } ) ;
442+ } )
443+ . join ( "\n---\n" ) ;
402444
403445 logger . info (
404446 `Writing IngressRoute YAML to '${ ingressRoutePathInStaticComponent } '`
405447 ) ;
406448
407- writeFileSync ( ingressRoutePathInStaticComponent , routeYaml ) ;
449+ fs . writeFileSync ( ingressRoutePathInStaticComponent , routeYaml ) ;
450+ return ingressRoutes ;
408451} ;
409452
410- const createMiddlewareForRing = (
453+ export const createMiddlewareForRing = (
411454 ringPathInHld : string ,
412455 serviceName : string ,
413456 ring : string ,
414- ingressVersionAndPath : string
415- ) : TraefikMiddleware => {
457+ ingressVersionAndPath : string ,
458+ ringIsDefault = false
459+ ) : MiddlewareMap => {
416460 // Create Middlewares
417461 const staticComponentPathInRing = path . join ( ringPathInHld , "static" ) ;
418462 const middlewaresPathInStaticComponent = path . join (
419463 staticComponentPathInRing ,
420464 "middlewares.yaml"
421465 ) ;
422466
423- const middlewares = TraefikMiddleware ( serviceName , ring , [
424- ingressVersionAndPath
425- ] ) ;
426- const middlewareYaml = yaml . safeDump ( middlewares , {
427- lineWidth : Number . MAX_SAFE_INTEGER
428- } ) ;
467+ // Create the standard ringed middleware as well as one without the ring in
468+ // the name if the ring isDefault
469+ const middlewares = {
470+ ringed : middleware . create ( serviceName , ring , [ ingressVersionAndPath ] ) ,
471+ default : ringIsDefault
472+ ? middleware . create ( serviceName , "" , [ ingressVersionAndPath ] )
473+ : undefined
474+ } ;
475+
476+ // Serialize all the middlewares to yaml separately and join the strings with
477+ // '---' to specify multiple yaml docs in a single string
478+ const middlewareYaml = Object . values ( middlewares )
479+ . filter ( ( e ) : e is NonNullable < typeof e > => ! ! e )
480+ . map ( str =>
481+ yaml . safeDump ( str , {
482+ lineWidth : Number . MAX_SAFE_INTEGER
483+ } )
484+ )
485+ . join ( "\n---\n" ) ;
429486
430487 logger . info (
431488 `Writing Middlewares YAML to '${ middlewaresPathInStaticComponent } '`
432489 ) ;
433- writeFileSync ( middlewaresPathInStaticComponent , middlewareYaml ) ;
490+ fs . writeFileSync ( middlewaresPathInStaticComponent , middlewareYaml ) ;
434491
435492 return middlewares ;
436493} ;
0 commit comments