@leverege/build-tools 2.110.0 â 2.111.0
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/package.json +3 -3
- package/src/helm-charts/prom-operator/helmup.plugin +1 -1
- package/src/helm-charts/prom-operator/prometheus-stack.yaml.ovh +1 -1
- package/src/helm-charts/prom-operator/rules/cnpg-rules.yaml +5 -5
- package/src/helm-charts/prom-operator/rules/elasticsearch-rules.yaml +3 -3
- package/src/helm-charts/prom-operator/rules/kubestate-rules.yaml +8 -5
- package/src/helm-charts/prom-operator/rules/redis-rules.yaml +4 -4
- package/src/helm-charts/prom-operator/rules/traefik-rules.yaml +1 -1
- package/src/helm-charts/valkey/helmup.bootstrap +12 -0
- package/src/helm-charts/valkey/valkey-local.yaml +9 -1
- package/src/helm-charts/velero/helmup.bootstrap +1 -1
- package/src/helm-charts/velero/helmup.plugin +2 -2
- package/src/overwhelm.mjs +0 -2
- package/src/chart-to-museum.mjs +0 -55
- package/src/fetch-awesome-alerts.mjs +0 -75
- package/src/modify-gcs-access.mjs +0 -85
- package/src/test.json +0 -4
- package/src/testOauthLogic.mjs +0 -12
- package/src/unleash.mjs +0 -57
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leverege/build-tools",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.111.0",
|
|
4
4
|
"description": "A collection of build / support tools for Leverege developers",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"googleapis": "^173.0.0",
|
|
99
99
|
"handlebars": "^4.7.9",
|
|
100
100
|
"ignore": "^7.0.5",
|
|
101
|
-
"inquirer": "14.0.2",
|
|
101
|
+
"inquirer": "^14.0.2",
|
|
102
102
|
"js-yaml": "^4.2.0",
|
|
103
103
|
"jsdoc": "^4.0.5",
|
|
104
104
|
"ms": "^2.1.3",
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"p-limit": "^7.3.0",
|
|
108
108
|
"package-up": "^5.0.0",
|
|
109
109
|
"readline-sync": "^1.4.10",
|
|
110
|
-
"semver": "7.8.4",
|
|
110
|
+
"semver": "^7.8.4",
|
|
111
111
|
"shell-quote": "^1.8.4",
|
|
112
112
|
"simple-git": "^3.36.0",
|
|
113
113
|
"sloc": "^0.3.2",
|
|
@@ -8,7 +8,7 @@ showInstalling "The Prometheus Operator (kube-prometheus-stack)"
|
|
|
8
8
|
|
|
9
9
|
# https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack
|
|
10
10
|
OCI_CHART="oci://ghcr.io/prometheus-community/charts/kube-prometheus-stack"
|
|
11
|
-
[ -z "$PROMETHEUS_STACK_CHART_VERSION" ] && PROMETHEUS_STACK_CHART_VERSION="
|
|
11
|
+
[ -z "$PROMETHEUS_STACK_CHART_VERSION" ] && PROMETHEUS_STACK_CHART_VERSION="86.2.3"
|
|
12
12
|
helm upgrade $NS --install prometheus-stack $OCI_CHART \
|
|
13
13
|
--values prom-operator/prometheus-stack.yaml \
|
|
14
14
|
--version $PROMETHEUS_STACK_CHART_VERSION $HELM_WHAT
|
|
@@ -342,7 +342,7 @@ alertmanager:
|
|
|
342
342
|
- alertname="GKEMaintenanceInProgress"
|
|
343
343
|
- maintenance="true"
|
|
344
344
|
target_matchers:
|
|
345
|
-
- alertname=~"KubernetesNodeNotReady|KubernetesPodNotHealthy|KubernetesPodCrashLooping|frequent_app_restarts"
|
|
345
|
+
- alertname=~"KubernetesNodeNotReady|KubernetesPodNotHealthy|KubernetesPodCrashLooping|frequent_app_restarts|KubernetesStatefulsetDown|KubernetesDaemonsetMisscheduled"
|
|
346
346
|
|
|
347
347
|
route:
|
|
348
348
|
receiver: slack # default for everything
|
|
@@ -68,7 +68,7 @@ spec:
|
|
|
68
68
|
summary: Checks the number of database conflicts
|
|
69
69
|
expr: |-
|
|
70
70
|
rate(cnpg_pg_stat_database_deadlocks[5m]) > 0
|
|
71
|
-
for:
|
|
71
|
+
for: 3m
|
|
72
72
|
labels:
|
|
73
73
|
severity: warning
|
|
74
74
|
- alert: ReplicaFailingReplication
|
|
@@ -77,7 +77,7 @@ spec:
|
|
|
77
77
|
summary: Checks if the replica is failing to replicate
|
|
78
78
|
expr: |-
|
|
79
79
|
cnpg_pg_replication_in_recovery > cnpg_pg_replication_is_wal_receiver_up
|
|
80
|
-
for:
|
|
80
|
+
for: 3m
|
|
81
81
|
labels:
|
|
82
82
|
severity: warning
|
|
83
83
|
#
|
|
@@ -85,7 +85,7 @@ spec:
|
|
|
85
85
|
#
|
|
86
86
|
- alert: PostgresqlDown
|
|
87
87
|
expr: cnpg_collector_up == 0
|
|
88
|
-
for:
|
|
88
|
+
for: 3m
|
|
89
89
|
labels:
|
|
90
90
|
severity: critical
|
|
91
91
|
annotations:
|
|
@@ -94,9 +94,9 @@ spec:
|
|
|
94
94
|
|
|
95
95
|
- alert: PostgresqlRestarted
|
|
96
96
|
expr: time() - cnpg_pg_postmaster_start_time < 60
|
|
97
|
-
for:
|
|
97
|
+
for: 0m
|
|
98
98
|
labels:
|
|
99
|
-
severity:
|
|
99
|
+
severity: warning
|
|
100
100
|
annotations:
|
|
101
101
|
summary: Postgresql restarted (instance {{ $labels.instance }})
|
|
102
102
|
description: "Postgresql restarted\n VALUE = {{ $value }}\n LABELS = {{ $labels }}"
|
|
@@ -76,7 +76,7 @@ spec:
|
|
|
76
76
|
LABELS = {{ $labels }}
|
|
77
77
|
- alert: ElasticsearchHealthyNodes
|
|
78
78
|
expr: elasticsearch_cluster_health_number_of_nodes < 3
|
|
79
|
-
for:
|
|
79
|
+
for: 5m
|
|
80
80
|
labels:
|
|
81
81
|
severity: critical
|
|
82
82
|
annotations:
|
|
@@ -87,7 +87,7 @@ spec:
|
|
|
87
87
|
LABELS = {{ $labels }}
|
|
88
88
|
- alert: ElasticsearchHealthyDataNodes
|
|
89
89
|
expr: elasticsearch_cluster_health_number_of_data_nodes < 3
|
|
90
|
-
for:
|
|
90
|
+
for: 5m
|
|
91
91
|
labels:
|
|
92
92
|
severity: critical
|
|
93
93
|
annotations:
|
|
@@ -142,7 +142,7 @@ spec:
|
|
|
142
142
|
LABELS = {{ $labels }}
|
|
143
143
|
- alert: ElasticsearchUnassignedShards
|
|
144
144
|
expr: elasticsearch_cluster_health_unassigned_shards > 0
|
|
145
|
-
for:
|
|
145
|
+
for: 5m
|
|
146
146
|
labels:
|
|
147
147
|
severity: critical
|
|
148
148
|
annotations:
|
|
@@ -154,7 +154,7 @@ spec:
|
|
|
154
154
|
LABELS = {{ $labels }}
|
|
155
155
|
- alert: KubernetesStatefulsetDown
|
|
156
156
|
expr: kube_statefulset_replicas != kube_statefulset_status_replicas_ready > 0
|
|
157
|
-
for:
|
|
157
|
+
for: 5m # awesome was 1m; bumped to 3m, then 5m for GKE rolling updates
|
|
158
158
|
labels:
|
|
159
159
|
severity: critical
|
|
160
160
|
annotations:
|
|
@@ -228,8 +228,11 @@ spec:
|
|
|
228
228
|
VALUE = {{ $value }}
|
|
229
229
|
LABELS = {{ $labels }}
|
|
230
230
|
- alert: KubernetesReplicasetReplicasMismatch
|
|
231
|
-
expr:
|
|
232
|
-
|
|
231
|
+
expr: |
|
|
232
|
+
(kube_replicaset_spec_replicas != kube_replicaset_status_ready_replicas)
|
|
233
|
+
* on (namespace, replicaset) group_left()
|
|
234
|
+
kube_replicaset_owner{owner_kind!="Deployment"}
|
|
235
|
+
for: 15m # awesome was => 10m; Deployment-owned RSes excluded (covered by KubernetesDeploymentReplicasMismatch)
|
|
233
236
|
labels:
|
|
234
237
|
severity: warning
|
|
235
238
|
annotations:
|
|
@@ -306,7 +309,7 @@ spec:
|
|
|
306
309
|
LABELS = {{ $labels }}
|
|
307
310
|
- alert: KubernetesDaemonsetMisscheduled
|
|
308
311
|
expr: kube_daemonset_status_number_misscheduled > 0
|
|
309
|
-
for:
|
|
312
|
+
for: 5m
|
|
310
313
|
labels:
|
|
311
314
|
severity: critical
|
|
312
315
|
annotations:
|
|
@@ -339,7 +342,7 @@ spec:
|
|
|
339
342
|
LABELS = {{ $labels }}
|
|
340
343
|
- alert: KubernetesApiServerErrors
|
|
341
344
|
expr: sum(rate(apiserver_request_total{job="apiserver",code=~"(?:5..)"}[1m])) by (instance, job) / sum(rate(apiserver_request_total{job="apiserver"}[1m])) by (instance, job) * 100 > 3
|
|
342
|
-
for:
|
|
345
|
+
for: 5m
|
|
343
346
|
labels:
|
|
344
347
|
severity: critical
|
|
345
348
|
annotations:
|
|
@@ -10,7 +10,7 @@ spec:
|
|
|
10
10
|
rules:
|
|
11
11
|
- alert: RedisDown
|
|
12
12
|
expr: redis_up == 0
|
|
13
|
-
for:
|
|
13
|
+
for: 2m
|
|
14
14
|
labels:
|
|
15
15
|
severity: critical
|
|
16
16
|
annotations:
|
|
@@ -21,7 +21,7 @@ spec:
|
|
|
21
21
|
LABELS = {{ $labels }}
|
|
22
22
|
- alert: RedisMissingMaster
|
|
23
23
|
expr: (count(redis_instance_info{role="master"}) or vector(0)) < 1
|
|
24
|
-
for:
|
|
24
|
+
for: 2m
|
|
25
25
|
labels:
|
|
26
26
|
severity: critical
|
|
27
27
|
annotations:
|
|
@@ -43,7 +43,7 @@ spec:
|
|
|
43
43
|
LABELS = {{ $labels }}
|
|
44
44
|
- alert: RedisDisconnectedSlaves
|
|
45
45
|
expr: count without (instance, job) (redis_connected_slaves) - sum without (instance, job) (redis_connected_slaves) - 1 > 0
|
|
46
|
-
for:
|
|
46
|
+
for: 2m
|
|
47
47
|
labels:
|
|
48
48
|
severity: critical
|
|
49
49
|
annotations:
|
|
@@ -54,7 +54,7 @@ spec:
|
|
|
54
54
|
LABELS = {{ $labels }}
|
|
55
55
|
- alert: RedisReplicationBroken
|
|
56
56
|
expr: delta(redis_connected_slaves[1m]) < 0
|
|
57
|
-
for:
|
|
57
|
+
for: 2m
|
|
58
58
|
labels:
|
|
59
59
|
severity: critical
|
|
60
60
|
annotations:
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# The network policy sets:
|
|
4
|
+
#
|
|
5
|
+
# ingressNSMatchLabels: # this is why default ns gets labelled
|
|
6
|
+
# redis: external
|
|
7
|
+
#
|
|
8
|
+
# which should be specifically set in redis client service helm charts but
|
|
9
|
+
# for now we set it hear without being forced to upgraed al of the legacy
|
|
10
|
+
# helm charts. This setting will eventually end up in the leverege base chart.
|
|
11
|
+
#
|
|
12
|
+
kubectl label namespace default redis=external
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
image:
|
|
2
2
|
registry: us-docker.pkg.dev
|
|
3
3
|
repository: leverege-registry/bitnami-mirror/images/valkey
|
|
4
|
-
# tag: 8.1.3-
|
|
4
|
+
# tag: 8.1.3-debian-12-r1
|
|
5
5
|
|
|
6
6
|
global:
|
|
7
7
|
security:
|
|
@@ -14,6 +14,14 @@ auth:
|
|
|
14
14
|
enabled: false
|
|
15
15
|
sentinel: true
|
|
16
16
|
|
|
17
|
+
networkPolicy:
|
|
18
|
+
enabled: true
|
|
19
|
+
allowExternal: false
|
|
20
|
+
ingressNSMatchLabels: # this is why default ns gets labelled
|
|
21
|
+
redis: external
|
|
22
|
+
ingressNSPodMatchLabels:
|
|
23
|
+
redis-client: true
|
|
24
|
+
|
|
17
25
|
primary:
|
|
18
26
|
replicaCount: 1
|
|
19
27
|
|
|
@@ -11,7 +11,7 @@ function installVeleroEnvironment() {
|
|
|
11
11
|
gcloud config set project $GCP_PROJECT_ID
|
|
12
12
|
|
|
13
13
|
## The major chart version will determine the bucket suffix
|
|
14
|
-
[ -z "$VELERO_CHART_VERSION" ] && VELERO_CHART_VERSION="
|
|
14
|
+
[ -z "$VELERO_CHART_VERSION" ] && VELERO_CHART_VERSION="12"
|
|
15
15
|
|
|
16
16
|
## Create a bucket
|
|
17
17
|
BUCKET="$GCP_PROJECT_ID-velero-$VELERO_CHART_VERSION"
|
|
@@ -5,8 +5,8 @@ showInstalling "Velero Backup System"
|
|
|
5
5
|
# We use a specific version for the velero chart because we need to control
|
|
6
6
|
# the version of the locally installed velero client application.
|
|
7
7
|
#
|
|
8
|
-
VELERO_CHART_VERSION="
|
|
9
|
-
EXPECTED_CLIENT_VERSION="v1.18.
|
|
8
|
+
VELERO_CHART_VERSION="12.0.3"
|
|
9
|
+
EXPECTED_CLIENT_VERSION="v1.18.1"
|
|
10
10
|
|
|
11
11
|
addHelmRepo vmware-tanzu https://vmware-tanzu.github.io/helm-charts
|
|
12
12
|
|
package/src/overwhelm.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// XXX console.log( `TOP=>[${process.cwd()}] PWD=[${process.env.PWD}]` )
|
|
4
3
|
/* eslint-disable security/detect-non-literal-fs-filename */
|
|
5
4
|
|
|
6
5
|
import path from 'node:path'
|
|
@@ -8,7 +7,6 @@ import fs from 'node:fs'
|
|
|
8
7
|
|
|
9
8
|
import chalk from 'chalk'
|
|
10
9
|
import cliArgs from 'command-line-args'
|
|
11
|
-
// const cliHelp = require( 'command-line-usage' )
|
|
12
10
|
import deepmerge from 'deepmerge'
|
|
13
11
|
import { glob } from 'glob'
|
|
14
12
|
import ask from 'readline-sync'
|
package/src/chart-to-museum.mjs
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// *** DEPRECATED: this goes away when we move away from the chart museum and
|
|
3
|
-
// push-my-chart already handles this a artifact pushes
|
|
4
|
-
//
|
|
5
|
-
import chalk from 'chalk'
|
|
6
|
-
import commandLineArgs from 'command-line-args'
|
|
7
|
-
import commandLineUsage from 'command-line-usage'
|
|
8
|
-
import { lt as semverLt } from 'semver'
|
|
9
|
-
|
|
10
|
-
import { log, shellCmd } from './Utils.mjs'
|
|
11
|
-
|
|
12
|
-
const commandLineOptions = [
|
|
13
|
-
{
|
|
14
|
-
name : 'help',
|
|
15
|
-
type : Boolean,
|
|
16
|
-
description : '{green display this help screen}',
|
|
17
|
-
},
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
const sections = [
|
|
21
|
-
{
|
|
22
|
-
header : 'Leverege Helm Chart Museum Pusher',
|
|
23
|
-
content : `{green This legacy tool acts as a simple method for assembling and storing a local
|
|
24
|
-
helm chart structure on the Leverege Chart Museum using Google Cloud Storage.
|
|
25
|
-
This approach is considered legacy since we have transitioned to using the
|
|
26
|
-
Google Artifact Registry for distributing helm charts.
|
|
27
|
-
}`
|
|
28
|
-
},
|
|
29
|
-
{ header : 'Options',
|
|
30
|
-
optionList : commandLineOptions,
|
|
31
|
-
},
|
|
32
|
-
]
|
|
33
|
-
|
|
34
|
-
const args = commandLineArgs( commandLineOptions, { camelCase : true, partial : true } )
|
|
35
|
-
const usage = commandLineUsage( sections )
|
|
36
|
-
|
|
37
|
-
if ( args.help ) {
|
|
38
|
-
log( usage )
|
|
39
|
-
process.exit( 0 )
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const pushItGood = async () => {
|
|
43
|
-
const version = await shellCmd( 'helm version --short' )
|
|
44
|
-
const pusher = ( semverLt( version, '3.7.0' ) ? 'push' : 'cm-push' )
|
|
45
|
-
|
|
46
|
-
try {
|
|
47
|
-
const museum = await shellCmd( `helm ${pusher} helm leverege` )
|
|
48
|
-
log( chalk.green( museum ) )
|
|
49
|
-
} catch ( ex ) {
|
|
50
|
-
log( chalk.red( ex.stderr ) )
|
|
51
|
-
process.exit( 1 )
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
pushItGood()
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import fs from 'node:fs'
|
|
4
|
-
import path from 'node:path'
|
|
5
|
-
|
|
6
|
-
import yaml from 'js-yaml'
|
|
7
|
-
import { Command } from 'commander'
|
|
8
|
-
import chalk from 'chalk'
|
|
9
|
-
|
|
10
|
-
import { log, err } from './Utils.mjs'
|
|
11
|
-
|
|
12
|
-
const program = new Command()
|
|
13
|
-
|
|
14
|
-
program
|
|
15
|
-
.name( 'fetch-alerts' )
|
|
16
|
-
.description( 'Fetch a Prometheus alert YAML file and convert it to a PrometheusRule CRD.' )
|
|
17
|
-
.argument( '<sourceFile>', 'YAML filename to fetch from awesome-prometheus' )
|
|
18
|
-
.option( '-o, --output <outputFile>', 'Output file name', 'converted-alerts.yaml' )
|
|
19
|
-
.option( '-n, --name <customName>', 'Override metadata.name in the PrometheusRule' )
|
|
20
|
-
.option( '--dry-run', 'Print output to console instead of writing to file' )
|
|
21
|
-
.parse()
|
|
22
|
-
|
|
23
|
-
const [ sourceFile ] = program.args
|
|
24
|
-
const { output : outputFile, dryRun, name : customName } = program.opts()
|
|
25
|
-
|
|
26
|
-
const BASE_URL = 'https://raw.githubusercontent.com/samber/awesome-prometheus-alerts/master/dist/rules'
|
|
27
|
-
const SOURCE_URL = `${BASE_URL}/${sourceFile}`
|
|
28
|
-
|
|
29
|
-
const defaultName = path.basename( sourceFile ).replace( /\.(yaml|yml)$/, '' )
|
|
30
|
-
const metadataName = customName || defaultName
|
|
31
|
-
|
|
32
|
-
async function fetchAndConvert() {
|
|
33
|
-
try {
|
|
34
|
-
log( chalk.blueBright( `đ Fetching YAML from ${SOURCE_URL}...` ) )
|
|
35
|
-
const response = await fetch( SOURCE_URL )
|
|
36
|
-
if ( !response.ok ) throw new Error( `Failed to fetch: ${response.statusText}` )
|
|
37
|
-
|
|
38
|
-
const rawYaml = await response.text()
|
|
39
|
-
const parsedYaml = yaml.load( rawYaml )
|
|
40
|
-
if ( !parsedYaml.groups ) throw new Error( 'Invalid YAML structure, missing "groups"' )
|
|
41
|
-
|
|
42
|
-
const prometheusRule = {
|
|
43
|
-
apiVersion : 'monitoring.coreos.com/v1',
|
|
44
|
-
kind : 'PrometheusRule',
|
|
45
|
-
metadata : {
|
|
46
|
-
name : metadataName,
|
|
47
|
-
namespace : 'prometheus',
|
|
48
|
-
},
|
|
49
|
-
spec : {
|
|
50
|
-
groups : parsedYaml.groups.map( group => ( {
|
|
51
|
-
name : `${group.name}-rules`,
|
|
52
|
-
rules : group.rules,
|
|
53
|
-
} ) ),
|
|
54
|
-
},
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const formattedYaml = yaml.dump( prometheusRule, {
|
|
58
|
-
noRefs : true,
|
|
59
|
-
indent : 2,
|
|
60
|
-
lineWidth : -1,
|
|
61
|
-
} )
|
|
62
|
-
|
|
63
|
-
if ( dryRun ) {
|
|
64
|
-
log( chalk.green( `\nđĸ Dry Run Output for: ${chalk.yellow( metadataName )}\n` ) )
|
|
65
|
-
log( formattedYaml )
|
|
66
|
-
} else {
|
|
67
|
-
fs.writeFileSync( outputFile, formattedYaml, 'utf8' ) // eslint-disable-line security/detect-non-literal-fs-filename
|
|
68
|
-
log( chalk.green( `â
Converted YAML written to ${chalk.cyan( outputFile )}` ) )
|
|
69
|
-
}
|
|
70
|
-
} catch ( error ) {
|
|
71
|
-
err( chalk.red( 'â Error:' ), error.message )
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
fetchAndConvert()
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { Command } from 'commander'
|
|
4
|
-
|
|
5
|
-
import { shellCmd, log, warning, errorExit } from './Utils.mjs'
|
|
6
|
-
|
|
7
|
-
const program = new Command()
|
|
8
|
-
|
|
9
|
-
program
|
|
10
|
-
.requiredOption( '--project <projectId>', 'GCP project ID of the GSA' )
|
|
11
|
-
.requiredOption( '--bucket <bucketName>', 'GCS bucket name (no gs:// prefix)' )
|
|
12
|
-
.requiredOption( '--gsa <gsaName>', 'Service account name, e.g. my-service' )
|
|
13
|
-
.requiredOption( '--role <roleId>', 'Custom role ID', 'leveregeStorageObjectReader' )
|
|
14
|
-
.option( '--org', 'Treat as GCP organizational level role' )
|
|
15
|
-
.option( '--add', 'Add IAM binding (default)' )
|
|
16
|
-
.option( '--remove', 'Remove IAM binding' )
|
|
17
|
-
.option( '--dry-run', 'Show command without executing' )
|
|
18
|
-
|
|
19
|
-
program.parse( process.argv )
|
|
20
|
-
|
|
21
|
-
const opts = program.opts()
|
|
22
|
-
|
|
23
|
-
// Validate mutually exclusive add/remove
|
|
24
|
-
if ( opts.add && opts.remove ) {
|
|
25
|
-
errorExit( 'You cannot specify both --add and --remove.' )
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const action = opts.remove ? 'remove' : 'add'
|
|
29
|
-
|
|
30
|
-
const {
|
|
31
|
-
project,
|
|
32
|
-
org,
|
|
33
|
-
bucket,
|
|
34
|
-
gsa,
|
|
35
|
-
role,
|
|
36
|
-
dryRun
|
|
37
|
-
} = opts
|
|
38
|
-
|
|
39
|
-
const orgId = await shellCmd( 'gcloud organizations list --format="value(ID)"' )
|
|
40
|
-
const gsaEmail = `${gsa}@${project}.iam.gserviceaccount.com`
|
|
41
|
-
const fullRole = org ? `organizations/${orgId}/roles/${role}` : `roles/${role}`
|
|
42
|
-
const bucketUri = `gs://${bucket}`
|
|
43
|
-
|
|
44
|
-
const iamMember = `serviceAccount:${gsaEmail}:${fullRole}`
|
|
45
|
-
|
|
46
|
-
const command = action === 'add' ?
|
|
47
|
-
[ 'gsutil', 'iam', 'ch', iamMember, bucketUri ] :
|
|
48
|
-
[ 'gsutil', 'iam', 'ch', '-d', iamMember, bucketUri ]
|
|
49
|
-
|
|
50
|
-
log( `đ§ ${action === 'add' ? 'Granting' : 'Removing'} ${fullRole} for ${gsaEmail} on ${bucketUri}...` )
|
|
51
|
-
|
|
52
|
-
if ( dryRun ) {
|
|
53
|
-
warning( 'đĄ DRY RUN mode â no commands executed.' )
|
|
54
|
-
log( `đ Command:\n$ ${command.join( ' ' )}` )
|
|
55
|
-
process.exit( 0 )
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
try {
|
|
59
|
-
const result = await shellCmd( command )
|
|
60
|
-
log( `â
IAM ${action} successful:\n${result}` )
|
|
61
|
-
} catch ( err ) {
|
|
62
|
-
errorExit( `â Failed to ${action} IAM binding: ${err.message}` )
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// === Post-change audit ===
|
|
66
|
-
log( 'đ Verifying IAM policy for this GSA on the bucket...\n' )
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
const policyRaw = await shellCmd( [ 'gsutil', 'iam', 'get', bucketUri ] )
|
|
70
|
-
const policy = JSON.parse( policyRaw )
|
|
71
|
-
|
|
72
|
-
const matchingRoles = policy.bindings.filter(
|
|
73
|
-
b => b.members.includes( `serviceAccount:${gsaEmail}` )
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
if ( matchingRoles.length === 0 ) {
|
|
77
|
-
log( `âšī¸ No IAM bindings found for ${gsaEmail} on ${bucketUri}.` )
|
|
78
|
-
} else {
|
|
79
|
-
for ( const binding of matchingRoles ) {
|
|
80
|
-
log( `đ ${gsaEmail} has role: ${binding.role}` )
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
} catch ( err ) {
|
|
84
|
-
warning( `â ī¸ Failed to retrieve or parse IAM policy: ${err.message}` )
|
|
85
|
-
}
|
package/src/test.json
DELETED
package/src/testOauthLogic.mjs
DELETED
package/src/unleash.mjs
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import SimpleGit from 'simple-git'
|
|
4
|
-
|
|
5
|
-
import { shellCmd } from './Utils.mjs'
|
|
6
|
-
|
|
7
|
-
const git = new SimpleGit()
|
|
8
|
-
|
|
9
|
-
const getCurrentBranchName = async () => {
|
|
10
|
-
const branchInfo = await git.branch()
|
|
11
|
-
|
|
12
|
-
// first check to see if there are no branches - new repo?
|
|
13
|
-
if ( Object.keys( branchInfo.branches ).length === 0 ) { return '' }
|
|
14
|
-
|
|
15
|
-
return Object
|
|
16
|
-
.entries( branchInfo.branches )
|
|
17
|
-
.find( ( [ name, info ] ) => ( info.current ) )[0]
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const isProtected = async () => {
|
|
21
|
-
const theBranch = await getCurrentBranchName()
|
|
22
|
-
|
|
23
|
-
const protectedBranches = [ 'main', 'master', 'development' ]
|
|
24
|
-
const additionalProtectedBranches = [ ...process.argv.slice( 2 ) ]
|
|
25
|
-
|
|
26
|
-
// Check if the current branch starts with any of the additional
|
|
27
|
-
// protected branch name
|
|
28
|
-
let matchesAdditionalBranch = false
|
|
29
|
-
for ( let i = 0; i < additionalProtectedBranches.length; i++ ) {
|
|
30
|
-
matchesAdditionalBranch = matchesAdditionalBranch ||
|
|
31
|
-
theBranch.startsWith( additionalProtectedBranches[i] )
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return protectedBranches.includes( theBranch ) || matchesAdditionalBranch
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const npmRunner = async ( script ) => {
|
|
38
|
-
try {
|
|
39
|
-
const cmd = `npm run ${script}`
|
|
40
|
-
const results = await shellCmd( cmd, { stdio : 'inherit' } )
|
|
41
|
-
return results
|
|
42
|
-
} catch ( err ) { // eslint-disable-line no-unused-vars
|
|
43
|
-
process.exit( 1 )
|
|
44
|
-
}
|
|
45
|
-
return undefined
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
( async () => {
|
|
49
|
-
if ( await isProtected() ) {
|
|
50
|
-
const script = [ 'test', 'lint' ]
|
|
51
|
-
for ( let i = 0; i < script.length; i++ ) {
|
|
52
|
-
await npmRunner( script[i] ) // eslint-disable-line no-await-in-loop
|
|
53
|
-
}
|
|
54
|
-
} else {
|
|
55
|
-
process.exit( 0 )
|
|
56
|
-
}
|
|
57
|
-
} )()
|