@firestartr/cli 2.2.0 → 2.3.0-snapshot
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/build/index.js +1035 -480
- package/build/packages/catalog_common/src/types/envvars.d.ts +1 -0
- package/build/packages/cdk8s_renderer/imports/firestartr.dev.d.ts +33 -0
- package/build/packages/cdk8s_renderer/src/claims/base/schemas/index.d.ts +36 -0
- package/build/packages/cdk8s_renderer/src/claims/github/component.labels.schema.d.ts +32 -0
- package/build/packages/cdk8s_renderer/src/claims/github/component.schema.d.ts +6 -0
- package/build/packages/cdk8s_renderer/src/claims/github/index.d.ts +36 -0
- package/build/packages/cdk8s_renderer/src/claims/github/repository.d.ts +6 -0
- package/build/packages/cdk8s_renderer/src/initializers/component_labels.d.ts +11 -0
- package/build/packages/gh_provisioner/src/entities/ghrepo/helpers/index.d.ts +1 -0
- package/build/packages/gh_provisioner/src/entities/ghrepo/helpers/labels.d.ts +1 -0
- package/build/packages/github/index.d.ts +2 -0
- package/build/packages/github/src/repository.d.ts +8 -0
- package/build/packages/operator/src/definitions.d.ts +59 -0
- package/build/packages/operator/src/informer.d.ts +4 -55
- package/build/packages/operator/src/metrics/processItem.global.metrics.d.ts +12 -0
- package/build/packages/operator/src/metrics/processItem.slot.metrics.d.ts +15 -0
- package/build/packages/operator/src/metricsServer.d.ts +1 -0
- package/build/packages/operator/src/processItem.slot.d.ts +45 -0
- package/build/packages/operator/src/signals.d.ts +1 -17
- package/build/packages/terraform_provisioner/src/process_handler.d.ts +1 -0
- package/build/packages/terraform_provisioner/src/project_tf.d.ts +0 -1
- package/build/packages/terraform_provisioner/src/utils.d.ts +2 -2
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -276790,6 +276790,7 @@ var envVars;
|
|
|
276790
276790
|
envVars["operatorDummyExec"] = "OPERATOR_DUMMY_EXEC";
|
|
276791
276791
|
envVars["operatorIgnoreLease"] = "OPERATOR_IGNORE_LEASE";
|
|
276792
276792
|
envVars["operatorDeploymentName"] = "OPERATOR_DEPLOYMENT_NAME";
|
|
276793
|
+
envVars["operatorNumberOfMaxSlots"] = "OPERATOR_NUMBER_OF_MAX_SLOTS";
|
|
276793
276794
|
// ---- KUBERNETES VARIABLES -----------------------------------------------
|
|
276794
276795
|
envVars["kubernetesServiceHost"] = "KUBERNETES_SERVICE_HOST";
|
|
276795
276796
|
envVars["kubernetesServicePort"] = "KUBERNETES_SERVICE_PORT";
|
|
@@ -284668,6 +284669,16 @@ async function getRepoInfo(owner, name) {
|
|
|
284668
284669
|
const res = await octokit.repos.get({ owner: owner, repo: name });
|
|
284669
284670
|
return res['data'];
|
|
284670
284671
|
}
|
|
284672
|
+
async function repoExists(owner, name) {
|
|
284673
|
+
github_src_logger.info(`Checking if repo exists: ${owner}/${name}`);
|
|
284674
|
+
try {
|
|
284675
|
+
await getRepoInfo(owner, name);
|
|
284676
|
+
return true;
|
|
284677
|
+
}
|
|
284678
|
+
catch (error) {
|
|
284679
|
+
return false;
|
|
284680
|
+
}
|
|
284681
|
+
}
|
|
284671
284682
|
async function getPages(owner, name) {
|
|
284672
284683
|
github_src_logger.info(`Getting pages for ${owner}/${name}`);
|
|
284673
284684
|
const octokit = await getOctokitForOrg(owner);
|
|
@@ -284795,6 +284806,20 @@ async function addCommitStatus(state, sha, repo, owner = 'prefapp', target_url =
|
|
|
284795
284806
|
context,
|
|
284796
284807
|
});
|
|
284797
284808
|
}
|
|
284809
|
+
async function getRepoIssuesLabels(owner, repo) {
|
|
284810
|
+
github_src_logger.info(`Getting issues labels for ${owner}/${repo}`);
|
|
284811
|
+
const octokit = await getOctokitForOrg(owner);
|
|
284812
|
+
const res = await octokit.issues.listLabelsForRepo({
|
|
284813
|
+
owner: owner,
|
|
284814
|
+
repo: repo,
|
|
284815
|
+
});
|
|
284816
|
+
const metadataLabels = res.data.map((label) => ({
|
|
284817
|
+
name: label.name,
|
|
284818
|
+
description: label.description || null,
|
|
284819
|
+
color: label.color,
|
|
284820
|
+
}));
|
|
284821
|
+
return metadataLabels;
|
|
284822
|
+
}
|
|
284798
284823
|
/* harmony default export */ const repository = ({
|
|
284799
284824
|
listReleases,
|
|
284800
284825
|
getReleaseByTag,
|
|
@@ -284803,6 +284828,7 @@ async function addCommitStatus(state, sha, repo, owner = 'prefapp', target_url =
|
|
|
284803
284828
|
uploadFile,
|
|
284804
284829
|
deleteFile,
|
|
284805
284830
|
getRepoInfo,
|
|
284831
|
+
repoExists,
|
|
284806
284832
|
getPages,
|
|
284807
284833
|
getBranchProtection,
|
|
284808
284834
|
getTeams,
|
|
@@ -284810,6 +284836,7 @@ async function addCommitStatus(state, sha, repo, owner = 'prefapp', target_url =
|
|
|
284810
284836
|
getOIDCRepo,
|
|
284811
284837
|
addStatusCheck,
|
|
284812
284838
|
addCommitStatus,
|
|
284839
|
+
getRepoIssuesLabels,
|
|
284813
284840
|
});
|
|
284814
284841
|
|
|
284815
284842
|
;// CONCATENATED MODULE: ../github/src/team.ts
|
|
@@ -287910,6 +287937,44 @@ MetadataInitializer.applicableKinds = [
|
|
|
287910
287937
|
];
|
|
287911
287938
|
|
|
287912
287939
|
|
|
287940
|
+
;// CONCATENATED MODULE: ../cdk8s_renderer/src/initializers/component_labels.ts
|
|
287941
|
+
|
|
287942
|
+
class ComponentLabelsInitializer extends InitializerPatches {
|
|
287943
|
+
constructor() {
|
|
287944
|
+
super(...arguments);
|
|
287945
|
+
this.applicableProviders = ['github'];
|
|
287946
|
+
}
|
|
287947
|
+
async __validate() {
|
|
287948
|
+
return true;
|
|
287949
|
+
}
|
|
287950
|
+
async __patches(claim, _previousCR) {
|
|
287951
|
+
return [
|
|
287952
|
+
{
|
|
287953
|
+
validate(cr) {
|
|
287954
|
+
if (cr.spec.repo.labels) {
|
|
287955
|
+
const visited = {};
|
|
287956
|
+
for (const label of cr.spec.repo.labels) {
|
|
287957
|
+
if (visited[label.name]) {
|
|
287958
|
+
throw `There is already a label called ${label.name} in the ComponentClaim ${cr.metadata.name}. Labels must be unique`;
|
|
287959
|
+
}
|
|
287960
|
+
visited[label.name] = true;
|
|
287961
|
+
}
|
|
287962
|
+
}
|
|
287963
|
+
return true;
|
|
287964
|
+
},
|
|
287965
|
+
apply(cr) {
|
|
287966
|
+
return cr;
|
|
287967
|
+
},
|
|
287968
|
+
identify() {
|
|
287969
|
+
return 'initializers/ComponentLabels';
|
|
287970
|
+
},
|
|
287971
|
+
},
|
|
287972
|
+
];
|
|
287973
|
+
}
|
|
287974
|
+
}
|
|
287975
|
+
ComponentLabelsInitializer.applicableKinds = ['ComponentClaim'];
|
|
287976
|
+
|
|
287977
|
+
|
|
287913
287978
|
;// CONCATENATED MODULE: ../cdk8s_renderer/src/initializers/index.ts
|
|
287914
287979
|
|
|
287915
287980
|
|
|
@@ -287921,6 +287986,7 @@ MetadataInitializer.applicableKinds = [
|
|
|
287921
287986
|
|
|
287922
287987
|
|
|
287923
287988
|
|
|
287989
|
+
|
|
287924
287990
|
const INITIALIZERS = [
|
|
287925
287991
|
UUIDInitializer,
|
|
287926
287992
|
InitializerClaimRef,
|
|
@@ -287928,6 +287994,7 @@ const INITIALIZERS = [
|
|
|
287928
287994
|
PolicyInitializer,
|
|
287929
287995
|
SyncerInitializer,
|
|
287930
287996
|
MetadataInitializer,
|
|
287997
|
+
ComponentLabelsInitializer,
|
|
287931
287998
|
];
|
|
287932
287999
|
const INITIALIZERS_BY_FILE_NAME = {
|
|
287933
288000
|
[TechnologyInitializer.FILE_NAME()]: TechnologyInitializer,
|
|
@@ -289337,6 +289404,12 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
|
|
|
289337
289404
|
pattern: '^[a-z0-9][a-z0-9-]*$',
|
|
289338
289405
|
},
|
|
289339
289406
|
},
|
|
289407
|
+
labels: {
|
|
289408
|
+
type: 'array',
|
|
289409
|
+
items: {
|
|
289410
|
+
$ref: 'firestartr.dev://github/GithubComponentClaimLabel',
|
|
289411
|
+
},
|
|
289412
|
+
},
|
|
289340
289413
|
overrides: {
|
|
289341
289414
|
type: 'object',
|
|
289342
289415
|
properties: {},
|
|
@@ -289545,6 +289618,40 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
|
|
|
289545
289618
|
},
|
|
289546
289619
|
});
|
|
289547
289620
|
|
|
289621
|
+
;// CONCATENATED MODULE: ../cdk8s_renderer/src/claims/github/component.labels.schema.ts
|
|
289622
|
+
|
|
289623
|
+
/* harmony default export */ const component_labels_schema = ({
|
|
289624
|
+
$schema: SCHEMA,
|
|
289625
|
+
$id: 'GithubComponentClaimLabels',
|
|
289626
|
+
definitions: {
|
|
289627
|
+
GithubComponentClaimLabel: {
|
|
289628
|
+
$id: 'firestartr.dev://github/GithubComponentClaimLabel',
|
|
289629
|
+
type: 'object',
|
|
289630
|
+
required: ['name', 'color'],
|
|
289631
|
+
properties: {
|
|
289632
|
+
name: {
|
|
289633
|
+
type: 'string',
|
|
289634
|
+
minLength: 1,
|
|
289635
|
+
maxLength: 50,
|
|
289636
|
+
pattern: '^(?!\\s+$).+$',
|
|
289637
|
+
description: 'Label name (1-50 chars, not only whitespaces)',
|
|
289638
|
+
},
|
|
289639
|
+
color: {
|
|
289640
|
+
type: 'string',
|
|
289641
|
+
pattern: '^[0-9a-fA-F]{6}$',
|
|
289642
|
+
description: 'Color in hexadecimal without # (6 chars)',
|
|
289643
|
+
},
|
|
289644
|
+
description: {
|
|
289645
|
+
type: 'string',
|
|
289646
|
+
maxLength: 100,
|
|
289647
|
+
description: 'Optional label description (máx 100 chars)',
|
|
289648
|
+
},
|
|
289649
|
+
},
|
|
289650
|
+
additionalProperties: false,
|
|
289651
|
+
},
|
|
289652
|
+
},
|
|
289653
|
+
});
|
|
289654
|
+
|
|
289548
289655
|
;// CONCATENATED MODULE: ../cdk8s_renderer/src/claims/github/index.ts
|
|
289549
289656
|
|
|
289550
289657
|
|
|
@@ -289552,6 +289659,7 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
|
|
|
289552
289659
|
|
|
289553
289660
|
|
|
289554
289661
|
|
|
289662
|
+
|
|
289555
289663
|
const GithubSchemas = [
|
|
289556
289664
|
github_group_schema,
|
|
289557
289665
|
github_user_schema,
|
|
@@ -289559,6 +289667,7 @@ const GithubSchemas = [
|
|
|
289559
289667
|
github_component_schema,
|
|
289560
289668
|
github_orgwebhook_schema,
|
|
289561
289669
|
component_secrets_vars_schema,
|
|
289670
|
+
component_labels_schema,
|
|
289562
289671
|
];
|
|
289563
289672
|
|
|
289564
289673
|
;// CONCATENATED MODULE: ../cdk8s_renderer/src/claims/tfworkspaces/terraform.schema.ts
|
|
@@ -293573,6 +293682,7 @@ function toJson_FirestartrGithubRepositorySpecRepo(obj) {
|
|
|
293573
293682
|
'hasWiki': obj.hasWiki,
|
|
293574
293683
|
'pages': obj.pages,
|
|
293575
293684
|
'topics': obj.topics?.map(y => y),
|
|
293685
|
+
'labels': obj.labels?.map(y => toJson_FirestartrGithubRepositorySpecRepoLabels(y)),
|
|
293576
293686
|
'visibility': obj.visibility,
|
|
293577
293687
|
'defaultBranch': obj.defaultBranch,
|
|
293578
293688
|
'additionalBranches': obj.additionalBranches?.map(y => toJson_FirestartrGithubRepositorySpecRepoAdditionalBranches(y)),
|
|
@@ -293719,6 +293829,22 @@ function toJson_FirestartrGithubRepositorySpecFirestartrTechnology(obj) {
|
|
|
293719
293829
|
// filter undefined values
|
|
293720
293830
|
return Object.entries(result).reduce((r, i) => (i[1] === undefined) ? r : ({ ...r, [i[0]]: i[1] }), {});
|
|
293721
293831
|
}
|
|
293832
|
+
/**
|
|
293833
|
+
* Converts an object of type 'FirestartrGithubRepositorySpecRepoLabels' to JSON representation.
|
|
293834
|
+
*/
|
|
293835
|
+
/* eslint-disable max-len, @stylistic/max-len, quote-props, @stylistic/quote-props */
|
|
293836
|
+
function toJson_FirestartrGithubRepositorySpecRepoLabels(obj) {
|
|
293837
|
+
if (obj === undefined) {
|
|
293838
|
+
return undefined;
|
|
293839
|
+
}
|
|
293840
|
+
const result = {
|
|
293841
|
+
'name': obj.name,
|
|
293842
|
+
'color': obj.color,
|
|
293843
|
+
'description': obj.description,
|
|
293844
|
+
};
|
|
293845
|
+
// filter undefined values
|
|
293846
|
+
return Object.entries(result).reduce((r, i) => (i[1] === undefined) ? r : ({ ...r, [i[0]]: i[1] }), {});
|
|
293847
|
+
}
|
|
293722
293848
|
/* eslint-enable max-len, @stylistic/max-len, quote-props, @stylistic/quote-props */
|
|
293723
293849
|
/**
|
|
293724
293850
|
* @schema FirestartrGithubRepositorySpecRepoVisibility
|
|
@@ -295714,6 +295840,7 @@ class GithubRepositoryChart extends BaseGithubChart {
|
|
|
295714
295840
|
defaultBranch: claim.providers.github?.branchStrategy?.defaultBranch,
|
|
295715
295841
|
codeowners: createCodeOwnersData(claim),
|
|
295716
295842
|
additionalBranches: claim.providers.github.additionalBranches || [],
|
|
295843
|
+
labels: claim.providers.github.labels || [],
|
|
295717
295844
|
topics: claim.providers.github.topics || [],
|
|
295718
295845
|
},
|
|
295719
295846
|
actions,
|
|
@@ -299074,6 +299201,9 @@ function itemPath(kind, item) {
|
|
|
299074
299201
|
}
|
|
299075
299202
|
|
|
299076
299203
|
;// CONCATENATED MODULE: ../operator/src/definitions.ts
|
|
299204
|
+
// ----------------------------------------------------
|
|
299205
|
+
// Kinds and plural-kinds definitions
|
|
299206
|
+
// ----------------------------------------------------
|
|
299077
299207
|
const kindPluralMap = {
|
|
299078
299208
|
githubgroups: 'FirestartrGithubGroup',
|
|
299079
299209
|
githubmemberships: 'FirestartrGithubMembership',
|
|
@@ -299098,6 +299228,25 @@ function definitions_getPluralFromKind(kind) {
|
|
|
299098
299228
|
const plural = Object.keys(kindPluralMap).find((key) => kindPluralMap[key] === kind);
|
|
299099
299229
|
return plural;
|
|
299100
299230
|
}
|
|
299231
|
+
var OperationType;
|
|
299232
|
+
(function (OperationType) {
|
|
299233
|
+
OperationType["RENAMED"] = "RENAMED";
|
|
299234
|
+
OperationType["UPDATED"] = "UPDATED";
|
|
299235
|
+
OperationType["CREATED"] = "CREATED";
|
|
299236
|
+
OperationType["SYNC"] = "SYNC";
|
|
299237
|
+
OperationType["MARKED_TO_DELETION"] = "MARKED_TO_DELETION";
|
|
299238
|
+
OperationType["NOTHING"] = "NOTHING";
|
|
299239
|
+
OperationType["RETRY"] = "RETRY";
|
|
299240
|
+
})(OperationType || (OperationType = {}));
|
|
299241
|
+
var WorkStatus;
|
|
299242
|
+
(function (WorkStatus) {
|
|
299243
|
+
WorkStatus["PENDING"] = "PENDING";
|
|
299244
|
+
WorkStatus["PROCESSING"] = "PROCESSING";
|
|
299245
|
+
WorkStatus["FINISHED"] = "FINISHED";
|
|
299246
|
+
})(WorkStatus || (WorkStatus = {}));
|
|
299247
|
+
// ----------------------------------------------------
|
|
299248
|
+
// Timeouts for operations, expressed in seconds
|
|
299249
|
+
// ----------------------------------------------------
|
|
299101
299250
|
const DAY_SECONDS = 24 * 60 * 60;
|
|
299102
299251
|
const TIMEOUTS = {
|
|
299103
299252
|
// expressed in seconds
|
|
@@ -300507,22 +300656,8 @@ function resolver_walkObject(item) {
|
|
|
300507
300656
|
|
|
300508
300657
|
;// CONCATENATED MODULE: ../operator/src/informer.ts
|
|
300509
300658
|
|
|
300510
|
-
|
|
300511
|
-
|
|
300512
|
-
OperationType["RENAMED"] = "RENAMED";
|
|
300513
|
-
OperationType["UPDATED"] = "UPDATED";
|
|
300514
|
-
OperationType["CREATED"] = "CREATED";
|
|
300515
|
-
OperationType["SYNC"] = "SYNC";
|
|
300516
|
-
OperationType["MARKED_TO_DELETION"] = "MARKED_TO_DELETION";
|
|
300517
|
-
OperationType["NOTHING"] = "NOTHING";
|
|
300518
|
-
OperationType["RETRY"] = "RETRY";
|
|
300519
|
-
})(OperationType || (OperationType = {}));
|
|
300520
|
-
var WorkStatus;
|
|
300521
|
-
(function (WorkStatus) {
|
|
300522
|
-
WorkStatus["PENDING"] = "PENDING";
|
|
300523
|
-
WorkStatus["PROCESSING"] = "PROCESSING";
|
|
300524
|
-
WorkStatus["FINISHED"] = "FINISHED";
|
|
300525
|
-
})(WorkStatus || (WorkStatus = {}));
|
|
300659
|
+
|
|
300660
|
+
|
|
300526
300661
|
|
|
300527
300662
|
|
|
300528
300663
|
|
|
@@ -300712,6 +300847,7 @@ function enqueue(pluralKind, workItem, queue, compute, syncCtl, retryCtl) {
|
|
|
300712
300847
|
return false;
|
|
300713
300848
|
}
|
|
300714
300849
|
},
|
|
300850
|
+
getSlotInfo: () => null,
|
|
300715
300851
|
};
|
|
300716
300852
|
workItem.process = async function* (item, operation, handler) {
|
|
300717
300853
|
const needsUpdateSyncConditions = operation === OperationType.RENAMED ||
|
|
@@ -301313,7 +301449,9 @@ async function writeDownQueueStatus(queue) {
|
|
|
301313
301449
|
for (const workItem of queue) {
|
|
301314
301450
|
const item = workItem.item;
|
|
301315
301451
|
const blockedText = workItem.isBlocked ? '[BLOCKED]' : '';
|
|
301316
|
-
|
|
301452
|
+
const slotInfo = workItem.slotId !== undefined ? ` (slot:${workItem.slotId})` : '';
|
|
301453
|
+
const upsertTimeText = formatElapsedTimeWithDate(workItem.upsertTime);
|
|
301454
|
+
output += `${item.kind}/${item.metadata.name} - ${workItem.workStatus} ${slotInfo} - ${workItem.operation} ${blockedText} - (upsert ${upsertTimeText})\n`;
|
|
301317
301455
|
}
|
|
301318
301456
|
return new Promise((ok, ko) => {
|
|
301319
301457
|
external_fs_.writeFile('/tmp/queue', output, (err) => {
|
|
@@ -301368,6 +301506,24 @@ function formatElapsedTimeWithDate(upsertTime) {
|
|
|
301368
301506
|
return `${parts.slice(0, 2).join(', ')} ago`;
|
|
301369
301507
|
}
|
|
301370
301508
|
|
|
301509
|
+
;// CONCATENATED MODULE: ../operator/src/signals.ts
|
|
301510
|
+
|
|
301511
|
+
function initSignalsHandler(Mapper) {
|
|
301512
|
+
for (const [signal, callback] of Mapper.entries()) {
|
|
301513
|
+
operator_src_logger.info(`Setting up handler for signal ${signal}... on process with PID ${process.pid}`);
|
|
301514
|
+
process.on(signal, async () => {
|
|
301515
|
+
operator_src_logger.info(`Received signal ${signal}, executing callback...`);
|
|
301516
|
+
try {
|
|
301517
|
+
await callback();
|
|
301518
|
+
operator_src_logger.info(`Callback for signal ${signal} executed successfully.`);
|
|
301519
|
+
}
|
|
301520
|
+
catch (err) {
|
|
301521
|
+
operator_src_logger.error(`Error executing callback for signal ${signal}:`, err);
|
|
301522
|
+
}
|
|
301523
|
+
});
|
|
301524
|
+
}
|
|
301525
|
+
}
|
|
301526
|
+
|
|
301371
301527
|
;// CONCATENATED MODULE: ../operator/src/processItemDLH.ts
|
|
301372
301528
|
|
|
301373
301529
|
|
|
@@ -301394,193 +301550,679 @@ async function deadLetterHandler(workItem) {
|
|
|
301394
301550
|
}
|
|
301395
301551
|
}
|
|
301396
301552
|
|
|
301397
|
-
|
|
301398
|
-
|
|
301399
|
-
|
|
301400
|
-
|
|
301401
|
-
|
|
301402
|
-
*
|
|
301403
|
-
* Currently this sets up handling for `SIGTERM`, using the callback
|
|
301404
|
-
* associated with the `"SIGTERM"` key from the provided {@link CallbacksMap}
|
|
301405
|
-
* to start the shutdown sequence. The returned {@link CallbacksMap} contains
|
|
301406
|
-
* handler-specific callbacks (such as `"FINISH_OK"`) that callers can invoke
|
|
301407
|
-
* to indicate that it is safe for the process to exit.
|
|
301408
|
-
*
|
|
301409
|
-
* @param callbacks A map of signal names to shutdown callbacks. The `"SIGTERM"`
|
|
301410
|
-
* entry (if present) is invoked when a `SIGTERM` signal is received to
|
|
301411
|
-
* perform any necessary cleanup before process termination.
|
|
301412
|
-
* @returns A {@link CallbacksMap} with control callbacks that allow the caller
|
|
301413
|
-
* to signal when shutdown has completed and the process may safely exit.
|
|
301414
|
-
*/
|
|
301415
|
-
function initSignalsHandler(callbacks) {
|
|
301416
|
-
operator_src_logger.info('Starting signals handler');
|
|
301417
|
-
const handlerCallbacks = new Map();
|
|
301418
|
-
initSigtermHandler(callbacks.get('SIGTERM'), handlerCallbacks);
|
|
301419
|
-
return handlerCallbacks;
|
|
301420
|
-
}
|
|
301421
|
-
function initSigtermHandler(callback, handlerCallbacks) {
|
|
301422
|
-
const raw_timeout_to_exit = Number(process.env['FIRESTARTR_TERMINATION_GRACE_PERIOD'] ||
|
|
301423
|
-
DEFAULT_TERMINATION_GRACE_PERIOD);
|
|
301424
|
-
const timeout_to_exit = raw_timeout_to_exit - Math.round(Math.min(5, raw_timeout_to_exit * 0.1));
|
|
301425
|
-
// The switch to control if it is safe to exit
|
|
301426
|
-
let finish = false;
|
|
301427
|
-
handlerCallbacks.set('FINISH_OK', () => {
|
|
301428
|
-
finish = true;
|
|
301429
|
-
});
|
|
301430
|
-
process.on('SIGTERM', async () => {
|
|
301431
|
-
operator_src_logger.info('The controller received a SIGTERM signal');
|
|
301432
|
-
// we signal to the callback the sigterm
|
|
301433
|
-
callback();
|
|
301434
|
-
const timeoutCb = setTimeout(() => {
|
|
301435
|
-
operator_src_logger.error('The controller could not shutdown properly. Timeout');
|
|
301436
|
-
process.exit(1);
|
|
301437
|
-
}, timeout_to_exit * 1000);
|
|
301438
|
-
// we wait
|
|
301439
|
-
while (!finish) {
|
|
301440
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
301441
|
-
}
|
|
301442
|
-
// all is clear!
|
|
301443
|
-
clearTimeout(timeoutCb);
|
|
301444
|
-
operator_src_logger.info('The controller has properly shutdown. Exiting');
|
|
301445
|
-
process.exit(0);
|
|
301446
|
-
});
|
|
301447
|
-
}
|
|
301448
|
-
|
|
301449
|
-
;// CONCATENATED MODULE: ../operator/src/processItem.ts
|
|
301450
|
-
|
|
301451
|
-
|
|
301452
|
-
|
|
301553
|
+
// EXTERNAL MODULE: ../operator/node_modules/@opentelemetry/exporter-prometheus/build/src/index.js
|
|
301554
|
+
var build_src = __nccwpck_require__(35116);
|
|
301555
|
+
// EXTERNAL MODULE: ../operator/node_modules/@opentelemetry/sdk-metrics/build/src/index.js
|
|
301556
|
+
var sdk_metrics_build_src = __nccwpck_require__(30481);
|
|
301557
|
+
;// CONCATENATED MODULE: ../operator/src/metrics/CRStates.ts
|
|
301453
301558
|
|
|
301454
301559
|
|
|
301455
301560
|
|
|
301456
301561
|
|
|
301457
|
-
const
|
|
301458
|
-
|
|
301459
|
-
|
|
301460
|
-
|
|
301461
|
-
|
|
301462
|
-
|
|
301463
|
-
MARKED_TO_DELETION: 6,
|
|
301464
|
-
SYNC: 1,
|
|
301465
|
-
NOTHING: 0,
|
|
301466
|
-
};
|
|
301467
|
-
function getQueueMetrics() {
|
|
301468
|
-
let renamed = 0;
|
|
301469
|
-
let updated = 0;
|
|
301470
|
-
let created = 0;
|
|
301471
|
-
let sync = 0;
|
|
301472
|
-
let marked_to_deletion = 0;
|
|
301473
|
-
let nothing = 0;
|
|
301474
|
-
let retry = 0;
|
|
301475
|
-
let unknown = 0;
|
|
301476
|
-
queue.forEach((workItem) => {
|
|
301477
|
-
switch (workItem.operation) {
|
|
301478
|
-
case 'RENAMED':
|
|
301479
|
-
renamed++;
|
|
301480
|
-
break;
|
|
301481
|
-
case 'UPDATED':
|
|
301482
|
-
updated++;
|
|
301483
|
-
break;
|
|
301484
|
-
case 'CREATED':
|
|
301485
|
-
created++;
|
|
301486
|
-
break;
|
|
301487
|
-
case 'SYNC':
|
|
301488
|
-
sync++;
|
|
301489
|
-
break;
|
|
301490
|
-
case 'MARKED_TO_DELETION':
|
|
301491
|
-
marked_to_deletion++;
|
|
301492
|
-
break;
|
|
301493
|
-
case 'NOTHING':
|
|
301494
|
-
nothing++;
|
|
301495
|
-
break;
|
|
301496
|
-
case 'RETRY':
|
|
301497
|
-
retry++;
|
|
301498
|
-
break;
|
|
301499
|
-
default:
|
|
301500
|
-
unknown++;
|
|
301501
|
-
break;
|
|
301502
|
-
}
|
|
301503
|
-
});
|
|
301504
|
-
return {
|
|
301505
|
-
nItems: queue.length,
|
|
301506
|
-
nItemsFinished: queue.filter((workItem) => workItem.workStatus === WorkStatus.FINISHED).length,
|
|
301507
|
-
nItemsPending: queue.filter((workItem) => workItem.workStatus === WorkStatus.PENDING).length,
|
|
301508
|
-
nItemsProcessing: queue.filter((workItem) => workItem.workStatus === WorkStatus.PROCESSING).length,
|
|
301509
|
-
nItemsInDeadLetterHandling: queue.filter((workItem) => workItem.isDeadLetter === true).length,
|
|
301510
|
-
nItemsTypes: {
|
|
301511
|
-
nothing,
|
|
301512
|
-
retry,
|
|
301513
|
-
renamed,
|
|
301514
|
-
updated,
|
|
301515
|
-
sync,
|
|
301516
|
-
created,
|
|
301517
|
-
marked_to_deletion,
|
|
301518
|
-
unknown,
|
|
301519
|
-
},
|
|
301520
|
-
};
|
|
301521
|
-
}
|
|
301522
|
-
// we need to assign weight to the deletion operation
|
|
301523
|
-
// to avoid blockades
|
|
301524
|
-
// https://github.com/prefapp/gitops-k8s/issues/1864
|
|
301525
|
-
// ghrepo feat | grss -> ghrepo -> ghgroup -> membership
|
|
301526
|
-
const DELETION_WEIGHTS = {
|
|
301527
|
-
FirestartrGithubRepositoryFeature: 5,
|
|
301528
|
-
FirestartrGithubRepositorySecretsSection: 4,
|
|
301529
|
-
FirestartrGithubRepository: 3,
|
|
301530
|
-
FirestartrGithubGroup: 2,
|
|
301531
|
-
FirestartrGithubMembership: 1,
|
|
301532
|
-
FirestartrTerraformWorkspace: 1,
|
|
301533
|
-
FirestartrGithubOrgWebhook: 1,
|
|
301534
|
-
};
|
|
301535
|
-
// Do the kinds need different weights for the operations?
|
|
301536
|
-
// We need to discuss this with the team in this issue: https://github.com/prefapp/gitops-k8s/issues/524
|
|
301537
|
-
// const KIND_WEIGHTS: any = {
|
|
301538
|
-
// "FirestartrTerraformWorkspace": 1,
|
|
301539
|
-
// "FirestartrGithubGroup": 1,
|
|
301540
|
-
// "FirestartrGithubMembership": 1,
|
|
301541
|
-
// "FirestartrGithubRepository": 1,
|
|
301542
|
-
// "FirestartrGithubRepositoryFeature": 1,
|
|
301543
|
-
// "FirestartrTerraformWorkspacePlan": 1,
|
|
301544
|
-
// }
|
|
301545
|
-
let INIT = false;
|
|
301546
|
-
/**
|
|
301547
|
-
* Pushes a WorkItem to the queue
|
|
301548
|
-
* @param {WorkItem} workItem - WorkItem to process
|
|
301549
|
-
*/
|
|
301550
|
-
async function processItem(workItem) {
|
|
301551
|
-
operator_src_logger.info(`The processor received a new work item for '${workItem.operation}' operation on '${workItem.item.kind}/${workItem.item.metadata.name}' in namespace '${workItem.item.metadata.namespace}'. Current work status is '${workItem.workStatus}'.`);
|
|
301552
|
-
queue.push(workItem);
|
|
301553
|
-
if (!INIT) {
|
|
301554
|
-
processItem_loop().catch((err) => {
|
|
301555
|
-
console.error(err);
|
|
301562
|
+
const INTERVAL_IN_SEGS = 60;
|
|
301563
|
+
class CRStateMetrics {
|
|
301564
|
+
constructor(kind, namespace, meter) {
|
|
301565
|
+
this.kind = kind;
|
|
301566
|
+
this.provisionedGauge = meter.createGauge('firestartr_provisioned_total', {
|
|
301567
|
+
description: 'Total number of CRs in PROVISIONED state',
|
|
301556
301568
|
});
|
|
301557
|
-
|
|
301558
|
-
|
|
301559
|
-
}
|
|
301560
|
-
|
|
301561
|
-
|
|
301562
|
-
|
|
301563
|
-
|
|
301564
|
-
|
|
301565
|
-
|
|
301566
|
-
.
|
|
301567
|
-
|
|
301568
|
-
|
|
301569
|
-
|
|
301570
|
-
|
|
301569
|
+
this.provisioningGauge = meter.createGauge('firestartr_provisioning_total', {
|
|
301570
|
+
description: 'Total number of CRs in PROVISIONING state',
|
|
301571
|
+
});
|
|
301572
|
+
this.outOfSyncGauge = meter.createGauge('firestartr_out_of_sync_total', {
|
|
301573
|
+
description: 'Total number of CRs in OUT_OF_SYNC state',
|
|
301574
|
+
});
|
|
301575
|
+
this.errorGauge = meter.createGauge('firestartr_error_total', {
|
|
301576
|
+
description: 'Total number of CRs in ERROR state',
|
|
301577
|
+
});
|
|
301578
|
+
this.planningGauge = meter.createGauge('firestartr_planning_total', {
|
|
301579
|
+
description: 'Total number of CRs in PLANNING state',
|
|
301580
|
+
});
|
|
301581
|
+
this.deletedGauge = meter.createGauge('firestartr_deleted_total', {
|
|
301582
|
+
description: 'Total number of CRs in DELETED state',
|
|
301583
|
+
});
|
|
301584
|
+
this.errorOnSyncGauge = meter.createGauge('firestartr_error_on_sync_total', {
|
|
301585
|
+
description: 'Total number of CRs with failed SYNCs',
|
|
301586
|
+
});
|
|
301587
|
+
this.namespace = namespace;
|
|
301588
|
+
}
|
|
301589
|
+
async start() {
|
|
301590
|
+
await this.__prepareConnection();
|
|
301591
|
+
this.onUpdate = false;
|
|
301592
|
+
this.updateInterval = setInterval(async () => {
|
|
301593
|
+
await this.update();
|
|
301594
|
+
}, 1000 * INTERVAL_IN_SEGS);
|
|
301595
|
+
await this.update();
|
|
301596
|
+
}
|
|
301597
|
+
stop() {
|
|
301598
|
+
if (this.updateInterval) {
|
|
301599
|
+
clearInterval(this.updateInterval);
|
|
301600
|
+
this.updateInterval = null;
|
|
301601
|
+
}
|
|
301602
|
+
}
|
|
301603
|
+
async update() {
|
|
301604
|
+
if (this.onUpdate)
|
|
301605
|
+
return;
|
|
301606
|
+
this.onUpdate = true;
|
|
301607
|
+
try {
|
|
301608
|
+
const items = await this.fListCRs();
|
|
301609
|
+
let provisionedCount = 0;
|
|
301610
|
+
let provisioningCount = 0;
|
|
301611
|
+
let outOfSyncCount = 0;
|
|
301612
|
+
let errorCount = 0;
|
|
301613
|
+
let planningCount = 0;
|
|
301614
|
+
let deletedCount = 0;
|
|
301615
|
+
let errorOnSyncCount = 0;
|
|
301616
|
+
for (const item of items) {
|
|
301617
|
+
const status = item.status?.conditions.find((condition) => condition.type !== 'SYNCHRONIZED' && condition.status === 'True');
|
|
301618
|
+
if (!status)
|
|
301619
|
+
continue;
|
|
301620
|
+
const syncCondition = item.status.conditions.find((condition) => {
|
|
301621
|
+
return condition.type === 'SYNCHRONIZED';
|
|
301622
|
+
});
|
|
301623
|
+
if (syncCondition &&
|
|
301624
|
+
syncCondition.message === SYNC_DEFAULT_ERROR_MESSAGE) {
|
|
301625
|
+
errorOnSyncCount++;
|
|
301626
|
+
}
|
|
301627
|
+
switch (status.type) {
|
|
301628
|
+
case 'PROVISIONED':
|
|
301629
|
+
provisionedCount++;
|
|
301630
|
+
break;
|
|
301631
|
+
case 'PROVISIONING':
|
|
301632
|
+
provisioningCount++;
|
|
301633
|
+
break;
|
|
301634
|
+
case 'OUT_OF_SYNC':
|
|
301635
|
+
outOfSyncCount++;
|
|
301636
|
+
break;
|
|
301637
|
+
case 'PLANNING':
|
|
301638
|
+
planningCount++;
|
|
301639
|
+
break;
|
|
301640
|
+
case 'DELETED':
|
|
301641
|
+
deletedCount++;
|
|
301642
|
+
break;
|
|
301643
|
+
case 'ERROR':
|
|
301644
|
+
errorCount++;
|
|
301645
|
+
break;
|
|
301646
|
+
}
|
|
301647
|
+
}
|
|
301648
|
+
this.provisionedGauge.record(provisionedCount, {
|
|
301649
|
+
namespace: this.namespace,
|
|
301650
|
+
kind: this.kind,
|
|
301651
|
+
});
|
|
301652
|
+
this.provisioningGauge.record(provisioningCount, {
|
|
301653
|
+
namespace: this.namespace,
|
|
301654
|
+
kind: this.kind,
|
|
301655
|
+
});
|
|
301656
|
+
this.planningGauge.record(planningCount, {
|
|
301657
|
+
namespace: this.namespace,
|
|
301658
|
+
kind: this.kind,
|
|
301659
|
+
});
|
|
301660
|
+
this.deletedGauge.record(deletedCount, {
|
|
301661
|
+
namespace: this.namespace,
|
|
301662
|
+
kind: this.kind,
|
|
301663
|
+
});
|
|
301664
|
+
this.outOfSyncGauge.record(outOfSyncCount, {
|
|
301665
|
+
namespace: this.namespace,
|
|
301666
|
+
kind: this.kind,
|
|
301667
|
+
});
|
|
301668
|
+
this.errorGauge.record(errorCount, {
|
|
301669
|
+
namespace: this.namespace,
|
|
301670
|
+
kind: this.kind,
|
|
301671
|
+
});
|
|
301672
|
+
this.errorOnSyncGauge.record(errorOnSyncCount, {
|
|
301673
|
+
namespace: this.namespace,
|
|
301674
|
+
kind: this.kind,
|
|
301675
|
+
});
|
|
301676
|
+
}
|
|
301677
|
+
catch (err) {
|
|
301678
|
+
console.log(`CRStateMetrics: update ${err}`);
|
|
301679
|
+
this.onUpdate = false;
|
|
301680
|
+
operator_src_logger.error(`On update of CR metrics: ${err}`);
|
|
301681
|
+
await this.__prepareConnection();
|
|
301682
|
+
}
|
|
301683
|
+
this.onUpdate = false;
|
|
301684
|
+
}
|
|
301685
|
+
async __prepareConnection() {
|
|
301686
|
+
const { kc, opts } = await ctl_getConnection();
|
|
301687
|
+
const k8sApi = kc.makeApiClient(client_node_dist.CustomObjectsApi);
|
|
301688
|
+
this.fListCRs = async () => {
|
|
301689
|
+
try {
|
|
301690
|
+
const response = await k8sApi.listNamespacedCustomObject('firestartr.dev', 'v1', this.namespace, this.kind);
|
|
301691
|
+
const body = response.body;
|
|
301692
|
+
return body.items;
|
|
301693
|
+
}
|
|
301694
|
+
catch (err) {
|
|
301695
|
+
throw new Error(`On listing CRs ${this.kind}: ${err}`);
|
|
301696
|
+
}
|
|
301697
|
+
};
|
|
301698
|
+
}
|
|
301699
|
+
}
|
|
301700
|
+
|
|
301701
|
+
;// CONCATENATED MODULE: ../operator/src/metrics/processItem.slot.metrics.ts
|
|
301702
|
+
class ProcessItemSlotMetrics {
|
|
301703
|
+
constructor(id, meter) {
|
|
301704
|
+
this._runningSeconds = 0;
|
|
301705
|
+
this._running = false;
|
|
301706
|
+
this._id = id;
|
|
301707
|
+
this._startTime = Date.now();
|
|
301708
|
+
this._lastStateChangeTime = Date.now();
|
|
301709
|
+
this._runningSeconds = 0;
|
|
301710
|
+
const commonAttributes = { slot_id: String(id) };
|
|
301711
|
+
// Counters (use .add())
|
|
301712
|
+
this._workItemsTotal = meter.createCounter('firestartr_slot.work_items_total', {
|
|
301713
|
+
description: 'Total number of work items processed by this slot',
|
|
301714
|
+
unit: '1',
|
|
301715
|
+
});
|
|
301716
|
+
this._runningTimeSecondsTotal = meter.createCounter('firestartr_slot.running_time_seconds_total', {
|
|
301717
|
+
description: 'Total time this slot has spent running work items',
|
|
301718
|
+
unit: 's',
|
|
301719
|
+
});
|
|
301720
|
+
// Observable Gauges (use .addCallback() + .observe())
|
|
301721
|
+
this._uptimeSeconds = meter.createObservableGauge('firestartr_slot.uptime_seconds', {
|
|
301722
|
+
description: 'Total runtime of this slot since creation (running + idle)',
|
|
301723
|
+
unit: 's',
|
|
301724
|
+
});
|
|
301725
|
+
this._uptimeSeconds.addCallback((observableResult) => {
|
|
301726
|
+
const uptime = (Date.now() - this._startTime) / 1000;
|
|
301727
|
+
observableResult.observe(uptime, commonAttributes);
|
|
301728
|
+
});
|
|
301729
|
+
this._idleTimeSecondsTotal = meter.createObservableGauge('firestartr_slot.idle_time_seconds_total', {
|
|
301730
|
+
description: 'Total time this slot has spent NOT processing items (idle)',
|
|
301731
|
+
unit: 's',
|
|
301732
|
+
});
|
|
301733
|
+
this._idleTimeSecondsTotal.addCallback((observableResult) => {
|
|
301734
|
+
const uptime = (Date.now() - this._startTime) / 1000;
|
|
301735
|
+
const inProgressRunning = this._running
|
|
301736
|
+
? (Date.now() - this._lastStateChangeTime) / 1000
|
|
301737
|
+
: 0;
|
|
301738
|
+
const idleTime = Math.max(0, uptime - this._runningSeconds - inProgressRunning);
|
|
301739
|
+
observableResult.observe(idleTime, commonAttributes);
|
|
301740
|
+
});
|
|
301741
|
+
}
|
|
301742
|
+
runItemStarted() {
|
|
301743
|
+
this._lastStateChangeTime = Date.now();
|
|
301744
|
+
this._running = true;
|
|
301745
|
+
}
|
|
301746
|
+
runItemFinished() {
|
|
301747
|
+
const now = Date.now();
|
|
301748
|
+
const runningDuration = (now - this._lastStateChangeTime) / 1000;
|
|
301749
|
+
this._runningSeconds += runningDuration;
|
|
301750
|
+
this._runningTimeSecondsTotal.add(runningDuration, {
|
|
301751
|
+
slot_id: String(this._id),
|
|
301752
|
+
});
|
|
301753
|
+
this._workItemsTotal.add(1, { slot_id: String(this._id) });
|
|
301754
|
+
this._lastStateChangeTime = now;
|
|
301755
|
+
this._running = false;
|
|
301756
|
+
}
|
|
301757
|
+
}
|
|
301758
|
+
|
|
301759
|
+
;// CONCATENATED MODULE: ../operator/src/metrics/processItem.global.metrics.ts
|
|
301760
|
+
// this is a singleton
|
|
301761
|
+
// to track global metrics across all slots, such as average processing time per item,
|
|
301762
|
+
// fastest and slowest processing times, etc.
|
|
301763
|
+
class ProcessorGlobalMetrics {
|
|
301764
|
+
constructor(meter) {
|
|
301765
|
+
this._processedItems = 0;
|
|
301766
|
+
this._totalRunningSeconds = 0;
|
|
301767
|
+
this._minTime = Infinity;
|
|
301768
|
+
this._maxTime = 0;
|
|
301769
|
+
// Global average processing time (no slot_id label)
|
|
301770
|
+
meter
|
|
301771
|
+
.createObservableGauge('firestartr.avg_processing_time_seconds', {
|
|
301772
|
+
description: 'Average processing time per item across ALL slots',
|
|
301773
|
+
unit: 's',
|
|
301774
|
+
})
|
|
301775
|
+
.addCallback((observableResult) => {
|
|
301776
|
+
const avg = this._processedItems > 0
|
|
301777
|
+
? this._totalRunningSeconds / this._processedItems
|
|
301778
|
+
: 0;
|
|
301779
|
+
observableResult.observe(avg);
|
|
301780
|
+
});
|
|
301781
|
+
// Global fastest
|
|
301782
|
+
meter
|
|
301783
|
+
.createObservableGauge('firestartr.fastest_processing_time_seconds', {
|
|
301784
|
+
description: 'Fastest single item processing time across ALL slots',
|
|
301785
|
+
unit: 's',
|
|
301786
|
+
})
|
|
301787
|
+
.addCallback((observableResult) => {
|
|
301788
|
+
observableResult.observe(this._processedItems > 0 ? this._minTime : 0);
|
|
301789
|
+
});
|
|
301790
|
+
// Global slowest
|
|
301791
|
+
meter
|
|
301792
|
+
.createObservableGauge('firestartr.slowest_processing_time_seconds', {
|
|
301793
|
+
description: 'Slowest single item processing time across ALL slots',
|
|
301794
|
+
unit: 's',
|
|
301795
|
+
})
|
|
301796
|
+
.addCallback((observableResult) => {
|
|
301797
|
+
observableResult.observe(this._processedItems > 0 ? this._maxTime : 0);
|
|
301798
|
+
});
|
|
301799
|
+
}
|
|
301800
|
+
static init(meter) {
|
|
301801
|
+
if (!ProcessorGlobalMetrics._instance) {
|
|
301802
|
+
ProcessorGlobalMetrics._instance = new ProcessorGlobalMetrics(meter);
|
|
301803
|
+
}
|
|
301804
|
+
}
|
|
301805
|
+
static getInstance() {
|
|
301806
|
+
if (!ProcessorGlobalMetrics._instance) {
|
|
301807
|
+
throw new Error('ProcessorGlobalMetrics must be initialized with .init(meter) first');
|
|
301808
|
+
}
|
|
301809
|
+
return ProcessorGlobalMetrics._instance;
|
|
301810
|
+
}
|
|
301811
|
+
recordProcessingDuration(durationSeconds) {
|
|
301812
|
+
if (durationSeconds <= 0)
|
|
301813
|
+
return;
|
|
301814
|
+
this._processedItems++;
|
|
301815
|
+
this._totalRunningSeconds += durationSeconds;
|
|
301816
|
+
this._minTime = Math.min(this._minTime, durationSeconds);
|
|
301817
|
+
this._maxTime = Math.max(this._maxTime, durationSeconds);
|
|
301818
|
+
}
|
|
301819
|
+
}
|
|
301820
|
+
ProcessorGlobalMetrics._instance = null;
|
|
301821
|
+
|
|
301822
|
+
|
|
301823
|
+
;// CONCATENATED MODULE: ../operator/src/metricsServer.ts
|
|
301824
|
+
|
|
301825
|
+
|
|
301826
|
+
|
|
301827
|
+
|
|
301828
|
+
|
|
301829
|
+
|
|
301830
|
+
let processMetricsSlotProvider = null;
|
|
301831
|
+
/* harmony default export */ async function metricsServer(kindList, namespace) {
|
|
301832
|
+
const portFromEnv = process.env.METRICS_PORT;
|
|
301833
|
+
const options = build_src/* PrometheusExporter.DEFAULT_OPTIONS */.rC.DEFAULT_OPTIONS;
|
|
301834
|
+
options.port = portFromEnv ? parseInt(portFromEnv) : options.port;
|
|
301835
|
+
options.endpoint = process.env.METRICS_ENDPOINT || options.endpoint;
|
|
301836
|
+
const exporter = new build_src/* PrometheusExporter */.rC(options, () => {
|
|
301837
|
+
console.log(`📊 Metrics endpoint: http://localhost:${options.port}${options.endpoint}`);
|
|
301838
|
+
});
|
|
301839
|
+
const attributes = { pid: process.pid };
|
|
301840
|
+
void startMetrics(exporter, attributes, kindList, namespace);
|
|
301841
|
+
}
|
|
301842
|
+
async function startMetrics(exporter, attributes, kindList, namespace) {
|
|
301843
|
+
const meterProvider = new sdk_metrics_build_src/* MeterProvider */.y0({
|
|
301844
|
+
readers: [exporter],
|
|
301845
|
+
});
|
|
301846
|
+
const meter = meterProvider.getMeter('firestartr');
|
|
301847
|
+
void startQueueMetrics(meter, attributes);
|
|
301848
|
+
void startMemoryMetrics(meter, attributes);
|
|
301849
|
+
void startCRStates(meter, kindList, namespace);
|
|
301850
|
+
processMetricsSlotProvider = (slotId) => {
|
|
301851
|
+
return new ProcessItemSlotMetrics(slotId, meter);
|
|
301852
|
+
};
|
|
301853
|
+
ProcessorGlobalMetrics.init(meter);
|
|
301854
|
+
}
|
|
301855
|
+
// whenever a new slot is created, the ProcessItemSlotMetrics instance for that slot can be obtained by calling
|
|
301856
|
+
// getProcessItemSlotMetrics(slotId)
|
|
301857
|
+
function getProcessItemSlotMetrics(slotId) {
|
|
301858
|
+
if (!processMetricsSlotProvider) {
|
|
301859
|
+
throw new Error('ProcessItemSlotMetrics provider not initialized');
|
|
301860
|
+
}
|
|
301861
|
+
return processMetricsSlotProvider(slotId);
|
|
301862
|
+
}
|
|
301863
|
+
async function startQueueMetrics(meter, attributes) {
|
|
301864
|
+
const nWorkItemsCounter = meter.createObservableGauge('firestartr_workitems_total', { description: 'Number of workitems in the queue' });
|
|
301865
|
+
const nWorkItemsPendingCounter = meter.createObservableGauge('firestartr_workitems_pending_total', { description: 'Number of workitems with PENDING status in the queue' });
|
|
301866
|
+
const nWorkItemsProcessingCounter = meter.createObservableGauge('firestartr_workitems_processing_total', { description: 'Number of workitems with PROCESSING status in the queue' });
|
|
301867
|
+
const nWorkItemsFinishedCounter = meter.createObservableGauge('firestartr_workitems_finished_total', { description: 'Number of workitems with FINISHED status in the queue' });
|
|
301868
|
+
const nWorkItemsInDeadLetterHandling = meter.createObservableGauge('firestartr_workitems_dead_letter_handling_total', {
|
|
301869
|
+
description: 'Number of workitems of the queue handled by the Dead Letter Handler',
|
|
301870
|
+
});
|
|
301871
|
+
const queueMetrics = getQueueMetrics();
|
|
301872
|
+
let total = queueMetrics.nItems;
|
|
301873
|
+
let pending = queueMetrics.nItemsPending;
|
|
301874
|
+
let processing = queueMetrics.nItemsProcessing;
|
|
301875
|
+
let finished = queueMetrics.nItemsFinished;
|
|
301876
|
+
let inDeadLetterHandling = queueMetrics.nItemsInDeadLetterHandling;
|
|
301877
|
+
nWorkItemsCounter.addCallback((observer) => observer.observe(total, { ...attributes, ...queueMetrics.nItemsTypes }));
|
|
301878
|
+
nWorkItemsPendingCounter.addCallback((observer) => observer.observe(pending, { ...attributes, ...queueMetrics.nItemsTypes }));
|
|
301879
|
+
nWorkItemsProcessingCounter.addCallback((observer) => observer.observe(processing, {
|
|
301880
|
+
...attributes,
|
|
301881
|
+
...queueMetrics.nItemsTypes,
|
|
301882
|
+
}));
|
|
301883
|
+
nWorkItemsFinishedCounter.addCallback((observer) => observer.observe(finished, { ...attributes, ...queueMetrics.nItemsTypes }));
|
|
301884
|
+
nWorkItemsInDeadLetterHandling.addCallback((observer) => observer.observe(inDeadLetterHandling, {
|
|
301885
|
+
...attributes,
|
|
301886
|
+
...queueMetrics.nItemsTypes,
|
|
301887
|
+
}));
|
|
301888
|
+
setInterval(() => {
|
|
301889
|
+
const queueMetrics = getQueueMetrics();
|
|
301890
|
+
total = queueMetrics.nItems;
|
|
301891
|
+
pending = queueMetrics.nItemsPending;
|
|
301892
|
+
processing = queueMetrics.nItemsProcessing;
|
|
301893
|
+
finished = queueMetrics.nItemsFinished;
|
|
301894
|
+
inDeadLetterHandling = queueMetrics.nItemsInDeadLetterHandling;
|
|
301895
|
+
}, 1000);
|
|
301896
|
+
}
|
|
301897
|
+
async function startMemoryMetrics(meter, attributes) {
|
|
301898
|
+
const usedMemoryCounter = meter.createObservableGauge('firestartr_used_memory', { description: 'Used memory in bytes' });
|
|
301899
|
+
let heapUsage = process.memoryUsage().heapUsed;
|
|
301900
|
+
usedMemoryCounter.addCallback((observer) => observer.observe(heapUsage, attributes));
|
|
301901
|
+
setInterval(() => {
|
|
301902
|
+
heapUsage = process.memoryUsage().heapUsed;
|
|
301903
|
+
}, 1000);
|
|
301904
|
+
}
|
|
301905
|
+
async function startCRStates(meter, kindList, namespace) {
|
|
301906
|
+
for (const kind of kindList) {
|
|
301907
|
+
const crStateMetrics = new CRStateMetrics(kind, namespace, meter);
|
|
301908
|
+
await crStateMetrics.start();
|
|
301909
|
+
}
|
|
301910
|
+
}
|
|
301911
|
+
|
|
301912
|
+
;// CONCATENATED MODULE: ../operator/src/processItem.slot.ts
|
|
301913
|
+
|
|
301914
|
+
|
|
301915
|
+
|
|
301916
|
+
|
|
301917
|
+
|
|
301918
|
+
|
|
301919
|
+
function initProcessItemsSlots(nSlots, initMetrics = true) {
|
|
301920
|
+
operator_src_logger.info(`Initializing ${nSlots} processing slots...`);
|
|
301921
|
+
return new ProcessItemSlots(nSlots, initMetrics);
|
|
301922
|
+
}
|
|
301923
|
+
class ProcessItemSlots {
|
|
301924
|
+
constructor(nSlots, initMetrics) {
|
|
301925
|
+
this._guardRails = new GuardRails();
|
|
301926
|
+
// let's init the slots
|
|
301927
|
+
this._nSlots = nSlots;
|
|
301928
|
+
this._slots = new Array(nSlots).fill(0).map((_, i) => {
|
|
301929
|
+
const slot = new ProcessItemSlot(i);
|
|
301930
|
+
if (initMetrics) {
|
|
301931
|
+
try {
|
|
301932
|
+
slot.metrics = getProcessItemSlotMetrics(i);
|
|
301933
|
+
}
|
|
301934
|
+
catch (error) {
|
|
301935
|
+
operator_src_logger.warn(`Process item slot metrics unavailable for slot ${i}; continuing without per-slot metrics.`, error);
|
|
301936
|
+
}
|
|
301937
|
+
}
|
|
301938
|
+
slot.actions = {
|
|
301939
|
+
blockItem: (item) => this._guardRails.block(item),
|
|
301940
|
+
unblockItem: (item) => this._guardRails.unblock(item),
|
|
301941
|
+
};
|
|
301942
|
+
return slot;
|
|
301943
|
+
});
|
|
301944
|
+
}
|
|
301945
|
+
*getIdleSlots() {
|
|
301946
|
+
for (const slot of this._slots) {
|
|
301947
|
+
if (slot.idle)
|
|
301948
|
+
yield slot;
|
|
301949
|
+
}
|
|
301950
|
+
}
|
|
301951
|
+
allSlotsAreIdle() {
|
|
301952
|
+
return this._slots.every((slot) => slot.idle);
|
|
301953
|
+
}
|
|
301954
|
+
isWorkItemBlocked(workItem) {
|
|
301955
|
+
return this._guardRails.isBlocked(workItem.item);
|
|
301956
|
+
}
|
|
301957
|
+
}
|
|
301958
|
+
class GuardRails {
|
|
301959
|
+
constructor() {
|
|
301960
|
+
this._processing = new Map();
|
|
301961
|
+
}
|
|
301962
|
+
block(item) {
|
|
301963
|
+
const itemPath = this.itemToGuard(item);
|
|
301964
|
+
this._processing.set(itemPath, true);
|
|
301965
|
+
}
|
|
301966
|
+
unblock(item) {
|
|
301967
|
+
const itemPath = this.itemToGuard(item);
|
|
301968
|
+
this._processing.delete(itemPath);
|
|
301969
|
+
}
|
|
301970
|
+
isBlocked(item) {
|
|
301971
|
+
const itemPath = this.itemToGuard(item);
|
|
301972
|
+
return this._processing.has(itemPath);
|
|
301973
|
+
}
|
|
301974
|
+
itemToGuard(item) {
|
|
301975
|
+
return `${item.kind}/${item.metadata.namespace}/${item.metadata.name}`;
|
|
301976
|
+
}
|
|
301977
|
+
}
|
|
301978
|
+
class ProcessItemSlot {
|
|
301979
|
+
constructor(id) {
|
|
301980
|
+
this._id = id;
|
|
301981
|
+
this._idle = true;
|
|
301982
|
+
}
|
|
301983
|
+
set actions(actions) {
|
|
301984
|
+
this._actions = actions;
|
|
301985
|
+
}
|
|
301986
|
+
get actions() {
|
|
301987
|
+
return this._actions;
|
|
301988
|
+
}
|
|
301989
|
+
get idle() {
|
|
301990
|
+
return this._idle;
|
|
301991
|
+
}
|
|
301992
|
+
get id() {
|
|
301993
|
+
return this._id;
|
|
301994
|
+
}
|
|
301995
|
+
set metrics(metrics) {
|
|
301996
|
+
this._metrics = metrics;
|
|
301997
|
+
}
|
|
301998
|
+
get metrics() {
|
|
301999
|
+
return this._metrics;
|
|
302000
|
+
}
|
|
302001
|
+
async runWorkItem(workItem) {
|
|
302002
|
+
this._idle = false;
|
|
302003
|
+
const startTime = Date.now(); // ← capture exact start for global metrics
|
|
302004
|
+
try {
|
|
302005
|
+
if (this.actions) {
|
|
302006
|
+
this.actions.blockItem(workItem.item);
|
|
302007
|
+
}
|
|
302008
|
+
if (this.metrics) {
|
|
302009
|
+
this.metrics.runItemStarted();
|
|
302010
|
+
}
|
|
302011
|
+
await this.__run(workItem);
|
|
302012
|
+
}
|
|
302013
|
+
catch (e) {
|
|
302014
|
+
if (e instanceof Error &&
|
|
302015
|
+
e.message.includes('Error on getItemByItemPath')) {
|
|
302016
|
+
operator_src_logger.debug(`Item '${workItem.item.kind}/${workItem.item.metadata.name}' was not found, so its work item is being removed from the processor queue.`);
|
|
302017
|
+
workItem.workStatus = WorkStatus.FINISHED;
|
|
302018
|
+
}
|
|
302019
|
+
else {
|
|
302020
|
+
operator_src_logger.error(`An unmanaged error occurred while the processor was handling the '${workItem.operation}' operation for item '${workItem.item.kind}/${workItem.item.metadata.name}' in namespace '${workItem.item.metadata.namespace}'. Current work status is '${workItem.workStatus}'. The error was: '${e}'.`);
|
|
302021
|
+
await deadLetterHandler(workItem);
|
|
302022
|
+
console.error(e);
|
|
302023
|
+
}
|
|
302024
|
+
}
|
|
302025
|
+
finally {
|
|
302026
|
+
if (this.actions) {
|
|
302027
|
+
this.actions.unblockItem(workItem.item);
|
|
302028
|
+
}
|
|
302029
|
+
// if metrics are enabled, we record the processing duration for this item
|
|
302030
|
+
// and update the global metrics
|
|
302031
|
+
if (this.metrics) {
|
|
302032
|
+
this.metrics.runItemFinished();
|
|
302033
|
+
// REPORT TO GLOBAL SINGLETON for metrics
|
|
302034
|
+
const durationSeconds = (Date.now() - startTime) / 1000;
|
|
302035
|
+
ProcessorGlobalMetrics.getInstance().recordProcessingDuration(durationSeconds);
|
|
302036
|
+
}
|
|
302037
|
+
workItem.isPicked = false;
|
|
302038
|
+
}
|
|
302039
|
+
this._idle = true;
|
|
302040
|
+
}
|
|
302041
|
+
async __run(workItem) {
|
|
302042
|
+
if (!workItem.getItem || !workItem.process || !workItem.operation)
|
|
302043
|
+
return;
|
|
302044
|
+
const item = await workItem.getItem();
|
|
302045
|
+
// we check if the workItem needs blocking
|
|
302046
|
+
// if it does need it we return because we cannot
|
|
302047
|
+
// process this item
|
|
302048
|
+
if ('needsBlocking' in workItem.handler &&
|
|
302049
|
+
workItem.handler.needsBlocking(item, workItem.operation)) {
|
|
302050
|
+
operator_src_logger.debug(`Item ${item.kind}/${item.metadata.namespace} needs blocking`);
|
|
302051
|
+
return;
|
|
302052
|
+
}
|
|
302053
|
+
workItem.workStatus = WorkStatus.PROCESSING;
|
|
302054
|
+
workItem.slotId = this.id;
|
|
302055
|
+
workItem.handler.getSlotInfo = () => ({ slotId: this.id });
|
|
302056
|
+
for await (const condition of workItem.process(item, workItem.operation, workItem.handler)) {
|
|
302057
|
+
if (workItem.handler === undefined)
|
|
302058
|
+
throw new Error('handler is undefined');
|
|
302059
|
+
await updateTransition(workItem.handler.itemPath(), condition.reason, condition.type, condition.status, condition.message, condition.updateStatusOnly || false);
|
|
302060
|
+
}
|
|
302061
|
+
workItem.workStatus = WorkStatus.FINISHED;
|
|
302062
|
+
}
|
|
302063
|
+
}
|
|
302064
|
+
|
|
302065
|
+
;// CONCATENATED MODULE: ../operator/src/processItem.ts
|
|
302066
|
+
|
|
302067
|
+
|
|
302068
|
+
|
|
302069
|
+
|
|
302070
|
+
|
|
302071
|
+
|
|
302072
|
+
const queue = [];
|
|
302073
|
+
const WEIGHTS = {
|
|
302074
|
+
RENAMED: 15,
|
|
302075
|
+
UPDATED: 10,
|
|
302076
|
+
CREATED: 9,
|
|
302077
|
+
RETRY: 8,
|
|
302078
|
+
MARKED_TO_DELETION: 6,
|
|
302079
|
+
SYNC: 1,
|
|
302080
|
+
NOTHING: 0,
|
|
302081
|
+
};
|
|
302082
|
+
// We get the number of max slots from the environment variable, if it is not set, we default to 1
|
|
302083
|
+
function getMaxSlotsFromEnvironment() {
|
|
302084
|
+
const rawMaxSlots = catalog_common.environment.getFromEnvironment(catalog_common.types.envVars.operatorNumberOfMaxSlots);
|
|
302085
|
+
const parsedMaxSlots = Number.parseInt(rawMaxSlots || '1', 10);
|
|
302086
|
+
if (!Number.isFinite(parsedMaxSlots) || parsedMaxSlots < 1) {
|
|
302087
|
+
return 1;
|
|
302088
|
+
}
|
|
302089
|
+
return parsedMaxSlots;
|
|
302090
|
+
}
|
|
302091
|
+
const MAX_SLOTS = getMaxSlotsFromEnvironment();
|
|
302092
|
+
function getQueueMetrics() {
|
|
302093
|
+
let renamed = 0;
|
|
302094
|
+
let updated = 0;
|
|
302095
|
+
let created = 0;
|
|
302096
|
+
let sync = 0;
|
|
302097
|
+
let marked_to_deletion = 0;
|
|
302098
|
+
let nothing = 0;
|
|
302099
|
+
let retry = 0;
|
|
302100
|
+
let unknown = 0;
|
|
302101
|
+
queue.forEach((workItem) => {
|
|
302102
|
+
switch (workItem.operation) {
|
|
302103
|
+
case 'RENAMED':
|
|
302104
|
+
renamed++;
|
|
302105
|
+
break;
|
|
302106
|
+
case 'UPDATED':
|
|
302107
|
+
updated++;
|
|
302108
|
+
break;
|
|
302109
|
+
case 'CREATED':
|
|
302110
|
+
created++;
|
|
302111
|
+
break;
|
|
302112
|
+
case 'SYNC':
|
|
302113
|
+
sync++;
|
|
302114
|
+
break;
|
|
302115
|
+
case 'MARKED_TO_DELETION':
|
|
302116
|
+
marked_to_deletion++;
|
|
302117
|
+
break;
|
|
302118
|
+
case 'NOTHING':
|
|
302119
|
+
nothing++;
|
|
302120
|
+
break;
|
|
302121
|
+
case 'RETRY':
|
|
302122
|
+
retry++;
|
|
302123
|
+
break;
|
|
302124
|
+
default:
|
|
302125
|
+
unknown++;
|
|
302126
|
+
break;
|
|
302127
|
+
}
|
|
302128
|
+
});
|
|
302129
|
+
return {
|
|
302130
|
+
nItems: queue.length,
|
|
302131
|
+
nItemsFinished: queue.filter((workItem) => workItem.workStatus === WorkStatus.FINISHED).length,
|
|
302132
|
+
nItemsPending: queue.filter((workItem) => workItem.workStatus === WorkStatus.PENDING).length,
|
|
302133
|
+
nItemsProcessing: queue.filter((workItem) => workItem.workStatus === WorkStatus.PROCESSING).length,
|
|
302134
|
+
nItemsInDeadLetterHandling: queue.filter((workItem) => workItem.isDeadLetter === true).length,
|
|
302135
|
+
nItemsTypes: {
|
|
302136
|
+
nothing,
|
|
302137
|
+
retry,
|
|
302138
|
+
renamed,
|
|
302139
|
+
updated,
|
|
302140
|
+
sync,
|
|
302141
|
+
created,
|
|
302142
|
+
marked_to_deletion,
|
|
302143
|
+
unknown,
|
|
302144
|
+
},
|
|
302145
|
+
};
|
|
302146
|
+
}
|
|
302147
|
+
// we need to assign weight to the deletion operation
|
|
302148
|
+
// to avoid blockades
|
|
302149
|
+
// https://github.com/prefapp/gitops-k8s/issues/1864
|
|
302150
|
+
// ghrepo feat | grss -> ghrepo -> ghgroup -> membership
|
|
302151
|
+
const DELETION_WEIGHTS = {
|
|
302152
|
+
FirestartrGithubRepositoryFeature: 5,
|
|
302153
|
+
FirestartrGithubRepositorySecretsSection: 4,
|
|
302154
|
+
FirestartrGithubRepository: 3,
|
|
302155
|
+
FirestartrGithubGroup: 2,
|
|
302156
|
+
FirestartrGithubMembership: 1,
|
|
302157
|
+
FirestartrTerraformWorkspace: 1,
|
|
302158
|
+
FirestartrGithubOrgWebhook: 1,
|
|
302159
|
+
};
|
|
302160
|
+
// Do the kinds need different weights for the operations?
|
|
302161
|
+
// We need to discuss this with the team in this issue: https://github.com/prefapp/gitops-k8s/issues/524
|
|
302162
|
+
// const KIND_WEIGHTS: any = {
|
|
302163
|
+
// "FirestartrTerraformWorkspace": 1,
|
|
302164
|
+
// "FirestartrGithubGroup": 1,
|
|
302165
|
+
// "FirestartrGithubMembership": 1,
|
|
302166
|
+
// "FirestartrGithubRepository": 1,
|
|
302167
|
+
// "FirestartrGithubRepositoryFeature": 1,
|
|
302168
|
+
// "FirestartrTerraformWorkspacePlan": 1,
|
|
302169
|
+
// }
|
|
302170
|
+
let INIT = false;
|
|
302171
|
+
/**
|
|
302172
|
+
* Pushes a WorkItem to the queue
|
|
302173
|
+
* @param {WorkItem} workItem - WorkItem to process
|
|
302174
|
+
*/
|
|
302175
|
+
async function processItem(workItem) {
|
|
302176
|
+
operator_src_logger.info(`The processor received a new work item for '${workItem.operation}' operation on '${workItem.item.kind}/${workItem.item.metadata.name}' in namespace '${workItem.item.metadata.namespace}'. Current work status is '${workItem.workStatus}'.`);
|
|
302177
|
+
queue.push(workItem);
|
|
302178
|
+
if (!INIT) {
|
|
302179
|
+
processItem_loop().catch((err) => {
|
|
302180
|
+
console.error(err);
|
|
302181
|
+
});
|
|
302182
|
+
INIT = true;
|
|
302183
|
+
}
|
|
302184
|
+
}
|
|
302185
|
+
/**
|
|
302186
|
+
* Wait until there is a WorkItem to process and then process it
|
|
302187
|
+
* @returns {void}
|
|
302188
|
+
*/
|
|
302189
|
+
async function processItem_loop() {
|
|
302190
|
+
loopWorkItemDebug(queue);
|
|
302191
|
+
let podIsTerminating = false;
|
|
302192
|
+
const processItemSlotsManager = initProcessItemsSlots(MAX_SLOTS);
|
|
302193
|
+
const nextWorkItem = () => sortQueue(queue)
|
|
302194
|
+
.filter((w) => !w.isBlocked &&
|
|
302195
|
+
!w.isPicked &&
|
|
302196
|
+
!processItemSlotsManager.isWorkItemBlocked(w))
|
|
302197
|
+
.find((w) => w.workStatus === WorkStatus.PENDING);
|
|
302198
|
+
initSignalsHandler(new Map([['SIGTERM', () => (podIsTerminating = true)]]));
|
|
302199
|
+
operator_src_logger.info(`
|
|
302200
|
+
------- Processor main loop started with a maximum of '${MAX_SLOTS}' concurrent slots to process items. -------
|
|
302201
|
+
`);
|
|
301571
302202
|
while (1) {
|
|
301572
|
-
if (podIsTerminating) {
|
|
302203
|
+
if (podIsTerminating && processItemSlotsManager.allSlotsAreIdle()) {
|
|
301573
302204
|
operator_src_logger.info('The processor is going to shutdown (pod is terminating)');
|
|
301574
|
-
|
|
301575
|
-
handlerCallbacks.get('FINISH_OK')();
|
|
302205
|
+
process.exit(0);
|
|
301576
302206
|
break;
|
|
301577
302207
|
}
|
|
301578
|
-
|
|
301579
|
-
if
|
|
301580
|
-
|
|
301581
|
-
|
|
301582
|
-
|
|
301583
|
-
|
|
302208
|
+
// every time an idle slot is available, we check if there is a work item to process and if there is, we process it
|
|
302209
|
+
// if there are more idle slots than work items, the next iterations of the loop will just do nothing
|
|
302210
|
+
// until there are new work items to process
|
|
302211
|
+
for (const idleSlot of processItemSlotsManager.getIdleSlots()) {
|
|
302212
|
+
if (podIsTerminating) {
|
|
302213
|
+
break;
|
|
302214
|
+
}
|
|
302215
|
+
const w = nextWorkItem();
|
|
302216
|
+
if (w) {
|
|
302217
|
+
// synchronously mark the work item as picked
|
|
302218
|
+
w.isPicked = true;
|
|
302219
|
+
const logMessage = `${new Date().toISOString()} : Processing OPERATION: ${w.operation} ITEM: ${w.item.kind}/${w.item.metadata.name}`;
|
|
302220
|
+
catalog_common.io.writeLogFile('process_item', logMessage);
|
|
302221
|
+
operator_src_logger.info(`The processor (${idleSlot.id}) is currently handling a '${w.operation}' operation for item '${w.item.kind}/${w.item.metadata.name}' in namespace '${w.item.metadata.namespace}'. The current work status is '${w.workStatus}'.`);
|
|
302222
|
+
// we do not wait for the item to be processed to start processing the next one,
|
|
302223
|
+
// we just start processing it in a new slot
|
|
302224
|
+
void idleSlot.runWorkItem(w);
|
|
302225
|
+
}
|
|
301584
302226
|
}
|
|
301585
302227
|
await processItem_wait();
|
|
301586
302228
|
}
|
|
@@ -301625,44 +302267,6 @@ function sortQueue(queue) {
|
|
|
301625
302267
|
function processItem_wait(t = 2000) {
|
|
301626
302268
|
return new Promise((ok) => setTimeout(ok, t));
|
|
301627
302269
|
}
|
|
301628
|
-
async function runWorkItem(workItem) {
|
|
301629
|
-
operator_src_logger.debug(`The processor is now running the '${workItem.operation}' operation for item '${workItem.item.kind}/${workItem.item.metadata.name}' in namespace '${workItem.item.metadata.namespace}'.`);
|
|
301630
|
-
if (!workItem.getItem || !workItem.process || !workItem.operation)
|
|
301631
|
-
return;
|
|
301632
|
-
try {
|
|
301633
|
-
const item = await workItem.getItem();
|
|
301634
|
-
// we check if the workItem needs blocking
|
|
301635
|
-
// if it does need it we return because we cannot
|
|
301636
|
-
// process this item
|
|
301637
|
-
if ('needsBlocking' in workItem.handler &&
|
|
301638
|
-
workItem.handler.needsBlocking(item, workItem.operation)) {
|
|
301639
|
-
operator_src_logger.debug(`Item ${item.kind}/${item.metadata.namespace} needs blocking`);
|
|
301640
|
-
return;
|
|
301641
|
-
}
|
|
301642
|
-
workItem.workStatus = WorkStatus.PROCESSING;
|
|
301643
|
-
for await (const condition of workItem.process(item, workItem.operation, workItem.handler)) {
|
|
301644
|
-
if (workItem.handler === undefined)
|
|
301645
|
-
throw new Error('handler is undefined');
|
|
301646
|
-
await updateTransition(workItem.handler.itemPath(), condition.reason, condition.type, condition.status, condition.message, condition.updateStatusOnly || false);
|
|
301647
|
-
}
|
|
301648
|
-
workItem.workStatus = WorkStatus.FINISHED;
|
|
301649
|
-
operator_src_logger.debug(`The processor has '${queue.length}' items remaining in the queue.`);
|
|
301650
|
-
}
|
|
301651
|
-
catch (e) {
|
|
301652
|
-
if (e instanceof Error &&
|
|
301653
|
-
e.message.includes('Error on getItemByItemPath')) {
|
|
301654
|
-
operator_src_logger.debug(`Item '${workItem.item.kind}/${workItem.item.metadata.name}' was not found, so its work item is being removed from the processor queue.`);
|
|
301655
|
-
workItem.workStatus = WorkStatus.FINISHED;
|
|
301656
|
-
return;
|
|
301657
|
-
}
|
|
301658
|
-
else {
|
|
301659
|
-
operator_src_logger.error(`An unmanaged error occurred while the processor was handling the '${workItem.operation}' operation for item '${workItem.item.kind}/${workItem.item.metadata.name}' in namespace '${workItem.item.metadata.namespace}'. Current work status is '${workItem.workStatus}'. The error was: '${e}'.`);
|
|
301660
|
-
await deadLetterHandler(workItem);
|
|
301661
|
-
console.error(e);
|
|
301662
|
-
}
|
|
301663
|
-
return;
|
|
301664
|
-
}
|
|
301665
|
-
}
|
|
301666
302270
|
/**
|
|
301667
302271
|
* Periodically checks the queue for finished WorkItems and removes them
|
|
301668
302272
|
* @param {WorkItem[]} queue - Queue of WorkItems
|
|
@@ -301869,15 +302473,16 @@ function processHandler(processToHandle, ctl, onTimedOut) {
|
|
|
301869
302473
|
|
|
301870
302474
|
|
|
301871
302475
|
|
|
302476
|
+
|
|
301872
302477
|
const TOFU_LOCK_TIMEOUT = '-lock-timeout=60s';
|
|
301873
302478
|
async function utils_validate(path, secrets) {
|
|
301874
302479
|
return await tfExec(path, ['validate'], secrets);
|
|
301875
302480
|
}
|
|
301876
|
-
async function init(path, secrets, stream) {
|
|
301877
|
-
return await tfExec(path, ['init'], secrets, ['-input=false'], stream);
|
|
302481
|
+
async function init(path, secrets, stream, ctl) {
|
|
302482
|
+
return await tfExec(path, ['init'], secrets, ['-input=false'], stream, ctl);
|
|
301878
302483
|
}
|
|
301879
|
-
async function initFromModule(path, source, secrets, stream) {
|
|
301880
|
-
return tfExec(path, ['init', `-from-module=${source}`], secrets, [], stream);
|
|
302484
|
+
async function initFromModule(path, source, secrets, stream, ctl) {
|
|
302485
|
+
return tfExec(path, ['init', `-from-module=${source}`], secrets, [], stream, ctl);
|
|
301881
302486
|
}
|
|
301882
302487
|
async function plan(path, secrets, format, args = ['plan'], stream, ctl) {
|
|
301883
302488
|
terraform_provisioner_src_logger.info(`Running terraform plan with ${format} in path ${path}`);
|
|
@@ -301905,10 +302510,27 @@ async function output(path, secrets) {
|
|
|
301905
302510
|
return await tfExec(path, ['output', '-json'], secrets, []);
|
|
301906
302511
|
}
|
|
301907
302512
|
async function tfExec(path, args, secrets, extraArgs = ['-input=false'], stream, ctl) {
|
|
302513
|
+
const env = {};
|
|
302514
|
+
const tfCacheDir = typeof ctl?.tfCacheDir === 'string' && ctl.tfCacheDir.trim() !== ''
|
|
302515
|
+
? ctl.tfCacheDir
|
|
302516
|
+
: undefined;
|
|
302517
|
+
if (tfCacheDir) {
|
|
302518
|
+
env['TF_PLUGIN_CACHE_DIR'] = tfCacheDir;
|
|
302519
|
+
try {
|
|
302520
|
+
external_fs_.mkdirSync(tfCacheDir, { recursive: true });
|
|
302521
|
+
}
|
|
302522
|
+
catch (error) {
|
|
302523
|
+
throw new Error(`Failed to create TF plugin cache directory "${tfCacheDir}": ${error?.message ?? error}`);
|
|
302524
|
+
}
|
|
302525
|
+
}
|
|
301908
302526
|
return new Promise((ok, ko) => {
|
|
301909
302527
|
const tfProcess = (0,external_child_process_.spawn)('tofu', args.concat(extraArgs), {
|
|
301910
302528
|
cwd: path,
|
|
301911
302529
|
stdio: ['inherit', 'pipe', 'pipe'],
|
|
302530
|
+
env: {
|
|
302531
|
+
...process.env,
|
|
302532
|
+
...env,
|
|
302533
|
+
},
|
|
301912
302534
|
});
|
|
301913
302535
|
let output = '';
|
|
301914
302536
|
let flagStdoutEnd = false;
|
|
@@ -302527,10 +303149,7 @@ class project_tf_TFProjectManager {
|
|
|
302527
303149
|
}
|
|
302528
303150
|
}
|
|
302529
303151
|
async __init() {
|
|
302530
|
-
this.tfOutput += await init(this.projectPath, this.secrets, this.stream);
|
|
302531
|
-
}
|
|
302532
|
-
async __initFromModule() {
|
|
302533
|
-
this.tfOutput += await init(this.projectPath, this.secrets, this.stream);
|
|
303152
|
+
this.tfOutput += await init(this.projectPath, this.secrets, this.stream, this.ctl);
|
|
302534
303153
|
}
|
|
302535
303154
|
async validate() {
|
|
302536
303155
|
await this.__init();
|
|
@@ -302758,11 +303377,11 @@ class TFProjectManagerRemote {
|
|
|
302758
303377
|
insteadOf = https://github.com`);
|
|
302759
303378
|
}
|
|
302760
303379
|
async __init() {
|
|
302761
|
-
this.tfOutput += await init(this.projectPath, this.secrets);
|
|
303380
|
+
this.tfOutput += await init(this.projectPath, this.secrets, undefined, this.ctl);
|
|
302762
303381
|
}
|
|
302763
303382
|
async __initFromModule() {
|
|
302764
303383
|
external_fs_.mkdirSync(this.projectPath, { recursive: true });
|
|
302765
|
-
this.tfOutput += await initFromModule(this.projectPath, this.ctx.module, this.secrets);
|
|
303384
|
+
this.tfOutput += await initFromModule(this.projectPath, this.ctx.module, this.secrets, undefined, this.ctl);
|
|
302766
303385
|
}
|
|
302767
303386
|
async validate() {
|
|
302768
303387
|
await this.__init();
|
|
@@ -303330,9 +303949,10 @@ function extractErrorDetails(error) {
|
|
|
303330
303949
|
|
|
303331
303950
|
|
|
303332
303951
|
const TF_PROJECTS_PATH = '/tmp/tfworkspaces';
|
|
303952
|
+
const TF_CACHES_PATH = '/tmp/tfcaches';
|
|
303333
303953
|
function processOperation(item, op, handler) {
|
|
303334
303954
|
try {
|
|
303335
|
-
|
|
303955
|
+
clearLocalTfProject(item);
|
|
303336
303956
|
const policy = getPolicy(item, 'firestartr.dev/policy');
|
|
303337
303957
|
const syncPolicy = getPolicy(item, 'firestartr.dev/sync-policy');
|
|
303338
303958
|
if (!policy || policy === 'observe' || policy === 'observe-only') {
|
|
@@ -303406,6 +304026,7 @@ async function* doPlanJSONFormat(item, op, handler, setResult = function (_r) {
|
|
|
303406
304026
|
await addPlanStatusCheck(item.metadata.annotations['firestartr.dev/last-state-pr'], 'Terraform plan in progress...');
|
|
303407
304027
|
}
|
|
303408
304028
|
const tfPlan = await runTerraformProvisioner(context, planType, null, {
|
|
304029
|
+
tfCacheDir: external_path_.join(TF_CACHES_PATH, `slot_${handler.getSlotInfo().slotId}`),
|
|
303409
304030
|
hardTimeout: handler.recommendedTimeout(),
|
|
303410
304031
|
processKilled: (killed) => {
|
|
303411
304032
|
if (killed) {
|
|
@@ -303703,6 +304324,7 @@ async function* process_operation_markedToDeletion(item, op, handler) {
|
|
|
303703
304324
|
const deps = await handler.resolveReferences();
|
|
303704
304325
|
const context = buildProvisionerContext(item, deps);
|
|
303705
304326
|
const destroyOutput = await runTerraformProvisioner(context, 'destroy', null, {
|
|
304327
|
+
tfCacheDir: external_path_.join(TF_CACHES_PATH, `slot_${handler.getSlotInfo().slotId}`),
|
|
303706
304328
|
hardTimeout: handler.recommendedTimeout(),
|
|
303707
304329
|
processKilled: (killed) => {
|
|
303708
304330
|
if (killed) {
|
|
@@ -303824,6 +304446,7 @@ async function* doApply(item, op, handler) {
|
|
|
303824
304446
|
operator_src_logger.info(`The Terraform processor is applying and assessing dependencies for item '${item.kind}/${item.metadata.name}' with dependencies: '${deps}'.`);
|
|
303825
304447
|
const context = buildProvisionerContext(item, deps);
|
|
303826
304448
|
const applyOutput = await runTerraformProvisioner(context, 'apply', checkRunCtl, {
|
|
304449
|
+
tfCacheDir: external_path_.join(TF_CACHES_PATH, `slot_${handler.getSlotInfo().slotId}`),
|
|
303827
304450
|
hardTimeout: handler.recommendedTimeout(),
|
|
303828
304451
|
processKilled: (killed) => {
|
|
303829
304452
|
if (killed) {
|
|
@@ -304119,8 +304742,72 @@ function resolveReferences(item, deps) {
|
|
|
304119
304742
|
throw new Error(`resolving references: ${e}`);
|
|
304120
304743
|
}
|
|
304121
304744
|
}
|
|
304122
|
-
function
|
|
304123
|
-
|
|
304745
|
+
function getValidatedTfProjectPath(item) {
|
|
304746
|
+
const tfStateKey = item?.spec?.firestartr?.tfStateKey;
|
|
304747
|
+
if (typeof tfStateKey !== 'string' || tfStateKey.trim() === '') {
|
|
304748
|
+
throw new Error('Invalid terraform state key');
|
|
304749
|
+
}
|
|
304750
|
+
if (external_path_.isAbsolute(tfStateKey)) {
|
|
304751
|
+
throw new Error(`Invalid terraform state key: ${tfStateKey}`);
|
|
304752
|
+
}
|
|
304753
|
+
const basePath = external_path_.resolve(TF_PROJECTS_PATH);
|
|
304754
|
+
const tfProjectPath = external_path_.resolve(basePath, tfStateKey);
|
|
304755
|
+
const relativePath = external_path_.relative(basePath, tfProjectPath);
|
|
304756
|
+
if (relativePath === '' ||
|
|
304757
|
+
relativePath === '.' ||
|
|
304758
|
+
relativePath.startsWith('..') ||
|
|
304759
|
+
external_path_.isAbsolute(relativePath)) {
|
|
304760
|
+
throw new Error(`Invalid terraform state key: ${tfStateKey}`);
|
|
304761
|
+
}
|
|
304762
|
+
return tfProjectPath;
|
|
304763
|
+
}
|
|
304764
|
+
function getValidatedTfProjectsRealPath() {
|
|
304765
|
+
const basePath = external_path_.resolve(TF_PROJECTS_PATH);
|
|
304766
|
+
external_fs_.mkdirSync(basePath, { recursive: true });
|
|
304767
|
+
const realBasePath = external_fs_.realpathSync(basePath);
|
|
304768
|
+
if (!external_fs_.statSync(realBasePath).isDirectory()) {
|
|
304769
|
+
throw new Error(`Invalid terraform projects path: ${TF_PROJECTS_PATH}`);
|
|
304770
|
+
}
|
|
304771
|
+
return realBasePath;
|
|
304772
|
+
}
|
|
304773
|
+
function assertPathWithinBase(candidatePath, realBasePath) {
|
|
304774
|
+
const relativePath = external_path_.relative(realBasePath, candidatePath);
|
|
304775
|
+
if (relativePath === '' ||
|
|
304776
|
+
relativePath === '.' ||
|
|
304777
|
+
relativePath.startsWith('..') ||
|
|
304778
|
+
external_path_.isAbsolute(relativePath)) {
|
|
304779
|
+
throw new Error(`Invalid terraform project path: ${candidatePath}`);
|
|
304780
|
+
}
|
|
304781
|
+
}
|
|
304782
|
+
function assertNoSymlinksInExistingPathComponents(realBasePath, candidatePath) {
|
|
304783
|
+
const relativePath = external_path_.relative(realBasePath, candidatePath);
|
|
304784
|
+
const pathSegments = relativePath
|
|
304785
|
+
.split(external_path_.sep)
|
|
304786
|
+
.filter((segment) => segment !== '');
|
|
304787
|
+
let currentPath = realBasePath;
|
|
304788
|
+
for (const pathSegment of pathSegments) {
|
|
304789
|
+
currentPath = external_path_.join(currentPath, pathSegment);
|
|
304790
|
+
if (!external_fs_.existsSync(currentPath)) {
|
|
304791
|
+
return;
|
|
304792
|
+
}
|
|
304793
|
+
if (external_fs_.lstatSync(currentPath).isSymbolicLink()) {
|
|
304794
|
+
throw new Error(`Invalid terraform project path: symlink component detected at ${currentPath}`);
|
|
304795
|
+
}
|
|
304796
|
+
}
|
|
304797
|
+
}
|
|
304798
|
+
function clearLocalTfProject(item) {
|
|
304799
|
+
const tfProjectPath = getValidatedTfProjectPath(item);
|
|
304800
|
+
const realBasePath = getValidatedTfProjectsRealPath();
|
|
304801
|
+
assertNoSymlinksInExistingPathComponents(realBasePath, tfProjectPath);
|
|
304802
|
+
let deletePath = tfProjectPath;
|
|
304803
|
+
if (external_fs_.existsSync(tfProjectPath)) {
|
|
304804
|
+
deletePath = external_fs_.realpathSync(tfProjectPath);
|
|
304805
|
+
assertPathWithinBase(deletePath, realBasePath);
|
|
304806
|
+
}
|
|
304807
|
+
external_fs_.rmSync(deletePath, {
|
|
304808
|
+
recursive: true,
|
|
304809
|
+
force: true,
|
|
304810
|
+
});
|
|
304124
304811
|
}
|
|
304125
304812
|
function getErrorOutputMessage(cr, key, ref) {
|
|
304126
304813
|
if (cr.spec.source === 'Remote') {
|
|
@@ -304269,7 +304956,7 @@ async function acquireLease(namespace, cb, interval = 10000) {
|
|
|
304269
304956
|
const processOperationPlan_TF_PROJECTS_PATH = '/tmp/tfworkspaces';
|
|
304270
304957
|
function processOperationPlan(item, op, handler) {
|
|
304271
304958
|
try {
|
|
304272
|
-
|
|
304959
|
+
clearLocalTfProjects();
|
|
304273
304960
|
const policy = processOperationPlan_getPolicy(item);
|
|
304274
304961
|
if (policy === 'observe' || policy === 'apply') {
|
|
304275
304962
|
return processOperationPlan_plan(item, op, handler);
|
|
@@ -304695,7 +305382,7 @@ function processOperationPlan_resolveReferences(item, deps) {
|
|
|
304695
305382
|
throw { error: `resolving references: ${e}` };
|
|
304696
305383
|
}
|
|
304697
305384
|
}
|
|
304698
|
-
function
|
|
305385
|
+
function clearLocalTfProjects() {
|
|
304699
305386
|
external_fs_.rmSync(processOperationPlan_TF_PROJECTS_PATH, { recursive: true, force: true });
|
|
304700
305387
|
}
|
|
304701
305388
|
function processOperationPlan_getErrorOutputMessage(cr, key, ref) {
|
|
@@ -305081,7 +305768,7 @@ const MODULES = {
|
|
|
305081
305768
|
},
|
|
305082
305769
|
FirestartrGithubRepository: {
|
|
305083
305770
|
module: 'git::https://github.com/prefapp/tfm.git//modules/github-repo',
|
|
305084
|
-
ref: '
|
|
305771
|
+
ref: 'feat/add-labels-support-gh-repo',
|
|
305085
305772
|
},
|
|
305086
305773
|
FirestartrGithubRepositoryFeature: {
|
|
305087
305774
|
module: 'git::https://github.com/prefapp/tfm.git//modules/github-files-set',
|
|
@@ -305660,20 +306347,98 @@ function provisionPermissions(fsGithubRepository) {
|
|
|
305660
306347
|
});
|
|
305661
306348
|
}
|
|
305662
306349
|
}
|
|
305663
|
-
else {
|
|
305664
|
-
gh_provisioner_src_logger.info(`[gh-provisioner] ${fsGithubRepository.k8sId} provisions a member ${permission.collaborator} with role ${permission.role}`);
|
|
305665
|
-
const config = {
|
|
305666
|
-
username: permission.collaborator,
|
|
305667
|
-
permission: permission.role,
|
|
305668
|
-
};
|
|
306350
|
+
else {
|
|
306351
|
+
gh_provisioner_src_logger.info(`[gh-provisioner] ${fsGithubRepository.k8sId} provisions a member ${permission.collaborator} with role ${permission.role}`);
|
|
306352
|
+
const config = {
|
|
306353
|
+
username: permission.collaborator,
|
|
306354
|
+
permission: permission.role,
|
|
306355
|
+
};
|
|
306356
|
+
fsGithubRepository.patchData({
|
|
306357
|
+
op: PatchOperations.add,
|
|
306358
|
+
path: '/config/collaborators/-',
|
|
306359
|
+
value: config,
|
|
306360
|
+
});
|
|
306361
|
+
}
|
|
306362
|
+
}
|
|
306363
|
+
}
|
|
306364
|
+
|
|
306365
|
+
;// CONCATENATED MODULE: ../gh_provisioner/src/entities/ghrepo/helpers/labels.ts
|
|
306366
|
+
// if we are to provision labels using tofu and the label already
|
|
306367
|
+
// exists, we should not attempt to re-create it. This function checks
|
|
306368
|
+
// if the label exists and if not, creates it. This is needed because
|
|
306369
|
+
// tofu does not have an "upsert" operation for labels, so we need to check
|
|
306370
|
+
// if the label exists before trying to create it, otherwise we will get an error.
|
|
306371
|
+
|
|
306372
|
+
|
|
306373
|
+
|
|
306374
|
+
async function provisionLabels(fsGithubRepository, repoAlreadyExists) {
|
|
306375
|
+
try {
|
|
306376
|
+
const cr = fsGithubRepository.cr;
|
|
306377
|
+
if (cr.spec.repo.labels.length === 0) {
|
|
306378
|
+
return;
|
|
306379
|
+
}
|
|
306380
|
+
if (repoAlreadyExists) {
|
|
306381
|
+
await importLabelsIfNeeded(fsGithubRepository);
|
|
306382
|
+
}
|
|
306383
|
+
const labels = cr.spec.repo.labels;
|
|
306384
|
+
for (const label of labels) {
|
|
306385
|
+
gh_provisioner_src_logger.debug(`[gh-provisioner] ${fsGithubRepository.k8sId} provisioning label ${label.name}`);
|
|
305669
306386
|
fsGithubRepository.patchData({
|
|
305670
306387
|
op: PatchOperations.add,
|
|
305671
|
-
path: '/config/
|
|
305672
|
-
value:
|
|
306388
|
+
path: '/config/labels/-',
|
|
306389
|
+
value: {
|
|
306390
|
+
name: label.name,
|
|
306391
|
+
color: label.color,
|
|
306392
|
+
description: label.description,
|
|
306393
|
+
},
|
|
306394
|
+
});
|
|
306395
|
+
}
|
|
306396
|
+
}
|
|
306397
|
+
catch (err) {
|
|
306398
|
+
gh_provisioner_src_logger.error(`[gh-provisioner] ${fsGithubRepository.k8sId} error on provisionLabels: ${err}`);
|
|
306399
|
+
throw new Error(`Error on provisionLabels: ${err}`);
|
|
306400
|
+
}
|
|
306401
|
+
}
|
|
306402
|
+
async function importLabelsIfNeeded(fsGithubRepository) {
|
|
306403
|
+
const cr = fsGithubRepository.cr;
|
|
306404
|
+
try {
|
|
306405
|
+
const labels = cr.spec.repo.labels;
|
|
306406
|
+
const labelsToImport = [];
|
|
306407
|
+
gh_provisioner_src_logger.info(`[gh-provisioner] ${fsGithubRepository.k8sId} checking if labels need to be imported`);
|
|
306408
|
+
const repoIssuesList = await fsGithubRepository.runWithGithubProvider(async () => {
|
|
306409
|
+
return await github.repo.getRepoIssuesLabels(cr.spec.org, cr.name);
|
|
306410
|
+
});
|
|
306411
|
+
// we need to check wether the label exists or not, if it does not exist we need to create it
|
|
306412
|
+
for (const label of labels) {
|
|
306413
|
+
if (checkIfLabelExists(label.name, repoIssuesList)) {
|
|
306414
|
+
continue;
|
|
306415
|
+
}
|
|
306416
|
+
labelsToImport.push({
|
|
306417
|
+
name: label.name,
|
|
306418
|
+
color: label.color,
|
|
306419
|
+
description: label.description,
|
|
306420
|
+
});
|
|
306421
|
+
}
|
|
306422
|
+
for (const label of labelsToImport) {
|
|
306423
|
+
this.patchImportData({
|
|
306424
|
+
op: PatchOperations.add,
|
|
306425
|
+
path: '/imports/-',
|
|
306426
|
+
value: {
|
|
306427
|
+
to: 'github_issue_label.this["' + label.name + '"]',
|
|
306428
|
+
id: `${this.cr.name}:${label.name}`,
|
|
306429
|
+
},
|
|
305673
306430
|
});
|
|
305674
306431
|
}
|
|
306432
|
+
return labelsToImport;
|
|
306433
|
+
}
|
|
306434
|
+
catch (err) {
|
|
306435
|
+
gh_provisioner_src_logger.error(`[gh-provisioner] ${fsGithubRepository.k8sId} error on importLabelsIfNeeded: ${err}`);
|
|
306436
|
+
throw new Error(`Error on importLabelsIfNeeded: ${err}`);
|
|
305675
306437
|
}
|
|
305676
306438
|
}
|
|
306439
|
+
function checkIfLabelExists(labelName, repoIssuesList) {
|
|
306440
|
+
return repoIssuesList.some((label) => label.name === labelName);
|
|
306441
|
+
}
|
|
305677
306442
|
|
|
305678
306443
|
;// CONCATENATED MODULE: ../gh_provisioner/src/entities/ghrepo/helpers/index.ts
|
|
305679
306444
|
|
|
@@ -305682,6 +306447,7 @@ function provisionPermissions(fsGithubRepository) {
|
|
|
305682
306447
|
|
|
305683
306448
|
|
|
305684
306449
|
|
|
306450
|
+
|
|
305685
306451
|
;// CONCATENATED MODULE: ../gh_provisioner/src/entities/ghrepo/post/additional_branches.ts
|
|
305686
306452
|
|
|
305687
306453
|
|
|
@@ -305746,6 +306512,7 @@ async function provisionRegularBranch(repo, branchName, sourceBranch, org) {
|
|
|
305746
306512
|
|
|
305747
306513
|
|
|
305748
306514
|
|
|
306515
|
+
|
|
305749
306516
|
class EntityGHRepo extends base_Entity {
|
|
305750
306517
|
constructor(artifact) {
|
|
305751
306518
|
super(artifact, {
|
|
@@ -305754,18 +306521,24 @@ class EntityGHRepo extends base_Entity {
|
|
|
305754
306521
|
variables: [],
|
|
305755
306522
|
teams: [],
|
|
305756
306523
|
collaborators: [],
|
|
306524
|
+
labels: [],
|
|
305757
306525
|
},
|
|
305758
306526
|
});
|
|
305759
306527
|
}
|
|
305760
306528
|
async loadResources(tfOp) {
|
|
306529
|
+
let repoAlreadyExists = false;
|
|
305761
306530
|
try {
|
|
305762
306531
|
this.synthMessage('Loading resources to Synth...');
|
|
306532
|
+
repoAlreadyExists = (await this.runWithGithubProvider(async () => {
|
|
306533
|
+
return await github.repo.repoExists(this.cr.spec.org, this.cr.name);
|
|
306534
|
+
}));
|
|
305763
306535
|
await this.provisionRepository();
|
|
305764
306536
|
await provisionDefaultBranch(this);
|
|
305765
306537
|
await provisionCodeowners(this);
|
|
305766
306538
|
await provisionVariables(this);
|
|
305767
306539
|
await provisionOIDCSubjectClaim(this);
|
|
305768
306540
|
await provisionPermissions(this);
|
|
306541
|
+
await provisionLabels(this, repoAlreadyExists);
|
|
305769
306542
|
this.synthMessage('Synth finished');
|
|
305770
306543
|
this.synthEnd(JSON.stringify(this.document, null, 2));
|
|
305771
306544
|
}
|
|
@@ -306876,6 +307649,8 @@ function gh_checkrun_helperCreateCheckRunName(cmd, item) {
|
|
|
306876
307649
|
|
|
306877
307650
|
|
|
306878
307651
|
|
|
307652
|
+
|
|
307653
|
+
const process_operation_TF_CACHES_PATH = '/tmp/tfcaches';
|
|
306879
307654
|
function process_operation_processOperation(item, op, handler) {
|
|
306880
307655
|
operator_src_logger.info(`Processing operation ${op} on ${item.kind}/${item.metadata?.name}`);
|
|
306881
307656
|
try {
|
|
@@ -307005,6 +307780,7 @@ async function* gh_process_operation_markedToDeletion(item, op, handler) {
|
|
|
307005
307780
|
operator_src_logger.error(`PANIC!!: The Terraform process for item '${item.kind}/${item.metadata.name}' could not be killed`);
|
|
307006
307781
|
}
|
|
307007
307782
|
};
|
|
307783
|
+
opts.ctl['tfCacheDir'] = external_path_default().join(process_operation_TF_CACHES_PATH, `slot_${handler.getSlotInfo().slotId}`);
|
|
307008
307784
|
const destroyOutput = await gh_provisioner.runGhProvisioner({
|
|
307009
307785
|
mainCr: item,
|
|
307010
307786
|
deps,
|
|
@@ -307153,6 +307929,7 @@ async function* process_operation_doApply(item, op, handler) {
|
|
|
307153
307929
|
operator_src_logger.error(`PANIC!!: The Terraform process for item '${item.kind}/${item.metadata.name}' could not be killed`);
|
|
307154
307930
|
}
|
|
307155
307931
|
};
|
|
307932
|
+
opts.ctl['tfCacheDir'] = external_path_default().join(process_operation_TF_CACHES_PATH, `slot_${handler.getSlotInfo().slotId}`);
|
|
307156
307933
|
const applyOutput = await gh_provisioner.runGhProvisioner({
|
|
307157
307934
|
mainCr: item,
|
|
307158
307935
|
deps,
|
|
@@ -307247,228 +308024,6 @@ async function* process_operation_doApply(item, op, handler) {
|
|
|
307247
308024
|
}
|
|
307248
308025
|
}
|
|
307249
308026
|
|
|
307250
|
-
// EXTERNAL MODULE: ../operator/node_modules/@opentelemetry/exporter-prometheus/build/src/index.js
|
|
307251
|
-
var build_src = __nccwpck_require__(35116);
|
|
307252
|
-
// EXTERNAL MODULE: ../operator/node_modules/@opentelemetry/sdk-metrics/build/src/index.js
|
|
307253
|
-
var sdk_metrics_build_src = __nccwpck_require__(30481);
|
|
307254
|
-
;// CONCATENATED MODULE: ../operator/src/metrics/CRStates.ts
|
|
307255
|
-
|
|
307256
|
-
|
|
307257
|
-
|
|
307258
|
-
|
|
307259
|
-
const INTERVAL_IN_SEGS = 60;
|
|
307260
|
-
class CRStateMetrics {
|
|
307261
|
-
constructor(kind, namespace, meter) {
|
|
307262
|
-
this.kind = kind;
|
|
307263
|
-
this.provisionedGauge = meter.createGauge('firestartr_provisioned_total', {
|
|
307264
|
-
description: 'Total number of CRs in PROVISIONED state',
|
|
307265
|
-
});
|
|
307266
|
-
this.provisioningGauge = meter.createGauge('firestartr_provisioning_total', {
|
|
307267
|
-
description: 'Total number of CRs in PROVISIONING state',
|
|
307268
|
-
});
|
|
307269
|
-
this.outOfSyncGauge = meter.createGauge('firestartr_out_of_sync_total', {
|
|
307270
|
-
description: 'Total number of CRs in OUT_OF_SYNC state',
|
|
307271
|
-
});
|
|
307272
|
-
this.errorGauge = meter.createGauge('firestartr_error_total', {
|
|
307273
|
-
description: 'Total number of CRs in ERROR state',
|
|
307274
|
-
});
|
|
307275
|
-
this.planningGauge = meter.createGauge('firestartr_planning_total', {
|
|
307276
|
-
description: 'Total number of CRs in PLANNING state',
|
|
307277
|
-
});
|
|
307278
|
-
this.deletedGauge = meter.createGauge('firestartr_deleted_total', {
|
|
307279
|
-
description: 'Total number of CRs in DELETED state',
|
|
307280
|
-
});
|
|
307281
|
-
this.errorOnSyncGauge = meter.createGauge('firestartr_error_on_sync_total', {
|
|
307282
|
-
description: 'Total number of CRs with failed SYNCs',
|
|
307283
|
-
});
|
|
307284
|
-
this.namespace = namespace;
|
|
307285
|
-
}
|
|
307286
|
-
async start() {
|
|
307287
|
-
await this.__prepareConnection();
|
|
307288
|
-
this.onUpdate = false;
|
|
307289
|
-
this.updateInterval = setInterval(async () => {
|
|
307290
|
-
await this.update();
|
|
307291
|
-
}, 1000 * INTERVAL_IN_SEGS);
|
|
307292
|
-
await this.update();
|
|
307293
|
-
}
|
|
307294
|
-
stop() {
|
|
307295
|
-
if (this.updateInterval) {
|
|
307296
|
-
clearInterval(this.updateInterval);
|
|
307297
|
-
this.updateInterval = null;
|
|
307298
|
-
}
|
|
307299
|
-
}
|
|
307300
|
-
async update() {
|
|
307301
|
-
if (this.onUpdate)
|
|
307302
|
-
return;
|
|
307303
|
-
this.onUpdate = true;
|
|
307304
|
-
try {
|
|
307305
|
-
const items = await this.fListCRs();
|
|
307306
|
-
let provisionedCount = 0;
|
|
307307
|
-
let provisioningCount = 0;
|
|
307308
|
-
let outOfSyncCount = 0;
|
|
307309
|
-
let errorCount = 0;
|
|
307310
|
-
let planningCount = 0;
|
|
307311
|
-
let deletedCount = 0;
|
|
307312
|
-
let errorOnSyncCount = 0;
|
|
307313
|
-
for (const item of items) {
|
|
307314
|
-
const status = item.status?.conditions.find((condition) => condition.type !== 'SYNCHRONIZED' && condition.status === 'True');
|
|
307315
|
-
if (!status)
|
|
307316
|
-
continue;
|
|
307317
|
-
const syncCondition = item.status.conditions.find((condition) => {
|
|
307318
|
-
return condition.type === 'SYNCHRONIZED';
|
|
307319
|
-
});
|
|
307320
|
-
if (syncCondition &&
|
|
307321
|
-
syncCondition.message === SYNC_DEFAULT_ERROR_MESSAGE) {
|
|
307322
|
-
errorOnSyncCount++;
|
|
307323
|
-
}
|
|
307324
|
-
switch (status.type) {
|
|
307325
|
-
case 'PROVISIONED':
|
|
307326
|
-
provisionedCount++;
|
|
307327
|
-
break;
|
|
307328
|
-
case 'PROVISIONING':
|
|
307329
|
-
provisioningCount++;
|
|
307330
|
-
break;
|
|
307331
|
-
case 'OUT_OF_SYNC':
|
|
307332
|
-
outOfSyncCount++;
|
|
307333
|
-
break;
|
|
307334
|
-
case 'PLANNING':
|
|
307335
|
-
planningCount++;
|
|
307336
|
-
break;
|
|
307337
|
-
case 'DELETED':
|
|
307338
|
-
deletedCount++;
|
|
307339
|
-
break;
|
|
307340
|
-
case 'ERROR':
|
|
307341
|
-
errorCount++;
|
|
307342
|
-
break;
|
|
307343
|
-
}
|
|
307344
|
-
}
|
|
307345
|
-
this.provisionedGauge.record(provisionedCount, {
|
|
307346
|
-
namespace: this.namespace,
|
|
307347
|
-
kind: this.kind,
|
|
307348
|
-
});
|
|
307349
|
-
this.provisioningGauge.record(provisioningCount, {
|
|
307350
|
-
namespace: this.namespace,
|
|
307351
|
-
kind: this.kind,
|
|
307352
|
-
});
|
|
307353
|
-
this.planningGauge.record(planningCount, {
|
|
307354
|
-
namespace: this.namespace,
|
|
307355
|
-
kind: this.kind,
|
|
307356
|
-
});
|
|
307357
|
-
this.deletedGauge.record(deletedCount, {
|
|
307358
|
-
namespace: this.namespace,
|
|
307359
|
-
kind: this.kind,
|
|
307360
|
-
});
|
|
307361
|
-
this.outOfSyncGauge.record(outOfSyncCount, {
|
|
307362
|
-
namespace: this.namespace,
|
|
307363
|
-
kind: this.kind,
|
|
307364
|
-
});
|
|
307365
|
-
this.errorGauge.record(errorCount, {
|
|
307366
|
-
namespace: this.namespace,
|
|
307367
|
-
kind: this.kind,
|
|
307368
|
-
});
|
|
307369
|
-
this.errorOnSyncGauge.record(errorOnSyncCount, {
|
|
307370
|
-
namespace: this.namespace,
|
|
307371
|
-
kind: this.kind,
|
|
307372
|
-
});
|
|
307373
|
-
}
|
|
307374
|
-
catch (err) {
|
|
307375
|
-
console.log(`CRStateMetrics: update ${err}`);
|
|
307376
|
-
this.onUpdate = false;
|
|
307377
|
-
operator_src_logger.error(`On update of CR metrics: ${err}`);
|
|
307378
|
-
await this.__prepareConnection();
|
|
307379
|
-
}
|
|
307380
|
-
this.onUpdate = false;
|
|
307381
|
-
}
|
|
307382
|
-
async __prepareConnection() {
|
|
307383
|
-
const { kc, opts } = await ctl_getConnection();
|
|
307384
|
-
const k8sApi = kc.makeApiClient(client_node_dist.CustomObjectsApi);
|
|
307385
|
-
this.fListCRs = async () => {
|
|
307386
|
-
try {
|
|
307387
|
-
const response = await k8sApi.listNamespacedCustomObject('firestartr.dev', 'v1', this.namespace, this.kind);
|
|
307388
|
-
const body = response.body;
|
|
307389
|
-
return body.items;
|
|
307390
|
-
}
|
|
307391
|
-
catch (err) {
|
|
307392
|
-
throw new Error(`On listing CRs ${this.kind}: ${err}`);
|
|
307393
|
-
}
|
|
307394
|
-
};
|
|
307395
|
-
}
|
|
307396
|
-
}
|
|
307397
|
-
|
|
307398
|
-
;// CONCATENATED MODULE: ../operator/src/metricsServer.ts
|
|
307399
|
-
|
|
307400
|
-
|
|
307401
|
-
|
|
307402
|
-
|
|
307403
|
-
/* harmony default export */ async function metricsServer(kindList, namespace) {
|
|
307404
|
-
const portFromEnv = process.env.METRICS_PORT;
|
|
307405
|
-
const options = build_src/* PrometheusExporter.DEFAULT_OPTIONS */.rC.DEFAULT_OPTIONS;
|
|
307406
|
-
options.port = portFromEnv ? parseInt(portFromEnv) : options.port;
|
|
307407
|
-
options.endpoint = process.env.METRICS_ENDPOINT || options.endpoint;
|
|
307408
|
-
const exporter = new build_src/* PrometheusExporter */.rC(options, () => {
|
|
307409
|
-
console.log(`📊 Metrics endpoint: http://localhost:${options.port}${options.endpoint}`);
|
|
307410
|
-
});
|
|
307411
|
-
const attributes = { pid: process.pid };
|
|
307412
|
-
void startMetrics(exporter, attributes, kindList, namespace);
|
|
307413
|
-
}
|
|
307414
|
-
async function startMetrics(exporter, attributes, kindList, namespace) {
|
|
307415
|
-
const meterProvider = new sdk_metrics_build_src/* MeterProvider */.y0({
|
|
307416
|
-
readers: [exporter],
|
|
307417
|
-
});
|
|
307418
|
-
const meter = meterProvider.getMeter('firestartr');
|
|
307419
|
-
void startQueueMetrics(meter, attributes);
|
|
307420
|
-
void startMemoryMetrics(meter, attributes);
|
|
307421
|
-
void startCRStates(meter, kindList, namespace);
|
|
307422
|
-
}
|
|
307423
|
-
async function startQueueMetrics(meter, attributes) {
|
|
307424
|
-
const nWorkItemsCounter = meter.createObservableGauge('firestartr_workitems_total', { description: 'Number of workitems in the queue' });
|
|
307425
|
-
const nWorkItemsPendingCounter = meter.createObservableGauge('firestartr_workitems_pending_total', { description: 'Number of workitems with PENDING status in the queue' });
|
|
307426
|
-
const nWorkItemsProcessingCounter = meter.createObservableGauge('firestartr_workitems_processing_total', { description: 'Number of workitems with PROCESSING status in the queue' });
|
|
307427
|
-
const nWorkItemsFinishedCounter = meter.createObservableGauge('firestartr_workitems_finished_total', { description: 'Number of workitems with FINISHED status in the queue' });
|
|
307428
|
-
const nWorkItemsInDeadLetterHandling = meter.createObservableGauge('firestartr_workitems_dead_letter_handling_total', {
|
|
307429
|
-
description: 'Number of workitems of the queue handled by the Dead Letter Handler',
|
|
307430
|
-
});
|
|
307431
|
-
const queueMetrics = getQueueMetrics();
|
|
307432
|
-
let total = queueMetrics.nItems;
|
|
307433
|
-
let pending = queueMetrics.nItemsPending;
|
|
307434
|
-
let processing = queueMetrics.nItemsProcessing;
|
|
307435
|
-
let finished = queueMetrics.nItemsFinished;
|
|
307436
|
-
let inDeadLetterHandling = queueMetrics.nItemsInDeadLetterHandling;
|
|
307437
|
-
nWorkItemsCounter.addCallback((observer) => observer.observe(total, { ...attributes, ...queueMetrics.nItemsTypes }));
|
|
307438
|
-
nWorkItemsPendingCounter.addCallback((observer) => observer.observe(pending, { ...attributes, ...queueMetrics.nItemsTypes }));
|
|
307439
|
-
nWorkItemsProcessingCounter.addCallback((observer) => observer.observe(processing, {
|
|
307440
|
-
...attributes,
|
|
307441
|
-
...queueMetrics.nItemsTypes,
|
|
307442
|
-
}));
|
|
307443
|
-
nWorkItemsFinishedCounter.addCallback((observer) => observer.observe(finished, { ...attributes, ...queueMetrics.nItemsTypes }));
|
|
307444
|
-
nWorkItemsInDeadLetterHandling.addCallback((observer) => observer.observe(inDeadLetterHandling, {
|
|
307445
|
-
...attributes,
|
|
307446
|
-
...queueMetrics.nItemsTypes,
|
|
307447
|
-
}));
|
|
307448
|
-
setInterval(() => {
|
|
307449
|
-
const queueMetrics = getQueueMetrics();
|
|
307450
|
-
total = queueMetrics.nItems;
|
|
307451
|
-
pending = queueMetrics.nItemsPending;
|
|
307452
|
-
processing = queueMetrics.nItemsProcessing;
|
|
307453
|
-
finished = queueMetrics.nItemsFinished;
|
|
307454
|
-
inDeadLetterHandling = queueMetrics.nItemsInDeadLetterHandling;
|
|
307455
|
-
}, 1000);
|
|
307456
|
-
}
|
|
307457
|
-
async function startMemoryMetrics(meter, attributes) {
|
|
307458
|
-
const usedMemoryCounter = meter.createObservableGauge('firestartr_used_memory', { description: 'Used memory in bytes' });
|
|
307459
|
-
let heapUsage = process.memoryUsage().heapUsed;
|
|
307460
|
-
usedMemoryCounter.addCallback((observer) => observer.observe(heapUsage, attributes));
|
|
307461
|
-
setInterval(() => {
|
|
307462
|
-
heapUsage = process.memoryUsage().heapUsed;
|
|
307463
|
-
}, 1000);
|
|
307464
|
-
}
|
|
307465
|
-
async function startCRStates(meter, kindList, namespace) {
|
|
307466
|
-
for (const kind of kindList) {
|
|
307467
|
-
const crStateMetrics = new CRStateMetrics(kind, namespace, meter);
|
|
307468
|
-
await crStateMetrics.start();
|
|
307469
|
-
}
|
|
307470
|
-
}
|
|
307471
|
-
|
|
307472
308027
|
// EXTERNAL MODULE: external "util"
|
|
307473
308028
|
var external_util_ = __nccwpck_require__(73837);
|
|
307474
308029
|
;// CONCATENATED MODULE: ../operator/src/cmd/tf_planner.ts
|
|
@@ -308182,7 +308737,7 @@ const crs_analyzerSubcommand = {
|
|
|
308182
308737
|
};
|
|
308183
308738
|
|
|
308184
308739
|
;// CONCATENATED MODULE: ./package.json
|
|
308185
|
-
const package_namespaceObject = {"i8":"2.
|
|
308740
|
+
const package_namespaceObject = JSON.parse('{"i8":"2.3.0-snapshot"}');
|
|
308186
308741
|
;// CONCATENATED MODULE: ../../package.json
|
|
308187
308742
|
const package_namespaceObject_1 = {"i8":"2.2.0"};
|
|
308188
308743
|
;// CONCATENATED MODULE: ./src/subcommands/index.ts
|