@leverege/build-tools 2.101.0 → 2.101.2
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 +5 -5
- package/src/Utils.mjs +5 -0
- package/src/bash-funcs +24 -19
- package/src/helm-charts/cnpg-operator/helmup.bootstrap +19 -21
- package/src/helm-charts/eso/helmup.bootstrap +4 -2
- package/src/helm-charts/prom-operator/README-CRD-UPGRADES.md +105 -0
- package/src/helm-charts/prom-operator/helmup.bootstrap +22 -14
- package/src/helm-charts/prom-operator/helmup.plugin +1 -1
- package/src/helm-charts/prom-operator/prometheus-stack.yaml.ovh +8 -45
- package/src/helm-charts/prom-operator/stackdriver-exporter.yaml.ovh +1 -1
- package/src/helm-charts/velero/helmup.bootstrap +2 -0
- package/src/helmup.sh +7 -3
- package/src/init-my-chart/Chartwright.mjs +1 -1
- package/src/init-my-chart/leaf-chart-templates/templates/ingressroute.yaml +1 -0
- package/src/service-man/config/secrets/npmrc.json +13 -0
- package/src/DockerPy.mjs +0 -266
- package/src/docker-to-registry-py.mjs +0 -287
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leverege/build-tools",
|
|
3
|
-
"version": "2.101.
|
|
3
|
+
"version": "2.101.2",
|
|
4
4
|
"description": "A collection of build / support tools for Leverege developers",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"helmup": "src/helmup.sh",
|
|
49
49
|
"helmwhat": "src/helmup.sh",
|
|
50
50
|
"hook-and-release": "src/hook-and-release.mjs",
|
|
51
|
-
"init-my-chart": "src/init-my-chart.mjs",
|
|
51
|
+
"init-my-chart": "src/init-my-chart/init-my-chart.mjs",
|
|
52
52
|
"k8scale": "src/k8scale.sh",
|
|
53
53
|
"k8x": "src/k8x.sh",
|
|
54
54
|
"klog": "src/klog.sh",
|
|
@@ -94,12 +94,12 @@
|
|
|
94
94
|
"googleapis": "^171.4.0",
|
|
95
95
|
"handlebars": "^4.7.9",
|
|
96
96
|
"ignore": "^7.0.5",
|
|
97
|
-
"inquirer": "^13.4.
|
|
97
|
+
"inquirer": "^13.4.2",
|
|
98
98
|
"js-yaml": "^4.1.1",
|
|
99
99
|
"jsdoc": "^4.0.5",
|
|
100
100
|
"ms": "^2.1.3",
|
|
101
101
|
"npm-registry-fetch": "^19.1.1",
|
|
102
|
-
"ora": "^9.
|
|
102
|
+
"ora": "^9.4.0",
|
|
103
103
|
"p-limit": "^7.3.0",
|
|
104
104
|
"package-up": "^5.0.0",
|
|
105
105
|
"readline-sync": "^1.4.10",
|
|
@@ -117,6 +117,6 @@
|
|
|
117
117
|
"@leverege/eslint-config-leverege": "^5.1.2",
|
|
118
118
|
"chai": "^6.2.2",
|
|
119
119
|
"mocha": "^11.7.5",
|
|
120
|
-
"npm": "^11.
|
|
120
|
+
"npm": "^11.13.0"
|
|
121
121
|
}
|
|
122
122
|
}
|
package/src/Utils.mjs
CHANGED
|
@@ -560,6 +560,11 @@ export const parseHelmChart = async ( helmroot = './helm' ) => {
|
|
|
560
560
|
} )
|
|
561
561
|
|
|
562
562
|
depsOutOfSync = mismatches.length > 0
|
|
563
|
+
|
|
564
|
+
// One final check - if the helm/charts dir is missing then we also need to update dependencies
|
|
565
|
+
if ( hasDependencies && !chartFiles.includes( 'charts' ) ) {
|
|
566
|
+
depsOutOfSync = true
|
|
567
|
+
}
|
|
563
568
|
}
|
|
564
569
|
/* eslint-enable security/detect-non-literal-fs-filename */
|
|
565
570
|
|
package/src/bash-funcs
CHANGED
|
@@ -229,26 +229,31 @@ function waitForIAM() {
|
|
|
229
229
|
done
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
-
#
|
|
233
|
-
#
|
|
234
|
-
#
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
then
|
|
240
|
-
BITNAMI_URL="https://charts.bitnami.com/bitnami"
|
|
241
|
-
else
|
|
242
|
-
cat<<CATNAMI
|
|
243
|
-
Using the pre 2022 legacy charts from bitnami. To use latest run:
|
|
244
|
-
|
|
245
|
-
`color y "BITNAMI_LATEST=1 $HELMUP_ARGS"`
|
|
246
|
-
|
|
247
|
-
CATNAMI
|
|
248
|
-
BITNAMI_URL="https://raw.githubusercontent.com/bitnami/charts/eb5f9a9513d987b519f0ecd732e7031241c50328/bitnami"
|
|
249
|
-
fi
|
|
232
|
+
# waitForSAIAM SVC_ACCT PROJECT ROLE MEMBER [TIMEOUT_SECONDS]
|
|
233
|
+
# Like waitForIAM but checks the SA's own IAM policy (for bindings like workloadIdentityUser
|
|
234
|
+
# that are set via `gcloud iam service-accounts add-iam-policy-binding`, not project-level).
|
|
235
|
+
function waitForSAIAM() {
|
|
236
|
+
local svcAcct="$1" project="$2" role="$3" member="$4" timeout="${5:-120}"
|
|
237
|
+
local svcAcctEmail="${svcAcct}@${project}.iam.gserviceaccount.com"
|
|
238
|
+
local start=$(date +%s) delay=2
|
|
250
239
|
|
|
251
|
-
|
|
240
|
+
echo "⏳ waiting for SA IAM binding ${role} → ${member} on ${svcAcctEmail} (timeout ${timeout}s)..." >&2
|
|
241
|
+
while true; do
|
|
242
|
+
local found
|
|
243
|
+
found="$(gcloud iam service-accounts get-iam-policy "${svcAcctEmail}" \
|
|
244
|
+
--flatten="bindings[].members" \
|
|
245
|
+
--filter="bindings.role=${role} AND bindings.members=${member}" \
|
|
246
|
+
--format="value(bindings.role)" 2> "$DEVNULL")"
|
|
247
|
+
if [[ -n "$found" ]]; then
|
|
248
|
+
echo "✅ SA IAM binding active: ${role} → ${member}" >&2
|
|
249
|
+
return 0
|
|
250
|
+
fi
|
|
251
|
+
if (( $(date +%s) - start >= timeout )); then
|
|
252
|
+
echo "❌ timed out waiting for SA IAM binding ${role} → ${member}" >&2
|
|
253
|
+
return 1
|
|
254
|
+
fi
|
|
255
|
+
sleep "$delay"; (( delay = delay < 20 ? delay * 2 : 20 ))
|
|
256
|
+
done
|
|
252
257
|
}
|
|
253
258
|
|
|
254
259
|
function removeHelmRepo(){
|
|
@@ -26,34 +26,33 @@ function installCnpgOperatorEnvironment() {
|
|
|
26
26
|
#
|
|
27
27
|
# kubectl delete serviceaccounts -n $CNPG_NAMESPACE $CNPG_SVC_ACCT &> $DEVNULL
|
|
28
28
|
# gcloud --quiet iam service-accounts delete $CNPG_SVC_EMAIL --project $GCP_PROJECT_ID&> $DEVNULL
|
|
29
|
-
|
|
29
|
+
|
|
30
|
+
createNamespaceIfNeeded $CNPG_NAMESPACE
|
|
31
|
+
|
|
32
|
+
# Guard against accidental SA deletion on re-runs. Check the GCP SA (not the
|
|
33
|
+
# k8s SA) — consistent with ESO bootstrap pattern and correct for new clusters
|
|
34
|
+
# where the GCP SA exists but the k8s SA does not yet.
|
|
35
|
+
gcloud iam service-accounts describe $CNPG_SVC_EMAIL --project $GCP_PROJECT_ID &> $DEVNULL
|
|
30
36
|
if [ $? -eq 0 ];
|
|
31
37
|
then
|
|
32
38
|
cat<<SKIP_CNPG_SA_CREATION
|
|
33
39
|
|
|
34
|
-
$YELO_WARN The CNPG SA $CNPG_SVC_ACCT already exists
|
|
40
|
+
$YELO_WARN The CNPG SA `color y $CNPG_SVC_ACCT` already exists - skipping creation
|
|
35
41
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
in place. To force a clean reinstall, delete both the k8s and gcp SA for
|
|
39
|
-
CNPG and try again. (see comments in helmup for more info)
|
|
42
|
+
To force a clean reinstall, delete both the GCP and k8s SAs for CNPG and
|
|
43
|
+
re-run helmup cnpg-operator.
|
|
40
44
|
|
|
41
45
|
SKIP_CNPG_SA_CREATION
|
|
42
|
-
|
|
46
|
+
else
|
|
47
|
+
printf "\nCreating the gcloud `color g $CNPG_SVC_ACCT` service account (SA)\n"
|
|
48
|
+
gcloud iam service-accounts create $CNPG_SVC_ACCT \
|
|
49
|
+
--project "$GCP_PROJECT_ID" \
|
|
50
|
+
--description "CNPG Operands SA" \
|
|
51
|
+
--display-name "CNPG Operands SA" &> $DEVNULL
|
|
52
|
+
exitOnError $? "Failed to create GCP SA `color y $CNPG_SVC_ACCT`"
|
|
53
|
+
waitForSA "$CNPG_SVC_ACCT" "$GCP_PROJECT_ID" 60
|
|
43
54
|
fi
|
|
44
55
|
|
|
45
|
-
# Create the operands namespace and annotate to allow redis access
|
|
46
|
-
createNamespaceIfNeeded $CNPG_NAMESPACE
|
|
47
|
-
|
|
48
|
-
# Create GCP SA and bind policies
|
|
49
|
-
printf "\nCreating the gcloud `color g $CNPG_SVC_ACCT` service account (SA)\n"
|
|
50
|
-
gcloud iam service-accounts create $CNPG_SVC_ACCT \
|
|
51
|
-
--project "$GCP_PROJECT_ID" \
|
|
52
|
-
--description "CNPG Operands SA" \
|
|
53
|
-
--display-name "CNPG Operands SA" &> $DEVNULL
|
|
54
|
-
warnOnError $? "the GCP SA `color y $CNPG_SVC_ACCT` may already exist"
|
|
55
|
-
waitForSA "$CNPG_SVC_ACCT" "$GCP_PROJECT_ID" 60
|
|
56
|
-
|
|
57
56
|
CNPG_OPS_ROLE="cnpg.operands"
|
|
58
57
|
printf "\nCreating the `color g $CNPG_OPS_ROLE` IAM role\n"
|
|
59
58
|
## Attach roles
|
|
@@ -136,7 +135,6 @@ driver: pd.csi.storage.gke.io
|
|
|
136
135
|
deletionPolicy: Retain
|
|
137
136
|
EOSNAPSC
|
|
138
137
|
|
|
139
|
-
sleep 2 # hold up processing for a moment to allow IAM mods to propagate
|
|
140
138
|
}
|
|
141
139
|
|
|
142
140
|
# this function is a little convoluted in the way it attempts to install the
|
|
@@ -184,7 +182,7 @@ CNPG_FETCH_CLEANUP_CRONJOB_YAML
|
|
|
184
182
|
--project="$GCP_PROJECT_ID" \
|
|
185
183
|
--description="Used for cronjob cleanup with Workload Identity" \
|
|
186
184
|
--display-name="CNPG Cleanup SA" &> $DEVNULL
|
|
187
|
-
|
|
185
|
+
warnOnError $? "the GCP SA $CNPG_CLEANUP_SA may already exist"
|
|
188
186
|
waitForSA "$CNPG_CLEANUP_SA" "$GCP_PROJECT_ID" 60
|
|
189
187
|
|
|
190
188
|
gcloud projects add-iam-policy-binding "$GCP_PROJECT_ID" \
|
|
@@ -11,6 +11,8 @@ function installEsoEnvironment() {
|
|
|
11
11
|
ESO_SA="eso-sa"
|
|
12
12
|
ESO_SA_EMAIL="$ESO_SA@$GCP_PROJECT_ID.iam.gserviceaccount.com"
|
|
13
13
|
|
|
14
|
+
createNamespaceIfNeeded $ESO_NAMESPACE
|
|
15
|
+
|
|
14
16
|
# Guard against accidental SA deletion on re-runs (see cnpg-operator for war story)
|
|
15
17
|
gcloud iam service-accounts describe $ESO_SA_EMAIL --project $GCP_PROJECT_ID &> $DEVNULL
|
|
16
18
|
if [ $? -eq 0 ];
|
|
@@ -50,8 +52,8 @@ SKIP_ESO_SA_CREATION
|
|
|
50
52
|
--member "serviceAccount:$GCP_PROJECT_ID.svc.id.goog[$ESO_NAMESPACE/external-secrets]" \
|
|
51
53
|
--role roles/iam.workloadIdentityUser $ESO_SA_EMAIL
|
|
52
54
|
warnOnError $? "gcloud workload identity binding may have failed"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
waitForSAIAM "$ESO_SA" "$GCP_PROJECT_ID" "roles/iam.workloadIdentityUser" \
|
|
56
|
+
"serviceAccount:$GCP_PROJECT_ID.svc.id.goog[$ESO_NAMESPACE/external-secrets]" 60
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
installEsoEnvironment
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# CRD Upgrades — kube-prometheus-stack
|
|
2
|
+
|
|
3
|
+
## Why this exists
|
|
4
|
+
|
|
5
|
+
Helm can install CustomResourceDefinitions (CRDs) but will not upgrade them
|
|
6
|
+
when you bump chart versions. This chart ships a CRD upgrade job that uses
|
|
7
|
+
server-side apply (SSA) to reconcile CRDs to the versions bundled with the
|
|
8
|
+
chart.
|
|
9
|
+
|
|
10
|
+
## Default: disabled
|
|
11
|
+
|
|
12
|
+
The CRD upgrade job is disabled by default (`crds.enabled: false`) because it
|
|
13
|
+
requires cluster-admin level permissions to run. Specifically, it creates and
|
|
14
|
+
deletes ClusterRole and ClusterRoleBinding resources as pre-upgrade Helm hooks,
|
|
15
|
+
which requires `container.clusterRoles.delete` in GCP IAM. Only enable it when
|
|
16
|
+
explicitly bumping the chart version, and only a devops engineer should run
|
|
17
|
+
`helmup prom-operator` when it is enabled.
|
|
18
|
+
|
|
19
|
+
## Settings
|
|
20
|
+
|
|
21
|
+
**`crds.enabled`** — controls whether the chart manages CRDs at all. When
|
|
22
|
+
`false`, the CRD upgrade job and its associated hook resources are not rendered.
|
|
23
|
+
Default: `false`.
|
|
24
|
+
|
|
25
|
+
**`crds.upgradeJob.enabled`** — controls whether the SSA upgrade job hook runs.
|
|
26
|
+
Must be `true` for CRDs to be reconciled on upgrade. Default: `false`.
|
|
27
|
+
|
|
28
|
+
**`crds.upgradeJob.forceConflicts`** — adds `--force-conflicts` to the SSA
|
|
29
|
+
apply, overriding fields owned by another field manager. Use only for a one-time
|
|
30
|
+
conflict resolution. Revert to `false` immediately after. Default: `false`.
|
|
31
|
+
|
|
32
|
+
## Permissions requirement
|
|
33
|
+
|
|
34
|
+
The upgrade job runs as a pair of Helm pre-upgrade hooks that create a
|
|
35
|
+
ClusterRole and ClusterRoleBinding. Helm's `before-hook-creation` delete policy
|
|
36
|
+
causes it to attempt deletion of these resources before creating them — even if
|
|
37
|
+
they don't yet exist in the cluster. The GCP IAM permission check fires before
|
|
38
|
+
the existence check, so a 403 is returned for users without
|
|
39
|
+
`container.clusterRoles.delete` regardless of whether the resources are present.
|
|
40
|
+
|
|
41
|
+
The `admission-webhooks/job-patch` hooks have the same requirement and are
|
|
42
|
+
always present, making `helmup prom-operator` a full-devops-only operation
|
|
43
|
+
regardless of the CRD upgrade setting.
|
|
44
|
+
|
|
45
|
+
## Hook lifecycle
|
|
46
|
+
|
|
47
|
+
The ClusterRole and ClusterRoleBinding created by the upgrade job are ephemeral.
|
|
48
|
+
They are deleted automatically after the job succeeds via the
|
|
49
|
+
`hook-delete-policy: hook-succeeded` annotation. You will not see them in
|
|
50
|
+
`kubectl get clusterrole` after a successful run.
|
|
51
|
+
|
|
52
|
+
## Switching from enabled to disabled
|
|
53
|
+
|
|
54
|
+
If `crds.enabled` was previously `true` and a successful upgrade ran, the CRD
|
|
55
|
+
hook resources are recorded in the Helm release manifest even though they no
|
|
56
|
+
longer exist in the cluster. Switching to `false` and running `helmup` will
|
|
57
|
+
cause Helm to attempt to delete those orphaned hook resources from the previous
|
|
58
|
+
release — again requiring cluster-admin permissions for that one transition run.
|
|
59
|
+
|
|
60
|
+
To verify what hooks are currently stored in the release:
|
|
61
|
+
```bash
|
|
62
|
+
helm get hooks prometheus-stack -n prometheus | grep ^"# Source"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
After a devops engineer runs `helmup prom-operator` with `crds.enabled: false`,
|
|
66
|
+
the hooks will be cleared from the release manifest and subsequent runs will not
|
|
67
|
+
require cluster-admin permissions (beyond the always-present admission-webhook
|
|
68
|
+
hooks).
|
|
69
|
+
|
|
70
|
+
## Runbook — bumping the chart version
|
|
71
|
+
|
|
72
|
+
1. **Preflight** — confirm current CRD state:
|
|
73
|
+
```bash
|
|
74
|
+
kubectl get crd | grep monitoring.coreos.com
|
|
75
|
+
kubectl apply --server-side --dry-run=server -f <crd-dir/>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
2. **Enable** — set both flags in `prometheus-stack.yaml.ovh`:
|
|
79
|
+
```yaml
|
|
80
|
+
crds:
|
|
81
|
+
enabled: true
|
|
82
|
+
upgradeJob:
|
|
83
|
+
enabled: true
|
|
84
|
+
forceConflicts: false
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
3. **Run** — as a devops engineer:
|
|
88
|
+
```bash
|
|
89
|
+
helmup prom-operator
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
4. **If the job fails with field-manager conflicts:**
|
|
93
|
+
- Confirm no other source manages these CRDs
|
|
94
|
+
- Temporarily set `forceConflicts: true` for this run only
|
|
95
|
+
- Revert to `false` immediately after
|
|
96
|
+
|
|
97
|
+
5. **Post-check:**
|
|
98
|
+
```bash
|
|
99
|
+
kubectl get crd | grep monitoring.coreos.com
|
|
100
|
+
# verify CRDs show Established: True
|
|
101
|
+
# verify operator pod is healthy
|
|
102
|
+
# verify alerts and rules are normal
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
6. **Disable** — revert both flags to `false` before committing.
|
|
@@ -17,11 +17,14 @@ function installStackdriverExporterEnvironment() {
|
|
|
17
17
|
|
|
18
18
|
createNamespaceIfNeeded $SDEXP_NS
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
# used to remove this to avoid weird behavior when attempting to reboot
|
|
21
|
+
# the service with udated configs - no longer doing this (for now)
|
|
22
|
+
#
|
|
23
|
+
# guarantee any previous remnants of the exporter are gone
|
|
24
|
+
# printf "\n*** Removing the previous $SDEXP_SA installation...\n"
|
|
25
|
+
# helm uninstall -n $SDEXP_NS "stackdriver-exporter" &> $DEVNULL
|
|
26
|
+
# kubectl delete serviceaccounts -n $SDEXP_NS $SDEXP_SA &> $DEVNULL
|
|
27
|
+
# printf "*** Deleting the gcloud SA $SDEXP_EM\n"
|
|
25
28
|
# gcloud --quiet iam service-accounts delete $SDEXP_EM \
|
|
26
29
|
# --project $GCP_PROJECT_ID &> $DEVNULL
|
|
27
30
|
|
|
@@ -58,18 +61,19 @@ function createGrafanaOAuthSecret() {
|
|
|
58
61
|
printf "\nCreating placeholder `color g grafana-google-oauth` secret in prometheus namespace\n"
|
|
59
62
|
kubectl create secret generic grafana-google-oauth \
|
|
60
63
|
--namespace prometheus \
|
|
61
|
-
--from-literal=GF_AUTH_GOOGLE_CLIENT_ID='' \
|
|
62
|
-
--from-literal=GF_AUTH_GOOGLE_CLIENT_SECRET=''
|
|
64
|
+
--from-literal=GF_AUTH_GOOGLE_CLIENT_ID='bootstrap id' \
|
|
65
|
+
--from-literal=GF_AUTH_GOOGLE_CLIENT_SECRET='boostrap secret'
|
|
63
66
|
warnOnError $? "Failed to create grafana-google-oauth placeholder secret"
|
|
64
67
|
else
|
|
65
68
|
printf "\n`color g grafana-google-oauth` secret already exists - skipping\n"
|
|
66
69
|
fi
|
|
67
70
|
|
|
68
|
-
# Wire up the ExternalSecret
|
|
69
|
-
#
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
# Wire up the ExternalSecret only if ESO is enabled for this cluster.
|
|
72
|
+
# Set ESO_ENABLED=true in overwhelm.yaml to activate (default: false).
|
|
73
|
+
if [ "${ESO_ENABLED}" = "true" ];
|
|
74
|
+
then
|
|
75
|
+
showInstalling "Grafana Google OAuth ExternalSecret"
|
|
76
|
+
cat<<GRAFANA_OAUTH_SECRET | kubectl apply -f -
|
|
73
77
|
apiVersion: external-secrets.io/v1
|
|
74
78
|
kind: ExternalSecret
|
|
75
79
|
metadata:
|
|
@@ -86,11 +90,15 @@ spec:
|
|
|
86
90
|
data:
|
|
87
91
|
- secretKey: GF_AUTH_GOOGLE_CLIENT_ID
|
|
88
92
|
remoteRef:
|
|
89
|
-
key:
|
|
93
|
+
key: GF_AUTH_GOOGLE_CLIENT_ID
|
|
90
94
|
- secretKey: GF_AUTH_GOOGLE_CLIENT_SECRET
|
|
91
95
|
remoteRef:
|
|
92
|
-
key:
|
|
96
|
+
key: GF_AUTH_GOOGLE_CLIENT_SECRET
|
|
93
97
|
GRAFANA_OAUTH_SECRET
|
|
98
|
+
else
|
|
99
|
+
printf "\n`color y '***WARNING:'` ESO_ENABLED is not set - skipping Grafana OAuth ExternalSecret\n"
|
|
100
|
+
printf " Set ESO_ENABLED=true in overwhelm.yaml and re-run helmup prom-operator to wire up the OAuth secret.\n\n"
|
|
101
|
+
fi
|
|
94
102
|
}
|
|
95
103
|
|
|
96
104
|
installStackdriverExporterEnvironment
|
|
@@ -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="83.7.0"
|
|
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
|
|
@@ -1,50 +1,13 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Apparently you must have devops access in order to deploy the prometheus
|
|
3
|
+
# operator since it attempts to create cluster roles during deployment.
|
|
4
|
+
#
|
|
1
5
|
crds:
|
|
2
|
-
#
|
|
3
|
-
# CRD MANAGEMENT — kube-prometheus-stack
|
|
4
|
-
#
|
|
5
|
-
# Why this exists
|
|
6
|
-
# Helm can install CustomResourceDefinitions (CRDs) but will not upgrade them
|
|
7
|
-
# when you bump chart versions. This chart ships a CRD upgrade job that uses
|
|
8
|
-
# server-side apply (SSA) to reconcile CRDs to the versions bundled with the
|
|
9
|
-
# chart.
|
|
10
|
-
#
|
|
11
|
-
# Safe operating model
|
|
12
|
-
# ✅ Leave the upgrade job ENABLED if this chart is the sole owner of the
|
|
13
|
-
# Prometheus Operator CRDs.
|
|
14
|
-
# ⚠️ Keep "forceConflicts" DISABLED unless you hit a specific field-manager
|
|
15
|
-
# conflict that you’ve reviewed and want this chart to own.
|
|
16
|
-
# ❌ If another source manages CRDs (e.g., a separate CRD chart, GitOps
|
|
17
|
-
# bootstrap, platform repo), DISABLE the job to avoid ownership fights.
|
|
18
|
-
#
|
|
19
|
-
# Settings
|
|
20
|
-
# enabled: runs the SSA job on chart upgrade to reconcile CRDs.
|
|
21
|
-
# Recommended: true if chart owns the CRDs; false if
|
|
22
|
-
# another tool owns them.
|
|
23
|
-
#
|
|
24
|
-
# forceConflicts: adds `--force-conflicts` to SSA, overriding fields owned by
|
|
25
|
-
# another controller. Use only for a one-time conflict
|
|
26
|
-
# resolution. Can overwrite annotations and mask real
|
|
27
|
-
# incompatibilities if left true.
|
|
28
|
-
#
|
|
29
|
-
# Recommended defaults
|
|
30
|
-
# enabled: true
|
|
31
|
-
# forceConflicts: false
|
|
32
|
-
#
|
|
33
|
-
# Mini-runbook
|
|
34
|
-
# 1) Preflight:
|
|
35
|
-
# kubectl get crd | grep monitoring.coreos.com
|
|
36
|
-
# kubectl apply --server-side --dry-run=server -f <crd-dir/>
|
|
37
|
-
# 2) Upgrade with enabled: true, forceConflicts: false.
|
|
38
|
-
# 3) If job fails with conflicts:
|
|
39
|
-
# - confirm no other source manages these CRDs
|
|
40
|
-
# - temporarily set forceConflicts: true for this upgrade only
|
|
41
|
-
# - revert to false afterwards
|
|
42
|
-
# 4) Post-check: CRDs Established, operator healthy, alerts/rules normal
|
|
43
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
+
# See README-CRD-UPGRADES.md — disabled by default, full-devops only
|
|
44
7
|
enabled: true
|
|
45
8
|
upgradeJob:
|
|
46
9
|
enabled: true
|
|
47
|
-
forceConflicts: false
|
|
10
|
+
forceConflicts: false
|
|
48
11
|
|
|
49
12
|
kubeControllerManager:
|
|
50
13
|
enabled: false
|
|
@@ -186,7 +149,7 @@ grafana:
|
|
|
186
149
|
envFromSecret: grafana-google-oauth
|
|
187
150
|
env:
|
|
188
151
|
GF_SERVER_ROOT_URL: https://${PROJECT_NAME}-monitoring.${HOST}.com
|
|
189
|
-
GF_AUTH_GOOGLE_ENABLED: "
|
|
152
|
+
GF_AUTH_GOOGLE_ENABLED: "${ESO_ENABLED}"
|
|
190
153
|
GF_AUTH_GOOGLE_SCOPES: "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email"
|
|
191
154
|
GF_AUTH_GOOGLE_AUTH_URL: "https://accounts.google.com/o/oauth2/auth"
|
|
192
155
|
GF_AUTH_GOOGLE_TOKEN_URL: "https://accounts.google.com/o/oauth2/token"
|
|
@@ -284,7 +247,7 @@ alertmanager:
|
|
|
284
247
|
{{- end }}
|
|
285
248
|
|
|
286
249
|
{{- if .Labels.pod }}
|
|
287
|
-
*Pod Link:* *<https://console.cloud.google.com/kubernetes/pod/${GCE_REGION}/${CLUSTER_NAME}/{{ .Labels.namespace }}/{{ .Labels.pod }}?project=${
|
|
250
|
+
*Pod Link:* *<https://console.cloud.google.com/kubernetes/pod/${GCE_REGION}/${CLUSTER_NAME}/{{ .Labels.namespace }}/{{ .Labels.pod }}?project=${PROJECT_ID}|`{{ .Labels.pod }}`>*
|
|
288
251
|
{{- end }}
|
|
289
252
|
|
|
290
253
|
*Status:* `{{ .Status }}` | *Severity:* `{{ .Labels.severity }}` {{- if .Labels.container }} *Container:* `{{ .Labels.container }}` {{- end }}
|
|
@@ -86,6 +86,8 @@ function installVeleroEnvironment() {
|
|
|
86
86
|
--role roles/iam.workloadIdentityUser \
|
|
87
87
|
$SERVICE_ACCOUNT_EMAIL
|
|
88
88
|
warnOnError $? "gcloud workload identity binding may have failed"
|
|
89
|
+
waitForSAIAM "velero" "$GCP_PROJECT_ID" "roles/iam.workloadIdentityUser" \
|
|
90
|
+
"serviceAccount:$GCP_PROJECT_ID.svc.id.goog[velero/velero]" 60
|
|
89
91
|
|
|
90
92
|
printf "\nCreate and annotate the k8s SA\n"
|
|
91
93
|
kubectl create serviceaccount velero --namespace velero
|
package/src/helmup.sh
CHANGED
|
@@ -135,7 +135,7 @@ SYSTEM=(
|
|
|
135
135
|
cnpg-operator
|
|
136
136
|
preemptible-killer
|
|
137
137
|
zombie-killer
|
|
138
|
-
|
|
138
|
+
# reflector
|
|
139
139
|
reloader
|
|
140
140
|
velero
|
|
141
141
|
cnpg-db-psql-stack
|
|
@@ -218,8 +218,8 @@ WORKLOAD_ID
|
|
|
218
218
|
--member "serviceAccount:$GCP_PROJECT_ID.svc.id.goog[${NAMESPACE}/${BOUND_SVC_ACCT}]" \
|
|
219
219
|
--role roles/iam.workloadIdentityUser $SVC_ACCT_EMAIL
|
|
220
220
|
warnOnError $? "gcloud workload identity binding may have failed"
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
waitForSAIAM "$SVC_ACCT" "$GCP_PROJECT_ID" "roles/iam.workloadIdentityUser" \
|
|
222
|
+
"serviceAccount:$GCP_PROJECT_ID.svc.id.goog[${NAMESPACE}/${BOUND_SVC_ACCT}]" 60
|
|
223
223
|
|
|
224
224
|
# Verify the Kubernetes SA and create it if necessary
|
|
225
225
|
printf "\nChecking for the `color g $NAMESPACE/$SVC_ACCT` k8s SA\n"
|
|
@@ -821,6 +821,10 @@ export GCP_PROJECT_ID="$(getProjectIdFromK8sContext)"
|
|
|
821
821
|
# Same for the region, but not as prevalent
|
|
822
822
|
export GCE_REGION=`overwhelm -k GCE_REGION`
|
|
823
823
|
|
|
824
|
+
# TODO: Temporary, until ESO is widely deployed, then the ESO_ENABLED logic
|
|
825
|
+
# can be removed from the configs entirely.
|
|
826
|
+
export ESO_ENABLED=`overwhelm -k ESO_ENABLED`
|
|
827
|
+
|
|
824
828
|
doInstall "${SERVICES[@]}"
|
|
825
829
|
|
|
826
830
|
displayBanner
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{ include "leverege.ingressroute" . }}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "managed",
|
|
3
|
+
"name": "NPMRC",
|
|
4
|
+
"source": "leverege-registry:DEPRECATED_NPMRC",
|
|
5
|
+
"labels": {
|
|
6
|
+
"env-type": "string"
|
|
7
|
+
},
|
|
8
|
+
"annotations": {
|
|
9
|
+
"description": "a deprecated npmrc key",
|
|
10
|
+
"owner": "platform-team",
|
|
11
|
+
"source": "service-man"
|
|
12
|
+
}
|
|
13
|
+
}
|
package/src/DockerPy.mjs
DELETED
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DEPRECATED - DO NOT MODIFY
|
|
3
|
-
*/
|
|
4
|
-
import fs from 'node:fs'
|
|
5
|
-
import path from 'node:path'
|
|
6
|
-
import url from 'node:url'
|
|
7
|
-
|
|
8
|
-
import chalk from 'chalk'
|
|
9
|
-
import handlebars from 'handlebars'
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
errorExit,
|
|
13
|
-
getCleanTag,
|
|
14
|
-
getDateTimestamp,
|
|
15
|
-
getGcpAccount,
|
|
16
|
-
log,
|
|
17
|
-
proceed,
|
|
18
|
-
shellCmd,
|
|
19
|
-
warning
|
|
20
|
-
} from './Utils.mjs'
|
|
21
|
-
|
|
22
|
-
const DIRNAME = path.dirname( url.fileURLToPath( import.meta.url ) )
|
|
23
|
-
|
|
24
|
-
// The contents of these variables were initially located in files that live in
|
|
25
|
-
// the build-tools repository, but it just became simpler to pull the contents
|
|
26
|
-
// directly into these variables and forego the file loading.
|
|
27
|
-
//
|
|
28
|
-
const pluginTemplate = `
|
|
29
|
-
# Dockerfile.plugin - optionally extend the service Docker image
|
|
30
|
-
#
|
|
31
|
-
# This file may be used to run additional docker commands during the image
|
|
32
|
-
# build process without needing to maintain a local custom Dockerfile. For
|
|
33
|
-
# example, uncommenting the following docker RUN command will cause the
|
|
34
|
-
# curl and vim packages to be added to the deployed image thus making them
|
|
35
|
-
# available from the pod's command line on k8s:
|
|
36
|
-
#
|
|
37
|
-
# RUN apk update && apk add --no-cache curl vim
|
|
38
|
-
#
|
|
39
|
-
# Keep in mind that the Alpine Linux base image is used to keep image
|
|
40
|
-
# footprints small, so adding packages "just because" is not considered
|
|
41
|
-
# a best practice.
|
|
42
|
-
`
|
|
43
|
-
const dockerfileTemplate = fs.readFileSync( path.join( DIRNAME, './templates/dockerfileTemplatePy.hbs' ), 'utf8' )
|
|
44
|
-
const cloudBuildSteps = fs.readFileSync( path.join( DIRNAME, './templates/cloudBuildTargetGKE.hbs' ), 'utf8' )
|
|
45
|
-
let bashrcTemplate = fs.readFileSync( path.join( DIRNAME, './templates/bashrcTemplate.hbs' ), 'utf8' )
|
|
46
|
-
const bashrcPyTemplate = fs.readFileSync( path.join( DIRNAME, './templates/bashrcTemplatePy.hbs' ), 'utf8' )
|
|
47
|
-
bashrcTemplate = `${bashrcTemplate}\n${bashrcPyTemplate}`
|
|
48
|
-
|
|
49
|
-
const defaultSettings = {
|
|
50
|
-
date : getDateTimestamp(),
|
|
51
|
-
imageBase : 'python:3.12.8-slim-bookworm',
|
|
52
|
-
npmlogging : process.env.VERBOSE_NPM_LOGGING ? '--ddd' : '--silent',
|
|
53
|
-
npmVersion : 'npm@11',
|
|
54
|
-
pluginfile : '# NO PLUGIN',
|
|
55
|
-
preInstallPluginfile : '# NO PRE-INSTALL PLUGIN',
|
|
56
|
-
regvers : 'package.version',
|
|
57
|
-
runuser : 'python',
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// the previous build version is simply informational for the user
|
|
61
|
-
const previousFile = './docker/.previous'
|
|
62
|
-
|
|
63
|
-
function getPreviousBuild() {
|
|
64
|
-
return fs.existsSync( previousFile ) ? fs.readFileSync( previousFile ) : 'FIRST BUILD'
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const setPreviousBuild = ( version ) => {
|
|
68
|
-
fs.writeFileSync( previousFile, version )
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// the base image string to be in the form of an image name and a version
|
|
72
|
-
// for example:
|
|
73
|
-
//
|
|
74
|
-
// python:3.12.8-slim-bookworm becomes python-3.12.8
|
|
75
|
-
// nvcr.io/nvidia/cuda:12.3.1-base-ubuntu22.04 becomes cuda-12.3.1
|
|
76
|
-
//
|
|
77
|
-
// which will then be used to tag the image for information purposes only
|
|
78
|
-
const extractBaseImageTag = ( imageStr ) => {
|
|
79
|
-
const match = imageStr.match( /(\w+:\d+(?:\.\d+)*)(?=-)/ ) // eslint-disable-line security/detect-unsafe-regex
|
|
80
|
-
return match ? match[1].replace( ':', '-' ) : null
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function generateDockerfile( options ) {
|
|
84
|
-
|
|
85
|
-
const settings = { ...defaultSettings, ...options }
|
|
86
|
-
settings.image = settings.imageBase
|
|
87
|
-
// TODO: this is a janky way to pass the image tag back out
|
|
88
|
-
defaultSettings.buildTag = extractBaseImageTag( settings.image )
|
|
89
|
-
|
|
90
|
-
// Ensure the presence of the docker dir with plugin and bashrc - this code
|
|
91
|
-
// could probably be tightened up a little.
|
|
92
|
-
try {
|
|
93
|
-
fs.readdirSync( './docker' )
|
|
94
|
-
} catch ( err ) {
|
|
95
|
-
warning( 'creating the missing ./docker directory' )
|
|
96
|
-
fs.mkdirSync( './docker' ) // TODO: blindly assumes the mkdir succeeds
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const bashrcFile = './docker/bashrc'
|
|
100
|
-
let bf = bashrcTemplate
|
|
101
|
-
const bashrcFilePlugin = './docker/Bashrc.plugin'
|
|
102
|
-
if ( fs.existsSync( bashrcFilePlugin ) ) {
|
|
103
|
-
const bashrcAdditions = fs.readFileSync( bashrcFilePlugin )
|
|
104
|
-
bf = `${bf}\n${bashrcAdditions}`
|
|
105
|
-
}
|
|
106
|
-
fs.writeFileSync( bashrcFile, bf )
|
|
107
|
-
|
|
108
|
-
const dockerfilePlugin = './docker/Dockerfile.plugin'
|
|
109
|
-
if ( !fs.existsSync( dockerfilePlugin ) ) {
|
|
110
|
-
warning( `creating the missing ${dockerfilePlugin} file in the docker directory` )
|
|
111
|
-
fs.writeFileSync( dockerfilePlugin, pluginTemplate )
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
try {
|
|
115
|
-
const build = fs.readdirSync( './build' )
|
|
116
|
-
if ( build.length === 0 ) {
|
|
117
|
-
errorExit( `***ERROR: the build dir is empty - ${chalk.yellow( 'npm run build' )}` )
|
|
118
|
-
}
|
|
119
|
-
} catch ( err ) {
|
|
120
|
-
errorExit( `***ERROR: expected the ./build directory to exist - ${chalk.yellow( 'npm run build' )}` )
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// At this point the plugin template better exist.
|
|
124
|
-
if ( fs.existsSync( dockerfilePlugin ) ) {
|
|
125
|
-
settings.pluginfile = fs.readFileSync( dockerfilePlugin )
|
|
126
|
-
} else {
|
|
127
|
-
errorExit( `***ERROR: something went wrong with ${dockerfilePlugin} plugin creation` )
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const dockerfilePrePlugin = './docker/DockerfilePreInstall.plugin'
|
|
131
|
-
if ( fs.existsSync( dockerfilePrePlugin ) ) {
|
|
132
|
-
settings.preInstallPluginfile = fs.readFileSync( dockerfilePrePlugin )
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const compiled = handlebars.compile( dockerfileTemplate, { noEscape : true } )
|
|
136
|
-
const replaced = compiled( settings )
|
|
137
|
-
// temporarily write the Dockerfile to the docker subdir - the container
|
|
138
|
-
// image builder will then move it over to the build subdir
|
|
139
|
-
const dockerfile = './docker/Dockerfile'
|
|
140
|
-
try {
|
|
141
|
-
fs.writeFileSync( dockerfile, replaced )
|
|
142
|
-
} catch ( err ) {
|
|
143
|
-
errorExit( `***ERROR: failed to write ${dockerfile}\n${err}` )
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const previousBuild = getPreviousBuild()
|
|
147
|
-
const dockerInfo = { dockerfile, previousBuild, ...settings }
|
|
148
|
-
delete dockerInfo.pluginfile
|
|
149
|
-
return dockerInfo
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function formCloudBuildBucketName( artifactProject ) {
|
|
153
|
-
return `gs://${artifactProject}_cloudbuild`
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
async function validateCloudBuildBucket( artifactProject ) {
|
|
157
|
-
const bucketName = formCloudBuildBucketName( artifactProject )
|
|
158
|
-
log( chalk.green.bold( `Verifying the build bucket exists => ${bucketName}\n` ) )
|
|
159
|
-
try {
|
|
160
|
-
await shellCmd( `gsutil ls -p ${artifactProject} -b ${bucketName}` )
|
|
161
|
-
} catch ( error ) {
|
|
162
|
-
errorExit( `***Error: from Docker.validateCloudBuildBucket\n\n${chalk.bold.yellow( error )}` )
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
async function buildContainerImage( {
|
|
167
|
-
artifactProject, artifactRegistry, containerName,
|
|
168
|
-
imageVersion, isNpmWorkspace, options
|
|
169
|
-
} ) {
|
|
170
|
-
const buildWorkspace = './build/workspace' // passed to Cloud Build
|
|
171
|
-
const imageName = `${artifactRegistry}/images/${containerName}`
|
|
172
|
-
|
|
173
|
-
// clean up any residual build cruft (should be clean though)
|
|
174
|
-
if ( fs.existsSync( buildWorkspace ) ) {
|
|
175
|
-
fs.renameSync( buildWorkspace, `${buildWorkspace}-junk` )
|
|
176
|
-
fs.rm( `${buildWorkspace}-junk`, { recursive : true }, ( err ) => {} ) // fire and forget
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// TODO: This is janky - I'll clean it up later
|
|
180
|
-
// NOTE: this is dealing with raw filesystem interactions, meaning there
|
|
181
|
-
// isn't a shell performing file globbing and what not
|
|
182
|
-
fs.renameSync( './build', './workspace' )
|
|
183
|
-
fs.mkdirSync( './build' )
|
|
184
|
-
fs.renameSync( './workspace', buildWorkspace )
|
|
185
|
-
fs.renameSync( './docker/Dockerfile', './build/Dockerfile' )
|
|
186
|
-
fs.renameSync( './docker/bashrc', './build/bashrc' )
|
|
187
|
-
// fs.cpSync( `${process.env.HOME}/.npmrc`, './build/.npmrc' )
|
|
188
|
-
// fs.cpSync( './package.json', `${buildWorkspace}/package.json` )
|
|
189
|
-
// fs.cpSync( './package-lock.json', `${buildWorkspace}/package-lock.json` )
|
|
190
|
-
|
|
191
|
-
setPreviousBuild( imageVersion ) // update docker/.previous file
|
|
192
|
-
|
|
193
|
-
const { fullName } = await getGcpAccount()
|
|
194
|
-
const imageBuilder = getCleanTag( fullName )
|
|
195
|
-
const imageArch = defaultSettings.buildTag
|
|
196
|
-
const imageInfoTag = `zzinfo_${imageArch}_${imageBuilder}_${defaultSettings.date}`
|
|
197
|
-
const gcloudProjectNumber = await shellCmd( `gcloud projects describe ${artifactProject} --format="value(projectNumber)"` )
|
|
198
|
-
|
|
199
|
-
// build the cloudbuild.yaml from the handlebars template
|
|
200
|
-
const cloudBuildFile = './build/cloudbuild.yaml'
|
|
201
|
-
const cbst = handlebars.compile( cloudBuildSteps, { noEscape : true } )
|
|
202
|
-
const cbs = cbst( { artifactProject, artifactRegistry, imageName, imageVersion, imageInfoTag, } )
|
|
203
|
-
fs.writeFileSync( cloudBuildFile, cbs )
|
|
204
|
-
|
|
205
|
-
// build up the cloud build command and options
|
|
206
|
-
const cloudBuildCmd = `gcloud builds submit --project ${artifactProject}`
|
|
207
|
-
const cloudBuildLogs = `--gcs-log-dir ${formCloudBuildBucketName( artifactProject )}/log`
|
|
208
|
-
const cloudBuildCfg = `--config=${cloudBuildFile}`
|
|
209
|
-
const cloudBuildSA = `--service-account=${gcloudProjectNumber}-compute@cloudbuild.gserviceaccount.com`
|
|
210
|
-
|
|
211
|
-
log( chalk.green.bold( `
|
|
212
|
-
*** Submitting Build as ${imageBuilder} ***
|
|
213
|
-
${cloudBuildCmd} \\
|
|
214
|
-
${cloudBuildLogs} \\
|
|
215
|
-
${cloudBuildCfg} \\
|
|
216
|
-
${cloudBuildSA}
|
|
217
|
-
` ) )
|
|
218
|
-
|
|
219
|
-
if ( process.env.DRY_RUN === '1' ) {
|
|
220
|
-
log( `Would have run => ${cloudBuildCmd} ${cloudBuildLogs} ${cloudBuildCfg} ./build` )
|
|
221
|
-
log( '\n***Exiting from DRY_RUN\n' )
|
|
222
|
-
process.exit( 0 )
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
await shellCmd( `${cloudBuildCmd} ${cloudBuildLogs} ${cloudBuildCfg} ./build`, { stdio : 'inherit' } )
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
async function repoCleanup() {
|
|
229
|
-
// remove garbage from the repo
|
|
230
|
-
const garbage = [
|
|
231
|
-
'build',
|
|
232
|
-
'dist',
|
|
233
|
-
'docker/.keep',
|
|
234
|
-
'docker/.npmrc',
|
|
235
|
-
'docker/.gitignore',
|
|
236
|
-
'docker/.previous',
|
|
237
|
-
'docker/Dockerfile',
|
|
238
|
-
'docker/workspace',
|
|
239
|
-
]
|
|
240
|
-
|
|
241
|
-
garbage.forEach( trash => fs.rmSync( trash, { recursive : true, force : true } ) )
|
|
242
|
-
|
|
243
|
-
fs.writeFileSync( 'docker/.gitignore', '.previous\nDockerfile' )
|
|
244
|
-
|
|
245
|
-
await shellCmd( 'git add -f docker' )
|
|
246
|
-
|
|
247
|
-
log( `
|
|
248
|
-
${chalk.yellow.bold( '***IMPORTANT***' )}
|
|
249
|
-
|
|
250
|
-
Please make the following .gitignore modifications:
|
|
251
|
-
change /dist to ${chalk.green.bold( '/build' )}
|
|
252
|
-
remove ${chalk.red.bold( '/docker' )}
|
|
253
|
-
` )
|
|
254
|
-
|
|
255
|
-
await proceed( 'Acknowledge the .gitignore mods were made?' )
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
export default {
|
|
259
|
-
getPreviousBuild,
|
|
260
|
-
setPreviousBuild,
|
|
261
|
-
generateDockerfile,
|
|
262
|
-
formCloudBuildBucketName,
|
|
263
|
-
validateCloudBuildBucket,
|
|
264
|
-
buildContainerImage,
|
|
265
|
-
repoCleanup,
|
|
266
|
-
}
|
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* DEPRECATED - DO NOT MODIFY
|
|
4
|
-
*/
|
|
5
|
-
import fs from 'node:fs'
|
|
6
|
-
|
|
7
|
-
import toml from 'toml'
|
|
8
|
-
import chalk from 'chalk'
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
debug, log, warning,
|
|
12
|
-
errorExit,
|
|
13
|
-
getGitBranchAndUpstream,
|
|
14
|
-
gitRepoIsDirty,
|
|
15
|
-
parseHelmChart,
|
|
16
|
-
proceed,
|
|
17
|
-
shellCmd } from './Utils.mjs'
|
|
18
|
-
import Docker from './DockerPy.mjs'
|
|
19
|
-
|
|
20
|
-
// refresh-npm-token does not throw so no need to try, but it emits in debug
|
|
21
|
-
// const refreshErr = await shellCmd( 'refresh-npm-token' )
|
|
22
|
-
// if ( refreshErr && !process.env.BUILD_TOOLS_DEBUG ) {
|
|
23
|
-
// log( refreshErr ) // most likely the token refreshed message
|
|
24
|
-
// }
|
|
25
|
-
|
|
26
|
-
warning( 'docker-to-registry-py has been DEPRECATED - use docker-to-registry' )
|
|
27
|
-
|
|
28
|
-
const options = {}
|
|
29
|
-
let imageVersion = null
|
|
30
|
-
// Expected to be invoked like docker-to-registry-py v1.2.3
|
|
31
|
-
for ( let n = 2; n < process.argv.length; n++ ) {
|
|
32
|
-
if ( process.argv[n].startsWith( '-' ) ) {
|
|
33
|
-
const str = process.argv[n].slice( 1 )
|
|
34
|
-
if ( str.indexOf( '=' ) > 0 ) {
|
|
35
|
-
const [ key, value ] = str.split( '=' )
|
|
36
|
-
options[key] = value
|
|
37
|
-
} else {
|
|
38
|
-
options[str] = true
|
|
39
|
-
}
|
|
40
|
-
} else {
|
|
41
|
-
imageVersion = process.argv[n]
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
if ( !imageVersion ) {
|
|
45
|
-
errorExit( '***Error: docker-to-registry-py requires an image version' )
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// may use guide-me or guideme to ask for update guidance
|
|
49
|
-
const giveGuidance = imageVersion.match( /^guide-*me$/ ) !== null
|
|
50
|
-
|
|
51
|
-
const willGitTag = imageVersion.match( /^v\d+\.\d+\.\d+$/ )
|
|
52
|
-
const tagInfo = willGitTag ?
|
|
53
|
-
chalk.yellow.bold( 'will be git tagged' ) :
|
|
54
|
-
chalk.red( 'BETA RELEASE WILL NOT BE GIT TAGGED' )
|
|
55
|
-
|
|
56
|
-
const repoIsDirty = await gitRepoIsDirty()
|
|
57
|
-
|
|
58
|
-
if ( willGitTag && repoIsDirty ) {
|
|
59
|
-
log( `
|
|
60
|
-
${chalk.red.bold( '***ERROR: attempting to tag a non-beta dirty git repository' )}
|
|
61
|
-
|
|
62
|
-
You are attempting to create a tagged release image for pushing to the
|
|
63
|
-
artifact registry, but there are locally modified files. This is not
|
|
64
|
-
allowed since the applied tag will not be relevant to the image version
|
|
65
|
-
due to the pending commits.
|
|
66
|
-
|
|
67
|
-
Available options for proceeding:
|
|
68
|
-
|
|
69
|
-
${chalk.green.bold( '1) cleanly commit all local mods assuming relevance' )}
|
|
70
|
-
${chalk.yellow( '2) eliminate unwanted local modifications' )}
|
|
71
|
-
${chalk.red( '3) stash anything that is irrelevant to this release' )}
|
|
72
|
-
|
|
73
|
-
` )
|
|
74
|
-
process.exit( 1 )
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if ( !fs.existsSync( './helm' ) ) {
|
|
78
|
-
log( `
|
|
79
|
-
${chalk.red.bold( '***Error: docker-to-registry-py requires a helm chart directory' )}
|
|
80
|
-
|
|
81
|
-
The docker-to-registry-py script expects to run in the root directory of a k8s
|
|
82
|
-
service, which requires a helm chart directory. It also expects a docker
|
|
83
|
-
directory but will build that if it does not exist.
|
|
84
|
-
|
|
85
|
-
` )
|
|
86
|
-
process.exit( 1 )
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const tomlStr = fs.readFileSync( './pyproject.toml' )
|
|
90
|
-
const pyproject = toml.parse( tomlStr )
|
|
91
|
-
const repoDescr = {
|
|
92
|
-
artifactProject : pyproject.leverege.project || 'leverege-registry',
|
|
93
|
-
containerName : pyproject.project.name,
|
|
94
|
-
packageVersion : `v${pyproject.project.version}`,
|
|
95
|
-
artifactRegistry : pyproject.leverege.registry || 'us-docker.pkg.dev/leverege-registry/stack',
|
|
96
|
-
registryFolder : `${pyproject.leverege['registry-folder']}/images/${pyproject.project.name}`,
|
|
97
|
-
imageBase : pyproject.leverege['image-base'],
|
|
98
|
-
helmChart : await parseHelmChart(),
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
debug( { repoDescr }, '<==The Repo Description' )
|
|
102
|
-
|
|
103
|
-
const dockerInfo = await Docker.generateDockerfile( {
|
|
104
|
-
regvers : repoDescr.packageVersion,
|
|
105
|
-
imageBase : repoDescr.imageBase,
|
|
106
|
-
} )
|
|
107
|
-
|
|
108
|
-
debug( { dockerInfo }, '<==The Docker Info' )
|
|
109
|
-
|
|
110
|
-
const {
|
|
111
|
-
artifactProject,
|
|
112
|
-
artifactRegistry,
|
|
113
|
-
packageVersion,
|
|
114
|
-
containerName,
|
|
115
|
-
helmChart,
|
|
116
|
-
} = repoDescr
|
|
117
|
-
|
|
118
|
-
if ( giveGuidance ) {
|
|
119
|
-
await Docker.validateCloudBuildBucket( artifactProject )
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const registryFolder = `${artifactRegistry}/images/${containerName}`
|
|
123
|
-
|
|
124
|
-
// Update the dependencies...
|
|
125
|
-
// log( chalk.green.bold( 'Updating dependencies and workspace...' ) )
|
|
126
|
-
// await shellCmd( 'npm install', { stdio : 'inherit' } )
|
|
127
|
-
|
|
128
|
-
if ( giveGuidance ) {
|
|
129
|
-
await Docker.repoCleanup() // adjusts gitignore files and removes cruft
|
|
130
|
-
log( `
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
${chalk.green.bold( '-------------------- Potential Helm Chart Mods --------------------' )}
|
|
135
|
-
|
|
136
|
-
Moving to the new artifact-registry for docker image storage may also require
|
|
137
|
-
helm chart changes depending on how old the current charts are. Charts that
|
|
138
|
-
contain helm/values.yaml files resembling this:
|
|
139
|
-
|
|
140
|
-
${chalk.yellow.bold( `serviceConfig:
|
|
141
|
-
VERSION: v1.2.3
|
|
142
|
-
PREEMPTIBLE: true` )}
|
|
143
|
-
|
|
144
|
-
are pre-ignition era charts and should be replaced with the latest helm chart
|
|
145
|
-
templates available from ignition. Either dive in or ask devops for a hand
|
|
146
|
-
when converting these legacy / deprecated charts.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
More recently updated helm charts that have already started the migration to
|
|
150
|
-
the new approach may already have registry entries like this:
|
|
151
|
-
|
|
152
|
-
${chalk.yellow.bold( `image:
|
|
153
|
-
registry: gcr.io/leverege-docker-images
|
|
154
|
-
tag: ""` )}
|
|
155
|
-
|
|
156
|
-
The above registry statement references the deprecated container registry and
|
|
157
|
-
must be adjusted accordingly to resemble:
|
|
158
|
-
|
|
159
|
-
${chalk.green.bold( 'registry: us-docker.pkg.dev/leverege-registry/<registry folder>/images' )}
|
|
160
|
-
|
|
161
|
-
There may also be a corresponding modification needed in the helm template
|
|
162
|
-
deployment.yaml file in the container image spec:
|
|
163
|
-
|
|
164
|
-
${chalk.green.bold( 'image: {{ .Values.image.registry }}/{{ .Chart.Name }}:{{ default .Chart.AppVersion .Values.image.tag }}' )}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
${chalk.magenta.bold( `Upgrading all service charts to the latest ignition template chart structure
|
|
168
|
-
is the preferred approach to making the transition to the artifact registry.` )}
|
|
169
|
-
|
|
170
|
-
` )
|
|
171
|
-
log( chalk.green.bold( '*** docker-to-registry-py guidance complete - ready for dockerization ***' ) )
|
|
172
|
-
process.exit( 0 )
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// do chart checks too
|
|
176
|
-
let chart
|
|
177
|
-
if ( options?.helmChecks !== 'false' ) {
|
|
178
|
-
chart = helmChart?.yaml['Chart.yaml']
|
|
179
|
-
const values = helmChart?.yaml['values.yaml']
|
|
180
|
-
const expectedChartRegistry = `${artifactRegistry}/images`
|
|
181
|
-
|
|
182
|
-
if ( expectedChartRegistry !== values.image?.registry ) {
|
|
183
|
-
log( `
|
|
184
|
-
${chalk.red.bold( '***Error: mismatched package.json registry and helm/values.yaml' )}
|
|
185
|
-
|
|
186
|
-
The registry specified in the package.json leverege stanza does not line up
|
|
187
|
-
with the image registry in helm/values.yaml. The naming convention expects
|
|
188
|
-
the '/images' suffix to be added to the package.json registry string and then
|
|
189
|
-
stored as the image.registry in the helm/values.yaml file.
|
|
190
|
-
|
|
191
|
-
Current settings:
|
|
192
|
-
package.json registry => ${chalk.yellow.bold( artifactRegistry )}
|
|
193
|
-
values.yaml registry => ${chalk.red.bold( values.image?.registry )}
|
|
194
|
-
|
|
195
|
-
Expected settings:
|
|
196
|
-
values.yaml registry => ${chalk.green.bold( expectedChartRegistry )}
|
|
197
|
-
` )
|
|
198
|
-
|
|
199
|
-
if ( !values.image ) {
|
|
200
|
-
log( `
|
|
201
|
-
${chalk.yellow.bold( '***DEPRECATED: legacy helm charts detected' )}
|
|
202
|
-
|
|
203
|
-
The helm chart structure appears to be based on the pre-ignition helm chart
|
|
204
|
-
layouts. The charts must be upgraded before docker-to-registry-py can complete
|
|
205
|
-
its job.
|
|
206
|
-
|
|
207
|
-
` )
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
process.exit( 1 )
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Give the summary and the user a chance to proceed or not
|
|
215
|
-
log( `
|
|
216
|
-
${chalk.green.bold( 'Build Information:' )}
|
|
217
|
-
Container: ${chalk.green.bold( containerName )}
|
|
218
|
-
Version: ${chalk.green.bold( imageVersion )} ${tagInfo}
|
|
219
|
-
Registry: ${chalk.green.bold( artifactRegistry )}
|
|
220
|
-
Image Folder: ${chalk.yellow.bold( registryFolder )}
|
|
221
|
-
|
|
222
|
-
${chalk.green.bold( 'Docker Information:' )}
|
|
223
|
-
NodeImage: ${chalk.green.bold( dockerInfo.imageBase || `node:${dockerInfo.nodeimage}` )}
|
|
224
|
-
Run User: ${chalk.green.bold( dockerInfo.runuser )}
|
|
225
|
-
Previous: ${chalk.yellow.bold( dockerInfo.previousBuild )}
|
|
226
|
-
DateStamp: ${chalk.green.bold( dockerInfo.date )}
|
|
227
|
-
` )
|
|
228
|
-
|
|
229
|
-
if ( imageVersion !== packageVersion ) {
|
|
230
|
-
log( `
|
|
231
|
-
${chalk.yellow.bold( '***WARNING: specified version does not match project version' )}
|
|
232
|
-
pyproject.toml => ${chalk.green.bold( packageVersion )}
|
|
233
|
-
specified => ${chalk.red.bold( imageVersion )}
|
|
234
|
-
` )
|
|
235
|
-
|
|
236
|
-
if ( willGitTag ) {
|
|
237
|
-
log( chalk.red.bold( '... and you are attempting to release so NOPE!\n' ) )
|
|
238
|
-
process.exit( 1 )
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const repoInfo = await getGitBranchAndUpstream()
|
|
243
|
-
|
|
244
|
-
if ( willGitTag && !repoInfo.upstream ) {
|
|
245
|
-
log( `
|
|
246
|
-
${chalk.red.bold( '***ERROR: the current branch must have an upstream' )}
|
|
247
|
-
|
|
248
|
-
You are attempting to create a tagged release image for pushing to the
|
|
249
|
-
container registry, but the current branch does not have an upstream to
|
|
250
|
-
push to. This is not allowed since the applied tag will be stranded here
|
|
251
|
-
in your local repository, which is what we are trying to avoid.
|
|
252
|
-
|
|
253
|
-
Available options for proceeding:` )
|
|
254
|
-
log( chalk.green.bold( `
|
|
255
|
-
1) set an upstream for this branch via:
|
|
256
|
-
git push --set-upstream origin ${repoInfo.branch}` ) )
|
|
257
|
-
log( chalk.yellow.bold( `
|
|
258
|
-
2) rebase, squash and merge onto a branch with an upstream` ) )
|
|
259
|
-
|
|
260
|
-
process.exit( 1 )
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if ( willGitTag ) {
|
|
264
|
-
if ( chart.appVersion !== imageVersion ) {
|
|
265
|
-
log( `
|
|
266
|
-
${chalk.yellow.bold( '***WARNING: the helm/Chart.yaml appVersion does not align with the build version' )}
|
|
267
|
-
chart appVersion => ${chalk.yellow.bold( chart.appVersion )}
|
|
268
|
-
build version => ${chalk.green.bold( imageVersion )}
|
|
269
|
-
` )
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
await proceed()
|
|
274
|
-
|
|
275
|
-
log( chalk.green.bold( 'Building the Docker Image...' ) )
|
|
276
|
-
await Docker.buildContainerImage( { ...repoDescr, options, imageVersion } )
|
|
277
|
-
log( chalk.blue.bold( 'Done Building the Docker Image...' ) )
|
|
278
|
-
|
|
279
|
-
if ( willGitTag ) {
|
|
280
|
-
const tagDescription = `docker_${imageVersion}`
|
|
281
|
-
const fullImageTag = repoDescr.isMonoRepo ? `${repoDescr.containerName}/${imageVersion}` : imageVersion
|
|
282
|
-
log( chalk.green.bold( `Tagging and Pushing ${fullImageTag}\n` ) )
|
|
283
|
-
await shellCmd( `git tag -f ${fullImageTag} -m ${tagDescription}` )
|
|
284
|
-
await shellCmd( 'git push --follow-tags' )
|
|
285
|
-
} else {
|
|
286
|
-
log( chalk.yellow.bold( '\n*** Skipped git tagging - beta release' ) )
|
|
287
|
-
}
|