Unverified Commit ea448070 authored by Jeremy Morrell's avatar Jeremy Morrell Committed by GitHub

Add metrics plugin + unit tests (#553)

* Add metrics plugin + unit tests

This merges in the build changes and plugin from https://github.com/heroku/heroku-nodejs-metrics-buildpack which installs the metrics plugin https://github.com/heroku/heroku-nodejs-plugin at runtime once a Heroku use opts in to the language metrics feature
parent eaca7485
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
## Master ## Master
- Add plugin for [Node.js Language Metrics](https://devcenter.heroku.com/articles/language-runtime-metrics-nodejs)
## v121 (2018-03-02) ## v121 (2018-03-02)
- Skip pruning if `NPM_CONFIG_PRODUCTION` or `YARN_PRODUCTION` is defined (#529) - Skip pruning if `NPM_CONFIG_PRODUCTION` or `YARN_PRODUCTION` is defined (#529)
......
...@@ -31,6 +31,7 @@ source $BP_DIR/lib/environment.sh ...@@ -31,6 +31,7 @@ source $BP_DIR/lib/environment.sh
source $BP_DIR/lib/binaries.sh source $BP_DIR/lib/binaries.sh
source $BP_DIR/lib/cache.sh source $BP_DIR/lib/cache.sh
source $BP_DIR/lib/dependencies.sh source $BP_DIR/lib/dependencies.sh
source $BP_DIR/lib/plugin.sh
export PATH="$BUILD_DIR/.heroku/node/bin:$BUILD_DIR/.heroku/yarn/bin":$PATH export PATH="$BUILD_DIR/.heroku/node/bin:$BUILD_DIR/.heroku/yarn/bin":$PATH
...@@ -230,6 +231,8 @@ summarize_build() { ...@@ -230,6 +231,8 @@ summarize_build() {
mmeasure 'modules.size' "$(measure_size)" mmeasure 'modules.size' "$(measure_size)"
} }
install_plugin $BP_DIR $BUILD_DIR
header "Build succeeded!" header "Build succeeded!"
mcount "compile" mcount "compile"
summarize_build | output "$LOG_FILE" summarize_build | output "$LOG_FILE"
......
get_node_major_version() {
local node_version="$(node --version)"
# major_string will be ex: "6." "8." "10"
local major_string=${node_version:1:2}
# strip any "."s from major_string
local major=${major_string//.}
echo $major
}
install_plugin() {
local bp_dir="$1"
local build_dir="$2"
local major=$(get_node_major_version)
local plugin="${bp_dir}/plugin/heroku-nodejs-plugin-node-${major}.tar.gz"
# If we have a version of the plugin compiled for this version of node, and the
# user has not opted out of including the plugin, copy it into the slug.
# It will be included at runtime once the user opts into the Node metrics feature
if [[ -f "${plugin}" ]] && [[ -z "$HEROKU_SKIP_NODE_PLUGIN" ]]; then
mkdir -p "${build_dir}/.heroku/"
tar -xzf ${plugin} -C "${build_dir}/.heroku/"
fi
}
#!/usr/bin/env bash
# download.sh <tag-name>
set -o errexit # always exit on error
set -o pipefail # don't ignore exit codes when piping output
TAG_NAME=${1:-}
PLUGIN_DIR=$(dirname $0)
handle_failure() {
echo "Failure running script."
echo "This may be rate-limiting from Github if you've run this script a few times. Here is the rate limit response:"
curl "https://api.github.com/rate_limit"
}
get_latest_release() {
# Get latest release tag from GitHub api
curl --silent --write-out "%{http_code}" "https://api.github.com/repos/heroku/heroku-nodejs-plugin/releases/latest" |
grep '"tag_name":' |
sed -E 's/.*"([^"]+)".*/\1/'
}
download() {
local url=${1}
local file=${2}
local code=$(curl "$url" -L --fail --retry 5 --retry-max-time 15 -o "${file}" --write-out "%{http_code}")
if [[ "$code" != "200" ]]; then
echo "Unable to download from url: $url http code: $code"
exit 1
fi
}
delete_old_plugin() {
local dir=${1}
rm -f "$dir/heroku-nodejs-plugin-node-10.sha512"
rm -f "$dir/heroku-nodejs-plugin-node-10.tar.gz"
rm -f "$dir/heroku-nodejs-plugin-node-8.sha512"
rm -f "$dir/heroku-nodejs-plugin-node-8.tar.gz"
rm -f "$dir/heroku-nodejs-plugin-node-9.sha512"
rm -f "$dir/heroku-nodejs-plugin-node-9.tar.gz"
rm -f "$dir/version"
}
download_assets_for_release() {
local tag=${1}
local dir=${2}
# Node 8
download "https://github.com/heroku/heroku-nodejs-plugin/releases/download/$tag/heroku-nodejs-plugin-node-8-$tag.sha512" "$dir/heroku-nodejs-plugin-node-8.sha512"
download "https://github.com/heroku/heroku-nodejs-plugin/releases/download/$tag/heroku-nodejs-plugin-node-8-$tag.tar.gz" "$dir/heroku-nodejs-plugin-node-8.tar.gz"
# Node 9
download "https://github.com/heroku/heroku-nodejs-plugin/releases/download/$tag/heroku-nodejs-plugin-node-9-$tag.sha512" "$dir/heroku-nodejs-plugin-node-9.sha512"
download "https://github.com/heroku/heroku-nodejs-plugin/releases/download/$tag/heroku-nodejs-plugin-node-9-$tag.tar.gz" "$dir/heroku-nodejs-plugin-node-9.tar.gz"
# Node 10
download "https://github.com/heroku/heroku-nodejs-plugin/releases/download/$tag/heroku-nodejs-plugin-node-10-$tag.sha512" "$dir/heroku-nodejs-plugin-node-10.sha512"
download "https://github.com/heroku/heroku-nodejs-plugin/releases/download/$tag/heroku-nodejs-plugin-node-10-$tag.tar.gz" "$dir/heroku-nodejs-plugin-node-10.tar.gz"
}
test_hash() {
local major=${1}
local dir=${2}
local downloaded_sha=$(cat $dir/heroku-nodejs-plugin-node-$major.sha512 | awk '{print substr($0,0,128)}')
local binary_sha=$(shasum -a 512 $dir/heroku-nodejs-plugin-node-$major.tar.gz | awk '{print substr($0,0,128)}')
if [[ "$downloaded_sha" != "$binary_sha" ]]; then
echo "Invalid SHA for file: $dir/heroku-nodejs-plugin-node-$major.tar.gz"
exit 1
else
echo "Verified SHA for file: $dir/heroku-nodejs-plugin-node-$major.tar.gz"
fi
}
trap 'handle_failure' ERR
if [[ -z $TAG_NAME ]]; then
TAG_NAME=$(get_latest_release)
fi
echo "Removing any old versions of the plugin"
delete_old_plugin $PLUGIN_DIR
echo "Downloading plugins"
download_assets_for_release $TAG_NAME $PLUGIN_DIR
echo $TAG_NAME > "$PLUGIN_DIR/version"
echo "Plugins downloaded"
test_hash 8 $PLUGIN_DIR
test_hash 9 $PLUGIN_DIR
test_hash 10 $PLUGIN_DIR
echo "Done"
34990097468575e5a46d805a7cf4de471a29834f8c6f3513cd11c00b641654682e72adbab82bb15590643eb8203be45b294a912e28a0b7984a149fd0d4da4fd4 heroku-nodejs-plugin-node-10-v2.tar.gz
29d68a7d6a41499c9d3f9d1ed5e56a827ae5dc3ce43ed55822cf1c6bdddbe1ba65fee3871c3f272414b39255e0a81538cae83f79e8899aef57095d02bdf2d401 heroku-nodejs-plugin-node-8-v2.tar.gz
2d0586809344cf1272cf2389df11df3d3398e6d75e86ce90447031d42d33c8d8c2ff60a7be9bb4fdbf7e557f91ce828f8d2db20aaf2d94458f6a6d5460041abf heroku-nodejs-plugin-node-9-v2.tar.gz
export PATH="$HOME/.heroku/node/bin:$HOME/.heroku/yarn/bin:$PATH:$HOME/bin:$HOME/node_modules/.bin" export PATH="$HOME/.heroku/node/bin:$HOME/.heroku/yarn/bin:$PATH:$HOME/bin:$HOME/node_modules/.bin"
export NODE_HOME="$HOME/.heroku/node" export NODE_HOME="$HOME/.heroku/node"
export NODE_ENV=${NODE_ENV:-production} export NODE_ENV=${NODE_ENV:-production}
# If the metrics url is not present, this is the wrong type of dyno, or the user has opted out,
# don't include the metrics plugin
if [[ -n "$HEROKU_METRICS_URL" ]] && [[ "${DYNO}" != run\.* ]] && [[ -z "$HEROKU_SKIP_NODE_PLUGIN" ]]; then
# Don't clobber NODE_OPTIONS if the user has set it, just add the require flag to the end
if [[ -z "$NODE_OPTIONS" ]]; then
export NODE_OPTIONS="--require $HOME/.heroku/heroku-nodejs-plugin"
else
export NODE_OPTIONS="${NODE_OPTIONS} --require $HOME/.heroku/heroku-nodejs-plugin"
fi
fi
...@@ -891,6 +891,61 @@ testNpmPrune56Issue() { ...@@ -891,6 +891,61 @@ testNpmPrune56Issue() {
assertCapturedSuccess assertCapturedSuccess
} }
testPluginInstallationBuildTime() {
# The plugin should be installed for Node 8, 9, 10
compile "node-8"
assertFileExists "${compile_dir}/.heroku/heroku-nodejs-plugin/heroku-nodejs-plugin.node"
compile "node-9"
assertFileExists "${compile_dir}/.heroku/heroku-nodejs-plugin/heroku-nodejs-plugin.node"
compile "node-10"
assertFileExists "${compile_dir}/.heroku/heroku-nodejs-plugin/heroku-nodejs-plugin.node"
# but not for earlier versions
compile "node-6"
assertFileDoesNotExist "${compile_dir}/.heroku/heroku-nodejs-plugin/heroku-nodejs-plugin.node"
}
testPluginInstallationRunTime() {
local env_dir=$(mktmpdir)
compile "node-8" "$(mktmpdir)" $env_dir
# by default $NODE_OPTIONS is unmodifed
executeStartup $env_dir
assertEquals "" "$NODE_OPTIONS"
cleanupStartup
# If $HEROKU_METRICS_URL is defined at run time, the script
# should add a require statement to $NODE_OPTIONS
export HEROKU_METRICS_URL=https://localhost:5000
executeStartup $env_dir
assertEquals "--require $compile_dir/.heroku/heroku-nodejs-plugin" "$NODE_OPTIONS"
cleanupStartup
# unless $HEROKU_SKIP_NODE_PLUGIN is defined
export HEROKU_METRICS_URL=https://localhost:5000
export HEROKU_SKIP_NODE_PLUGIN=true
executeStartup $env_dir
assertEquals "" "$NODE_OPTIONS"
cleanupStartup
# if $NODE_OPTIONS already exists, it will append the require command
export HEROKU_METRICS_URL=https://localhost:5000
export NODE_OPTIONS="--max-old-space-size=128"
executeStartup $env_dir
assertEquals "--max-old-space-size=128 --require $compile_dir/.heroku/heroku-nodejs-plugin" "$NODE_OPTIONS"
cleanupStartup
# and it will leave it unchanged if $HEROKU_SKIP_NODE_PLUGIN is defined
export HEROKU_METRICS_URL=https://localhost:5000
export NODE_OPTIONS="--max-old-space-size=128"
export HEROKU_SKIP_NODE_PLUGIN=true
executeStartup $env_dir
assertEquals "--max-old-space-size=128" "$NODE_OPTIONS"
cleanupStartup
}
# Utils # Utils
pushd $(dirname 0) >/dev/null pushd $(dirname 0) >/dev/null
...@@ -928,6 +983,32 @@ compile() { ...@@ -928,6 +983,32 @@ compile() {
capture ${bp_dir}/bin/compile ${compile_dir} ${2:-$(mktmpdir)} $3 capture ${bp_dir}/bin/compile ${compile_dir} ${2:-$(mktmpdir)} $3
} }
# This is meant to be run after `compile`. `cleanupStartup` must be run
# after this function is called before other tests are executed
executeStartup() {
local env_dir=$1
# On Heroku, $HOME is the /app dir, so we need to set it to
# the compile_dir here
export HOME=${compile_dir}
# we need to set any environment variables set via the env_dir and run
# all of the .profile.d scripts
export_env_dir $env_dir
for f in ${compile_dir}/.profile.d/*; do source $f > /dev/null 2> /dev/null ; done
}
cleanupStartup() {
unset HOME
unset NODE_ENV
unset NODE_HOME
unset NODE_OPTIONS
unset DYNO
unset HEROKU_METRICS_URL
unset HEROKU_SKIP_NODE_PLUGIN
}
compileTest() { compileTest() {
default_process_types_cleanup default_process_types_cleanup
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment