From cb500c908e459897f7ab5f5d8acc46a71b310be6 Mon Sep 17 00:00:00 2001 From: Mario Castro Squella Date: Tue, 20 Aug 2024 18:12:27 -0400 Subject: [PATCH] Rocket health sensors (#1546) * Add rocket function app names to main function env vars * Add sensors for rockets * Correct app settings for event hub function app * Add rush change file * Update pnpm-lock.yaml * Update pnpm-lock.yaml * Update docs and English grammar corrections * Handle case for app that doesn't have any rockets --------- Co-authored-by: Castro, Mario --- ...ocket_health_sensors_2024-08-19-17-06.json | 10 + common/config/rush/pnpm-lock.yaml | 175 +++++++++++------- .../sensor/health/booster-health-service.ts | 8 +- .../default-booster-health-indicators.ts | 8 + .../rockets-health-indicator.ts | 52 ++++++ .../src/sensor/health/health-utils.ts | 2 +- .../health/booster-health-service.test.ts | 17 +- .../test/sensor/health/health-utils.test.ts | 12 +- packages/framework-provider-aws/src/setup.ts | 2 + .../src/infrastructure/application-builder.ts | 2 + .../src/infrastructure/azure-stack.ts | 29 ++- .../infrastructure/synth/application-synth.ts | 17 +- .../synth/terraform-function-app-settings.ts | 40 ++++ .../synth/terraform-function-app.ts | 32 +--- .../framework-provider-azure/src/constants.ts | 1 + .../framework-provider-azure/src/index.ts | 2 + .../src/library/health-adapter.ts | 27 +++ .../framework-provider-local/src/index.ts | 1 + packages/framework-types/src/provider.ts | 1 + .../sensor/health-indicator-configuration.ts | 4 + .../10_going-deeper/health/sensor-health.md | 18 +- 21 files changed, 325 insertions(+), 135 deletions(-) create mode 100644 common/changes/@boostercloud/framework-core/rocket_health_sensors_2024-08-19-17-06.json create mode 100644 packages/framework-core/src/sensor/health/health-indicators/rockets-health-indicator.ts create mode 100644 packages/framework-provider-azure-infrastructure/src/infrastructure/synth/terraform-function-app-settings.ts diff --git a/common/changes/@boostercloud/framework-core/rocket_health_sensors_2024-08-19-17-06.json b/common/changes/@boostercloud/framework-core/rocket_health_sensors_2024-08-19-17-06.json new file mode 100644 index 000000000..57c71392f --- /dev/null +++ b/common/changes/@boostercloud/framework-core/rocket_health_sensors_2024-08-19-17-06.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@boostercloud/framework-core", + "comment": "Health sensors for Rockets", + "type": "minor" + } + ], + "packageName": "@boostercloud/framework-core" +} \ No newline at end of file diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index cb6d01232..a867549d2 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -8,8 +8,8 @@ importers: ../../packages/application-tester: specifiers: '@apollo/client': 3.7.13 - '@boostercloud/eslint-config': workspace:^2.15.0 - '@boostercloud/framework-types': workspace:^2.15.0 + '@boostercloud/eslint-config': workspace:^2.16.0 + '@boostercloud/framework-types': workspace:^2.16.0 '@effect-ts/core': ^0.60.4 '@types/jsonwebtoken': 9.0.1 '@types/node': ^18.18.2 @@ -70,10 +70,10 @@ importers: ../../packages/cli: specifiers: - '@boostercloud/application-tester': workspace:^2.15.0 - '@boostercloud/eslint-config': workspace:^2.15.0 - '@boostercloud/framework-core': workspace:^2.15.0 - '@boostercloud/framework-types': workspace:^2.15.0 + '@boostercloud/application-tester': workspace:^2.16.0 + '@boostercloud/eslint-config': workspace:^2.16.0 + '@boostercloud/framework-core': workspace:^2.16.0 + '@boostercloud/framework-types': workspace:^2.16.0 '@effect-ts/core': ^0.60.4 '@oclif/core': 3.15.0 '@oclif/plugin-help': ^5 @@ -183,8 +183,8 @@ importers: ../../packages/framework-common-helpers: specifiers: - '@boostercloud/eslint-config': workspace:^2.15.0 - '@boostercloud/framework-types': workspace:^2.15.0 + '@boostercloud/eslint-config': workspace:^2.16.0 + '@boostercloud/framework-types': workspace:^2.16.0 '@effect-ts/core': ^0.60.4 '@types/chai': 4.2.18 '@types/chai-as-promised': 7.1.4 @@ -258,10 +258,10 @@ importers: ../../packages/framework-core: specifiers: - '@boostercloud/eslint-config': workspace:^2.15.0 - '@boostercloud/framework-common-helpers': workspace:^2.15.0 - '@boostercloud/framework-types': workspace:^2.15.0 - '@boostercloud/metadata-booster': workspace:^2.15.0 + '@boostercloud/eslint-config': workspace:^2.16.0 + '@boostercloud/framework-common-helpers': workspace:^2.16.0 + '@boostercloud/framework-types': workspace:^2.16.0 + '@boostercloud/metadata-booster': workspace:^2.16.0 '@effect/cli': 0.35.26 '@effect/platform': 0.48.24 '@effect/platform-node': 0.45.26 @@ -378,19 +378,19 @@ importers: ../../packages/framework-integration-tests: specifiers: '@apollo/client': 3.7.13 - '@boostercloud/application-tester': workspace:^2.15.0 - '@boostercloud/cli': workspace:^2.15.0 - '@boostercloud/eslint-config': workspace:^2.15.0 - '@boostercloud/framework-common-helpers': workspace:^2.15.0 - '@boostercloud/framework-core': workspace:^2.15.0 - '@boostercloud/framework-provider-aws': workspace:^2.15.0 - '@boostercloud/framework-provider-aws-infrastructure': workspace:^2.15.0 - '@boostercloud/framework-provider-azure': workspace:^2.15.0 - '@boostercloud/framework-provider-azure-infrastructure': workspace:^2.15.0 - '@boostercloud/framework-provider-local': workspace:^2.15.0 - '@boostercloud/framework-provider-local-infrastructure': workspace:^2.15.0 - '@boostercloud/framework-types': workspace:^2.15.0 - '@boostercloud/metadata-booster': workspace:^2.15.0 + '@boostercloud/application-tester': workspace:^2.16.0 + '@boostercloud/cli': workspace:^2.16.0 + '@boostercloud/eslint-config': workspace:^2.16.0 + '@boostercloud/framework-common-helpers': workspace:^2.16.0 + '@boostercloud/framework-core': workspace:^2.16.0 + '@boostercloud/framework-provider-aws': workspace:^2.16.0 + '@boostercloud/framework-provider-aws-infrastructure': workspace:^2.16.0 + '@boostercloud/framework-provider-azure': workspace:^2.16.0 + '@boostercloud/framework-provider-azure-infrastructure': workspace:^2.16.0 + '@boostercloud/framework-provider-local': workspace:^2.16.0 + '@boostercloud/framework-provider-local-infrastructure': workspace:^2.16.0 + '@boostercloud/framework-types': workspace:^2.16.0 + '@boostercloud/metadata-booster': workspace:^2.16.0 '@effect-ts/core': ^0.60.4 '@effect/cli': 0.35.26 '@effect/platform': 0.48.24 @@ -536,9 +536,9 @@ importers: ../../packages/framework-provider-aws: specifiers: - '@boostercloud/eslint-config': workspace:^2.15.0 - '@boostercloud/framework-common-helpers': workspace:^2.15.0 - '@boostercloud/framework-types': workspace:^2.15.0 + '@boostercloud/eslint-config': workspace:^2.16.0 + '@boostercloud/framework-common-helpers': workspace:^2.16.0 + '@boostercloud/framework-types': workspace:^2.16.0 '@effect-ts/core': ^0.60.4 '@types/aws-lambda': 8.10.48 '@types/chai': 4.2.18 @@ -632,10 +632,10 @@ importers: '@aws-cdk/core': ^1.170.0 '@aws-cdk/custom-resources': ^1.170.0 '@aws-cdk/cx-api': ^1.170.0 - '@boostercloud/eslint-config': workspace:^2.15.0 - '@boostercloud/framework-common-helpers': workspace:^2.15.0 - '@boostercloud/framework-provider-aws': workspace:^2.15.0 - '@boostercloud/framework-types': workspace:^2.15.0 + '@boostercloud/eslint-config': workspace:^2.16.0 + '@boostercloud/framework-common-helpers': workspace:^2.16.0 + '@boostercloud/framework-provider-aws': workspace:^2.16.0 + '@boostercloud/framework-types': workspace:^2.16.0 '@effect-ts/core': ^0.60.4 '@types/archiver': 5.1.0 '@types/aws-lambda': 8.10.48 @@ -749,9 +749,9 @@ importers: '@azure/functions': ^1.2.2 '@azure/identity': ~2.1.0 '@azure/web-pubsub': ~1.1.0 - '@boostercloud/eslint-config': workspace:^2.15.0 - '@boostercloud/framework-common-helpers': workspace:^2.15.0 - '@boostercloud/framework-types': workspace:^2.15.0 + '@boostercloud/eslint-config': workspace:^2.16.0 + '@boostercloud/framework-common-helpers': workspace:^2.16.0 + '@boostercloud/framework-types': workspace:^2.16.0 '@effect-ts/core': ^0.60.4 '@types/chai': 4.2.18 '@types/chai-as-promised': 7.1.4 @@ -823,11 +823,11 @@ importers: '@azure/arm-resources': ^5.0.1 '@azure/cosmos': ^4.0.0 '@azure/identity': ~2.1.0 - '@boostercloud/eslint-config': workspace:^2.15.0 - '@boostercloud/framework-common-helpers': workspace:^2.15.0 - '@boostercloud/framework-core': workspace:^2.15.0 - '@boostercloud/framework-provider-azure': workspace:^2.15.0 - '@boostercloud/framework-types': workspace:^2.15.0 + '@boostercloud/eslint-config': workspace:^2.16.0 + '@boostercloud/framework-common-helpers': workspace:^2.16.0 + '@boostercloud/framework-core': workspace:^2.16.0 + '@boostercloud/framework-provider-azure': workspace:^2.16.0 + '@boostercloud/framework-types': workspace:^2.16.0 '@cdktf/provider-azurerm': 11.2.0 '@cdktf/provider-time': 9.0.2 '@effect-ts/core': ^0.60.4 @@ -934,9 +934,9 @@ importers: ../../packages/framework-provider-local: specifiers: - '@boostercloud/eslint-config': workspace:^2.15.0 - '@boostercloud/framework-common-helpers': workspace:^2.15.0 - '@boostercloud/framework-types': workspace:^2.15.0 + '@boostercloud/eslint-config': workspace:^2.16.0 + '@boostercloud/framework-common-helpers': workspace:^2.16.0 + '@boostercloud/framework-types': workspace:^2.16.0 '@effect-ts/core': ^0.60.4 '@seald-io/nedb': 4.0.2 '@types/chai': 4.2.18 @@ -1013,10 +1013,10 @@ importers: ../../packages/framework-provider-local-infrastructure: specifiers: - '@boostercloud/eslint-config': workspace:^2.15.0 - '@boostercloud/framework-common-helpers': workspace:^2.15.0 - '@boostercloud/framework-provider-local': workspace:^2.15.0 - '@boostercloud/framework-types': workspace:^2.15.0 + '@boostercloud/eslint-config': workspace:^2.16.0 + '@boostercloud/framework-common-helpers': workspace:^2.16.0 + '@boostercloud/framework-provider-local': workspace:^2.16.0 + '@boostercloud/framework-types': workspace:^2.16.0 '@effect-ts/core': ^0.60.4 '@types/chai': 4.2.18 '@types/chai-as-promised': 7.1.4 @@ -1096,8 +1096,8 @@ importers: ../../packages/framework-types: specifiers: - '@boostercloud/eslint-config': workspace:^2.15.0 - '@boostercloud/metadata-booster': workspace:^2.15.0 + '@boostercloud/eslint-config': workspace:^2.16.0 + '@boostercloud/metadata-booster': workspace:^2.16.0 '@effect-ts/core': ^0.60.4 '@effect-ts/node': ~0.39.0 '@effect/cli': 0.35.26 @@ -1181,7 +1181,7 @@ importers: ../../packages/metadata-booster: specifiers: - '@boostercloud/eslint-config': workspace:^2.15.0 + '@boostercloud/eslint-config': workspace:^2.16.0 '@effect-ts/core': ^0.60.4 '@types/node': ^18.18.2 '@typescript-eslint/eslint-plugin': ^5.0.0 @@ -1660,7 +1660,7 @@ packages: '@aws-cdk/aws-codecommit': 1.204.0_scjupxxta56mdpzkdveav52ufq '@aws-cdk/aws-codestarnotifications': 1.204.0_add7c2jq5lcc6idtuigbkwnzeu '@aws-cdk/aws-ec2': 1.204.0_r4d2a6r7lnkv26zjzkdsvuam2a - '@aws-cdk/aws-ecr': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi + '@aws-cdk/aws-ecr': 1.204.0_4bnk2gpayjo75fecjckge2dkni '@aws-cdk/aws-ecr-assets': 1.204.0_scjupxxta56mdpzkdveav52ufq '@aws-cdk/aws-events': 1.204.0_w2xl3dexbzdynnzeafah4cuzfm '@aws-cdk/aws-iam': 1.204.0_add7c2jq5lcc6idtuigbkwnzeu @@ -1668,10 +1668,11 @@ packages: '@aws-cdk/aws-logs': 1.204.0_l4ztnfmrjykhsbk6ow7yhidayu '@aws-cdk/aws-s3': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi '@aws-cdk/aws-s3-assets': 1.204.0_l4ztnfmrjykhsbk6ow7yhidayu - '@aws-cdk/aws-secretsmanager': 1.204.0_2o53qceqenzlpxe4mjswmsqfiq + '@aws-cdk/aws-secretsmanager': 1.204.0_336juigttbrwz7tyvm6a6wfpy4 '@aws-cdk/core': 1.204.0_hol6usdabdbzhugfw355k4ebam '@aws-cdk/region-info': 1.204.0 constructs: 3.4.344 + yaml: 1.10.2 transitivePeerDependencies: - '@aws-cdk/aws-lambda' - '@aws-cdk/cx-api' @@ -1787,6 +1788,7 @@ packages: '@aws-cdk/core': 1.204.0_hol6usdabdbzhugfw355k4ebam '@aws-cdk/custom-resources': 1.204.0_c23kgzmvfhgnr6qpzzlbsfzuc4 constructs: 3.4.344 + punycode: 2.3.1 transitivePeerDependencies: - '@aws-cdk/aws-ec2' - '@aws-cdk/aws-logs' @@ -1873,7 +1875,7 @@ packages: constructs: ^3.3.69 dependencies: '@aws-cdk/assets': 1.204.0_uszt2j4mor3yrbm3tre3az4zvy - '@aws-cdk/aws-ecr': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi + '@aws-cdk/aws-ecr': 1.204.0_4bnk2gpayjo75fecjckge2dkni '@aws-cdk/aws-iam': 1.204.0_add7c2jq5lcc6idtuigbkwnzeu '@aws-cdk/aws-s3': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi '@aws-cdk/core': 1.204.0_hol6usdabdbzhugfw355k4ebam @@ -1883,7 +1885,7 @@ packages: - '@aws-cdk/aws-events' dev: false - /@aws-cdk/aws-ecr/1.204.0_bi2u42js5xhxqcsg5gqefde4xi: + /@aws-cdk/aws-ecr/1.204.0_4bnk2gpayjo75fecjckge2dkni: resolution: {integrity: sha512-oCts9e+ackWoFHeyn/3oKm3X1lSizleWNNXHp5WGM38lpNVrtCLMKSShu5iXJBhqRH2Mz1AcA4fDMWhe8DvJFA==} engines: {node: '>= 14.15.0'} deprecated: |- @@ -1899,11 +1901,8 @@ packages: dependencies: '@aws-cdk/aws-events': 1.204.0_w2xl3dexbzdynnzeafah4cuzfm '@aws-cdk/aws-iam': 1.204.0_add7c2jq5lcc6idtuigbkwnzeu - '@aws-cdk/aws-kms': 1.204.0_cttdkzy7hngahjug7jmkfylr2y '@aws-cdk/core': 1.204.0_hol6usdabdbzhugfw355k4ebam constructs: 3.4.344 - transitivePeerDependencies: - - '@aws-cdk/cx-api' dev: false /@aws-cdk/aws-ecs/1.204.0_iu2vquo67t63xu6vdymsg3ufny: @@ -1929,7 +1928,7 @@ packages: '@aws-cdk/aws-certificatemanager': 1.204.0_xtqk4litqxecxsqs3sd6ajo2ja '@aws-cdk/aws-cloudwatch': 1.204.0_w2xl3dexbzdynnzeafah4cuzfm '@aws-cdk/aws-ec2': 1.204.0_r4d2a6r7lnkv26zjzkdsvuam2a - '@aws-cdk/aws-ecr': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi + '@aws-cdk/aws-ecr': 1.204.0_4bnk2gpayjo75fecjckge2dkni '@aws-cdk/aws-ecr-assets': 1.204.0_scjupxxta56mdpzkdveav52ufq '@aws-cdk/aws-elasticloadbalancing': 1.204.0_s2iwowsvskkmujjbrmx4g5hlsi '@aws-cdk/aws-elasticloadbalancingv2': 1.204.0_xbmlyikxd4zabyotfrt4oo4gli @@ -1941,7 +1940,7 @@ packages: '@aws-cdk/aws-route53-targets': 1.204.0_2eviprr3zwoouaslbumtdekrhi '@aws-cdk/aws-s3': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi '@aws-cdk/aws-s3-assets': 1.204.0_l4ztnfmrjykhsbk6ow7yhidayu - '@aws-cdk/aws-secretsmanager': 1.204.0_2o53qceqenzlpxe4mjswmsqfiq + '@aws-cdk/aws-secretsmanager': 1.204.0_336juigttbrwz7tyvm6a6wfpy4 '@aws-cdk/aws-servicediscovery': 1.204.0_nu23nesxfni464wb5cy4ehgagi '@aws-cdk/aws-sns': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi '@aws-cdk/aws-sqs': 1.204.0_cttdkzy7hngahjug7jmkfylr2y @@ -2242,7 +2241,7 @@ packages: '@aws-cdk/aws-lambda': 1.204.0_afnjft5qr3fswieaeg3dwwhnvm '@aws-cdk/aws-s3': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi '@aws-cdk/aws-s3-notifications': 1.204.0_xguspq3b5n56mo6dsez57f32qa - '@aws-cdk/aws-secretsmanager': 1.204.0_2o53qceqenzlpxe4mjswmsqfiq + '@aws-cdk/aws-secretsmanager': 1.204.0_336juigttbrwz7tyvm6a6wfpy4 '@aws-cdk/aws-sns': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi '@aws-cdk/aws-sns-subscriptions': 1.204.0_bpkznh2gsccwq6qpaogbkb4psu '@aws-cdk/aws-sqs': 1.204.0_cttdkzy7hngahjug7jmkfylr2y @@ -2275,7 +2274,7 @@ packages: '@aws-cdk/aws-cloudwatch': 1.204.0_w2xl3dexbzdynnzeafah4cuzfm '@aws-cdk/aws-codeguruprofiler': 1.204.0_w2xl3dexbzdynnzeafah4cuzfm '@aws-cdk/aws-ec2': 1.204.0_r4d2a6r7lnkv26zjzkdsvuam2a - '@aws-cdk/aws-ecr': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi + '@aws-cdk/aws-ecr': 1.204.0_4bnk2gpayjo75fecjckge2dkni '@aws-cdk/aws-ecr-assets': 1.204.0_scjupxxta56mdpzkdveav52ufq '@aws-cdk/aws-efs': 1.204.0_r4d2a6r7lnkv26zjzkdsvuam2a '@aws-cdk/aws-events': 1.204.0_w2xl3dexbzdynnzeafah4cuzfm @@ -2436,6 +2435,7 @@ packages: '@aws-cdk/aws-s3-assets': 1.204.0_l4ztnfmrjykhsbk6ow7yhidayu '@aws-cdk/core': 1.204.0_hol6usdabdbzhugfw355k4ebam '@aws-cdk/lambda-layer-awscli': 1.204.0_e7ybiu4yrrtvf3zlvzrvcjkvyy + case: 1.6.3 constructs: 3.4.344 transitivePeerDependencies: - '@aws-cdk/assets' @@ -2511,7 +2511,7 @@ packages: constructs: 3.4.344 dev: false - /@aws-cdk/aws-secretsmanager/1.204.0_2o53qceqenzlpxe4mjswmsqfiq: + /@aws-cdk/aws-secretsmanager/1.204.0_336juigttbrwz7tyvm6a6wfpy4: resolution: {integrity: sha512-ykpjYmP6qVOFbHtkaQBu3Xk7xp2UTR0ouzk7pb+zrEHKGmRvzGq+8J0IU+qXBJgQIVwFAPf2IgOSTzj6FJPdyA==} engines: {node: '>= 14.15.0'} deprecated: |- @@ -2526,18 +2526,12 @@ packages: '@aws-cdk/cx-api': 1.204.0 constructs: ^3.3.69 dependencies: - '@aws-cdk/aws-ec2': 1.204.0_r4d2a6r7lnkv26zjzkdsvuam2a '@aws-cdk/aws-iam': 1.204.0_add7c2jq5lcc6idtuigbkwnzeu - '@aws-cdk/aws-kms': 1.204.0_cttdkzy7hngahjug7jmkfylr2y '@aws-cdk/aws-lambda': 1.204.0_afnjft5qr3fswieaeg3dwwhnvm '@aws-cdk/aws-sam': 1.204.0_add7c2jq5lcc6idtuigbkwnzeu '@aws-cdk/core': 1.204.0_hol6usdabdbzhugfw355k4ebam '@aws-cdk/cx-api': 1.203.0 constructs: 3.4.344 - transitivePeerDependencies: - - '@aws-cdk/assets' - - '@aws-cdk/aws-logs' - - '@aws-cdk/aws-s3' dev: false /@aws-cdk/aws-servicediscovery/1.204.0_nu23nesxfni464wb5cy4ehgagi: @@ -2715,6 +2709,9 @@ packages: /@aws-cdk/cloud-assembly-schema/1.203.0: resolution: {integrity: sha512-r252InZ8Oh7q7ztriaA3n6F48QOFVfNcT/KO4XOlYyt1xDWRMENDYf+D+DVr6O5klcaa3ivvvDT7DRuW3xdVOQ==} engines: {node: '>= 14.15.0'} + dependencies: + jsonschema: 1.4.1 + semver: 7.6.3 dev: false bundledDependencies: - jsonschema @@ -2728,6 +2725,9 @@ packages: This package is no longer being updated, and users should migrate to AWS CDK v2. For more information on how to migrate, see https://docs.aws.amazon.com/cdk/v2/guide/migrating-v2.html + dependencies: + jsonschema: 1.4.1 + semver: 7.6.3 dev: false bundledDependencies: - jsonschema @@ -2736,6 +2736,9 @@ packages: /@aws-cdk/cloud-assembly-schema/2.39.1: resolution: {integrity: sha512-lSVaaedXWeK08uoq0IXDCspz9U/H4qIERemdsMQrMUDTiUe/JBby7vtmyMvOdEscE8GMAmiOzoPmAE0Uf+yw5A==} engines: {node: '>= 14.15.0'} + dependencies: + jsonschema: 1.4.1 + semver: 7.6.3 dev: false bundledDependencies: - jsonschema @@ -2769,7 +2772,11 @@ packages: '@aws-cdk/cloud-assembly-schema': 1.204.0 '@aws-cdk/cx-api': 1.203.0 '@aws-cdk/region-info': 1.204.0 + '@balena/dockerignore': 1.0.2 constructs: 3.4.344 + fs-extra: 9.1.0 + ignore: 5.3.1 + minimatch: 3.1.2 dev: false bundledDependencies: - fs-extra @@ -2812,6 +2819,7 @@ packages: engines: {node: '>= 14.15.0'} dependencies: '@aws-cdk/cloud-assembly-schema': 1.203.0 + semver: 7.6.3 dev: false bundledDependencies: - semver @@ -2826,6 +2834,7 @@ packages: For more information on how to migrate, see https://docs.aws.amazon.com/cdk/v2/guide/migrating-v2.html dependencies: '@aws-cdk/cloud-assembly-schema': 1.204.0 + semver: 7.6.3 dev: false bundledDependencies: - semver @@ -2835,6 +2844,7 @@ packages: engines: {node: '>= 14.15.0'} dependencies: '@aws-cdk/cloud-assembly-schema': 2.39.1 + semver: 7.6.3 dev: false bundledDependencies: - semver @@ -3291,6 +3301,10 @@ packages: '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 + /@balena/dockerignore/1.0.2: + resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} + dev: false + /@cdktf/cli-core/0.19.2_react@17.0.2: resolution: {integrity: sha512-kjgEUhrHx3kUPfL7KsTo6GrurVUPT77FmOUf7wWXt7ajNE5zCPvx/HKGmQruzt0n6eLZp1aKT+r/D6YRfXcIGA==} dependencies: @@ -5493,7 +5507,10 @@ packages: peerDependencies: constructs: ^10.0.25 dependencies: + archiver: 5.3.2 constructs: 10.3.0 + json-stable-stringify: 1.1.1 + semver: 7.6.3 bundledDependencies: - archiver - json-stable-stringify @@ -6385,7 +6402,7 @@ packages: dependencies: semver: 7.6.3 shelljs: 0.8.5 - typescript: 5.6.0-dev.20240805 + typescript: 5.6.0-dev.20240819 /duration/0.2.2: resolution: {integrity: sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg==} @@ -8558,6 +8575,15 @@ packages: /json-stable-stringify-without-jsonify/1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + /json-stable-stringify/1.1.1: + resolution: {integrity: sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + isarray: 2.0.5 + jsonify: 0.0.1 + object-keys: 1.1.1 + /json-stringify-safe/5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} dev: true @@ -8586,6 +8612,13 @@ packages: optionalDependencies: graceful-fs: 4.2.11 + /jsonify/0.0.1: + resolution: {integrity: sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==} + + /jsonschema/1.4.1: + resolution: {integrity: sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==} + dev: false + /jsonwebtoken/8.5.1: resolution: {integrity: sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==} engines: {node: '>=4', npm: '>=1.4.28'} @@ -11519,8 +11552,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - /typescript/5.6.0-dev.20240805: - resolution: {integrity: sha512-M+pWeLKA2huCTB2GLE0/0U/ezJh51Wdmm6J3BS1UxieKcnx1Kz2/NivXOnmCZY40gY6ItpMlLjnJIMXprNG9UQ==} + /typescript/5.6.0-dev.20240819: + resolution: {integrity: sha512-uNOMaNm8jBELjhuXZG5tSS6Pa6O/Wf89hawKkWaZcZvbkgkY2ykvNTNrkCP7QCQTSS3jwEVvd+pRhxJPxUeG4g==} engines: {node: '>=14.17'} hasBin: true diff --git a/packages/framework-core/src/sensor/health/booster-health-service.ts b/packages/framework-core/src/sensor/health/booster-health-service.ts index ad2e383a6..5bd68ac63 100644 --- a/packages/framework-core/src/sensor/health/booster-health-service.ts +++ b/packages/framework-core/src/sensor/health/booster-health-service.ts @@ -7,7 +7,7 @@ import { HealthIndicatorsResult, UserEnvelope, } from '@boostercloud/framework-types' -import { childrenHealthProviders, isEnabled, metadataFromId, rootHealthProviders } from './health-utils' +import { childHealthProviders, isEnabled, metadataFromId, rootHealthProviders } from './health-utils' import { createInstance } from '@boostercloud/framework-common-helpers' import { defaultBoosterHealthIndicators } from './health-indicators' import { BoosterTokenVerifier } from '../../booster-token-verifier' @@ -51,14 +51,14 @@ export class BoosterHealthService { if (!indicatorResult) { continue } - const childrens = childrenHealthProviders(current, healthProviders) + const children = childHealthProviders(current, healthProviders) const newResult: HealthIndicatorsResult = { ...indicatorResult, name: current.healthIndicatorConfiguration.name, id: current.healthIndicatorConfiguration.id, } - if (childrens && childrens?.length > 0) { - newResult.components = await this.boosterHealthProviderResolver(childrens, healthProviders) + if (children && children?.length > 0) { + newResult.components = await this.boosterHealthProviderResolver(children, healthProviders) } result.push(newResult) } diff --git a/packages/framework-core/src/sensor/health/health-indicators/default-booster-health-indicators.ts b/packages/framework-core/src/sensor/health/health-indicators/default-booster-health-indicators.ts index 05eca5611..3e2787cdd 100644 --- a/packages/framework-core/src/sensor/health/health-indicators/default-booster-health-indicators.ts +++ b/packages/framework-core/src/sensor/health/health-indicators/default-booster-health-indicators.ts @@ -10,6 +10,7 @@ import { BoosterDatabaseHealthIndicator } from './booster-database-health-indica import { BoosterDatabaseEventsHealthIndicator } from './booster-database-events-health-indicator' import { BoosterFunctionHealthIndicator } from './booster-function-health-indicator' import { BoosterDatabaseReadModelsHealthIndicator } from './booster-database-read-models-health-indicator' +import { RocketsHealthIndicator } from './rockets-health-indicator' function buildMetadata( config: BoosterConfig, @@ -59,11 +60,18 @@ export function defaultBoosterHealthIndicators(config: BoosterConfig): Record { + const results = await config.provider.sensor.areRocketFunctionsUp(config) + if (Object.keys(results).length === 0) { + return { + name: 'Rockets', + id: BOOSTER_HEALTH_INDICATORS_IDS.ROCKETS, + status: HealthStatus.UNKNOWN, + details: { + reason: 'No Rockets found', + }, + } + } + return { + name: 'Rockets', + id: BOOSTER_HEALTH_INDICATORS_IDS.ROCKETS, + status: this.getOverAllHealthStatus(results), + components: Object.entries(results).map(([rocketFunctionApp, status]) => { + return { + name: rocketFunctionApp, + id: rocketFunctionApp, // @TODO: put the rocket's id instead of its name + status: status ? HealthStatus.UP : HealthStatus.DOWN, + } + }), + } + } + + private getOverAllHealthStatus(results: { [key: string]: boolean }): HealthStatus { + const statusValues = Object.values(results) + + if (statusValues.every((status) => status)) { + return HealthStatus.UP + } + + if (statusValues.every((status) => !status)) { + return HealthStatus.DOWN + } + + return HealthStatus.PARTIALLY_UP + } +} diff --git a/packages/framework-core/src/sensor/health/health-utils.ts b/packages/framework-core/src/sensor/health/health-utils.ts index 40d74542e..a5373b566 100644 --- a/packages/framework-core/src/sensor/health/health-utils.ts +++ b/packages/framework-core/src/sensor/health/health-utils.ts @@ -27,7 +27,7 @@ export function rootHealthProviders( ) } -export function childrenHealthProviders( +export function childHealthProviders( healthIndicatorMetadata: HealthIndicatorMetadata, healthProviders: Record ): Array { diff --git a/packages/framework-core/test/sensor/health/booster-health-service.test.ts b/packages/framework-core/test/sensor/health/booster-health-service.test.ts index 2183b8eff..fcdc107d6 100644 --- a/packages/framework-core/test/sensor/health/booster-health-service.test.ts +++ b/packages/framework-core/test/sensor/health/booster-health-service.test.ts @@ -223,6 +223,7 @@ function defaultSensor(token?: string, url?: string) { rawRequestToHealthEnvelope: fake(() => { return { token: token, componentPath: url } }), + areRocketFunctionsUp: fake(() => ''), } } @@ -289,24 +290,12 @@ function expectDatabaseEventsWithDetails(databaseEvents: any, status: string, de } function expectDatabaseReadModels(databaseReadModels: any, status: string): void { - expectDefaultResult( - databaseReadModels, - status, - 'booster/database/readmodels', - 'Booster Database ReadModels', - 0 - ) + expectDefaultResult(databaseReadModels, status, 'booster/database/readmodels', 'Booster Database ReadModels', 0) expect(databaseReadModels.details).to.be.undefined } function expectDatabaseReadModelsWithDetails(databaseReadModels: any, status: string, details: any): void { - expectDefaultResult( - databaseReadModels, - status, - 'booster/database/readmodels', - 'Booster Database ReadModels', - 0 - ) + expectDefaultResult(databaseReadModels, status, 'booster/database/readmodels', 'Booster Database ReadModels', 0) expect(databaseReadModels.details).to.be.deep.eq(details) } diff --git a/packages/framework-core/test/sensor/health/health-utils.test.ts b/packages/framework-core/test/sensor/health/health-utils.test.ts index ec5bec1ab..360537f0f 100644 --- a/packages/framework-core/test/sensor/health/health-utils.test.ts +++ b/packages/framework-core/test/sensor/health/health-utils.test.ts @@ -1,7 +1,7 @@ import { HealthIndicatorMetadata } from '@boostercloud/framework-types' import 'mocha' import { - childrenHealthProviders, + childHealthProviders, isEnabled, metadataFromId, parentId, @@ -165,13 +165,13 @@ describe('Health utils', () => { }) it('childrenHealthProviders', () => { - expect(childrenHealthProviders(root, healthProviders)).to.be.deep.equal([rootChildren1, rootChildren2]) - expect(childrenHealthProviders(rootChildren1, healthProviders)).to.be.deep.equal([ + expect(childHealthProviders(root, healthProviders)).to.be.deep.equal([rootChildren1, rootChildren2]) + expect(childHealthProviders(rootChildren1, healthProviders)).to.be.deep.equal([ rootChildren1Children1, rootChildren1Children2, ]) - expect(childrenHealthProviders(rootChildren1Children1, healthProviders)).to.be.deep.equal([]) - expect(childrenHealthProviders(rootChildren1Children2, healthProviders)).to.be.deep.equal([]) - expect(childrenHealthProviders(rootChildren2, healthProviders)).to.be.deep.equal([]) + expect(childHealthProviders(rootChildren1Children1, healthProviders)).to.be.deep.equal([]) + expect(childHealthProviders(rootChildren1Children2, healthProviders)).to.be.deep.equal([]) + expect(childHealthProviders(rootChildren2, healthProviders)).to.be.deep.equal([]) }) }) diff --git a/packages/framework-provider-aws/src/setup.ts b/packages/framework-provider-aws/src/setup.ts index bd6cf1d18..2b30e45e7 100644 --- a/packages/framework-provider-aws/src/setup.ts +++ b/packages/framework-provider-aws/src/setup.ts @@ -124,6 +124,8 @@ export const Provider = (rockets?: RocketDescriptor[]): ProviderLibrary => { rawRequestToHealthEnvelope: (rawRequest: unknown): HealthEnvelope => { throw new Error('Not implemented') }, + areRocketFunctionsUp: async (config: BoosterConfig): Promise<{ [key: string]: boolean }> => + notImplementedResult(), }, // ProviderInfrastructureGetter infrastructure: () => { diff --git a/packages/framework-provider-azure-infrastructure/src/infrastructure/application-builder.ts b/packages/framework-provider-azure-infrastructure/src/infrastructure/application-builder.ts index 472ad84d5..2d08080b4 100644 --- a/packages/framework-provider-azure-infrastructure/src/infrastructure/application-builder.ts +++ b/packages/framework-provider-azure-infrastructure/src/infrastructure/application-builder.ts @@ -30,6 +30,8 @@ export class ApplicationBuilder { const azureStack = await this.synthApplication(app, webPubSubBaseFile) const rocketBuilder = new RocketBuilder(this.config, azureStack.applicationStack, this.rockets) await rocketBuilder.synthRocket() + // add rocket-related env vars to main function app settings + azureStack.addAppSettingsToFunctionApp(this.rockets) app.synth() azureStack.applicationStack.functionDefinitions = FunctionZip.buildAzureFunctions(this.config) diff --git a/packages/framework-provider-azure-infrastructure/src/infrastructure/azure-stack.ts b/packages/framework-provider-azure-infrastructure/src/infrastructure/azure-stack.ts index b3982b7b8..1fcfb9607 100644 --- a/packages/framework-provider-azure-infrastructure/src/infrastructure/azure-stack.ts +++ b/packages/framework-provider-azure-infrastructure/src/infrastructure/azure-stack.ts @@ -1,15 +1,42 @@ import { Construct } from 'constructs' -import { TerraformStack } from 'cdktf' +import { Fn, TerraformStack } from 'cdktf' import { ApplicationSynth } from './synth/application-synth' import { ApplicationSynthStack } from './types/application-synth-stack' +import { InfrastructureRocket } from './rockets/infrastructure-rocket' +import { environmentVarNames } from '@boostercloud/framework-provider-azure' export class AzureStack extends TerraformStack { readonly applicationStack: ApplicationSynthStack + readonly defaultApplicationSettings: { [key: string]: string } constructor(scope: Construct, name: string, zipFile?: string) { super(scope, name) const applicationSynth = new ApplicationSynth(this) this.applicationStack = applicationSynth.synth(zipFile) + this.defaultApplicationSettings = applicationSynth.buildDefaultAppSettings( + this.applicationStack, + this.applicationStack.storageAccount!, + 'func' + ) + } + + public addAppSettingsToFunctionApp(rockets?: InfrastructureRocket[]): void { + if (!this.applicationStack.functionApp) { + throw new Error('Function app not defined') + } + + const functionAppNames = rockets + ? rockets + .map((rocket: InfrastructureRocket) => + rocket.getFunctionAppName ? rocket.getFunctionAppName(this.applicationStack) : '' + ) + .join(',') + : '' + + this.applicationStack.functionApp.appSettings = Fn.merge([ + this.defaultApplicationSettings, + { [environmentVarNames.rocketFunctionAppNames]: functionAppNames }, + ]) } } diff --git a/packages/framework-provider-azure-infrastructure/src/infrastructure/synth/application-synth.ts b/packages/framework-provider-azure-infrastructure/src/infrastructure/synth/application-synth.ts index ab5cf63bd..0adec05c0 100644 --- a/packages/framework-provider-azure-infrastructure/src/infrastructure/synth/application-synth.ts +++ b/packages/framework-provider-azure-infrastructure/src/infrastructure/synth/application-synth.ts @@ -25,7 +25,7 @@ import { TerraformWebPubsubHub } from './terraform-web-pubsub-hub' import { TerraformWebPubSubExtensionKey } from './terraform-web-pub-sub-extension-key' import { TerraformEventHubNamespace } from './terraform-event-hub-namespace' import { TerraformEventHub } from './terraform-event-hub' -import { windowsFunctionApp } from '@cdktf/provider-azurerm' +import { storageAccount, windowsFunctionApp } from '@cdktf/provider-azurerm' import { TerraformNetworkSecurityGroup } from './gateway/terraform-network-security-group' import { TerraformVirtualNetwork } from './gateway/terraform-virtual-network' import { TerraformPublicIp } from './gateway/terraform-public-ip' @@ -33,6 +33,7 @@ import { TerraformPublicIpData } from './gateway/terraform-public-ip-data' import { TerraformSubnet } from './gateway/terraform-subnet' import { TerraformSubnetSecurity } from './gateway/terraform-subnet-security' import { BASIC_SERVICE_PLAN } from '../constants' +import { TerraformFunctionAppSettings } from './terraform-function-app-settings' export class ApplicationSynth { readonly config: BoosterConfig @@ -121,7 +122,6 @@ export class ApplicationSynth { ): windowsFunctionApp.WindowsFunctionApp { return TerraformFunctionApp.build( stack, - this.config, stack.applicationServicePlan!, stack.storageAccount!, 'func', @@ -139,11 +139,12 @@ export class ApplicationSynth { stack.eventConsumerStorageAccount = TerraformStorageAccount.build(stack, 'sc') stack.eventConsumerFunctionApp = TerraformFunctionApp.build( stack, - this.config, stack.eventConsumerServicePlan, stack.eventConsumerStorageAccount, 'fhub', - stack.streamFunctionAppName + stack.streamFunctionAppName, + undefined, + this.buildDefaultAppSettings(stack, stack.eventConsumerStorageAccount, 'fhub') ) if (!stack.containers) { stack.containers = [] @@ -164,4 +165,12 @@ export class ApplicationSynth { stack.webPubSubHub = TerraformWebPubsubHub.build(stack) } } + + public buildDefaultAppSettings( + stack: ApplicationSynthStack, + storageAccount: storageAccount.StorageAccount, + suffixName: string + ) { + return TerraformFunctionAppSettings.build(stack, this.config, storageAccount!, suffixName) + } } diff --git a/packages/framework-provider-azure-infrastructure/src/infrastructure/synth/terraform-function-app-settings.ts b/packages/framework-provider-azure-infrastructure/src/infrastructure/synth/terraform-function-app-settings.ts new file mode 100644 index 000000000..54907f464 --- /dev/null +++ b/packages/framework-provider-azure-infrastructure/src/infrastructure/synth/terraform-function-app-settings.ts @@ -0,0 +1,40 @@ +import { environmentVarNames } from '@boostercloud/framework-provider-azure' +import { ApplicationSynthStack } from '../types/application-synth-stack' +import { toTerraformName } from '../helper/utils' +import { BoosterConfig } from '@boostercloud/framework-types' +import { storageAccount } from '@cdktf/provider-azurerm' + +export class TerraformFunctionAppSettings { + static build( + { appPrefix, cosmosdbDatabase, domainNameLabel, eventHubNamespace, eventHub, webPubSub }: ApplicationSynthStack, + config: BoosterConfig, + storageAccount: storageAccount.StorageAccount, + suffixName: string + ): { [key: string]: string } { + if (!cosmosdbDatabase) { + throw new Error('Undefined cosmosdbDatabase resource') + } + const id = toTerraformName(appPrefix, suffixName) + const eventHubConnectionString = + eventHubNamespace?.defaultPrimaryConnectionString && eventHub?.name + ? `${eventHubNamespace.defaultPrimaryConnectionString};EntityPath=${eventHub.name}` + : '' + const region = (process.env['REGION'] ?? '').toLowerCase().replace(/ /g, '') + return { + WEBSITE_RUN_FROM_PACKAGE: '1', + WEBSITE_CONTENTSHARE: id, + ...config.env, + WebPubSubConnectionString: webPubSub?.primaryConnectionString || '', + BOOSTER_ENV: config.environmentName, + [environmentVarNames.restAPIURL]: `http://${domainNameLabel}.${region}.cloudapp.azure.com/${config.environmentName}`, + [environmentVarNames.eventHubConnectionString]: eventHubConnectionString, + [environmentVarNames.eventHubName]: config.resourceNames.streamTopic, + [environmentVarNames.eventHubMaxRetries]: + config.eventStreamConfiguration.parameters?.maxRetries?.toString() || '5', + [environmentVarNames.eventHubMode]: config.eventStreamConfiguration.parameters?.mode || 'exponential', + COSMOSDB_CONNECTION_STRING: `AccountEndpoint=https://${cosmosdbDatabase.name}.documents.azure.com:443/;AccountKey=${cosmosdbDatabase.primaryKey};`, + WEBSITE_CONTENTAZUREFILECONNECTIONSTRING: storageAccount.primaryConnectionString, // Terraform bug: https://github.com/hashicorp/terraform-provider-azurerm/issues/16650 + BOOSTER_APP_NAME: process.env['BOOSTER_APP_NAME'] ?? '', + } + } +} diff --git a/packages/framework-provider-azure-infrastructure/src/infrastructure/synth/terraform-function-app.ts b/packages/framework-provider-azure-infrastructure/src/infrastructure/synth/terraform-function-app.ts index 8e0628f99..13bc112a7 100644 --- a/packages/framework-provider-azure-infrastructure/src/infrastructure/synth/terraform-function-app.ts +++ b/packages/framework-provider-azure-infrastructure/src/infrastructure/synth/terraform-function-app.ts @@ -1,8 +1,6 @@ import { servicePlan, storageAccount, windowsFunctionApp } from '@cdktf/provider-azurerm' import { toTerraformName } from '../helper/utils' -import { BoosterConfig } from '@boostercloud/framework-types' import { ApplicationSynthStack } from '../types/application-synth-stack' -import { environmentVarNames } from '@boostercloud/framework-provider-azure' import { WindowsFunctionAppConfig } from '@cdktf/provider-azurerm/lib/windows-function-app' export class TerraformFunctionApp { @@ -14,17 +12,13 @@ export class TerraformFunctionApp { resourceGroup, resourceGroupName, cosmosdbDatabase, - domainNameLabel, - eventHubNamespace, - eventHub, - webPubSub, }: ApplicationSynthStack, - config: BoosterConfig, applicationServicePlan: servicePlan.ServicePlan, storageAccount: storageAccount.StorageAccount, suffixName: string, functionAppName: string, - zipFile?: string + zipFile?: string, + appSettings?: { [key: string]: string } ): windowsFunctionApp.WindowsFunctionApp { if (!cosmosdbDatabase) { throw new Error('Undefined cosmosdbDatabase resource') @@ -33,32 +27,12 @@ export class TerraformFunctionApp { throw new Error('Undefined applicationServicePlan resource') } const id = toTerraformName(appPrefix, suffixName) - const eventHubConnectionString = - eventHubNamespace?.defaultPrimaryConnectionString && eventHub?.name - ? `${eventHubNamespace.defaultPrimaryConnectionString};EntityPath=${eventHub.name}` - : '' - const region = (process.env['REGION'] ?? '').toLowerCase().replace(/ /g, '') const functionConfig: Exclude = { name: functionAppName, location: resourceGroup.location, resourceGroupName: resourceGroupName, servicePlanId: applicationServicePlan.id, - appSettings: { - WEBSITE_RUN_FROM_PACKAGE: '1', - WEBSITE_CONTENTSHARE: id, - ...config.env, - WebPubSubConnectionString: webPubSub?.primaryConnectionString || '', - BOOSTER_ENV: config.environmentName, - [environmentVarNames.restAPIURL]: `http://${domainNameLabel}.${region}.cloudapp.azure.com/${config.environmentName}`, - [environmentVarNames.eventHubConnectionString]: eventHubConnectionString, - [environmentVarNames.eventHubName]: config.resourceNames.streamTopic, - [environmentVarNames.eventHubMaxRetries]: - config.eventStreamConfiguration.parameters?.maxRetries?.toString() || '5', - [environmentVarNames.eventHubMode]: config.eventStreamConfiguration.parameters?.mode || 'exponential', - COSMOSDB_CONNECTION_STRING: `AccountEndpoint=https://${cosmosdbDatabase.name}.documents.azure.com:443/;AccountKey=${cosmosdbDatabase.primaryKey};`, - WEBSITE_CONTENTAZUREFILECONNECTIONSTRING: storageAccount.primaryConnectionString, // Terraform bug: https://github.com/hashicorp/terraform-provider-azurerm/issues/16650 - BOOSTER_APP_NAME: process.env['BOOSTER_APP_NAME'] ?? '', - }, + appSettings: appSettings ?? {}, storageAccountName: storageAccount.name, storageAccountAccessKey: storageAccount.primaryAccessKey, dependsOn: [resourceGroup], diff --git a/packages/framework-provider-azure/src/constants.ts b/packages/framework-provider-azure/src/constants.ts index 8ef6c28ca..f3e6f5d0e 100644 --- a/packages/framework-provider-azure/src/constants.ts +++ b/packages/framework-provider-azure/src/constants.ts @@ -32,6 +32,7 @@ export const environmentVarNames = { eventHubName: 'EVENTHUB_NAME', eventHubMaxRetries: 'EVENTHUB_MAX_RETRIES', eventHubMode: 'EVENTHUB_MODE', + rocketFunctionAppNames: 'ROCKET_FUNCTION_APP_NAMES', } as const // Azure special error codes diff --git a/packages/framework-provider-azure/src/index.ts b/packages/framework-provider-azure/src/index.ts index b40afbe10..0e402c6d6 100644 --- a/packages/framework-provider-azure/src/index.ts +++ b/packages/framework-provider-azure/src/index.ts @@ -38,6 +38,7 @@ import { EventHubProducerClient, RetryMode } from '@azure/event-hubs' import { dedupEventStream, rawEventsStreamToEnvelopes } from './library/events-stream-consumer-adapter' import { areDatabaseReadModelsUp, + areRocketFunctionsUp, databaseEventsHealthDetails, databaseReadModelsHealthDetails, databaseUrl, @@ -151,6 +152,7 @@ export const Provider = (rockets?: RocketDescriptor[]): ProviderLibrary => ({ graphQLFunctionUrl: graphqlFunctionUrl, isGraphQLFunctionUp: isGraphQLFunctionUp, rawRequestToHealthEnvelope: rawRequestToSensorHealth, + areRocketFunctionsUp: areRocketFunctionsUp, }, // ProviderInfrastructureGetter infrastructure: () => { diff --git a/packages/framework-provider-azure/src/library/health-adapter.ts b/packages/framework-provider-azure/src/library/health-adapter.ts index 1b87c3ebe..c983d9738 100644 --- a/packages/framework-provider-azure/src/library/health-adapter.ts +++ b/packages/framework-provider-azure/src/library/health-adapter.ts @@ -47,6 +47,10 @@ export async function graphqlFunctionUrl(): Promise { } } +export async function rocketFunctionAppUrl(functionAppName: string): Promise { + return `https://${functionAppName}.azurewebsites.net` +} + export async function isDatabaseEventUp(cosmosDb: CosmosClient, config: BoosterConfig): Promise { return await isContainerUp(cosmosDb, config, config.resourceNames.eventsStore) } @@ -71,6 +75,29 @@ export async function isGraphQLFunctionUp(): Promise { } } +export async function isRocketFunctionUp(rocketFunctionAppName: string): Promise { + try { + const functionAppUrl = await rocketFunctionAppUrl(rocketFunctionAppName) + const response = await request(functionAppUrl, 'GET') + return response.status === 200 + } catch (e) { + return false + } +} + +export async function areRocketFunctionsUp(): Promise<{ [key: string]: boolean }> { + const functionAppNames = + process.env[environmentVarNames.rocketFunctionAppNames]?.split(',').filter((str) => str.trim() !== '') || [] + const results = await Promise.all( + functionAppNames.map(async (functionAppName: string) => { + const isUp = await isRocketFunctionUp(functionAppName) + return { [functionAppName]: isUp } + }) + ) + + return results.reduce((acc, result) => ({ ...acc, ...result }), {}) +} + export function rawRequestToSensorHealthComponentPath(rawRequest: Context): string { const parameters = rawRequest.req?.url.replace(/^.*sensor\/health\/?/, '') return parameters ?? '' diff --git a/packages/framework-provider-local/src/index.ts b/packages/framework-provider-local/src/index.ts index eb1aec9a3..3676d89d3 100644 --- a/packages/framework-provider-local/src/index.ts +++ b/packages/framework-provider-local/src/index.ts @@ -127,6 +127,7 @@ export const Provider = (rocketDescriptors?: RocketDescriptor[]): ProviderLibrar isGraphQLFunctionUp: isGraphQLFunctionUp, graphQLFunctionUrl: graphqlFunctionUrl, rawRequestToHealthEnvelope: rawRequestToSensorHealth, + areRocketFunctionsUp: notImplemented as any, }, // ProviderInfrastructureGetter infrastructure: () => { diff --git a/packages/framework-types/src/provider.ts b/packages/framework-types/src/provider.ts index 24dd61816..efb2efae0 100644 --- a/packages/framework-types/src/provider.ts +++ b/packages/framework-types/src/provider.ts @@ -47,6 +47,7 @@ export interface ProviderSensorLibrary { isGraphQLFunctionUp(config: BoosterConfig): Promise graphQLFunctionUrl(config: BoosterConfig): Promise rawRequestToHealthEnvelope(rawRequest: unknown): HealthEnvelope + areRocketFunctionsUp(config: BoosterConfig): Promise<{ [key: string]: boolean }> } export interface ProviderEventsLibrary { diff --git a/packages/framework-types/src/sensor/health-indicator-configuration.ts b/packages/framework-types/src/sensor/health-indicator-configuration.ts index 3556a8aa0..351753cb7 100644 --- a/packages/framework-types/src/sensor/health-indicator-configuration.ts +++ b/packages/framework-types/src/sensor/health-indicator-configuration.ts @@ -4,6 +4,7 @@ import { Class } from '../typelevel' export enum HealthStatus { UP = 'UP', // The component or subsystem is working as expected + PARTIALLY_UP = 'PARTIALLY_UP', // The component is partially working or has reduced functionality DOWN = 'DOWN', // The component is not working OUT_OF_SERVICE = 'OUT_OF_SERVICE', // The component is out of service temporarily UNKNOWN = 'UNKNOWN', // The component state is unknown @@ -28,6 +29,7 @@ export enum BOOSTER_HEALTH_INDICATORS_IDS { DATABASE = 'booster/database', DATABASE_EVENTS = 'booster/database/events', DATABASE_READ_MODELS = 'booster/database/readmodels', + ROCKETS = 'rockets', } export const DEFAULT_HEALTH_CONFIGURATION_BOOSTER: SensorBoosterHealthConfigurationDetails = { @@ -45,6 +47,7 @@ export const DEFAULT_SENSOR_HEALTH_BOOSTER_CONFIGURATIONS: Record< [BOOSTER_HEALTH_INDICATORS_IDS.DATABASE]: { ...DEFAULT_HEALTH_CONFIGURATION_BOOSTER }, [BOOSTER_HEALTH_INDICATORS_IDS.DATABASE_EVENTS]: { ...DEFAULT_HEALTH_CONFIGURATION_BOOSTER }, [BOOSTER_HEALTH_INDICATORS_IDS.DATABASE_READ_MODELS]: { ...DEFAULT_HEALTH_CONFIGURATION_BOOSTER }, + [BOOSTER_HEALTH_INDICATORS_IDS.ROCKETS]: { ...DEFAULT_HEALTH_CONFIGURATION_BOOSTER }, } export type SensorBoosterHealthConfigurationDetails = HealthIndicatorConfigurationBase @@ -57,6 +60,7 @@ export interface SensorBoosterHealthConfiguration { [BOOSTER_HEALTH_INDICATORS_IDS.DATABASE]: SensorBoosterHealthConfigurationDetails [BOOSTER_HEALTH_INDICATORS_IDS.DATABASE_EVENTS]: SensorBoosterHealthConfigurationDetails [BOOSTER_HEALTH_INDICATORS_IDS.DATABASE_READ_MODELS]: SensorBoosterHealthConfigurationDetails + [BOOSTER_HEALTH_INDICATORS_IDS.ROCKETS]: SensorBoosterHealthConfigurationDetails } } diff --git a/website/docs/10_going-deeper/health/sensor-health.md b/website/docs/10_going-deeper/health/sensor-health.md index bdf4eb8af..3a769a538 100644 --- a/website/docs/10_going-deeper/health/sensor-health.md +++ b/website/docs/10_going-deeper/health/sensor-health.md @@ -87,10 +87,11 @@ Booster provides the following endpoints to retrieve the enabled components: * https://your-application-url/sensor/health/booster/database/events: Events status * https://your-application-url/sensor/health/booster/database/readmodels: ReadModels status * https://your-application-url/sensor/health/booster/function: Functions status +* https://your-application-url/sensor/health/rockets: Rockets status * https://your-application-url/sensor/health/your-component-id: User defined status * https://your-application-url/sensor/health/your-component-id/your-component-child-id: User child component status -Depending on the `showChildren` configuration, children components will be included or not. +Depending on the `showChildren` configuration, child components will be included or not. ### Health Status Response @@ -100,7 +101,7 @@ Each component response will contain the following information: * name: component description * id: string. unique component identifier. You can request a component status using the id in the url * details: optional object. If `details` is true, specific details about this component. -* components: optional object. If `showChildren` is true, children components health status. +* components: optional object. If `showChildren` is true, child components health status. Example: @@ -150,6 +151,7 @@ Use the `id` field to get specific component health information. Booster provide * booster/database * booster/database/events * booster/database/readmodels +* rockets You can provide new components: ```typescript @@ -189,8 +191,8 @@ Health components are fully configurable, allowing you to display the informatio Configuration options: * enabled: If false, this indicator and the components of this indicator will be skipped * details: If false, the indicator will not include the details -* showChildren: If false, this indicator will not include children components in the tree. - * Children components will be shown through children urls +* showChildren: If false, this indicator will not include child components in the tree. + * Child components will be shown through child urls * authorize: Authorize configuration. [See security documentation](https://docs.boosterframework.com/security/security) #### Booster components default configuration @@ -316,12 +318,18 @@ export class ApplicationHealthIndicator { > **Note**: details will be included only if `details` is enabled +#### rockets + +* status: UP if and only if all rockets are UP, PARTIALLY_UP if not all rockets are UP + +> **Note**: sensors for rockets is only available for the Azure provider ### Health status Available status are * UP: The component or subsystem is working as expected +* PARTIALLY_UP: The component is partially working or has reduced functionality * DOWN: The component is not working * OUT_OF_SERVICE: The component is out of service temporarily * UNKNOWN: The component state is unknown @@ -370,7 +378,7 @@ If the request url is https://your-application-url/sensor/health/database, the c [Empty] ``` -And the children components will be disabled too using direct url https://your-application-url/sensor/health/database/events +And the child components will be disabled too using direct url https://your-application-url/sensor/health/database/events ```text [Empty]