Skip to content

Commit

Permalink
Tweak Hatchet tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Malax committed Jan 31, 2025
1 parent cede2dd commit 9b61564
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 195 deletions.
13 changes: 3 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,9 @@ jobs:
HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
HEROKU_API_USER: ${{ secrets.HEROKU_API_USER }}
HEROKU_DISABLE_AUTOUPDATE: 1
# We use the Java buildpack for all Hatchet tests and instruct it to use the code of the current branch as the
# source for the JVM common buildpack with the 'DEFAULT_APP_CONFIG_JVM_COMMON_BUILDPACK' environment
# variable.
HATCHET_BUILDPACK_BASE: https://github.com/heroku/heroku-buildpack-java
HATCHET_BUILDPACK_BRANCH: main
# Default stack for all Heroku apps created by Hatchet
DEFAULT_APP_STACK: ${{ matrix.stack }}
# Default config variables for all Heroku apps created by Hatchet, prefixed with 'DEFAULT_APP_CONFIG_'
DEFAULT_APP_CONFIG_JVM_COMMON_BUILDPACK: https://api.github.com/repos/heroku/heroku-buildpack-jvm-common/tarball/${{ github.head_ref || github.ref_name }}
DEFAULT_APP_CONFIG_JVM_BUILDPACK_ASSETS_BASE_URL: ${{ matrix.assets-base-url }}
HATCHET_BUILDPACK_BASE: https://github.com/heroku/heroku-buildpack-jvm-common
HATCHET_DEFAULT_STACK: ${{ matrix.stack }}
JVM_COMMON_BUILDPACK_TARBALL: https://api.github.com/repos/heroku/heroku-buildpack-jvm-common/tarball/${{ github.head_ref || github.ref_name }}
steps:
- uses: actions/checkout@v4
- name: Install Ruby and dependencies
Expand Down
Empty file.
211 changes: 70 additions & 141 deletions test/spec/java_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,151 +2,80 @@

require_relative 'spec_helper'

describe 'Java' do
%w[1.8 8 11 17 23 11.0.23 openjdk-11.0.23 zulu-11.0.23 heroku-17 zulu-17 heroku-21 zulu-21].each do |jdk_version|
context "when a simple java app on jdk-#{jdk_version}" do
it 'deploys' do
new_default_hatchet_runner('java-servlets-sample').tap do |app|
EXPECTED_JAVA_VERSIONS = {
'heroku-20' => {
'1.8' => 'OpenJDK Runtime Environment (build 1.8.0_432-heroku-b06)',
'8' => 'OpenJDK Runtime Environment (build 1.8.0_432-heroku-b06)',
'11' => 'OpenJDK Runtime Environment (build 11.0.25+9)',
'17' => 'OpenJDK Runtime Environment (build 17.0.13+11)',
'21' => 'OpenJDK Runtime Environment (build 21.0.5+11)',
'23' => 'OpenJDK Runtime Environment (build 23.0.1+11)',
'heroku-21' => 'OpenJDK Runtime Environment (build 21.0.5+11)',
'zulu-21' => 'OpenJDK Runtime Environment Zulu21.38+21-CA (build 21.0.5+11-LTS)',
},
'heroku-22' => {
'1.8' => 'OpenJDK 64-Bit Server VM (Zulu 8.82.0.21-CA-linux64) (build 25.432-b06, mixed mode)',
'8' => 'OpenJDK 64-Bit Server VM (Zulu 8.82.0.21-CA-linux64) (build 25.432-b06, mixed mode)',
'11' => 'OpenJDK Runtime Environment Zulu11.76+21-CA (build 11.0.25+9-LTS)',
'17' => 'OpenJDK Runtime Environment Zulu17.54+21-CA (build 17.0.13+11-LTS)',
'21' => 'OpenJDK Runtime Environment Zulu21.38+21-CA (build 21.0.5+11-LTS)',
'23' => 'OpenJDK Runtime Environment Zulu23.30+13-CA (build 23.0.1+11)',
'heroku-21' => 'OpenJDK Runtime Environment (build 21.0.5+11)',
'zulu-21' => 'OpenJDK Runtime Environment Zulu21.38+21-CA (build 21.0.5+11-LTS)',
},
'heroku-24' => {
'1.8' => 'OpenJDK 64-Bit Server VM (Zulu 8.82.0.21-CA-linux64) (build 25.432-b06, mixed mode)',
'8' => 'OpenJDK 64-Bit Server VM (Zulu 8.82.0.21-CA-linux64) (build 25.432-b06, mixed mode)',
'11' => 'OpenJDK Runtime Environment Zulu11.76+21-CA (build 11.0.25+9-LTS)',
'17' => 'OpenJDK Runtime Environment Zulu17.54+21-CA (build 17.0.13+11-LTS)',
'21' => 'OpenJDK Runtime Environment Zulu21.38+21-CA (build 21.0.5+11-LTS)',
'23' => 'OpenJDK Runtime Environment Zulu23.30+13-CA (build 23.0.1+11)',
'heroku-21' => 'OpenJDK Runtime Environment (build 21.0.5+11)',
'zulu-21' => 'OpenJDK Runtime Environment Zulu21.38+21-CA (build 21.0.5+11-LTS)',
# Ensure that slightly incorrect version strings work
' 21 ' => 'OpenJDK Runtime Environment Zulu21.38+21-CA (build 21.0.5+11-LTS)',
},
}.freeze

# These installed files rarely change so we can check their MD5 hashes
# to validate they were properly installed. However, This is not a
# replacement for testing their functionality in dedicated tests!
FILE_MD5_HASHES = {
'.profile.d/jvmcommon.sh' => 'f4727529254d7e5bd9f7de2ec110bffe',
'.profile.d/default-proc-warning.sh' => 'ad34c4eb52bb81556a9ad77753ee25ed',
'.profile.d/heroku-jvm-metrics.sh' => '9f48b384bc3d9161e45e15906793b191',
'.profile.d/jdbc.sh' => '7a5ce665af22b057c027b1080d2df3d3',
'.profile.d/jvm-redis.sh' => '7873896b3392a91b35af7dcfad115d6b',
'.heroku/with_jmap/bin/java' => 'adc9d1e5abbc1b39d56dc37ce3e26ab8',
'.heroku/with_jmap_and_jstack/bin/java' => '5dbd18ed94a0b1f3542ab100f88fd47d',
'.heroku/with_jstack/bin/java' => '9155584750b1dd1fa621f1e28eab5b04',
'.heroku/bin/heroku-metrics-agent.jar' => '050e7e5b418d0fccd020fa825e915f58',
'.heroku/bin/with_jmap' => '6674c0a0be0ac28ac34767da39e8d2ec',
'.heroku/bin/with_jmap_and_jstack' => '4e3d7b42abfb20502a4e47f963286625',
'.heroku/bin/with_jstack' => '31eb167b16d3dcc2450983964aa57ee7',
}.freeze

RSpec.describe 'Java installation' do
EXPECTED_JAVA_VERSIONS.each do |stack, expected_java_versions|
expected_java_versions.each do |openjdk_selection_string, java_version|
# Skip any tests where the Hatchet stack does not match the stack to test.
# We're not using the tagging approach with "stacks" as it does not work with dynamically
# generated tests.
next if ENV.fetch('HATCHET_DEFAULT_STACK') != stack

context "when stack is '#{stack}' and selection string is '#{openjdk_selection_string}'" do
let(:app) { Hatchet::Runner.new('empty') }

it 'installs the correct OpenJDK version, metrics agent, tools and profile.d scripts' do
app.before_deploy do
set_java_version(Dir.pwd, jdk_version)
set_java_version(Dir.pwd, openjdk_selection_string)
end
app.deploy do
if jdk_version.start_with?('zulu')
expect(app.output).to include("Installing Azul Zulu OpenJDK #{jdk_version.gsub('zulu-', '')}")
elsif jdk_version.start_with?('openjdk')
expect(app.output).to include("Installing Heroku OpenJDK #{jdk_version.gsub('openjdk-', '')}")
elsif jdk_version.start_with?('heroku')
expect(app.output).to include("Installing Heroku OpenJDK #{jdk_version.gsub('heroku-', '')}")
else
expect(app.output).to include("Installing OpenJDK #{jdk_version}")
end

expect(app.output).not_to include('WARNING: No OpenJDK version specified')
expect(app.output).to include('BUILD SUCCESS')
expect(successful_body(app)).to eq('Hello from Java!')
end
end
end
end
end

context 'when a system.properties file with no java.runtime.version' do
it 'deploys' do
new_default_hatchet_runner('java-servlets-sample').tap do |app|
app.before_deploy do
write_sys_props(Dir.pwd, 'maven.version=3.3.9')
end
app.deploy do
expect(app.output).to include('WARNING: No OpenJDK version specified')

if app.stack == 'heroku-24'
expect(app.output).to include('Installing OpenJDK 21')
else
expect(app.output).to include('Installing OpenJDK 1.8')
end

expect(app.output).to include('BUILD SUCCESS')
expect(successful_body(app)).to eq('Hello from Java!')
end
end
end
end

%w[1.8 8 11 17 21 23].each do |jdk_version|
context "when jdk-overlay on #{jdk_version}" do
it 'deploys' do
new_default_hatchet_runner('java-overlay-test').tap do |app|
app.before_deploy do
set_java_version(Dir.pwd, jdk_version)
end
app.deploy do
if jdk_version.start_with?('zulu')
expect(app.output).to include("Installing Azul Zulu OpenJDK #{jdk_version.gsub('zulu-', '')}")
elsif jdk_version.start_with?('openjdk')
expect(app.output).to include("Installing Heroku OpenJDK #{jdk_version.gsub('openjdk-', '')}")
elsif jdk_version.start_with?('heroku')
expect(app.output).to include("Installing Heroku OpenJDK #{jdk_version.gsub('heroku-', '')}")
else
expect(app.output).to include("Installing OpenJDK #{jdk_version}")
end

expect(app.output).not_to include('WARNING: No OpenJDK version specified')
expect(app.output).to include('BUILD SUCCESS')

# Workaround (August 2020):
# When running on CircleCI (and only there), the first app.run command of the test suite will have ^@^@
# prepended to the result string. It looks like caret encoding for two null bytes and the cause is still
# unknown.
cacerts_md5_jdk = app.run('md5sum .jdk/jre/lib/security/cacerts').split[0].delete('^@^@')
sleep 5
cacerts_md5_overlay = app.run('md5sum .jdk-overlay/jre/lib/security/cacerts').split[0].delete('^@^@')

expect(cacerts_md5_jdk).to eq(cacerts_md5_overlay)
end
end
end
end

context "when korvan on jdk-#{jdk_version}" do
it 'runs commands' do
new_default_hatchet_runner('korvan').tap do |app|
app.before_deploy do
set_java_version(Dir.pwd, jdk_version)
end

app.deploy do |deployed_app|
if jdk_version.start_with?('zulu')
expect(deployed_app.output).to include("Installing Azul Zulu OpenJDK #{jdk_version.gsub('zulu-', '')}")
elsif jdk_version.start_with?('openjdk')
expect(deployed_app.output).to include("Installing Heroku OpenJDK #{jdk_version.gsub('openjdk-', '')}")
elsif jdk_version.start_with?('heroku')
expect(deployed_app.output).to include("Installing Heroku OpenJDK #{jdk_version.gsub('heroku-', '')}")
else
expect(deployed_app.output).to include("Installing OpenJDK #{jdk_version}")
end

sleep 1
expect(deployed_app.run('echo $JAVA_TOOL_OPTIONS'))
.not_to include('-Xmx300m -Xss512k')

sleep 1
expect(deployed_app.run('echo $JAVA_OPTS'))
.to include('-Xmx300m -Xss512k')

sleep 1
if jdk_version.start_with?('zulu-1.')
# Skip exit-code default option, required to execute a process from Procfile instead of a command in bash.
expect(deployed_app.run('jce', { heroku: { 'exit-code' => Hatchet::App::SkipDefaultOption } }))
.to include('Illegal key size or default parameters')
else
# Skip exit-code default option, required to execute a process from Procfile instead of a command in bash.
expect(deployed_app.run('jce', { heroku: { 'exit-code' => Hatchet::App::SkipDefaultOption } }))
.to include('Encrypting, "Test"')
.and include('Decrypted: Test')
end

sleep 1
# Skip exit-code default option, required to execute a process from Procfile instead of a command in bash.
expect(deployed_app.run('netpatch', { heroku: { 'exit-code' => Hatchet::App::SkipDefaultOption } }))
.to include('name:eth0 (eth0)')
.and include('name:lo (lo)')

sleep 1
# Skip exit-code default option, required to execute a process from Procfile instead of a command in bash.
expect(deployed_app.run('https', { heroku: { 'exit-code' => Hatchet::App::SkipDefaultOption } }))
.to include('Successfully invoked HTTPS service.')
.and match(/"X-Forwarded-Proto(col)?":\s?"https"/)

if !jdk_version.match(/^9/) &&
!jdk_version.match(/^openjdk-9/) &&
!jdk_version.match(/^zulu-9/) &&
!jdk_version.match(/^[12][0-9]/) &&
!jdk_version.match(/^openjdk-[12][0-9]/)
app.deploy do |app|
expect(app.run('java -version')).to include(java_version)

sleep 1
# Skip exit-code default option, required to execute a process from Procfile instead of a command in bash.
expect(deployed_app.run('pgssl', { heroku: { 'exit-code' => Hatchet::App::SkipDefaultOption } }))
.to match(/sslmode: require/)
FILE_MD5_HASHES.each do |file_path, md5_hash|
expect(app.run("md5sum #{file_path}")).to start_with md5_hash
end
end
end
Expand Down
41 changes: 28 additions & 13 deletions test/spec/jvm_options_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,41 @@

require_relative 'spec_helper'

describe 'Java' do
RSpec.describe 'Java' do
expected_max_ram = {
Basic: 536_870_912,
'Standard-1X': 536_870_912,
'Standard-2X': 1_073_741_824,
'Performance-M': 2_684_354_560,
'Performance-L': 15_032_385_536,
'Performance-L-RAM': 32_212_254_720,
'Performance-XL': 66_571_993_088,
'Performance-2XL': 135_291_469_824,
'Basic' => 536_870_912,
'Standard-1X' => 536_870_912,
'Standard-2X' => 1_073_741_824,
'Performance-M' => 2_684_354_560,
'Performance-L' => 15_032_385_536,
'Performance-L-RAM' => 32_212_254_720,
'Performance-XL' => 66_571_993_088,
'Performance-2XL' => 135_291_469_824,
}

expected_max_ram.each do |size, bytes|
context "when JVM options for #{size}" do
let(:app) do
Hatchet::Runner.new('jvmoptions',
buildpack: 'https://github.com/heroku/heroku-buildpack-java#main',
config: { JVM_COMMON_BUILDPACK: ENV.fetch('JVM_COMMON_BUILDPACK_TARBALL') })
end

it 'has the correct MaxRAM JVM option set' do
new_default_hatchet_runner('jvmoptions').tap do |app|
app.deploy do
app.platform_api.formation.update(app.name, 'web', { 'size' => size })
expect(successful_body(app)).to eq("MaxRAM=#{bytes}")
app.deploy do
app.platform_api.formation.update(app.name, 'web', { 'size' => size })

# It sometimes takes a moment for the formation to be actually updated, even though the API reports it to be.
# To avoid flappy tests, we retry a couple of times to get the expected result.
body = ''
10.times do
body = successful_body(app)
break if body == "MaxRAM=#{bytes}"

sleep(1)
end

expect(body).to eq("MaxRAM=#{bytes}")
end
end
end
Expand Down
21 changes: 0 additions & 21 deletions test/spec/metrics_spec.rb

This file was deleted.

22 changes: 12 additions & 10 deletions test/spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@
require 'date'

ENV['RACK_ENV'] = 'test'
ENV['HATCHET_BUILDPACK_BASE'] ||= 'https://github.com/heroku/heroku-buildpack-jvm-common.git'

RSpec.configure do |config|
config.filter_run focused: true unless ENV['CI']
config.run_all_when_everything_filtered = true
config.alias_example_to :fit, focused: true
config.full_backtrace = true
config.verbose_retry = true # show retry status in spec process
config.default_retry_count = 2 if ENV['CI'] # retry all tests that fail again

config.expect_with :rspec do |c|
c.syntax = :expect
end
# Disables the legacy rspec globals and monkey-patched `should` syntax.
config.disable_monkey_patching!
# Enable flags like --only-failures and --next-failure.
config.example_status_persistence_file_path = '.rspec_status'
# Allows limiting a spec run to individual examples or groups by tagging them
# with `:focus` metadata via the `fit`, `fcontext` and `fdescribe` aliases.
config.filter_run_when_matching :focus
# Allows declaring on which stacks a test/group should run by tagging it with `stacks`.
config.filter_run_excluding stacks: ->(stacks) { !stacks.include?(ENV.fetch('HATCHET_DEFAULT_STACK')) }
# Make rspec-retry output a retry message when its had to retry a test.
config.verbose_retry = true
end

def new_default_hatchet_runner(*, **kwargs)
Expand Down

0 comments on commit 9b61564

Please sign in to comment.