@leverege/build-tools 2.66.7 → 2.67.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/settings.json +5 -0
- package/package.json +10 -10
- package/src/Docker.mjs +19 -24
- package/src/DockerPy.mjs +41 -40
- package/src/Utils.mjs +31 -28
- package/src/bash-funcs +0 -1
- package/src/docker-to-registry-py.mjs +0 -4
- package/src/helm-charts/cnpg-operator/helmup.plugin +5 -15
- package/src/helm-charts/prom-operator/helmup.plugin +5 -1
- package/src/helm-charts/prom-operator/prometheus-stack.yaml.ovh +20 -7
- package/src/helm-charts/prom-operator/rules/cnpg-rules.yaml +1 -1
- package/src/helm-charts/prom-operator/rules/elasticsearch-rules.yaml +6 -6
- package/src/helm-charts/prom-operator/rules/gcp-rules.yaml +1 -2
- package/src/helm-charts/prom-operator/rules/kubestate-rules.yaml +4 -4
- package/src/helm-charts/prom-operator/rules/prometheus-rules.yaml +3 -3
- package/src/helm-charts/prom-operator/stackdriver-exporter-gpu-rules.yaml +1 -1
- package/src/helm-charts/redis/values-local.yaml +1 -1
- package/src/helm-charts/traefik/helmup.plugin +1 -1
- package/src/helm-charts/velero/helmup.plugin +1 -1
- package/src/helm-charts/velero/values-local.yaml +1 -1
- package/src/helmup.sh +20 -11
- package/src/modify-gcs-access.mjs +84 -0
- package/src/refresh-py-idx.sh +9 -0
- package/src/templates/cloudBuildSteps.hbs +26 -1
- package/src/templates/dockerfileTemplatePy.hbs +38 -34
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leverege/build-tools",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.67.0",
|
|
4
4
|
"description": "A collection of build / support tools for Leverege developers",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -66,14 +66,14 @@
|
|
|
66
66
|
"chalk": "^5.4.1",
|
|
67
67
|
"command-line-args": "^6.0.1",
|
|
68
68
|
"command-line-usage": "^7.0.3",
|
|
69
|
-
"commander": "^
|
|
69
|
+
"commander": "^14.0.0",
|
|
70
70
|
"deepmerge": "^4.3.1",
|
|
71
71
|
"enquirer": "^2.4.1",
|
|
72
|
-
"execa": "^9.
|
|
72
|
+
"execa": "^9.6.0",
|
|
73
73
|
"glob": "^11.0.2",
|
|
74
74
|
"handlebars": "^4.7.8",
|
|
75
|
-
"ignore": "^7.0.
|
|
76
|
-
"inquirer": "^12.6.
|
|
75
|
+
"ignore": "^7.0.5",
|
|
76
|
+
"inquirer": "^12.6.3",
|
|
77
77
|
"js-yaml": "^4.1.0",
|
|
78
78
|
"jsdoc": "^4.0.4",
|
|
79
79
|
"ms": "^2.1.3",
|
|
@@ -82,14 +82,14 @@
|
|
|
82
82
|
"p-limit": "^6.2.0",
|
|
83
83
|
"package-up": "^5.0.0",
|
|
84
84
|
"readline-sync": "^1.4.10",
|
|
85
|
-
"semver": "^7.7.
|
|
86
|
-
"shell-quote": "^1.8.
|
|
87
|
-
"simple-git": "^3.
|
|
85
|
+
"semver": "^7.7.2",
|
|
86
|
+
"shell-quote": "^1.8.3",
|
|
87
|
+
"simple-git": "^3.28.0",
|
|
88
88
|
"toml": "^3.0.0",
|
|
89
|
-
"zx": "^8.5.
|
|
89
|
+
"zx": "^8.5.5"
|
|
90
90
|
},
|
|
91
91
|
"devDependencies": {
|
|
92
92
|
"@leverege/eslint-config-leverege": "^5.0.1",
|
|
93
|
-
"npm": "^11.
|
|
93
|
+
"npm": "^11.4.1"
|
|
94
94
|
}
|
|
95
95
|
}
|
package/src/Docker.mjs
CHANGED
|
@@ -177,7 +177,6 @@ const buildContainerImage = async ( {
|
|
|
177
177
|
artifactProject, artifactRegistry, containerName,
|
|
178
178
|
imageVersion, isNpmWorkspace, options
|
|
179
179
|
} ) => {
|
|
180
|
-
const { useCloudBuildFile } = options || {}
|
|
181
180
|
const buildWorkspace = './build/workspace' // passed to Cloud Build
|
|
182
181
|
const imageName = `${artifactRegistry}/images/${containerName}`
|
|
183
182
|
|
|
@@ -213,41 +212,37 @@ const buildContainerImage = async ( {
|
|
|
213
212
|
|
|
214
213
|
setPreviousBuild( imageVersion ) // update docker/.previous file
|
|
215
214
|
|
|
216
|
-
const { fullName :
|
|
217
|
-
const
|
|
215
|
+
const { fullName : imageBuilder } = await getGcpAccount()
|
|
216
|
+
const imageInfoTag = `zzinfo_${defaultSettings.date}_${defaultSettings.nodeimage}_${imageBuilder}`
|
|
218
217
|
const gcloudProjectNumber = await shellCmd( `gcloud projects describe ${artifactProject} --format="value(projectNumber)"` )
|
|
218
|
+
|
|
219
|
+
// build the cloudbuild.yaml from the handlebars template
|
|
220
|
+
const cloudBuildFile = './build/cloudbuild.yaml'
|
|
221
|
+
const cbst = handlebars.compile( cloudBuildSteps, { noEscape : true } )
|
|
222
|
+
const cbs = cbst( { artifactProject, artifactRegistry, imageName, imageVersion, imageInfoTag, } )
|
|
223
|
+
fs.writeFileSync( cloudBuildFile, cbs )
|
|
224
|
+
|
|
225
|
+
// build up the cloud build command and options
|
|
226
|
+
const cloudBuildCmd = `gcloud builds submit --project ${artifactProject}`
|
|
227
|
+
const cloudBuildLogs = `--gcs-log-dir ${formCloudBuildBucketName( artifactProject )}/log`
|
|
228
|
+
const cloudBuildCfg = `--config=${cloudBuildFile}`
|
|
219
229
|
const cloudBuildSA = `--service-account=${gcloudProjectNumber}-compute@cloudbuild.gserviceaccount.com`
|
|
220
|
-
const gcloudLogs = `--gcs-log-dir ${formCloudBuildBucketName( artifactProject )}/log`
|
|
221
|
-
let gcloudTags = `--tag ${imageName}:${imageVersion}`
|
|
222
230
|
|
|
223
|
-
if ( useCloudBuildFile ) {
|
|
224
|
-
const cloudBuildFile = './docker/cloudbuild.yaml'
|
|
225
|
-
gcloudTags = `--config=${cloudBuildFile}`
|
|
226
|
-
const cbst = handlebars.compile( cloudBuildSteps, { noEscape : true } )
|
|
227
|
-
const cbs = cbst( { artifactProject, artifactRegistry, imageName, imageVersion } )
|
|
228
|
-
|
|
229
|
-
fs.writeFileSync( cloudBuildFile, cbs )
|
|
230
|
-
}
|
|
231
|
-
|
|
232
231
|
log( chalk.green.bold( `
|
|
233
|
-
*** Submitting Build as ${
|
|
234
|
-
${
|
|
235
|
-
${
|
|
236
|
-
${
|
|
232
|
+
*** Submitting Build as ${imageBuilder} ***
|
|
233
|
+
${cloudBuildCmd} \\
|
|
234
|
+
${cloudBuildLogs} \\
|
|
235
|
+
${cloudBuildCfg} \\
|
|
237
236
|
${cloudBuildSA}
|
|
238
237
|
` ) )
|
|
239
238
|
|
|
240
239
|
if ( process.env.DRY_RUN === '1' ) {
|
|
240
|
+
log( `Would have run => ${cloudBuildCmd} ${cloudBuildLogs} ${cloudBuildCfg} ./build` )
|
|
241
241
|
log( '\n***Exiting from DRY_RUN\n' )
|
|
242
242
|
process.exit( 0 )
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
await shellCmd( `${
|
|
246
|
-
|
|
247
|
-
const imageTag = `${defaultSettings.date}-${defaultSettings.nodeimage}-${builderName}`
|
|
248
|
-
await shellCmd(
|
|
249
|
-
`gcloud container images add-tag --quiet ${imageName}:${imageVersion} ${imageName}:${imageTag} ${imageName}:latest`
|
|
250
|
-
)
|
|
245
|
+
await shellCmd( `${cloudBuildCmd} ${cloudBuildLogs} ${cloudBuildCfg} ./build`, { stdio : 'inherit' } )
|
|
251
246
|
}
|
|
252
247
|
|
|
253
248
|
const repoCleanup = async () => {
|
package/src/DockerPy.mjs
CHANGED
|
@@ -43,10 +43,8 @@ const bashrcPyTemplate = fs.readFileSync( path.join( DIRNAME, './templates/bashr
|
|
|
43
43
|
bashrcTemplate = `${bashrcTemplate}\n${bashrcPyTemplate}`
|
|
44
44
|
|
|
45
45
|
const defaultSettings = {
|
|
46
|
-
apkadds : '',
|
|
47
46
|
date : getDateTimestamp(),
|
|
48
|
-
|
|
49
|
-
imageBase : 'node:jod-alpine',
|
|
47
|
+
imageBase : 'python:3.12.8-slim-bookworm',
|
|
50
48
|
npmlogging : process.env.VERBOSE_NPM_LOGGING ? '--ddd' : '--silent',
|
|
51
49
|
npmVersion : 'npm@11',
|
|
52
50
|
pluginfile : '# NO PLUGIN',
|
|
@@ -55,6 +53,7 @@ const defaultSettings = {
|
|
|
55
53
|
runuser : 'python',
|
|
56
54
|
}
|
|
57
55
|
|
|
56
|
+
// the previous build version is simply informational for the user
|
|
58
57
|
const previousFile = './docker/.previous'
|
|
59
58
|
|
|
60
59
|
function getPreviousBuild() {
|
|
@@ -65,16 +64,26 @@ const setPreviousBuild = ( version ) => {
|
|
|
65
64
|
fs.writeFileSync( previousFile, version )
|
|
66
65
|
}
|
|
67
66
|
|
|
67
|
+
// the base image string to be in the form of an image name and a version
|
|
68
|
+
// for example:
|
|
69
|
+
//
|
|
70
|
+
// python:3.12.8-slim-bookworm becomes python-3.12.8
|
|
71
|
+
// nvcr.io/nvidia/cuda:12.3.1-base-ubuntu22.04 becomes cuda-12.3.1
|
|
72
|
+
//
|
|
73
|
+
// which will then be used to tag the image for information purposes only
|
|
74
|
+
const extractBaseImageTag = ( imageStr ) => {
|
|
75
|
+
const match = imageStr.match( /(\w+:\d+(?:\.\d+)*)(?=-)/ ) // eslint-disable-line security/detect-unsafe-regex
|
|
76
|
+
return match ? match[1].replace( ':', '-' ) : null
|
|
77
|
+
}
|
|
78
|
+
|
|
68
79
|
function generateDockerfile( options ) {
|
|
69
80
|
|
|
70
81
|
const settings = { ...defaultSettings, ...options }
|
|
71
82
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
settings.image = settings.imageBase
|
|
77
|
-
}
|
|
83
|
+
settings.image = settings.imageBase
|
|
84
|
+
// TODO: this is a janky way to pass the image tag back out
|
|
85
|
+
defaultSettings.buildTag = extractBaseImageTag( settings.image )
|
|
86
|
+
|
|
78
87
|
// Ensure the presence of the docker dir with plugin and bashrc - this code
|
|
79
88
|
// could probably be tightened up a little.
|
|
80
89
|
try {
|
|
@@ -155,18 +164,13 @@ async function buildContainerImage( {
|
|
|
155
164
|
artifactProject, artifactRegistry, containerName,
|
|
156
165
|
imageVersion, isNpmWorkspace, options
|
|
157
166
|
} ) {
|
|
158
|
-
|
|
159
|
-
const useCloudBuildFile = true
|
|
160
167
|
const buildWorkspace = './build/workspace' // passed to Cloud Build
|
|
161
168
|
const imageName = `${artifactRegistry}/images/${containerName}`
|
|
162
169
|
|
|
170
|
+
// clean up any residual build cruft (should be clean though)
|
|
163
171
|
if ( fs.existsSync( buildWorkspace ) ) {
|
|
164
172
|
fs.renameSync( buildWorkspace, `${buildWorkspace}-junk` )
|
|
165
|
-
|
|
166
|
-
fs.rmSync( `${buildWorkspace}-junk`, { recursive : true } )
|
|
167
|
-
} catch ( err ) {
|
|
168
|
-
log( `***Error: from Docker.buildContainerImage\n\n${chalk.bold.yellow( err )}` )
|
|
169
|
-
}
|
|
173
|
+
fs.rm( `${buildWorkspace}-junk`, { recursive : true }, ( err ) => {} ) // fire and forget
|
|
170
174
|
}
|
|
171
175
|
|
|
172
176
|
// TODO: This is janky - I'll clean it up later
|
|
@@ -176,48 +180,45 @@ async function buildContainerImage( {
|
|
|
176
180
|
fs.mkdirSync( './build' )
|
|
177
181
|
fs.renameSync( './workspace', buildWorkspace )
|
|
178
182
|
fs.renameSync( './docker/Dockerfile', './build/Dockerfile' )
|
|
183
|
+
fs.renameSync( './docker/bashrc', './build/bashrc' )
|
|
179
184
|
// fs.cpSync( `${process.env.HOME}/.npmrc`, './build/.npmrc' )
|
|
180
|
-
fs.cpSync( './docker/bashrc', './build/bashrc' )
|
|
181
185
|
// fs.cpSync( './package.json', `${buildWorkspace}/package.json` )
|
|
182
186
|
// fs.cpSync( './package-lock.json', `${buildWorkspace}/package-lock.json` )
|
|
183
187
|
|
|
184
188
|
setPreviousBuild( imageVersion ) // update docker/.previous file
|
|
185
189
|
|
|
186
|
-
const { fullName :
|
|
187
|
-
const
|
|
190
|
+
const { fullName : imageBuilder } = await getGcpAccount()
|
|
191
|
+
const imageArch = defaultSettings.buildTag
|
|
192
|
+
const imageInfoTag = `zzinfo_${imageArch}_${imageBuilder}_${defaultSettings.date}`
|
|
188
193
|
const gcloudProjectNumber = await shellCmd( `gcloud projects describe ${artifactProject} --format="value(projectNumber)"` )
|
|
194
|
+
|
|
195
|
+
// build the cloudbuild.yaml from the handlebars template
|
|
196
|
+
const cloudBuildFile = './build/cloudbuild.yaml'
|
|
197
|
+
const cbst = handlebars.compile( cloudBuildSteps, { noEscape : true } )
|
|
198
|
+
const cbs = cbst( { artifactProject, artifactRegistry, imageName, imageVersion, imageInfoTag, } )
|
|
199
|
+
fs.writeFileSync( cloudBuildFile, cbs )
|
|
200
|
+
|
|
201
|
+
// build up the cloud build command and options
|
|
202
|
+
const cloudBuildCmd = `gcloud builds submit --project ${artifactProject}`
|
|
203
|
+
const cloudBuildLogs = `--gcs-log-dir ${formCloudBuildBucketName( artifactProject )}/log`
|
|
204
|
+
const cloudBuildCfg = `--config=${cloudBuildFile}`
|
|
189
205
|
const cloudBuildSA = `--service-account=${gcloudProjectNumber}-compute@cloudbuild.gserviceaccount.com`
|
|
190
|
-
const gcloudLogs = `--gcs-log-dir ${formCloudBuildBucketName( artifactProject )}/log`
|
|
191
|
-
let gcloudTags = `--tag ${imageName}:${imageVersion}`
|
|
192
|
-
|
|
193
|
-
if ( useCloudBuildFile ) {
|
|
194
|
-
const cloudBuildFile = './docker/cloudbuild.yaml'
|
|
195
|
-
gcloudTags = `--config=${cloudBuildFile}`
|
|
196
|
-
const cbst = handlebars.compile( cloudBuildSteps, { noEscape : true } )
|
|
197
|
-
const cbs = cbst( { artifactProject, artifactRegistry, imageName, imageVersion } )
|
|
198
|
-
|
|
199
|
-
fs.writeFileSync( cloudBuildFile, cbs )
|
|
200
|
-
}
|
|
201
206
|
|
|
202
207
|
log( chalk.green.bold( `
|
|
203
|
-
*** Submitting Build as ${
|
|
204
|
-
${
|
|
205
|
-
${
|
|
206
|
-
${
|
|
208
|
+
*** Submitting Build as ${imageBuilder} ***
|
|
209
|
+
${cloudBuildCmd} \\
|
|
210
|
+
${cloudBuildLogs} \\
|
|
211
|
+
${cloudBuildCfg} \\
|
|
207
212
|
${cloudBuildSA}
|
|
208
213
|
` ) )
|
|
209
214
|
|
|
210
215
|
if ( process.env.DRY_RUN === '1' ) {
|
|
216
|
+
log( `Would have run => ${cloudBuildCmd} ${cloudBuildLogs} ${cloudBuildCfg} ./build` )
|
|
211
217
|
log( '\n***Exiting from DRY_RUN\n' )
|
|
212
218
|
process.exit( 0 )
|
|
213
219
|
}
|
|
214
220
|
|
|
215
|
-
await shellCmd( `${
|
|
216
|
-
|
|
217
|
-
const imageTag = `${defaultSettings.date}-${defaultSettings.nodeimage}-${builderName}`
|
|
218
|
-
await shellCmd(
|
|
219
|
-
`gcloud container images add-tag --quiet ${imageName}:${imageVersion} ${imageName}:${imageTag} ${imageName}:latest`
|
|
220
|
-
)
|
|
221
|
+
await shellCmd( `${cloudBuildCmd} ${cloudBuildLogs} ${cloudBuildCfg} ./build`, { stdio : 'inherit' } )
|
|
221
222
|
}
|
|
222
223
|
|
|
223
224
|
async function repoCleanup() {
|
package/src/Utils.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import inquirer from 'inquirer'
|
|
|
9
9
|
import { glob } from 'glob'
|
|
10
10
|
import { packageUp } from 'package-up'
|
|
11
11
|
import { parse } from 'shell-quote'
|
|
12
|
+
import toml from 'toml'
|
|
12
13
|
import YAML from 'js-yaml'
|
|
13
14
|
|
|
14
15
|
// __dirname goes away with ESM so we take this approach instead and
|
|
@@ -127,12 +128,6 @@ export const getDirectories = ( { path : srcPath = process.cwd(), respectGitigno
|
|
|
127
128
|
.map( dirent => dirent.name )
|
|
128
129
|
}
|
|
129
130
|
|
|
130
|
-
export const xgetDirectories = ( srcPath = process.cwd() ) => {
|
|
131
|
-
return fs.readdirSync( srcPath, { withFileTypes : true } ) // eslint-disable-line security/detect-non-literal-fs-filename
|
|
132
|
-
.filter( dirent => dirent.isDirectory() )
|
|
133
|
-
.map( dirent => dirent.name )
|
|
134
|
-
}
|
|
135
|
-
|
|
136
131
|
// returns YYYMMDD-hhmm
|
|
137
132
|
export const getDateTimestamp = () => {
|
|
138
133
|
const fullDTS = new Date().toISOString().replace( /[-T:]/g, '' ).slice( 0, 14 )
|
|
@@ -170,7 +165,8 @@ export const getGcpSecret = async ( gcpProject, secretName, opts ) => {
|
|
|
170
165
|
}
|
|
171
166
|
}
|
|
172
167
|
|
|
173
|
-
|
|
168
|
+
// just a wrapper to run command with try/catch
|
|
169
|
+
const tryCommand = async ( cmd, debugLabel ) => {
|
|
174
170
|
try {
|
|
175
171
|
const result = await shellCmd( cmd )
|
|
176
172
|
debug( { result }, `<==${debugLabel}` )
|
|
@@ -181,16 +177,16 @@ const runGitCommand = async ( cmd, debugLabel ) => {
|
|
|
181
177
|
}
|
|
182
178
|
}
|
|
183
179
|
|
|
184
|
-
export const getGitBranch = async () =>
|
|
185
|
-
export const getGitDiff = async opts =>
|
|
186
|
-
export const getGitHooksPath = async () =>
|
|
187
|
-
export const setGitHooksPath = async hookPath =>
|
|
188
|
-
export const getGitRootDirectory = async () =>
|
|
180
|
+
export const getGitBranch = async () => tryCommand( 'git rev-parse --abbrev-ref HEAD', 'getGitCurrentBranch' )
|
|
181
|
+
export const getGitDiff = async opts => tryCommand( `git diff ${opts}`, 'getGitDiff' )
|
|
182
|
+
export const getGitHooksPath = async () => tryCommand( 'git config --get core.hooksPath', 'getGitHooksPath' )
|
|
183
|
+
export const setGitHooksPath = async hookPath => tryCommand( `git config core.hooksPath ${hookPath}`, 'setGitHooksPath' )
|
|
184
|
+
export const getGitRootDirectory = async () => tryCommand( 'git rev-parse --show-toplevel', 'getGitRootDirectory' )
|
|
189
185
|
export const getGitBranchAndUpstream = async () => {
|
|
190
186
|
const branch = await getGitBranch() ?? 'detached'
|
|
191
187
|
let upstream = 'no-upstream'
|
|
192
188
|
try {
|
|
193
|
-
upstream = await
|
|
189
|
+
upstream = await tryCommand( `git rev-parse --abbrev-ref ${branch}@{u}`, 'getGitUpstream' ) ?? 'no-upstream'
|
|
194
190
|
} catch ( error ) {}
|
|
195
191
|
return { branch, upstream }
|
|
196
192
|
}
|
|
@@ -201,6 +197,8 @@ export const gitRepoIsDirty = async () => {
|
|
|
201
197
|
}
|
|
202
198
|
|
|
203
199
|
export const parseJsonFile = async ( jsonFile ) => {
|
|
200
|
+
// TODO: rework this file exists logic / throw
|
|
201
|
+
if ( !fs.existsSync( jsonFile ) ) { return undefined } // eslint-disable-line security/detect-non-literal-fs-filename
|
|
204
202
|
/* eslint-disable security/detect-non-literal-fs-filename */
|
|
205
203
|
if ( fs.existsSync( jsonFile ) ) {
|
|
206
204
|
try {
|
|
@@ -222,7 +220,9 @@ const deprecated = ( opts ) => {
|
|
|
222
220
|
process.exit( 1 )
|
|
223
221
|
}
|
|
224
222
|
|
|
225
|
-
export const parsePackageJson = async ( packageFileName ) => {
|
|
223
|
+
export const parsePackageJson = async ( packageFileName = './package.json' ) => {
|
|
224
|
+
if ( !fs.existsSync( packageFileName ) ) { return undefined } // eslint-disable-line security/detect-non-literal-fs-filename
|
|
225
|
+
|
|
226
226
|
const packageJson = await parseJsonFile( packageFileName )
|
|
227
227
|
|
|
228
228
|
// deprecation checks are here to ease the pain of upgrading older repos
|
|
@@ -411,6 +411,13 @@ https://console.cloud.google.com/artifacts/browse/leverege-registry?project=leve
|
|
|
411
411
|
return { artifactRegistry, ...packageJson }
|
|
412
412
|
}
|
|
413
413
|
|
|
414
|
+
export const parsePyProjectToml = async ( packageFileName = './pyproject.toml' ) => {
|
|
415
|
+
if ( !fs.existsSync( packageFileName ) ) { return undefined } // eslint-disable-line security/detect-non-literal-fs-filename
|
|
416
|
+
const tomlStr = fs.readFileSync( packageFileName ) // eslint-disable-line security/detect-non-literal-fs-filename
|
|
417
|
+
const pyproject = toml.parse( tomlStr )
|
|
418
|
+
return pyproject
|
|
419
|
+
}
|
|
420
|
+
|
|
414
421
|
export const parseHelmChart = async ( helmroot = './helm' ) => {
|
|
415
422
|
/* eslint-disable security/detect-non-literal-fs-filename */
|
|
416
423
|
if ( !fs.existsSync( helmroot ) ) { return undefined }
|
|
@@ -453,12 +460,8 @@ export const getRepositoryDescr = async () => {
|
|
|
453
460
|
const subPkgs = await glob( `${gitRoot}/packages/**/package.json`, { ignore : '/**/node_modules/**' } )
|
|
454
461
|
const helmChart = await parseHelmChart()
|
|
455
462
|
const isMonoRepo = subPkgs?.length > 0
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
rootPackageJson = await parseJsonFile( `${gitRoot}/package.json` )
|
|
459
|
-
} catch ( error ) {
|
|
460
|
-
rootPackageJson = { isInvalid : true, error : 'invalid or non-existent root package.json file' }
|
|
461
|
-
}
|
|
463
|
+
const rootPackageJson = await parseJsonFile( `${gitRoot}/package.json` )
|
|
464
|
+
const rootPyProjectToml = await parsePyProjectToml( `${gitRoot}/pyproject.toml` )
|
|
462
465
|
const isNpmWorkspace = rootPackageJson?.workspaces?.length > 0
|
|
463
466
|
|
|
464
467
|
return {
|
|
@@ -473,6 +476,7 @@ export const getRepositoryDescr = async () => {
|
|
|
473
476
|
isMonoRepo,
|
|
474
477
|
isNpmWorkspace,
|
|
475
478
|
rootPackageJson,
|
|
479
|
+
rootPyProjectToml,
|
|
476
480
|
subPkgs,
|
|
477
481
|
}
|
|
478
482
|
}
|
|
@@ -484,6 +488,7 @@ export const analyzeRepository = async ( giveGuidance ) => {
|
|
|
484
488
|
helmChart,
|
|
485
489
|
isMonoRepo,
|
|
486
490
|
rootPackageJson,
|
|
491
|
+
rootPyProjectToml,
|
|
487
492
|
isNpmWorkspace,
|
|
488
493
|
} = await getRepositoryDescr()
|
|
489
494
|
|
|
@@ -527,15 +532,12 @@ export const analyzeRepository = async ( giveGuidance ) => {
|
|
|
527
532
|
await shellCmd( 'npm run build' )
|
|
528
533
|
}
|
|
529
534
|
|
|
530
|
-
const buildToolsVersion = closestPackageJson
|
|
531
|
-
if ( !buildToolsVersion ) {
|
|
532
|
-
errorExit( 'Error: missing devDependency for @leverege/build-tools in package.json' )
|
|
533
|
-
}
|
|
535
|
+
const buildToolsVersion = closestPackageJson?.devDependencies['@leverege/build-tools'] || closestPackageJson?.dependencies['@leverege/build-tools']
|
|
534
536
|
|
|
535
|
-
const containerName = closestPackageJson
|
|
536
|
-
const artifactProject = closestPackageJson
|
|
537
|
-
const artifactRegistry = closestPackageJson
|
|
538
|
-
const packageVersion = `v${closestPackageJson
|
|
537
|
+
const containerName = closestPackageJson?.name.replace( /^@[^/]+\//, '' ) || rootPyProjectToml?.project.name
|
|
538
|
+
const artifactProject = closestPackageJson?.artifactRegistry.project || 'unused'
|
|
539
|
+
const artifactRegistry = closestPackageJson?.leverege.registry || rootPyProjectToml?.registry
|
|
540
|
+
const packageVersion = `v${closestPackageJson?.version}` || rootPyProjectToml?.project.version
|
|
539
541
|
|
|
540
542
|
const currentDir = process.cwd()
|
|
541
543
|
const closestPkg = await packageUp()
|
|
@@ -567,6 +569,7 @@ export const analyzeRepository = async ( giveGuidance ) => {
|
|
|
567
569
|
isMonoRepo,
|
|
568
570
|
isNpmWorkspace,
|
|
569
571
|
rootPackageJson,
|
|
572
|
+
rootPyProjectToml,
|
|
570
573
|
closestPackageJson,
|
|
571
574
|
packageVersion,
|
|
572
575
|
subPkgs,
|
package/src/bash-funcs
CHANGED
|
@@ -219,13 +219,9 @@ log( `
|
|
|
219
219
|
|
|
220
220
|
${chalk.green.bold( 'Docker Information:' )}
|
|
221
221
|
NodeImage: ${chalk.green.bold( dockerInfo.imageBase || `node:${dockerInfo.nodeimage}` )}
|
|
222
|
-
Docker Env: ${chalk.green.bold( dockerInfo.buildEnv || 'alpine' )}
|
|
223
|
-
AddedPkgs: ${chalk.green.bold( dockerInfo.apkadds )}
|
|
224
222
|
Run User: ${chalk.green.bold( dockerInfo.runuser )}
|
|
225
223
|
Previous: ${chalk.yellow.bold( dockerInfo.previousBuild )}
|
|
226
224
|
DateStamp: ${chalk.green.bold( dockerInfo.date )}
|
|
227
|
-
NPM Logs: ${chalk.green.bold( dockerInfo.npmlogging )}
|
|
228
|
-
NPM Version: ${chalk.green.bold( dockerInfo.npmVersion )}
|
|
229
225
|
` )
|
|
230
226
|
|
|
231
227
|
if ( imageVersion !== packageVersion ) {
|
|
@@ -2,26 +2,16 @@
|
|
|
2
2
|
#
|
|
3
3
|
# See => https://cloudnative-pg.io/documentation/current/installation_upgrade/
|
|
4
4
|
#
|
|
5
|
-
OPVER="1.
|
|
5
|
+
OPVER="1.26.0" # 05/23/2025
|
|
6
6
|
|
|
7
7
|
createNamespaceIfNeeded cnpg-system
|
|
8
8
|
|
|
9
9
|
kubectl apply --server-side -f \
|
|
10
10
|
https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-${OPVER%.*}/releases/cnpg-${OPVER}.yaml $K8S_WHAT
|
|
11
11
|
|
|
12
|
-
#
|
|
13
|
-
# added to the network layer on k8s. It should look like this:
|
|
12
|
+
# NOTE: installing release candidates may reference something that looks like:
|
|
14
13
|
#
|
|
15
|
-
#
|
|
16
|
-
# Ports : 8000,9443 <= kubectl cnpg status and webhooks
|
|
17
|
-
# Filters: 172.16.0.0/28 <= k8s control plane
|
|
14
|
+
# OPVER="1.26.0-rc1" # 03/28/2025
|
|
18
15
|
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#addHelmRepo cnpg https://cloudnative-pg.github.io/charts
|
|
22
|
-
#
|
|
23
|
-
#helm upgrade --install cnpg cnpg/cloudnative-pg \
|
|
24
|
-
# --namespace cnpg-system --create-namespace \
|
|
25
|
-
# --set webhook.port="10250" $HELM_WHAT
|
|
26
|
-
#
|
|
27
|
-
#removeHelmRepo cnpg
|
|
16
|
+
# kubectl apply --server-side -f \
|
|
17
|
+
# https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/main/releases/cnpg-${OPVER}.yaml $K8S_WHAT
|
|
@@ -6,7 +6,7 @@ addHelmRepo prometheus-community https://prometheus-community.github.io/helm-cha
|
|
|
6
6
|
NS="--namespace prometheus"
|
|
7
7
|
|
|
8
8
|
showInstalling "The Prometheus Operator (kube-prometheus-stack)"
|
|
9
|
-
[ -z "$PROMETHEUS_STACK_CHART_VERSION" ] && PROMETHEUS_STACK_CHART_VERSION="
|
|
9
|
+
[ -z "$PROMETHEUS_STACK_CHART_VERSION" ] && PROMETHEUS_STACK_CHART_VERSION="73"
|
|
10
10
|
|
|
11
11
|
# https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack
|
|
12
12
|
helm upgrade $NS --install prometheus-stack prometheus-community/kube-prometheus-stack \
|
|
@@ -34,4 +34,8 @@ helm upgrade $NS --install prometheus-adapter prometheus-community/prometheus-ad
|
|
|
34
34
|
--values prom-operator/prometheus-adapter.yaml \
|
|
35
35
|
--version $PROMETHEUS_ADAPTER_CHART_VERSION $HELM_WHAT
|
|
36
36
|
|
|
37
|
+
# finally, apply the PrometheusRule to generate the GPU duty cycle metric
|
|
38
|
+
showInstalling "The Prometheus Rule (custom GPU metrics rule)"
|
|
39
|
+
kubectl apply -n prometheus -f prom-operator/stackdriver-exporter-gpu-rules.yaml
|
|
40
|
+
|
|
37
41
|
removeHelmRepo prometheus-community
|
|
@@ -39,6 +39,11 @@ prometheus:
|
|
|
39
39
|
ruleSelectorNilUsesHelmValues: false
|
|
40
40
|
serviceMonitorSelectorNilUsesHelmValues: false
|
|
41
41
|
probeSelectorNilUsesHelmValues: false
|
|
42
|
+
maximumStartupDurationSeconds: 60
|
|
43
|
+
resources:
|
|
44
|
+
requests:
|
|
45
|
+
cpu: 500m
|
|
46
|
+
memory: 2Gi
|
|
42
47
|
storageSpec:
|
|
43
48
|
volumeClaimTemplate:
|
|
44
49
|
spec:
|
|
@@ -191,6 +196,7 @@ alertmanager:
|
|
|
191
196
|
*Description:* {{ .Annotations.description }}
|
|
192
197
|
*Dashboard:* {{ .Annotations.dashboard }}
|
|
193
198
|
{{ end }}
|
|
199
|
+
- name: discard
|
|
194
200
|
|
|
195
201
|
route:
|
|
196
202
|
group_wait: 30s
|
|
@@ -198,12 +204,19 @@ alertmanager:
|
|
|
198
204
|
group_by:
|
|
199
205
|
- alertname
|
|
200
206
|
- namespace
|
|
201
|
-
receiver: slack
|
|
207
|
+
receiver: slack # default for everything
|
|
202
208
|
routes:
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
+
- matchers:
|
|
210
|
+
- severity="page" # 📢 paging DevOps
|
|
211
|
+
receiver: opsgenie
|
|
212
|
+
continue: true # 👈 KEEP GOING after this match
|
|
213
|
+
|
|
214
|
+
- matchers:
|
|
215
|
+
- namespace="kube-system" # 🙈 ignore these alerts - GKE will handle them
|
|
216
|
+
receiver: discard
|
|
217
|
+
|
|
218
|
+
- matchers:
|
|
219
|
+
- alertname="Watchdog"
|
|
220
|
+
receiver: slack-watchdog
|
|
221
|
+
|
|
209
222
|
repeat_interval: 6h
|
|
@@ -10,7 +10,7 @@ spec:
|
|
|
10
10
|
rules:
|
|
11
11
|
- alert: ElasticsearchHeapUsageTooHigh
|
|
12
12
|
expr: (elasticsearch_jvm_memory_used_bytes{area="heap"} / elasticsearch_jvm_memory_max_bytes{area="heap"}) * 100 > 90
|
|
13
|
-
for: 2m
|
|
13
|
+
for: 5m # awesome was => 2m
|
|
14
14
|
labels:
|
|
15
15
|
severity: critical
|
|
16
16
|
annotations:
|
|
@@ -21,7 +21,7 @@ spec:
|
|
|
21
21
|
LABELS = {{ $labels }}
|
|
22
22
|
- alert: ElasticsearchHeapUsageWarning
|
|
23
23
|
expr: (elasticsearch_jvm_memory_used_bytes{area="heap"} / elasticsearch_jvm_memory_max_bytes{area="heap"}) * 100 > 80
|
|
24
|
-
for: 2m
|
|
24
|
+
for: 5m # awesome was => 2m
|
|
25
25
|
labels:
|
|
26
26
|
severity: warning
|
|
27
27
|
annotations:
|
|
@@ -43,7 +43,7 @@ spec:
|
|
|
43
43
|
LABELS = {{ $labels }}
|
|
44
44
|
- alert: ElasticsearchDiskSpaceLow
|
|
45
45
|
expr: elasticsearch_filesystem_data_available_bytes / elasticsearch_filesystem_data_size_bytes * 100 < 20
|
|
46
|
-
for: 2m
|
|
46
|
+
for: 5m # awesome was => 2m
|
|
47
47
|
labels:
|
|
48
48
|
severity: warning
|
|
49
49
|
annotations:
|
|
@@ -54,7 +54,7 @@ spec:
|
|
|
54
54
|
LABELS = {{ $labels }}
|
|
55
55
|
- alert: ElasticsearchClusterRed
|
|
56
56
|
expr: elasticsearch_cluster_health_status{color="red"} == 1
|
|
57
|
-
for: 0m
|
|
57
|
+
for: 5m # awesome was => 0m
|
|
58
58
|
labels:
|
|
59
59
|
severity: critical
|
|
60
60
|
annotations:
|
|
@@ -65,7 +65,7 @@ spec:
|
|
|
65
65
|
LABELS = {{ $labels }}
|
|
66
66
|
- alert: ElasticsearchClusterYellow
|
|
67
67
|
expr: elasticsearch_cluster_health_status{color="yellow"} == 1
|
|
68
|
-
for: 0m
|
|
68
|
+
for: 5m # awesome was => 0m
|
|
69
69
|
labels:
|
|
70
70
|
severity: warning
|
|
71
71
|
annotations:
|
|
@@ -174,7 +174,7 @@ spec:
|
|
|
174
174
|
VALUE = {{ $value }}
|
|
175
175
|
LABELS = {{ $labels }}
|
|
176
176
|
- alert: ElasticsearchHighIndexingLatency
|
|
177
|
-
expr: elasticsearch_indices_indexing_index_time_seconds_total / elasticsearch_indices_indexing_index_total > 0.
|
|
177
|
+
expr: elasticsearch_indices_indexing_index_time_seconds_total / elasticsearch_indices_indexing_index_total > 0.005 # awesome was 0.0005
|
|
178
178
|
for: 10m
|
|
179
179
|
labels:
|
|
180
180
|
severity: warning
|
|
@@ -29,8 +29,7 @@ spec:
|
|
|
29
29
|
expr: sum(stackdriver_pubsub_subscription_pubsub_googleapis_com_subscription_num_undelivered_messages) by (subscription_id) > 1000
|
|
30
30
|
for: 5m
|
|
31
31
|
labels:
|
|
32
|
-
severity: page
|
|
33
|
-
priority: P3
|
|
32
|
+
severity: info # awesome was => page / priority: P3
|
|
34
33
|
annotations:
|
|
35
34
|
summary: "High Number of Undelivered Messages on PubSub"
|
|
36
35
|
description: "Undelivered message count on topic {{$labels.subscription_id}} is greater than 1000"
|
|
@@ -21,7 +21,7 @@ spec:
|
|
|
21
21
|
LABELS = {{ $labels }}
|
|
22
22
|
- alert: KubernetesNodeMemoryPressure
|
|
23
23
|
expr: kube_node_status_condition{condition="MemoryPressure",status="true"} == 1
|
|
24
|
-
for: 2m
|
|
24
|
+
for: 5m # awesome was => 2m
|
|
25
25
|
labels:
|
|
26
26
|
severity: critical
|
|
27
27
|
annotations:
|
|
@@ -32,7 +32,7 @@ spec:
|
|
|
32
32
|
LABELS = {{ $labels }}
|
|
33
33
|
- alert: KubernetesNodeDiskPressure
|
|
34
34
|
expr: kube_node_status_condition{condition="DiskPressure",status="true",node!~".*preemptible.*"} == 1 # awesome mod => added preemptible check
|
|
35
|
-
for: 2m
|
|
35
|
+
for: 5m # awesome was => 2m
|
|
36
36
|
labels:
|
|
37
37
|
severity: critical
|
|
38
38
|
annotations:
|
|
@@ -43,7 +43,7 @@ spec:
|
|
|
43
43
|
LABELS = {{ $labels }}
|
|
44
44
|
- alert: KubernetesNodeNetworkUnavailable
|
|
45
45
|
expr: kube_node_status_condition{condition="NetworkUnavailable",status="true"} == 1
|
|
46
|
-
for: 2m
|
|
46
|
+
for: 5m # awesome was => 2m
|
|
47
47
|
labels:
|
|
48
48
|
severity: critical
|
|
49
49
|
annotations:
|
|
@@ -54,7 +54,7 @@ spec:
|
|
|
54
54
|
LABELS = {{ $labels }}
|
|
55
55
|
- alert: KubernetesNodeOutOfPodCapacity
|
|
56
56
|
expr: sum by (node) ((kube_pod_status_phase{phase="Running"} == 1) + on(uid, instance) group_left(node) (0 * kube_pod_info{pod_template_hash=""})) / sum by (node) (kube_node_status_allocatable{resource="pods"}) * 100 > 90
|
|
57
|
-
for: 2m
|
|
57
|
+
for: 5m # awesome was => 2m
|
|
58
58
|
labels:
|
|
59
59
|
severity: warning
|
|
60
60
|
annotations:
|
|
@@ -130,8 +130,8 @@ spec:
|
|
|
130
130
|
VALUE = {{ $value }}
|
|
131
131
|
LABELS = {{ $labels }}
|
|
132
132
|
- alert: PrometheusRuleEvaluationFailures
|
|
133
|
-
expr: increase(prometheus_rule_evaluation_failures_total[
|
|
134
|
-
for: 0m
|
|
133
|
+
expr: increase(prometheus_rule_evaluation_failures_total[5m]) > 0 # awesome was 3m
|
|
134
|
+
for: 5m # awesome was 0m
|
|
135
135
|
labels:
|
|
136
136
|
severity: critical
|
|
137
137
|
annotations:
|
|
@@ -186,7 +186,7 @@ spec:
|
|
|
186
186
|
LABELS = {{ $labels }}
|
|
187
187
|
- alert: PrometheusTargetEmpty
|
|
188
188
|
expr: prometheus_sd_discovered_targets == 0
|
|
189
|
-
for: 0m
|
|
189
|
+
for: 3m # awesome was => 0m
|
|
190
190
|
labels:
|
|
191
191
|
severity: critical
|
|
192
192
|
annotations:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#
|
|
2
2
|
# these custom rules exist to essentially boil down the GPU information provided
|
|
3
|
-
# by the stackdriver exporter into a form that is a little more
|
|
3
|
+
# by the stackdriver exporter into a form that is a little more human readable
|
|
4
4
|
# in an attempt to keep the grafana / alert manager rules a little less "verbose"
|
|
5
5
|
#
|
|
6
6
|
# In a nutshell, we take metrics in the form of:
|
|
@@ -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="36"
|
|
21
21
|
helm upgrade --install traefik traefik/traefik \
|
|
22
22
|
--namespace $TRAEFIK_NAMESPACE --create-namespace \
|
|
23
23
|
--values traefik/values.yaml \
|
|
@@ -4,7 +4,7 @@ showInstalling "Velero Backup System"
|
|
|
4
4
|
|
|
5
5
|
addHelmRepo vmware-tanzu https://vmware-tanzu.github.io/helm-charts
|
|
6
6
|
|
|
7
|
-
[ -z "$VELERO_CHART_VERSION" ] && VELERO_CHART_VERSION="
|
|
7
|
+
[ -z "$VELERO_CHART_VERSION" ] && VELERO_CHART_VERSION="10"
|
|
8
8
|
#
|
|
9
9
|
# Build the bucket, region and SA email variables and use --set to mod the
|
|
10
10
|
# chart values as opposed to using an OVH:<label> approach. This eliminates
|
package/src/helmup.sh
CHANGED
|
@@ -12,8 +12,8 @@ REGISTRY_DOMAIN="us-docker.pkg.dev"
|
|
|
12
12
|
# open container initiative (oci) => https://opencontainers.org/
|
|
13
13
|
ARTIFACT_REGISTRY="oci://$REGISTRY_DOMAIN/leverege-registry"
|
|
14
14
|
# login to the registry in the background
|
|
15
|
-
gcloud auth print-access-token | helm registry login
|
|
16
|
-
--password-stdin
|
|
15
|
+
gcloud auth print-access-token | helm registry login \
|
|
16
|
+
-u oauth2accesstoken --password-stdin $REGISTRY_DOMAIN &> $DEVNULL
|
|
17
17
|
|
|
18
18
|
# make sure we have a current npmrc file
|
|
19
19
|
refresh-npm-token
|
|
@@ -175,21 +175,21 @@ function createNamespaceIfNeeded() {
|
|
|
175
175
|
function makeUniformBucketWithCors() {
|
|
176
176
|
local BUCKET_NAME="gs://${GCP_PROJECT_ID}-${1}"
|
|
177
177
|
printf "Creating/Updating bucket: `color g $BUCKET_NAME`\n"
|
|
178
|
-
gsutil ls -p $GCP_PROJECT_ID -b $BUCKET_NAME &>
|
|
178
|
+
gsutil ls -p $GCP_PROJECT_ID -b $BUCKET_NAME &> $DEVNULL
|
|
179
179
|
if [ "$?" -ne 0 ];
|
|
180
180
|
then
|
|
181
|
-
gsutil mb -l $GCE_REGION -p $GCP_PROJECT_ID $BUCKET_NAME &>
|
|
182
|
-
|
|
181
|
+
gsutil mb -l $GCE_REGION -p $GCP_PROJECT_ID $BUCKET_NAME &> $DEVNULL
|
|
182
|
+
exitOnError $? "failed to create the bucket: `color y $BUCKET_NAME`"
|
|
183
183
|
fi
|
|
184
184
|
gcloud storage buckets update $BUCKET_NAME --uniform-bucket-level-access
|
|
185
185
|
CORS_JSON_FILE="$(build-tools --reporoot)/src/helmup-cors-for-buckets.json"
|
|
186
186
|
printf " Applying CORS policy\n"
|
|
187
|
-
gsutil cors set $CORS_JSON_FILE $BUCKET_NAME &>
|
|
187
|
+
gsutil cors set $CORS_JSON_FILE $BUCKET_NAME &> $DEVNULL
|
|
188
188
|
|
|
189
189
|
if [ "$2" == "PUBLIC" ];
|
|
190
190
|
then
|
|
191
191
|
printf " Making the bucket `color r PUBLIC`\n"
|
|
192
|
-
gsutil iam ch allUsers:objectViewer $BUCKET_NAME &>
|
|
192
|
+
gsutil iam ch allUsers:objectViewer $BUCKET_NAME &> $DEVNULL
|
|
193
193
|
fi
|
|
194
194
|
}
|
|
195
195
|
|
|
@@ -394,7 +394,7 @@ function installPreemptibleKiller() {
|
|
|
394
394
|
gcloud iam service-accounts create $SVC_ACCT \
|
|
395
395
|
--project $GCP_PROJECT_ID \
|
|
396
396
|
--description "Estafette Node Killer SA" \
|
|
397
|
-
--display-name "Estafette Node Killer SA"
|
|
397
|
+
--display-name "Estafette Node Killer SA" &> $DEVNULL
|
|
398
398
|
warnOnError $? "SA creation may have failed or it already exists"
|
|
399
399
|
|
|
400
400
|
printf "\nCreate the $SVC_ACCT IAM role\n"
|
|
@@ -402,7 +402,7 @@ function installPreemptibleKiller() {
|
|
|
402
402
|
--project $GCP_PROJECT_ID \
|
|
403
403
|
--title "Estafette Node Killer" \
|
|
404
404
|
--description "Delete compute instances" \
|
|
405
|
-
--permissions compute.instances.delete
|
|
405
|
+
--permissions compute.instances.delete &> $DEVNULL
|
|
406
406
|
warnOnError $? "Role creation may have failed or it already exists"
|
|
407
407
|
|
|
408
408
|
local service_account_email=$(gcloud iam --project=$GCP_PROJECT_ID service-accounts list --filter $SVC_ACCT --format 'value([email])')
|
|
@@ -417,6 +417,7 @@ function installPreemptibleKiller() {
|
|
|
417
417
|
helm upgrade --install estafette-gke-preemptible-killer \
|
|
418
418
|
estafette/estafette-gke-preemptible-killer \
|
|
419
419
|
--namespace ${NAMESPACE} \
|
|
420
|
+
--set resources.limits.cpu="100m" \
|
|
420
421
|
--set secret.workloadIdentityServiceAccount="$SVC_ACCT@$GCP_PROJECT_ID.iam.gserviceaccount.com" \
|
|
421
422
|
--set extraEnv.BLACKLIST_HOURS="10:00-02:00" # No killing from 5AM-9PM EST
|
|
422
423
|
# --set extraEnv.WHITELIST_HOURS="12:30-13:30"
|
|
@@ -478,7 +479,15 @@ spec:
|
|
|
478
479
|
- "/bin/sh"
|
|
479
480
|
args:
|
|
480
481
|
- "-c"
|
|
481
|
-
-
|
|
482
|
+
- |
|
|
483
|
+
echo 'Hunting Zombies' ;
|
|
484
|
+
namespaces="default cnpg-operands elastic prometheus traefik velero"
|
|
485
|
+
for ns in \$namespaces; do
|
|
486
|
+
pods=\$(kubectl get pods -n "\$ns" --field-selector=status.phase=Failed -o name 2>/dev/null)
|
|
487
|
+
if [ -n "\$pods" ]; then
|
|
488
|
+
echo "\$pods" | xargs -r kubectl delete -n "\$ns"
|
|
489
|
+
fi
|
|
490
|
+
done
|
|
482
491
|
status: {}
|
|
483
492
|
END_OF_ZOMBIE
|
|
484
493
|
}
|
|
@@ -792,7 +801,7 @@ function installVeleroEnvironment() {
|
|
|
792
801
|
gcloud config set project $GCP_PROJECT_ID
|
|
793
802
|
|
|
794
803
|
## The major chart version will determine the bucket suffix
|
|
795
|
-
[ -z "$VELERO_CHART_VERSION" ] && VELERO_CHART_VERSION="
|
|
804
|
+
[ -z "$VELERO_CHART_VERSION" ] && VELERO_CHART_VERSION="10"
|
|
796
805
|
|
|
797
806
|
## Create a bucket
|
|
798
807
|
BUCKET="$GCP_PROJECT_ID-velero-$VELERO_CHART_VERSION"
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander'
|
|
4
|
+
import { shellCmd, log, warning, errorExit } from './Utils.mjs'
|
|
5
|
+
|
|
6
|
+
const program = new Command()
|
|
7
|
+
|
|
8
|
+
program
|
|
9
|
+
.requiredOption( '--project <projectId>', 'GCP project ID of the GSA' )
|
|
10
|
+
.requiredOption( '--bucket <bucketName>', 'GCS bucket name (no gs:// prefix)' )
|
|
11
|
+
.requiredOption( '--gsa <gsaName>', 'Service account name, e.g. my-service' )
|
|
12
|
+
.requiredOption( '--role <roleId>', 'Custom role ID', 'leveregeStorageObjectReader' )
|
|
13
|
+
.option( '--org', 'Treat as GCP organizational level role' )
|
|
14
|
+
.option( '--add', 'Add IAM binding (default)' )
|
|
15
|
+
.option( '--remove', 'Remove IAM binding' )
|
|
16
|
+
.option( '--dry-run', 'Show command without executing' )
|
|
17
|
+
|
|
18
|
+
program.parse( process.argv )
|
|
19
|
+
|
|
20
|
+
const opts = program.opts()
|
|
21
|
+
|
|
22
|
+
// Validate mutually exclusive add/remove
|
|
23
|
+
if ( opts.add && opts.remove ) {
|
|
24
|
+
errorExit( 'You cannot specify both --add and --remove.' )
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const action = opts.remove ? 'remove' : 'add'
|
|
28
|
+
|
|
29
|
+
const {
|
|
30
|
+
project,
|
|
31
|
+
org,
|
|
32
|
+
bucket,
|
|
33
|
+
gsa,
|
|
34
|
+
role,
|
|
35
|
+
dryRun
|
|
36
|
+
} = opts
|
|
37
|
+
|
|
38
|
+
const orgId = await shellCmd( 'gcloud organizations list --format="value(ID)"' )
|
|
39
|
+
const gsaEmail = `${gsa}@${project}.iam.gserviceaccount.com`
|
|
40
|
+
const fullRole = org ? `organizations/${orgId}/roles/${role}` : `roles/${role}`
|
|
41
|
+
const bucketUri = `gs://${bucket}`
|
|
42
|
+
|
|
43
|
+
const iamMember = `serviceAccount:${gsaEmail}:${fullRole}`
|
|
44
|
+
|
|
45
|
+
const command = action === 'add' ?
|
|
46
|
+
[ 'gsutil', 'iam', 'ch', iamMember, bucketUri ] :
|
|
47
|
+
[ 'gsutil', 'iam', 'ch', '-d', iamMember, bucketUri ]
|
|
48
|
+
|
|
49
|
+
log( `🔧 ${action === 'add' ? 'Granting' : 'Removing'} ${fullRole} for ${gsaEmail} on ${bucketUri}...` )
|
|
50
|
+
|
|
51
|
+
if ( dryRun ) {
|
|
52
|
+
warning( '💡 DRY RUN mode — no commands executed.' )
|
|
53
|
+
log( `🔗 Command:\n$ ${command.join( ' ' )}` )
|
|
54
|
+
process.exit( 0 )
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const result = await shellCmd( command )
|
|
59
|
+
log( `✅ IAM ${action} successful:\n${result}` )
|
|
60
|
+
} catch ( err ) {
|
|
61
|
+
errorExit( `❌ Failed to ${action} IAM binding: ${err.message}` )
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// === Post-change audit ===
|
|
65
|
+
log( '🔍 Verifying IAM policy for this GSA on the bucket...\n' )
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const policyRaw = await shellCmd( [ 'gsutil', 'iam', 'get', bucketUri ] )
|
|
69
|
+
const policy = JSON.parse( policyRaw )
|
|
70
|
+
|
|
71
|
+
const matchingRoles = policy.bindings.filter(
|
|
72
|
+
b => b.members.includes( `serviceAccount:${gsaEmail}` )
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if ( matchingRoles.length === 0 ) {
|
|
76
|
+
log( `ℹ️ No IAM bindings found for ${gsaEmail} on ${bucketUri}.` )
|
|
77
|
+
} else {
|
|
78
|
+
for ( const binding of matchingRoles ) {
|
|
79
|
+
log( `🔐 ${gsaEmail} has role: ${binding.role}` )
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} catch ( err ) {
|
|
83
|
+
warning( `⚠️ Failed to retrieve or parse IAM policy: ${err.message}` )
|
|
84
|
+
}
|
package/src/refresh-py-idx.sh
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
|
|
3
|
+
# Load the standard helper functions
|
|
4
|
+
. `build-tools --bashfun`
|
|
5
|
+
|
|
6
|
+
cat<<DEPRECATED
|
|
7
|
+
|
|
8
|
+
`color r "**DEPRECATED - refresh-py-idx is no longer used"`
|
|
9
|
+
DEPRECATED
|
|
10
|
+
exit 1
|
|
11
|
+
|
|
3
12
|
function leveregeBuildToolsRefreshPyIdx() {
|
|
4
13
|
# Execute gcloud command to get the token
|
|
5
14
|
local TOKEN=$(gcloud auth print-access-token)
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
options:
|
|
2
|
+
machineType: E2_HIGHCPU_8
|
|
3
|
+
|
|
1
4
|
steps:
|
|
2
5
|
- name: 'gcr.io/cloud-builders/docker'
|
|
3
6
|
entrypoint: 'bash'
|
|
@@ -12,6 +15,28 @@ steps:
|
|
|
12
15
|
- '--cache-from'
|
|
13
16
|
- '{{imageName}}:latest'
|
|
14
17
|
- '.'
|
|
15
|
-
|
|
18
|
+
|
|
19
|
+
- name: 'gcr.io/cloud-builders/docker'
|
|
20
|
+
args:
|
|
21
|
+
- 'tag'
|
|
22
|
+
- '{{imageName}}:{{imageVersion}}'
|
|
23
|
+
- '{{imageName}}:latest'
|
|
24
|
+
|
|
25
|
+
- name: 'gcr.io/cloud-builders/docker'
|
|
26
|
+
args:
|
|
27
|
+
- 'push'
|
|
28
|
+
- '{{imageName}}:latest'
|
|
29
|
+
|
|
30
|
+
- name: 'gcr.io/cloud-builders/docker'
|
|
31
|
+
args:
|
|
32
|
+
- 'tag'
|
|
33
|
+
- '{{imageName}}:{{imageVersion}}'
|
|
34
|
+
- '{{imageName}}:{{imageInfoTag}}'
|
|
35
|
+
|
|
36
|
+
- name: 'gcr.io/cloud-builders/docker'
|
|
37
|
+
args:
|
|
38
|
+
- 'push'
|
|
39
|
+
- '{{imageName}}:{{imageInfoTag}}'
|
|
40
|
+
|
|
16
41
|
images:
|
|
17
42
|
- '{{imageName}}:{{imageVersion}}'
|
|
@@ -1,60 +1,64 @@
|
|
|
1
1
|
# Version {{regvers}} @ {{date}}
|
|
2
2
|
|
|
3
|
-
# The FROM directive sets the Base Image for subsequent instructions
|
|
4
3
|
FROM {{image}}
|
|
5
4
|
|
|
6
5
|
ARG BUILD_ENV="{{buildEnv}}"
|
|
7
6
|
|
|
8
7
|
RUN useradd -ms /bin/bash {{runuser}} && \
|
|
9
|
-
mkdir -p /usr/src/app && \
|
|
10
|
-
chown {{runuser}} /usr/src/app && \
|
|
8
|
+
mkdir -p /usr/src/app /home/{{runuser}} && \
|
|
9
|
+
chown -R {{runuser}} /usr/src/app /home/{{runuser}} && \
|
|
11
10
|
apt-get update && apt-get install -y \
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
wget git libgl1 libglib2.0-0 && \
|
|
12
|
+
rm -rf /var/lib/apt/lists/*
|
|
14
13
|
|
|
15
|
-
#
|
|
16
|
-
# git \
|
|
17
|
-
# && rm -rf /var/lib/apt/lists/*
|
|
18
|
-
|
|
19
|
-
# Copy uv binary from the official distroless Docker image.
|
|
14
|
+
# Copy uv binary from distroless image
|
|
20
15
|
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
|
|
21
16
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
# Install app dependencies
|
|
25
|
-
COPY --chown={{runuser}}:{{runuser}} ./workspace/ /usr/src/app/
|
|
26
|
-
ENV GRPC_VERBOSITY ERROR
|
|
27
|
-
|
|
28
|
-
# Setup pip.conf for Google Artifact Registry.
|
|
29
|
-
RUN mkdir -p ~/.config/pip && \
|
|
30
|
-
echo "[global]" >> ~/.config/pip/pip.conf && \
|
|
31
|
-
echo "break-system-packages = true" >> ~/.config/pip/pip.conf
|
|
32
|
-
|
|
17
|
+
# Copy bashrc early
|
|
18
|
+
COPY --chown={{runuser}}:{{runuser}} ./bashrc /home/{{runuser}}/.bashrc
|
|
33
19
|
|
|
34
|
-
|
|
20
|
+
WORKDIR /usr/src/app
|
|
35
21
|
USER {{runuser}}
|
|
36
|
-
COPY --chown={{runuser}}:{{runuser}} ./bashrc /home/{{runuser}}/.bashrc
|
|
37
22
|
|
|
38
|
-
|
|
23
|
+
ENV GRPC_VERBOSITY=ERROR
|
|
39
24
|
ENV VIRTUAL_ENV=/usr/src/app/.venv
|
|
40
25
|
ENV PATH="$VIRTUAL_ENV/bin:$PATH:/home/{{runuser}}/.local/bin"
|
|
41
26
|
|
|
42
|
-
# Setup
|
|
43
|
-
RUN
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
#ENV UV_EXTRA_INDEX_URL "https://oauth2accesstoken@us-python.pkg.dev/leverege-registry/leverege-python-packages/simple"
|
|
27
|
+
# Setup pip.conf for GCP Artifact Registry in case pip is needed for building
|
|
28
|
+
RUN mkdir -p /home/{{runuser}}/.config/pip && \
|
|
29
|
+
echo "[global]" >> /home/{{runuser}}/.config/pip/pip.conf && \
|
|
30
|
+
echo "break-system-packages = true" >> /home/{{runuser}}/.config/pip/pip.conf
|
|
47
31
|
|
|
32
|
+
# Set up venv and keyring tools before app copy
|
|
33
|
+
RUN uv venv && \
|
|
34
|
+
uv tool install keyring --with keyrings.google-artifactregistry-auth
|
|
48
35
|
|
|
36
|
+
# Pre-plugin block: models or pre-install commands
|
|
49
37
|
{{preInstallPluginfile}}
|
|
50
38
|
|
|
51
|
-
|
|
39
|
+
# Copy only dependency definitions first
|
|
40
|
+
COPY --chown=python:python ./workspace/pyproject.toml \
|
|
41
|
+
./workspace/uv.lock \
|
|
42
|
+
./workspace/.python-version \
|
|
43
|
+
./workspace/README.md \
|
|
44
|
+
./workspace/LICENSE.md \
|
|
45
|
+
/usr/src/app/
|
|
52
46
|
|
|
53
|
-
#
|
|
47
|
+
# Sync dependencies
|
|
48
|
+
RUN uv sync --keyring-provider subprocess \
|
|
49
|
+
--extra-index-url https://oauth2accesstoken@us-python.pkg.dev/leverege-registry/leverege-python-packages/simple \
|
|
50
|
+
&& rm -rf /home/{{runuser}}/.cache
|
|
51
|
+
|
|
52
|
+
# Copy application source AFTER venv + tool setup
|
|
53
|
+
COPY --chown={{runuser}}:{{runuser}} ./workspace/ /usr/src/app/
|
|
54
|
+
|
|
55
|
+
# Plugin block for additional pip packages
|
|
54
56
|
{{pluginfile}}
|
|
55
57
|
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
+
# Ensure src/ layout is importable from anywhere in the container
|
|
59
|
+
ENV PYTHONPATH="/usr/src/app/src"
|
|
60
|
+
|
|
61
|
+
# Show final venv state (optional)
|
|
58
62
|
RUN ls /usr/src/app/.venv/bin
|
|
63
|
+
|
|
59
64
|
CMD [ "/bin/bash", "-c", "source /home/{{runuser}}/.bashrc && python scripts/server.py" ]
|
|
60
|
-
#CMD [ "/bin/bash" ]
|