@leverege/build-tools 2.94.0 → 2.95.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/.vscode/launch.json +14 -0
- package/package.json +5 -2
- package/src/Docker.mjs +3 -2
- package/src/Utils.mjs +53 -6
- package/src/artifacts/Utils.mjs +1 -1
- package/src/bash-funcs +1 -1
- package/src/git-tar.sh +1 -1
- package/src/helm-charts/emailer/values-local.yaml +1 -8
- package/src/helm-charts/prom-operator/helmup.plugin +1 -1
- package/src/helm-charts/traefik/helmup.plugin +1 -1
- package/src/helm-charts/velero/README.md +135 -0
- package/src/helm-charts/velero/helmup.bootstrap +1 -0
- package/src/helm-charts/velero/helmup.plugin +29 -2
- package/src/helm-charts/velero/values-local.yaml +1 -1
- package/src/helmup.sh +49 -41
- package/src/service-man/ServiceMan.mjs +75 -0
- package/src/service-man/config/CommandLine.mjs +199 -0
- package/src/service-man/config/Config.mjs +203 -0
- package/src/service-man/config/VersionCheck.mjs +79 -0
- package/src/service-man/config/iam-roles/leveregeBigQueryReader.json +10 -0
- package/src/service-man/config/iam-roles/leveregeBigQueryWriter.json +13 -0
- package/src/service-man/config/iam-roles/leveregeCloudFunctionsManager.json +12 -0
- package/src/service-man/config/iam-roles/leveregeFirebaseHosting.json +9 -0
- package/src/service-man/config/iam-roles/leveregeFirebaseMessaging.json +13 -0
- package/src/service-man/config/iam-roles/leveregeFirebaseUpdates.json +10 -0
- package/src/service-man/config/iam-roles/leveregeLogsViewer.json +8 -0
- package/src/service-man/config/iam-roles/leveregePubSub.json +13 -0
- package/src/service-man/config/iam-roles/leveregeSecretsViewer.json +9 -0
- package/src/service-man/config/iam-roles/leveregeStorageObjectAdmin.json +21 -0
- package/src/service-man/config/iam-roles/leveregeStorageObjectReader.json +13 -0
- package/src/service-man/config/iam-roles/leveregeSvcAcctActor.json +8 -0
- package/src/service-man/config/iam-roles/leveregeSvcAcctTokenCreator.json +8 -0
- package/src/service-man/config/index.mjs +7 -0
- package/src/service-man/config/secrets/authz-jwt.json +6 -0
- package/src/service-man/config/secrets/authz-postgres.json +7 -0
- package/src/service-man/config/secrets/db-timescale-dense.json +8 -0
- package/src/service-man/config/secrets/jwt-options.json +7 -0
- package/src/service-man/config/secrets/mail-gun-api-key.json +6 -0
- package/src/service-man/config/secrets/npmrc.json +6 -0
- package/src/service-man/config/secrets/oidc-client-secret.json +6 -0
- package/src/service-man/config/secrets/postgresql-password.json +8 -0
- package/src/service-man/config/secrets/session-secret.json +6 -0
- package/src/service-man/config/secrets/twilio.json +8 -0
- package/src/service-man/config/service-accounts/api-server.json +12 -0
- package/src/service-man/config/service-accounts/authz-server.json +5 -0
- package/src/service-man/config/service-accounts/db-curator.json +8 -0
- package/src/service-man/config/service-accounts/emailer.json +7 -0
- package/src/service-man/config/service-accounts/fota-server.json +6 -0
- package/src/service-man/config/service-accounts/geotile-server.json +8 -0
- package/src/service-man/config/service-accounts/imagine.json +6 -0
- package/src/service-man/config/service-accounts/message-processor.json +6 -0
- package/src/service-man/config/service-accounts/messenger.json +7 -0
- package/src/service-man/config/service-accounts/pubsub-pulse.json +6 -0
- package/src/service-man/config/service-accounts/push-notifier.json +8 -0
- package/src/service-man/config/service-accounts/reason.json +5 -0
- package/src/service-man/config/service-accounts/resource-server.json +9 -0
- package/src/service-man/config/service-accounts/rule-engine.json +7 -0
- package/src/service-man/config/service-accounts/scheduler.json +6 -0
- package/src/service-man/config/service-accounts/transponder.json +15 -0
- package/src/service-man/gcloud-k8s/RolesManager.mjs +41 -0
- package/src/service-man/gcloud-k8s/SecretsManager.mjs +182 -0
- package/src/service-man/gcloud-k8s/ServiceAccountManager.mjs +246 -0
- package/src/service-man/gcloud-k8s/VersionManager.mjs +114 -0
- package/src/service-man/gcloud-k8s/index.mjs +11 -0
- package/src/service-man/service-man.mjs +18 -0
- package/src/service-man/utils/index.mjs +83 -0
- package/leverege-build-tools-2.21.13.tgz +0 -0
- package/lib/server/build-tools.js +0 -138
- package/lib/server/firebaseDeploy.js +0 -195
- package/lib/server/firebaseServe.js +0 -116
- package/lib/server/getfbcfg.js +0 -25
- package/lib/server/getjson.js +0 -70
- package/lib/server/overwhelm.js +0 -447
- package/lib/server/push-my-chart.js +0 -18
- package/lib/server/refresh-npm-token.js +0 -135
- package/lib/server/tag-release.js +0 -681
- package/lib/server/unleash.js +0 -50
- package/lib/web/build-tools.js +0 -138
- package/lib/web/firebaseDeploy.js +0 -195
- package/lib/web/firebaseServe.js +0 -116
- package/lib/web/getfbcfg.js +0 -25
- package/lib/web/getjson.js +0 -70
- package/lib/web/overwhelm.js +0 -447
- package/lib/web/push-my-chart.js +0 -18
- package/lib/web/refresh-npm-token.js +0 -135
- package/lib/web/tag-release.js +0 -681
- package/lib/web/unleash.js +0 -50
package/.vscode/launch.json
CHANGED
|
@@ -17,6 +17,20 @@
|
|
|
17
17
|
"env": {
|
|
18
18
|
},
|
|
19
19
|
"outputCapture": "std"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"name": "service-man (launch)",
|
|
23
|
+
"type": "node",
|
|
24
|
+
"request": "launch",
|
|
25
|
+
"program": "${workspaceFolder}/src/service-man/service-man.mjs",
|
|
26
|
+
"args": ["--dump-configs","dbg-cfgs"],
|
|
27
|
+
"cwd": "/Users/john/Work/leverege/k8s/tst-builder",
|
|
28
|
+
"runtimeExecutable": "node",
|
|
29
|
+
"console": "integratedTerminal",
|
|
30
|
+
"skipFiles": ["<node_internals>/**"],
|
|
31
|
+
"env": {
|
|
32
|
+
"NODE_ENV": "development"
|
|
33
|
+
}
|
|
20
34
|
}
|
|
21
35
|
]
|
|
22
36
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leverege/build-tools",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.95.0",
|
|
4
4
|
"description": "A collection of build / support tools for Leverege developers",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"getjson": "src/getjson.mjs",
|
|
41
41
|
"git-tar": "src/git-tar.sh",
|
|
42
42
|
"helm-audit": "src/helm-audit.mjs",
|
|
43
|
+
"helmboot": "src/helmup.sh",
|
|
43
44
|
"helmcycle": "src/helmup.sh",
|
|
44
45
|
"helmdn": "src/helmdn.sh",
|
|
45
46
|
"helminit": "src/helmup.sh",
|
|
@@ -64,6 +65,7 @@
|
|
|
64
65
|
"refresh-npm-token": "src/refresh-npm-token.mjs",
|
|
65
66
|
"refresh-py-idx": "src/refresh-py-idx.sh",
|
|
66
67
|
"send-to-slack": "src/send-to-slack.mjs",
|
|
68
|
+
"service-man": "src/service-man/service-man.mjs",
|
|
67
69
|
"snapshot-cnpg": "src/snapshot-cnpg.mjs",
|
|
68
70
|
"tag-release": "src/tag-release.mjs",
|
|
69
71
|
"unleash": "src/unleash.mjs",
|
|
@@ -99,12 +101,13 @@
|
|
|
99
101
|
"simple-git": "^3.30.0",
|
|
100
102
|
"sloc": "^0.3.2",
|
|
101
103
|
"superstruct": "^2.0.2",
|
|
104
|
+
"tmp": "^0.2.5",
|
|
102
105
|
"toml": "^3.0.0",
|
|
103
106
|
"zx": "^8.8.5"
|
|
104
107
|
},
|
|
105
108
|
"devDependencies": {
|
|
106
109
|
"@leverege/eslint-config-leverege": "^5.1.1",
|
|
107
|
-
"chai": "^6.2.
|
|
110
|
+
"chai": "^6.2.2",
|
|
108
111
|
"mocha": "^11.7.5",
|
|
109
112
|
"npm": "^11.7.0"
|
|
110
113
|
}
|
package/src/Docker.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable security/detect-non-literal-fs-filename */
|
|
1
2
|
import fs from 'node:fs'
|
|
2
3
|
import path from 'node:path'
|
|
3
4
|
import url from 'node:url'
|
|
@@ -23,7 +24,7 @@ const DIRNAME = path.dirname( url.fileURLToPath( import.meta.url ) )
|
|
|
23
24
|
//
|
|
24
25
|
// See the latest docker images here => https://hub.docker.com/_/node
|
|
25
26
|
//
|
|
26
|
-
const dockerfileNodeVersion = process.env.DOCKER_BUILD_NODE_VERSION || '
|
|
27
|
+
const dockerfileNodeVersion = process.env.DOCKER_BUILD_NODE_VERSION || '24.12.0-alpine3.23'
|
|
27
28
|
const dockerfileNpmVersion = process.env.DOCKER_BUILD_NPM_VERSION || '11.6.0'
|
|
28
29
|
|
|
29
30
|
// The contents of these variables were initially located in files that live in
|
|
@@ -83,7 +84,7 @@ const generateDockerfile = async ( options ) => {
|
|
|
83
84
|
const gitReference = await shellCmd( 'git rev-parse --short HEAD' )
|
|
84
85
|
const settings = { gitReference, ...defaultSettings, ...options }
|
|
85
86
|
const dockerTemplate = options.isNodeJsProject ? 'dockerfileTemplateNodeJs' : 'dockerfileTemplatePython'
|
|
86
|
-
const dockerfileTemplate = fs.readFileSync( path.join( DIRNAME, `./templates/${dockerTemplate}.hbs` ), 'utf8' )
|
|
87
|
+
const dockerfileTemplate = fs.readFileSync( path.join( DIRNAME, `./templates/${dockerTemplate}.hbs` ), 'utf8' )
|
|
87
88
|
|
|
88
89
|
// defaults to the current "official" version of node unless it gets
|
|
89
90
|
// overridden in the leverege.nodeimg package.json stanza
|
package/src/Utils.mjs
CHANGED
|
@@ -39,11 +39,42 @@ export const warning = ( warningText ) => {
|
|
|
39
39
|
console.error( `\n${chalk.yellow.bold( fullWarning )}` )
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
export const errorExit = (
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
export const errorExit = ( arg, opts = {} ) => {
|
|
43
|
+
const exitCode =
|
|
44
|
+
typeof opts.exitCode === 'number' ? opts.exitCode :
|
|
45
|
+
( arg && typeof arg.exitCode === 'number' ) ? arg.exitCode : 1
|
|
46
|
+
|
|
47
|
+
// lets figure out what to print
|
|
48
|
+
let message
|
|
49
|
+
let details
|
|
50
|
+
|
|
51
|
+
if ( arg instanceof Error ) {
|
|
52
|
+
message = arg.message || 'Error'
|
|
53
|
+
details = arg.stack
|
|
54
|
+
} else if ( typeof arg === 'string' ) {
|
|
55
|
+
message = arg
|
|
56
|
+
} else if ( arg && typeof arg === 'object' ) {
|
|
57
|
+
message = 'Error'
|
|
58
|
+
details = JSON.stringify( arg, null, 2 )
|
|
59
|
+
} else if ( arg != null ) {
|
|
60
|
+
message = String( arg )
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.error( `\n${chalk.red.bold( message )}\n` )
|
|
64
|
+
if ( details && details !== message ) {
|
|
65
|
+
console.error( `${details}\n` )
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
process.exit( exitCode )
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Clears the screen and optionally homes the cursor.
|
|
73
|
+
*/
|
|
74
|
+
export const clear = ( home = true ) => {
|
|
75
|
+
log( `${home ? '\x1B[0f' : ''}\x1B[2J` )
|
|
46
76
|
}
|
|
77
|
+
|
|
47
78
|
/* eslint-enable no-console */
|
|
48
79
|
|
|
49
80
|
export const isHex = ( hex ) => {
|
|
@@ -54,6 +85,22 @@ export const isString = ( s ) => {
|
|
|
54
85
|
return typeof s === 'string' || s instanceof String
|
|
55
86
|
}
|
|
56
87
|
|
|
88
|
+
/**
|
|
89
|
+
* * Sets first char to Upper
|
|
90
|
+
* */
|
|
91
|
+
export const capitalize = ( norm ) => {
|
|
92
|
+
return norm.charAt( 0 ).toUpperCase() + norm.slice( 1 )
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* A simple mS sleep delay.
|
|
97
|
+
*/
|
|
98
|
+
export const sleep = ( ms = 0 ) => {
|
|
99
|
+
return new Promise( ( resolve ) => {
|
|
100
|
+
setTimeout( resolve, ms )
|
|
101
|
+
} )
|
|
102
|
+
}
|
|
103
|
+
|
|
57
104
|
/**
|
|
58
105
|
* General "yes" to proceed function.
|
|
59
106
|
*/
|
|
@@ -226,7 +273,7 @@ export const gitRepoIsDirty = async () => {
|
|
|
226
273
|
|
|
227
274
|
export const parseYamlFile = async ( filePath ) => {
|
|
228
275
|
try {
|
|
229
|
-
const fileContent = await fs.promises.readFile( filePath, 'utf8' )
|
|
276
|
+
const fileContent = await fs.promises.readFile( filePath, 'utf8' ) /* eslint-disable-line security/detect-non-literal-fs-filename */
|
|
230
277
|
return YAML.load( fileContent )
|
|
231
278
|
} catch ( error ) {
|
|
232
279
|
throw new Error( `Failed to parse YAML file ${filePath}: ${error.message}` )
|
|
@@ -538,7 +585,7 @@ export const getRepositoryDescr = async () => {
|
|
|
538
585
|
const helmChart = await parseHelmChart()
|
|
539
586
|
const rootPackageJson = await parseJsonFile( `${gitRoot}/package.json` )
|
|
540
587
|
const rootPyProjectToml = await parsePyProjectToml( `${gitRoot}/pyproject.toml` )
|
|
541
|
-
const isYarnProject = fs.existsSync( path.join( gitRoot, 'yarn.lock' ) ) || fs.existsSync( path.join( gitRoot, '.yarn' ) )
|
|
588
|
+
const isYarnProject = fs.existsSync( path.join( gitRoot, 'yarn.lock' ) ) || fs.existsSync( path.join( gitRoot, '.yarn' ) ) /* eslint-disable-line security/detect-non-literal-fs-filename */
|
|
542
589
|
|
|
543
590
|
return {
|
|
544
591
|
currentDir : process.cwd(),
|
package/src/artifacts/Utils.mjs
CHANGED
package/src/bash-funcs
CHANGED
package/src/git-tar.sh
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
image:
|
|
2
|
-
tag: #
|
|
2
|
+
tag: # v5.1.0
|
|
3
3
|
|
|
4
4
|
config:
|
|
5
5
|
LOG_CONFIG: '{"type":"pino","level":"warn"}'
|
|
6
|
-
|
|
7
|
-
API_UI_URL: "${PROJECT_ID}.web.app"
|
|
8
|
-
MAIL_GUN_DOMAIN: "${MAIL_GUN_DOMAIN}"
|
|
9
|
-
MAIL_GUN_DEFAULT_SENDER: "no-reply@${MAIL_GUN_DOMAIN}"
|
|
10
|
-
|
|
11
|
-
serviceMonitor:
|
|
12
|
-
enabled: true
|
|
@@ -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="80.
|
|
11
|
+
[ -z "$PROMETHEUS_STACK_CHART_VERSION" ] && PROMETHEUS_STACK_CHART_VERSION="80.11.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
|
|
@@ -17,7 +17,7 @@ TRAEFIK_NAMESPACE="traefik"
|
|
|
17
17
|
|
|
18
18
|
addHelmRepo traefik https://helm.traefik.io/traefik
|
|
19
19
|
|
|
20
|
-
[ -z "$TRAEFIK_CHART_VERSION" ] && TRAEFIK_CHART_VERSION="
|
|
20
|
+
[ -z "$TRAEFIK_CHART_VERSION" ] && TRAEFIK_CHART_VERSION="38"
|
|
21
21
|
helm upgrade --install traefik traefik/traefik \
|
|
22
22
|
--namespace $TRAEFIK_NAMESPACE --create-namespace \
|
|
23
23
|
--values traefik/values.yaml \
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Velero Deployment Notes
|
|
2
|
+
|
|
3
|
+
## Why this document exists
|
|
4
|
+
|
|
5
|
+
This document explains **why we had to change the Velero deployment process** and how we now safely manage **CRDs and Helm upgrades** without relying on Bitnami images or brittle Helm hook jobs.
|
|
6
|
+
|
|
7
|
+
These changes were driven by:
|
|
8
|
+
- Removal / breakage of Bitnami `kubectl` images referenced by the `vmware-tanzu/velero` Helm chart
|
|
9
|
+
- Broadcom’s changes to Bitnami image access and licensing
|
|
10
|
+
- The need for **deterministic, version-safe CRD management** on new and existing GKE clusters
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Background: What broke
|
|
15
|
+
|
|
16
|
+
The Velero Helm chart (`vmware-tanzu/velero` >= 11.x) includes a Helm **hook job** that attempts to install or upgrade CRDs using an initContainer based on:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
docker.io/bitnamilegacy/kubectl:<k8s-version>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
On Kubernetes 1.34+ clusters, this image **no longer exists**, causing installs to fail with errors such as:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
ErrImagePull: bitnamilegacy/kubectl:1.34 not found
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Attempts to substitute other kubectl images failed due to hard assumptions in the hook job (e.g. `/tmp/sh`, shell paths, filesystem layout).
|
|
29
|
+
|
|
30
|
+
Because this hook runs **before Helm considers the release successful**, it blocks both fresh installs and upgrades.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Why we disabled Helm-based CRD upgrades
|
|
35
|
+
|
|
36
|
+
Helm does not natively manage CRD upgrades well:
|
|
37
|
+
- CRDs in `crds/` directories are only installed once
|
|
38
|
+
- Schema upgrades require hooks or out-of-band logic
|
|
39
|
+
- Hook jobs are fragile and opaque when they fail
|
|
40
|
+
|
|
41
|
+
The Velero chart’s CRD hook job is effectively doing:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
velero install --crds-only | kubectl apply
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
…but wrapped in a container that is now broken due to image dependencies.
|
|
48
|
+
|
|
49
|
+
**Conclusion:** We disable the Helm CRD hook entirely and manage CRDs explicitly.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Our current approach (high-level)
|
|
54
|
+
|
|
55
|
+
1. **CRDs are managed explicitly**, outside Helm
|
|
56
|
+
2. **CRDs are generated from the exact Velero version being deployed**
|
|
57
|
+
3. Helm is used only for deploying/upgrading the Velero controller and configuration
|
|
58
|
+
4. Helm CRD hooks are disabled (`upgradeCRDs=false`)
|
|
59
|
+
|
|
60
|
+
This makes installs:
|
|
61
|
+
- Reproducible
|
|
62
|
+
- Debuggable
|
|
63
|
+
- Independent of third-party image publishing decisions
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Version alignment is critical
|
|
68
|
+
|
|
69
|
+
The command:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
velero install --crds-only --dry-run -o yaml
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**uses the local `velero` CLI version** to generate CRDs.
|
|
76
|
+
|
|
77
|
+
If the CLI version does not match the Velero server version deployed by Helm, you can:
|
|
78
|
+
- Apply incompatible CRD schemas
|
|
79
|
+
- Introduce validation failures on existing custom resources
|
|
80
|
+
|
|
81
|
+
### Our safeguard
|
|
82
|
+
|
|
83
|
+
`helmup.plugin` now:
|
|
84
|
+
- Reads the chart `appVersion` directly from Helm
|
|
85
|
+
- Verifies the local `velero` CLI version matches exactly
|
|
86
|
+
- Fails fast if versions are mismatched
|
|
87
|
+
|
|
88
|
+
This guarantees CRDs and controllers stay in lockstep.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## When CRDs are applied
|
|
93
|
+
|
|
94
|
+
CRDs are **not applied on every deploy**.
|
|
95
|
+
|
|
96
|
+
They are applied only when:
|
|
97
|
+
- Bootstrapping a new cluster
|
|
98
|
+
- Upgrading the Velero chart to a version with a new `appVersion`
|
|
99
|
+
|
|
100
|
+
This is controlled explicitly via:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
VELERO_APPLY_CRDS=1 helmup velero
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Normal configuration-only updates do **not** touch CRDs.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Summary of changes
|
|
111
|
+
|
|
112
|
+
| Area | Old behavior | New behavior |
|
|
113
|
+
|-----|-------------|-------------|
|
|
114
|
+
| CRD management | Helm hook job | Explicit, pinned CRD apply |
|
|
115
|
+
| kubectl dependency | Bitnami legacy image | None |
|
|
116
|
+
| Version safety | Implicit | Enforced |
|
|
117
|
+
| Failure mode | Late, opaque | Early, explicit |
|
|
118
|
+
| New cluster installs | Unreliable | Deterministic |
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Key takeaways
|
|
123
|
+
|
|
124
|
+
- **CRDs are part of the API, not application config** — they must be versioned and applied intentionally
|
|
125
|
+
- Helm hooks are not a reliable mechanism for critical cluster-scoped resources
|
|
126
|
+
- Pinning tool versions eliminates an entire class of upgrade failures
|
|
127
|
+
- This approach aligns with GitOps and production-safe Kubernetes practices
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## References
|
|
132
|
+
|
|
133
|
+
- Velero documentation: https://velero.io/docs/
|
|
134
|
+
- Velero Helm charts: https://github.com/vmware-tanzu/helm-charts
|
|
135
|
+
- Kubernetes CRD management: https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/
|
|
@@ -2,10 +2,36 @@
|
|
|
2
2
|
#
|
|
3
3
|
showInstalling "Velero Backup System"
|
|
4
4
|
|
|
5
|
+
# We use a specific version for the velero chart because we need to control
|
|
6
|
+
# the version of the locally installed velero client application.
|
|
7
|
+
#
|
|
8
|
+
VELERO_CHART_VERSION="11.3.1"
|
|
9
|
+
EXPECTED_CLIENT_VERSION="v1.17.1"
|
|
10
|
+
|
|
5
11
|
addHelmRepo vmware-tanzu https://vmware-tanzu.github.io/helm-charts
|
|
6
12
|
|
|
7
|
-
|
|
13
|
+
commandChecker velero # verify the velero client is installed
|
|
14
|
+
|
|
15
|
+
local client_version="$(velero version --client-only 2>/dev/null | awk -F': ' '/Version:/ {print $2; exit}')"
|
|
16
|
+
|
|
17
|
+
if [[ "$client_version" != "$EXPECTED_CLIENT_VERSION" ]]; then
|
|
18
|
+
cat<<WRONG_VELERO_APP_VERSION
|
|
19
|
+
|
|
20
|
+
ERROR: velero CLI version mismatch.
|
|
21
|
+
Expected: `color g "${EXPECTED_CLIENT_VERSION}"`
|
|
22
|
+
Found: `color r "${client_version}"`
|
|
23
|
+
|
|
24
|
+
Fix => `color y "install velero ${EXPECTED_CLIENT_VERSION}"`
|
|
25
|
+
WRONG_VELERO_APP_VERSION
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# If the CRDs need to be updated set VELERO_APPLY_CRDS!
|
|
8
30
|
#
|
|
31
|
+
if [[ "${VELERO_APPLY_CRDS:-0}" == "1" ]]; then
|
|
32
|
+
velero install --crds-only --dry-run -o yaml | kubectl apply -f - --validate=false
|
|
33
|
+
fi
|
|
34
|
+
|
|
9
35
|
# Build the bucket, region and SA email variables and use --set to mod the
|
|
10
36
|
# chart values as opposed to using an ${label} approach. This eliminates
|
|
11
37
|
# the need for an overwhelm generated values.yaml file which also contains
|
|
@@ -13,7 +39,7 @@ addHelmRepo vmware-tanzu https://vmware-tanzu.github.io/helm-charts
|
|
|
13
39
|
#
|
|
14
40
|
# Note: GCP_PROJECT_ID is passed down from helmup's environment.
|
|
15
41
|
#
|
|
16
|
-
GCE_BUCKET="$GCP_PROJECT_ID-velero-$VELERO_CHART_VERSION"
|
|
42
|
+
GCE_BUCKET="$GCP_PROJECT_ID-velero-${VELERO_CHART_VERSION%%.*}"
|
|
17
43
|
GCE_REGION=`overwhelm -k GCE_REGION`
|
|
18
44
|
GCE_SA_EMAIL="velero@${GCP_PROJECT_ID}.iam.gserviceaccount.com"
|
|
19
45
|
|
|
@@ -27,6 +53,7 @@ helm upgrade \
|
|
|
27
53
|
--namespace velero --create-namespace \
|
|
28
54
|
--values velero/values-local.yaml \
|
|
29
55
|
--version $VELERO_CHART_VERSION $HELM_WHAT \
|
|
56
|
+
--set upgradeCRDs=false \
|
|
30
57
|
--set configuration.backupStorageLocation[0].bucket="$GCE_BUCKET" \
|
|
31
58
|
--set configuration.backupStorageLocation[0].config.serviceAccount="$GCE_SA_EMAIL" \
|
|
32
59
|
--set configuration.volumeSnapshotLocation[0].config.snapshotLocation="$GCE_REGION"
|
|
@@ -137,7 +137,7 @@ nodeSelector: {}
|
|
|
137
137
|
# See => https://github.com/vmware-tanzu/velero-plugin-for-gcp
|
|
138
138
|
initContainers:
|
|
139
139
|
- name: velero-plugin-for-gcp
|
|
140
|
-
image: velero/velero-plugin-for-gcp:v1.
|
|
140
|
+
image: velero/velero-plugin-for-gcp:v1.13.1
|
|
141
141
|
volumeMounts:
|
|
142
142
|
- mountPath: /target
|
|
143
143
|
name: plugins
|
package/src/helmup.sh
CHANGED
|
@@ -447,35 +447,50 @@ BAD_SECRETS
|
|
|
447
447
|
fi
|
|
448
448
|
}
|
|
449
449
|
|
|
450
|
-
function
|
|
450
|
+
function findChartLocation() {
|
|
451
|
+
local service_name="$1"
|
|
452
|
+
local output
|
|
453
|
+
|
|
454
|
+
# check the compass for a chart location
|
|
455
|
+
if output="$(chart-compass --service "$service_name")";
|
|
456
|
+
then
|
|
457
|
+
# chart exists in local chart-compass.yaml
|
|
458
|
+
local chart_location="oci://$output"
|
|
459
|
+
else
|
|
460
|
+
# nope - fallback to using the legacy museum location
|
|
461
|
+
local chart_location="leverege/$service_name"
|
|
462
|
+
fi
|
|
463
|
+
|
|
464
|
+
echo "$chart_location"
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function getServiceAndChartInfo() {
|
|
451
468
|
SERVICE=
|
|
469
|
+
CHARTLOC=
|
|
452
470
|
CHARTVER=
|
|
453
471
|
FROM_VERSIONS=
|
|
472
|
+
|
|
473
|
+
local input="${1:-}"
|
|
474
|
+
local versions_file="./Versions.json"
|
|
475
|
+
|
|
454
476
|
# Allows chart version from command line via <deployment>/<x.y.z>
|
|
455
477
|
IFS="/" read SERVICE CHARTVER <<< "$1"
|
|
456
478
|
|
|
457
|
-
|
|
458
|
-
[ ! -z "$CHARTVER" ] && return # service and chart version from CLI
|
|
479
|
+
[[ -z "$SERVICE" ]] && return 1
|
|
459
480
|
|
|
460
|
-
|
|
461
|
-
LCL_CHART=`compgen -G $SERVICE/Chart-?*.?*.?*` &> $DEVNULL
|
|
462
|
-
if [ ! -z "$LCL_CHART" ];
|
|
463
|
-
then
|
|
464
|
-
CHARTVER=${LCL_CHART#*/Chart-}
|
|
465
|
-
cat<<DEPRECATED_CHART
|
|
466
|
-
$YELO_WARN The use of $SERVICE/$LCL_CHART to lock the chart version has
|
|
467
|
-
been deprecated. Create a top level `color g "Versions.json"` file instead:
|
|
481
|
+
CHARTLOC="$(findChartLocation $SERVICE)"
|
|
468
482
|
|
|
469
|
-
|
|
470
|
-
|
|
483
|
+
# Allows chart version as <deployment>/x.y.z>
|
|
484
|
+
if [[ -n "$CHARTVER" ]]; then
|
|
485
|
+
return 0 # service and chart version from CLI
|
|
471
486
|
fi
|
|
472
487
|
|
|
473
488
|
# check for the top level Versions.json file
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
489
|
+
if [[ -f "$versions_file" ]]; then
|
|
490
|
+
CHARTVER=$(jq -r --arg svc "$SERVICE" '.[$svc].chart // ""' "$versions_file")
|
|
491
|
+
if [[ -n "$CHARTVER" ]]; then
|
|
492
|
+
FROM_VERSIONS=1
|
|
493
|
+
fi
|
|
479
494
|
fi
|
|
480
495
|
}
|
|
481
496
|
|
|
@@ -516,23 +531,6 @@ $BANNER_TXT
|
|
|
516
531
|
HELMUP_BANNER
|
|
517
532
|
}
|
|
518
533
|
|
|
519
|
-
findChartLocation() {
|
|
520
|
-
local service_name="$1"
|
|
521
|
-
local output
|
|
522
|
-
|
|
523
|
-
# check the compass for a chart location
|
|
524
|
-
if output="$(chart-compass --service "$service_name")";
|
|
525
|
-
then
|
|
526
|
-
# chart exists in local chart-compass.yaml
|
|
527
|
-
local chart_location="oci://$output"
|
|
528
|
-
else
|
|
529
|
-
# nope - fallback to using the legacy museum location
|
|
530
|
-
local chart_location="leverege/$service_name"
|
|
531
|
-
fi
|
|
532
|
-
|
|
533
|
-
echo "$chart_location"
|
|
534
|
-
}
|
|
535
|
-
|
|
536
534
|
function doHelmup() {
|
|
537
535
|
local culprit="--set helmup.culprit=\"$(gcloud config get account)\""
|
|
538
536
|
local deployT="--set helmup.deployT=\"$(date -u +'%Y-%m-%dT%H:%M:%SZ')\""
|
|
@@ -551,8 +549,9 @@ function doHelmup() {
|
|
|
551
549
|
esac
|
|
552
550
|
}
|
|
553
551
|
|
|
554
|
-
# not a fan of global vars but bash forced me to - set by
|
|
552
|
+
# not a fan of global vars but bash forced me to - set by getServiceAndChartInfo
|
|
555
553
|
SERVICE=
|
|
554
|
+
CHARTLOC=
|
|
556
555
|
CHARTVER=
|
|
557
556
|
FROM_VERSIONS=
|
|
558
557
|
function doInstall() {
|
|
@@ -562,7 +561,7 @@ function doInstall() {
|
|
|
562
561
|
for PARAM in "$@";
|
|
563
562
|
do
|
|
564
563
|
# Allows chart version from command line via <deployment>/<x.y.z>
|
|
565
|
-
|
|
564
|
+
getServiceAndChartInfo "$PARAM"
|
|
566
565
|
#printf "SVC=[$SERVICE] CHT=[$CHARTVER]\n"
|
|
567
566
|
|
|
568
567
|
# slap additional helm syntax in there if the version is set
|
|
@@ -593,6 +592,17 @@ CATBACKUP
|
|
|
593
592
|
fi
|
|
594
593
|
fi
|
|
595
594
|
|
|
595
|
+
if [[ "$INVOKED_AS" == "helmboot" ]]; then
|
|
596
|
+
local BOOTSTRAP="$SERVICE/helmup.bootstrap"
|
|
597
|
+
cat<<REBOOTSTRAP
|
|
598
|
+
|
|
599
|
+
Rerunning service bootstrap script => `color g "$BOOTSTRAP"`
|
|
600
|
+
|
|
601
|
+
REBOOTSTRAP
|
|
602
|
+
[[ -x "$BOOTSTRAP" ]] && . "$BOOTSTRAP"
|
|
603
|
+
exit 0
|
|
604
|
+
fi
|
|
605
|
+
|
|
596
606
|
if [ "$INVOKED_AS" == "helmcycle" ];
|
|
597
607
|
then
|
|
598
608
|
helmdn $SERVICE
|
|
@@ -602,8 +612,6 @@ CATBACKUP
|
|
|
602
612
|
# during initial install then do so here
|
|
603
613
|
ifPluggedInOrBootstrapped $SERVICE && continue
|
|
604
614
|
|
|
605
|
-
local CHART_LOCATION="$(findChartLocation $SERVICE)"
|
|
606
|
-
|
|
607
615
|
case "$SERVICE" in
|
|
608
616
|
"platform")
|
|
609
617
|
doPlatormPreInstallChecks
|
|
@@ -647,7 +655,7 @@ CATBACKUP
|
|
|
647
655
|
transponder-*)
|
|
648
656
|
# transponders use the same chart but different deployment name
|
|
649
657
|
[ ! -d $SERVICE ] && bootstrapLocalSetup $SERVICE
|
|
650
|
-
HELMUP="helm upgrade --install $SERVICE
|
|
658
|
+
HELMUP="helm upgrade --install $SERVICE $CHARTLOC --values $SERVICE/values.yaml $CHARTVER $HELM_WHAT"
|
|
651
659
|
doHelmup "$HELMUP"
|
|
652
660
|
;;
|
|
653
661
|
|
|
@@ -692,7 +700,7 @@ NO_SERVICE_MAN
|
|
|
692
700
|
|
|
693
701
|
checkUpgradeNotes $SERVICE
|
|
694
702
|
|
|
695
|
-
HELMUP="helm upgrade --install $SERVICE $
|
|
703
|
+
HELMUP="helm upgrade --install $SERVICE $CHARTLOC -f $SERVICE/values.yaml $CHARTVER $HELM_WHAT"
|
|
696
704
|
|
|
697
705
|
doHelmup $HELMUP
|
|
698
706
|
SAVERR=$?
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import chalk from 'chalk'
|
|
2
|
+
|
|
3
|
+
import { log, } from '../Utils.mjs'
|
|
4
|
+
|
|
5
|
+
import { CommandLine, Config, } from './config/index.mjs'
|
|
6
|
+
import {
|
|
7
|
+
RolesManager,
|
|
8
|
+
SecretsManager,
|
|
9
|
+
ServiceAccountManager,
|
|
10
|
+
VersionManager,
|
|
11
|
+
} from './gcloud-k8s/index.mjs'
|
|
12
|
+
|
|
13
|
+
const showKubernetesConfig = ( svcCfg ) => {
|
|
14
|
+
log( chalk.green`
|
|
15
|
+
Current k8s / gcloud settings:
|
|
16
|
+
|
|
17
|
+
Project => `, chalk.yellow( svcCfg.getK8sProject() ), chalk.green`
|
|
18
|
+
Cluster => `, chalk.yellow( svcCfg.getK8sCluster() ), `
|
|
19
|
+
`
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const start = async ( options ) => {
|
|
24
|
+
|
|
25
|
+
const CLI = await new CommandLine( options ).init()
|
|
26
|
+
const serviceManCfg = await new Config( options ).init( CLI )
|
|
27
|
+
|
|
28
|
+
// also let user know which gke/gcp context is going to be used
|
|
29
|
+
showKubernetesConfig( serviceManCfg )
|
|
30
|
+
|
|
31
|
+
if ( CLI.args.dumpConfigs ) {
|
|
32
|
+
await serviceManCfg.dump( CLI.args.dumpConfigs )
|
|
33
|
+
process.exit( 0 )
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// create an unbound service account
|
|
37
|
+
if ( CLI.args.createSa ) {
|
|
38
|
+
await new ServiceAccountManager( serviceManCfg ).createServiceAccount( CLI.args.createSa, serviceManCfg.k8sProject )
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// check for a service account and create it if needed
|
|
42
|
+
if ( CLI.args.createService ) {
|
|
43
|
+
await new ServiceAccountManager( serviceManCfg ).createBoundServiceAccount( CLI.args.createService )
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// retrieve image and chart versions of all deployments on the cluster
|
|
47
|
+
if ( CLI.args.fetchVersions ) {
|
|
48
|
+
await new VersionManager( serviceManCfg ).start()
|
|
49
|
+
process.exit( 0 )
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// create or update the custom IAM roles on the project
|
|
53
|
+
if ( CLI.args.setupRoles ) {
|
|
54
|
+
await new RolesManager( serviceManCfg ).start()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// build the service accounts
|
|
58
|
+
if ( CLI.args.setupServices ) {
|
|
59
|
+
await new ServiceAccountManager( serviceManCfg ).start( CLI )
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// inject the randomly generated secrets
|
|
63
|
+
if ( CLI.args.setupSecrets ) {
|
|
64
|
+
await new SecretsManager( serviceManCfg ).start()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// synchronize a secret manager secret to its k8s secret counterpart
|
|
68
|
+
if ( CLI.args.syncSecrets ) {
|
|
69
|
+
await new SecretsManager( serviceManCfg ).synchronize()
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export default {
|
|
74
|
+
start
|
|
75
|
+
}
|