-
-
Notifications
You must be signed in to change notification settings - Fork 218
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(#8551): removes python from cht-deploy
- Loading branch information
1 parent
f784c0d
commit 4692ee7
Showing
15 changed files
with
829 additions
and
282 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"parserOptions": { | ||
"sourceType": "module" | ||
}, | ||
"rules": { | ||
"no-console": "off" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
*.tar.gz | ||
*.tgz |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,83 @@ | ||
#!/bin/bash | ||
|
||
# Check if Python is installed | ||
if ! command -v python3 &> /dev/null ; then | ||
echo "Python is not installed. Please install Python 3." | ||
exit 1 | ||
fi | ||
|
||
# Check if pip is installed | ||
if ! command -v pip3 &> /dev/null ; then | ||
echo "pip is not installed. Please install pip for Python 3." | ||
exit 1 | ||
fi | ||
|
||
# Check if Invoke is installed | ||
if ! python3 -c "import invoke" &> /dev/null ; then | ||
echo "Invoke is not installed. Installing it..." | ||
pip3 install invoke --quiet | ||
fi | ||
|
||
# Check if PyYAML is installed | ||
if ! python3 -c "import yaml" &> /dev/null ; then | ||
echo "PyYAML is not installed. Installing it..." | ||
pip3 install PyYAML --quiet | ||
fi | ||
|
||
# Check if requests is installed | ||
if ! python3 -c "import requests" &> /dev/null ; then | ||
echo "Requests is not installed. Installing it..." | ||
pip3 install requests --quiet | ||
fi | ||
|
||
# Validate that -f argument is provided | ||
if [[ $1 != "-f" || -z $2 ]]; then | ||
echo "No values file provided. Please specify a values file using -f <file>" | ||
exit 1 | ||
fi | ||
|
||
# Pass command line arguments to invoke script | ||
# shellcheck disable=SC2068 # wontfix script will be replaced "soon" | ||
invoke install $@ | ||
#!/usr/bin/env node | ||
|
||
import { install } from './src/install.js'; | ||
import fs from 'fs'; | ||
import semver from 'semver'; | ||
import path from 'path'; | ||
|
||
import { dirname } from 'path'; | ||
import { fileURLToPath } from 'url'; | ||
|
||
const __filename = fileURLToPath(import.meta.url); | ||
const __dirname = dirname(__filename); | ||
|
||
const validateNodeVersion = function() { | ||
const packageJsonPath = path.resolve(__dirname, 'package.json'); | ||
|
||
try { | ||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); | ||
const requiredNodeVersion = packageJson.engines && packageJson.engines.node; | ||
|
||
if (requiredNodeVersion && !semver.satisfies(process.version, requiredNodeVersion)) { | ||
console.error(`Invalid Node.js version. Required: ${requiredNodeVersion}. Current: ${process.version}`); | ||
process.exit(1); | ||
} | ||
} catch (err) { | ||
console.error('Error reading package.json:', err.message); | ||
process.exit(1); | ||
} | ||
}; | ||
|
||
const validateArguments = function() { | ||
const args = process.argv.slice(2); | ||
if (args.length < 2 || args[0] !== '-f' || !args[1]) { | ||
console.error('No values file provided. Please specify a values file using -f <file>'); | ||
process.exit(1); | ||
} | ||
if (validateFileExists(args[1])) { | ||
return args; | ||
} | ||
}; | ||
|
||
const validateFileExists = function(filePath) { | ||
try { | ||
fs.accessSync(filePath); | ||
return true; | ||
} catch (err) { | ||
console.error(`File not found: ${filePath}`); | ||
process.exit(1); | ||
} | ||
}; | ||
|
||
const runInstallScript = async function(args) { | ||
try { | ||
const valuesFilePath = args[1]; | ||
await install(valuesFilePath); | ||
} catch (err) { | ||
console.error('Error executing the install script:', err.message); | ||
console.error(JSON.stringify(err)); | ||
process.exit(1); | ||
} | ||
}; | ||
|
||
const main = async function() { | ||
validateNodeVersion(); | ||
const args = validateArguments(); | ||
await runInstallScript(args); | ||
}; | ||
|
||
if (import.meta.url === `file://${process.argv[1]}`) { //Make sure the script is being run directly and not being imported | ||
main().catch((err) => { | ||
console.error('An error occurred:', err.message); | ||
console.error(JSON.stringify(err)); | ||
process.exit(1); | ||
}); | ||
} | ||
|
||
export { | ||
main, | ||
validateNodeVersion, | ||
validateArguments, | ||
validateFileExists, | ||
runInstallScript | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
{ | ||
"name": "@medic/cht-deploy", | ||
"version": "1.0.10", | ||
"description": "## Deploy the CHT", | ||
"main": "src/install.js", | ||
"directories": { | ||
"test": "tests" | ||
}, | ||
"engines": { | ||
"node": ">=20.11.0", | ||
"npm": ">=10.2.4" | ||
}, | ||
"bin": { | ||
"cht-deploy": "./cht-deploy", | ||
"upload": "./cht-deploy", | ||
"get-all-logs": "./troubleshooting/get-all-logs", | ||
"describe-deployment": "./troubleshooting/describe-deployment", | ||
"list-all-resources": "./troubleshooting/list-all-resources", | ||
"list-deployments": "./troubleshooting/list-deployments", | ||
"restart-deployment": "./troubleshooting/restart-deployment", | ||
"view-logs": "./troubleshooting/view-logs" | ||
}, | ||
"scripts": { | ||
"test": "mocha 'tests/*.js'" | ||
}, | ||
"mocha": { | ||
"timeout": 5000, | ||
"recursive": true | ||
}, | ||
"files": [ | ||
"src", | ||
"cht-deploy", | ||
"troubleshooting", | ||
"README.md", | ||
"package.json" | ||
], | ||
"keywords": [], | ||
"author": "", | ||
"license": "AGPL-3.0-only", | ||
"type": "module", | ||
"devDependencies": { | ||
"@kubernetes/client-node": "^0.19.0", | ||
"@sinonjs/fake-timers": "^11.2.2", | ||
"chai": "^4.4.1", | ||
"chai-as-promised": "^7.1.2", | ||
"mocha": "^10.4.0", | ||
"sinon": "^17.0.1" | ||
}, | ||
"dependencies": { | ||
"js-yaml": "^4.1.0", | ||
"node-fetch": "^3.3.2", | ||
"semver": "^7.6.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import fs from 'fs'; | ||
import { execSync } from 'child_process'; | ||
import fetch from 'node-fetch'; | ||
import { UserRuntimeError, CertificateError } from './error.js'; | ||
import config from './config.js'; | ||
|
||
const CERT_SOURCES = { | ||
FILE: 'specify-file-path', | ||
MY_IP: 'my-ip-co', | ||
EKS: 'eks-medic' | ||
}; | ||
|
||
const cert = config.CERT_FILE; | ||
const key = config.KEY_FILE; | ||
|
||
const obtainCertificateAndKey = async function(values) { | ||
console.log('Obtaining certificate...'); | ||
const certSource = values.cert_source || ''; | ||
|
||
const handlers = { | ||
[CERT_SOURCES.FILE]: () => handleFileSource(values), | ||
[CERT_SOURCES.MY_IP]: () => handleMyIpSource(), | ||
[CERT_SOURCES.EKS]: () => {} // No action needed for 'eks-medic' | ||
}; | ||
|
||
const handler = handlers[certSource]; | ||
if (!handler) { | ||
throw new UserRuntimeError(`cert_source must be one of: ${Object.values(CERT_SOURCES).join(', ')}`); | ||
} | ||
|
||
await handler(); | ||
}; | ||
|
||
const handleFileSource = function({ certificate_crt_file_path, certificate_key_file_path }) { | ||
if (!certificate_crt_file_path || !certificate_key_file_path) { | ||
throw new CertificateError('certificate_crt_file_path and certificate_key_file_path must be set for file source'); | ||
} | ||
|
||
copyFile(certificate_crt_file_path, cert); | ||
copyFile(certificate_key_file_path, key); | ||
}; | ||
|
||
const handleMyIpSource = async function() { | ||
const [crtData, keyData] = await Promise.all([ | ||
fetchData(`${config.CERT_API_URL}/fullchain`), | ||
fetchData(`${config.CERT_API_URL}/key`) | ||
]); | ||
|
||
writeFile(cert, crtData); | ||
writeFile(key, keyData); | ||
}; | ||
|
||
const copyFile = function(src, dest) { | ||
try { | ||
fs.copyFileSync(src, dest); | ||
} catch (error) { | ||
throw new CertificateError(`Failed to copy file from ${src} to ${dest}: ${error.message}`); | ||
} | ||
}; | ||
|
||
const writeFile = function(filename, data) { | ||
try { | ||
fs.writeFileSync(filename, data); | ||
} catch (error) { | ||
throw new CertificateError(`Failed to write file ${filename}: ${error.message}`); | ||
} | ||
}; | ||
|
||
const fetchData = async function(url) { | ||
try { | ||
const controller = new AbortController(); | ||
const timeoutId = setTimeout(() => controller.abort(), config.FETCH_TIMEOUT); | ||
const response = await fetch(url, { signal: controller.signal }); | ||
clearTimeout(timeoutId); | ||
return await response.text(); | ||
} catch (error) { | ||
if (error.name === 'AbortError') { | ||
throw new CertificateError(`Request to ${url} timed out`); | ||
} | ||
throw new CertificateError(`Failed to fetch data from ${url}: ${error.message}`); | ||
} | ||
}; | ||
|
||
const createSecret = async function (namespace, values) { | ||
console.log('Checking if secret api-tls-secret already exists...'); | ||
try { | ||
execSync(`kubectl get secret api-tls-secret -n ${namespace}`, { stdio: 'inherit' }); //NoSONAR | ||
console.log('TLS secret already exists. Skipping creation.'); | ||
return; | ||
} catch (err) { | ||
if (err.message.includes('NotFound')) { | ||
console.log('Secret does not exist. Creating Secret from certificate and key...'); | ||
} else { | ||
throw err; | ||
} | ||
} | ||
|
||
await obtainCertificateAndKey(values); | ||
|
||
execSync( //NoSONAR | ||
`kubectl -n ${namespace} create secret tls api-tls-secret --cert=${cert} --key=${key}`, //NoSONAR | ||
{ stdio: 'inherit' } | ||
); | ||
cleanupFiles(); | ||
}; | ||
|
||
const cleanupFiles = function() { | ||
deleteFile(cert); | ||
deleteFile(key); | ||
}; | ||
|
||
const deleteFile = function(filename) { | ||
try { | ||
if (fs.existsSync(filename)) { | ||
fs.unlinkSync(filename); | ||
} | ||
} catch (error) { | ||
console.error(`Failed to delete file ${filename}: ${error.message}`); | ||
} | ||
}; | ||
|
||
export { obtainCertificateAndKey, createSecret }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export default { | ||
MEDIC_REPO_NAME: process.env.MEDIC_REPO_NAME || 'medic', | ||
MEDIC_REPO_URL: process.env.MEDIC_REPO_URL || 'https://docs.communityhealthtoolkit.org/helm-charts', | ||
CHT_CHART_NAME: process.env.CHT_CHART_NAME || 'medic/cht-chart-4x', | ||
DEFAULT_CHART_VERSION: process.env.DEFAULT_CHART_VERSION || '1.0.*', | ||
IMAGE_TAG_API_URL: process.env.IMAGE_TAG_API_URL || 'https://staging.dev.medicmobile.org/_couch/builds_4', | ||
CERT_FILE: process.env.CERT_FILE || 'certificate.crt', | ||
KEY_FILE: process.env.KEY_FILE || 'private.key', | ||
CERT_API_URL: process.env.CERT_API_URL || 'https://local-ip.medicmobile.org', | ||
FETCH_TIMEOUT: parseInt(process.env.FETCH_TIMEOUT, 10) || 5000, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
export class UserRuntimeError extends Error { | ||
constructor(message) { | ||
super(message); | ||
this.name = 'UserRuntimeError'; | ||
} | ||
} | ||
|
||
export class CertificateError extends Error { | ||
constructor(message) { | ||
super(message); | ||
this.name = 'CertificateError'; | ||
} | ||
} |
Oops, something went wrong.