diff --git a/.github/workflows/release-reminder.yml b/.github/workflows/release-reminder.yml new file mode 100644 index 0000000000..ad47caae1d --- /dev/null +++ b/.github/workflows/release-reminder.yml @@ -0,0 +1,94 @@ +name: Release Reminder + +on: + schedule: + - cron: '0 0 * * 4' # Run at midnight on Thursdays + workflow_dispatch: {} + +jobs: + determine-date: + name: Release buildpacks on 2nd and last Thursdays of the month + runs-on: ubuntu-22.04 + outputs: + should_run: ${{ steps.should_run.outputs.bool }} + steps: + - name: Should run + id: should_run + run: | + if [[ ${{ github.event_name }} == 'workflow_dispatch' ]]; then + echo "Skipping date check, because workflow was run manually" + echo "bool=true" >> "${GITHUB_OUTPUT}" + else + day_of_month=$(date +%d) + last_day_cutoff=$(expr $(date -d "-$(date +%d) days month" +%d) - 6) + # Check if it's the second or last Thursday of the month + # second thursday of the month will always be between day 8 and 14 (inclusive) + if [ "$day_of_month" -ge "8" && "$day_of_month" -le "14" ]; then + echo "It's the second Thursday of the month" + echo "bool=true" >> "${GITHUB_OUTPUT}" + # last thursday of the month will always be within 6 days of the last day of the month + # $last_day_cutoff=(# days in this month - 6) + elif [ "$day_of_month" -ge "$last_day_cutoff" ]; then + echo "It's the last Thursday of the month" + echo "bool=true" >> "${GITHUB_OUTPUT}" + else + echo "It's another Thursday of the month" + echo "bool=false" >> "${GITHUB_OUTPUT}" + fi + fi + reminder: + name: Reminder + runs-on: ubuntu-22.04 + needs: [ determine-date ] + if: ${{ needs.determine-date.outputs.should_run == 'true' }} + steps: + - name: Get Date + id: date + run: | + today=$(date +'%m-%d') + window_close_date=$(date -d "+5 days" +'%m-%d') + + echo "today=$today" >> "${GITHUB_OUTPUT}" + echo "window_close_date=$window_close_date" >> "${GITHUB_OUTPUT}" + + - name: Checkout + uses: actions/checkout@v3 + with: + token: ${{ secrets.CF_BOT_GITHUB_TOKEN }} + fetch-depth: 0 + + - name: Get Latest Version + id: latest-version + run: | + echo "val=$(git describe --abbrev=0 --tag)" >> "${GITHUB_OUTPUT}" + + - name: PHP specific task + id: php-specific + if: github.repository == 'cloudfoundry/php-buildpack' + run: | + echo 'task=* Bump PHP modules. See [doc](https://github.com/cloudfoundry/buildpacks-ci/tree/master/scripts/php-modules#pre-buildpack-release-task)' >> "${GITHUB_OUTPUT}" + echo 'title=Bump PHP Modules and ' >> "${GITHUB_OUTPUT}" + + - name: File Issue + id: file-issue + uses: paketo-buildpacks/github-config/actions/issue/file@main + with: + token: ${{ secrets.CF_BOT_GITHUB_TOKEN }} + repo: ${{ github.repository }} + issue_title: "${{ steps.php-specific.outputs.title }}Release: ${{ github.event.repository.name }} (${{ steps.date.outputs.today }})" + issue_body: | + Release reminder for ${{ github.event.repository.name }} + + The ideal release date window for this buildpack starts on: ${{ steps.date.outputs.today }} and ends on ${{ steps.date.outputs.window_close_date }}. + + - name: Add issue to project + id: issue-to-proj + uses: paketo-buildpacks/github-config/actions/issue/add-to-project@main + with: + # CF buildpacks project - https://github.com/orgs/cloudfoundry/projects/37 + project-org: cloudfoundry + project-num: 37 + field-name: Workstream + option-name: Release Train + issue-node-id: ${{ steps.file-issue.outputs.node-id }} + token: ${{ secrets.CF_BOT_GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 63c17b0afd..75d6b50bdb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ +.bundle/ +.idea/.rakeTasks .idea/tasks.xml .idea/workspace.xml .yardoc +.DS_Store build/ -coverage doc +.envrc diff --git a/.idea/.rakeTasks b/.idea/.rakeTasks deleted file mode 100644 index 67bf632628..0000000000 --- a/.idea/.rakeTasks +++ /dev/null @@ -1,7 +0,0 @@ - - diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml deleted file mode 100644 index 72613537be..0000000000 --- a/.idea/codeStyleSettings.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000000..3d20c859f3 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000000..79ee123c2b --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/copyright/Apache_License__Version_2_0.xml b/.idea/copyright/Apache_License__Version_2_0.xml index b07dfd5502..34853bafd0 100644 --- a/.idea/copyright/Apache_License__Version_2_0.xml +++ b/.idea/copyright/Apache_License__Version_2_0.xml @@ -1,9 +1,6 @@ - \ No newline at end of file diff --git a/.idea/dictionaries/bhale.xml b/.idea/dictionaries/bhale.xml index b0b77ac213..9d6586b479 100644 --- a/.idea/dictionaries/bhale.xml +++ b/.idea/dictionaries/bhale.xml @@ -2,17 +2,31 @@ addons + agentpath appdynamics + appinternals + applicationid argv + aspectsecurity atpack + bootclasspath + buildpack + cacert + chrystoki + cklog codeclimate constantized constantizes + cpio + cprof creat + cryptoki dhttp dirname distapplication dnewrelic + dotmatch + enterprisemanager etag extname ffoo @@ -24,13 +38,28 @@ getwd heroku httpok + introscope + isengard javaagent jdk's + jmxremote + jrebel jres jsome jtest killjava + libcklog + libcrpytoki + libcryptoki + libjprofilerti + libjvm + librpilj + libruxitagentloader + libyjpagent ljust + logtostderr + lunaclient + lunajsp mainclass metaspace mkdir @@ -41,11 +70,16 @@ myhost mypass myuser + networkaddress + newrelic newrelicagent overweaving pathnames permgen pkill + postofficehub + preformatted + priv progname proto ratpack @@ -58,9 +92,15 @@ rstrip rubo rubocop + ruxit + rvbd + safenet scriptdir + sessionname shellwords simplecov + socketfactory + stackdriver stderr strftime stringifies @@ -68,7 +108,12 @@ stubframework stubjre submodules + synopsys + takipi tasklib + tcpclient + teamserver + tenanttoken tmpdir tokenized uber @@ -82,6 +127,7 @@ webmock wildcarded wronly + xbootclasspath yardoc yieldparam zipfile diff --git a/.idea/encodings.xml b/.idea/encodings.xml index e206d70d85..f758959656 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -1,5 +1,6 @@ - - - + + + + \ No newline at end of file diff --git a/.idea/google-java-format.xml b/.idea/google-java-format.xml new file mode 100644 index 0000000000..2aa056da34 --- /dev/null +++ b/.idea/google-java-format.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index e32723892f..7ea8392e66 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,7 +1,8 @@ \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 3b312839bf..0000000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 22286b4b73..61a60b2e60 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 4b1ec9f41a..e80b43be78 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -5,5 +5,4 @@ - - + \ No newline at end of file diff --git a/.idea/runConfigurations/All_Tests.xml b/.idea/runConfigurations/All_Tests__2_5_.xml similarity index 66% rename from .idea/runConfigurations/All_Tests.xml rename to .idea/runConfigurations/All_Tests__2_5_.xml index 4762424e81..679ca51e5e 100644 --- a/.idea/runConfigurations/All_Tests.xml +++ b/.idea/runConfigurations/All_Tests__2_5_.xml @@ -1,25 +1,27 @@ - - + - - - - + + + + + - + - + - + + + @@ -37,6 +39,8 @@ - + + \ No newline at end of file diff --git a/.idea/runConfigurations/All_Tests__2_7_.xml b/.idea/runConfigurations/All_Tests__2_7_.xml new file mode 100644 index 0000000000..fcde938296 --- /dev/null +++ b/.idea/runConfigurations/All_Tests__2_7_.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/All_Tests__3_0_.xml b/.idea/runConfigurations/All_Tests__3_0_.xml new file mode 100644 index 0000000000..e0118a93ec --- /dev/null +++ b/.idea/runConfigurations/All_Tests__3_0_.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Without_Integration_Tests.xml b/.idea/runConfigurations/Without_Integration_Tests__2_5_.xml similarity index 69% rename from .idea/runConfigurations/Without_Integration_Tests.xml rename to .idea/runConfigurations/Without_Integration_Tests__2_5_.xml index ddbc97e621..43ee491dd2 100644 --- a/.idea/runConfigurations/Without_Integration_Tests.xml +++ b/.idea/runConfigurations/Without_Integration_Tests__2_5_.xml @@ -1,25 +1,27 @@ - - + - - - - + + + + + - + - + - + + + @@ -37,6 +39,8 @@ - + + \ No newline at end of file diff --git a/.idea/runConfigurations/Without_Integration_Tests__2_7_.xml b/.idea/runConfigurations/Without_Integration_Tests__2_7_.xml new file mode 100644 index 0000000000..a95a86acf7 --- /dev/null +++ b/.idea/runConfigurations/Without_Integration_Tests__2_7_.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Without_Integration_Tests__3_0_.xml b/.idea/runConfigurations/Without_Integration_Tests__3_0_.xml new file mode 100644 index 0000000000..221ce17870 --- /dev/null +++ b/.idea/runConfigurations/Without_Integration_Tests__3_0_.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/rubocop.xml b/.idea/runConfigurations/rubocop.xml new file mode 100644 index 0000000000..e6ac5bd81b --- /dev/null +++ b/.idea/runConfigurations/rubocop.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/versions.xml b/.idea/runConfigurations/versions.xml new file mode 100644 index 0000000000..419c96cf97 --- /dev/null +++ b/.idea/runConfigurations/versions.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/versions__Markdown_.xml b/.idea/runConfigurations/versions__Markdown_.xml new file mode 100644 index 0000000000..98c17ad2ba --- /dev/null +++ b/.idea/runConfigurations/versions__Markdown_.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/versions__Pivotal_Network_.xml b/.idea/runConfigurations/versions__Pivotal_Network_.xml new file mode 100644 index 0000000000..5139f49ed7 --- /dev/null +++ b/.idea/runConfigurations/versions__Pivotal_Network_.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/versions__YAML_.xml b/.idea/runConfigurations/versions__YAML_.xml new file mode 100644 index 0000000000..4c2a1a108c --- /dev/null +++ b/.idea/runConfigurations/versions__YAML_.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.rubocop.yml b/.rubocop.yml index 89ed16fc24..38f85fc115 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,37 +1,80 @@ --- -require: rubocop-rspec +plugins: + - rubocop-rspec AllCops: - Include: - - '**/Rakefile' - - 'rakelib/**/*' + NewCops: enable + SuggestExtensions: false Exclude: - - 'build/**/*' - - 'vendor/**/*' + - 'build/**/*' +Layout/EmptyLinesAroundBlockBody: + Exclude: + - 'spec/**/*.rb' +Layout/EmptyLinesAroundClassBody: + Enabled: false +Layout/EmptyLinesAroundModuleBody: + Enabled: false +Layout/LineLength: + Max: 120 +Layout/MultilineOperationIndentation: + Enabled: false Metrics/AbcSize: - Max: 18 + Max: 25 +Metrics/BlockLength: + Exclude: + - 'spec/**/*.rb' Metrics/ClassLength: - Max: 200 + Max: 250 Metrics/CyclomaticComplexity: Max: 10 -Metrics/LineLength: - Max: 120 Metrics/MethodLength: - Max: 17 + Max: 25 +Naming/MethodParameterName: + Enabled: false Metrics/ParameterLists: - Max: 6 + Max: 10 Metrics/PerceivedComplexity: Max: 10 -RSpec/FilePath: +RSpec/AnyInstance: + Enabled: false +RSpec/ExampleLength: + Max: 20 +RSpec/ExpectOutput: Enabled: false +RSpec/SpecFilePathFormat: + Enabled: false +RSpec/SpecFilePathSuffix: + Enabled: false +RSpec/MissingExampleGroupArgument: + Enabled: false +RSpec/MultipleExpectations: + Enabled: false +RSpec/NestedGroups: + Max: 4 +Security/Open: + Enabled: false # False positive at the moment Style/Documentation: Enabled: false -Style/EmptyLinesAroundBlockBody: - Exclude: - - 'spec/**/*' -Style/EmptyLinesAroundClassBody: +Style/FormatStringToken: Enabled: false -Style/EmptyLinesAroundModuleBody: +Lint/MissingSuper: + Enabled: false +Style/StringConcatenation: + Enabled: false +Lint/EmptyBlock: + Exclude: + - 'spec/**/*.rb' +RSpec/MultipleMemoizedHelpers: + Max: 10 +RSpec/VerifiedDoubleReference: Enabled: false -Style/MultilineOperationIndentation: +Style/OptionalBooleanParameter: Enabled: false +Lint/RedundantCopDisableDirective: + Enabled: false # disabled as this can be tough to manage across multiple ruby versions +Style/RedundantFreeze: + Enabled: false # enable when we only support ruby 3.0+ +Naming/BlockForwarding: + Enabled: false # enable when we only support Ruby 3.1+ +Style/HashSyntax: + EnforcedShorthandSyntax: either diff --git a/.ruby-version b/.ruby-version index 68b3a4cd70..2aa5131992 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -1.9.3-p551 +3.4.7 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6cd43c6747..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: ruby -rvm: -- 2.1.5 -- 1.9.3-p551 -before_script: if [[ $TRAVIS_RUBY_VERSION != '1.9.3-p551' || $TRAVIS_SECURE_ENV_VARS - != 'true' ]]; then unset CODECLIMATE_REPO_TOKEN; fi -notifications: - slack: - secure: TjtH21rnlUlNVu47g+cZSfgI+01RyAZzuMNU1nZskuUMJzf0PHGQAvA9VVVtC5SQiipGg0eB60xIlFN1VW/pYGw5yUcRkuqp9BUywC6CQFpduOc8fyBJ/ZD90Uu3tS/dCc5WCPRA1KPvGgtd6biofBuC/PJ8rMHQO6SVHUxGVvI= - webhooks: - urls: - - http://build-monitor.cfapps.io/projects/JBP/webhook - on_start: true -env: - global: - secure: chtqT1mTsdgd7WFeieNcMnwK4bI+4kzpNbzHo18ITOEsjrTxHinYGRitS+CbJ0sVivOlfunWkF2mcEs1941LCSGsUzNVv8tEYfAS94HO/dK/WJMEtpH8r1JTXoP4yY2rb86ynSB79LuIzjBHpEv4FohrxdScLaoy88+Aw3boauM= #CODECLIMATE_REPO_TOKEN diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 75a843f580..d1c35a9bc9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -_Have something you’d like to contribute to the buildpack? We welcome pull requests, but ask that you carefully read this document first to understand how best to submit them; what kind of changes are likely to be accepted; and what to expect from the Cloud Foundry Java Experience team when evaluating your submission._ +_Have something you'd like to contribute to the buildpack? We welcome pull requests, but ask that you carefully read this document first to understand how best to submit them; what kind of changes are likely to be accepted; and what to expect from the Cloud Foundry Java Experience team when evaluating your submission._ _Please refer back to this document as a checklist before issuing any pull request; this will save time for everyone!_ @@ -19,7 +19,9 @@ If you're considering anything more than correcting a typo or fixing a minor bug [vcap-dev]: https://groups.google.com/a/cloudfoundry.org/forum/#!forum/vcap-dev ## Sign the Contributor License Agreement -Please open an issue in the [GitHub issue tracker][] to receive instructions on how to fill out the Contributor License Agreement. +If you are not yet covered under a Corporate CLA or Individual CLA, you'll be prompted to sign or be approved by your company when you put in your first Pull Request. Please follow the prompts in the EasyCLA check within that Pull Request. For additional assistance please [open a ticket here][]. + +[open a ticket here]: https://jira.linuxfoundation.org/servicedesk/customer/portal/4 ## Use short branch names Branches used when submitting pull requests should preferably using succinct, lower-case, dash (-) delimited names, such as 'fix-warnings', 'fix-typo', etc. In [fork-and-edit][] cases, the GitHub default 'patch-1' is fine as well. This is important, because branch names show up in the merge commits that result from accepting pull requests, and should be as expressive and concise as possible. @@ -41,7 +43,7 @@ Please carefully follow the whitespace and formatting conventions already presen ## Add Apache license header to all new classes ```ruby # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -58,16 +60,16 @@ Please carefully follow the whitespace and formatting conventions already presen require ...; ``` ## Update Apache license header to modified files as necessary -Always check the date range in the license header. For example, if you've modified a file in 2014 whose header still reads +Always check the date range in the license header. For example, if you've modified a file in 2016 whose header still reads ```ruby # Copyright 2013 the original author or authors. ``` -then be sure to update it to 2014 appropriately +then be sure to update it to 2020 appropriately ```ruby - # Copyright 2013-2014 the original author or authors. + # Copyright 2013-2020 the original author or authors. ``` ## Submit RSpec test cases for all behavior changes diff --git a/Gemfile b/Gemfile index 0dccab2b8f..9c057db63f 100644 --- a/Gemfile +++ b/Gemfile @@ -1,15 +1,24 @@ +# frozen_string_literal: true + source 'https://rubygems.org' +# Ruby 3.4+ standard library gems that need to be explicit +gem 'base64' +gem 'bigdecimal' +gem 'digest' +gem 'racc' +gem 'set' +gem 'tmpdir' + group :development do - gem 'codeclimate-test-reporter' gem 'rake' gem 'redcarpet' gem 'rspec' - gem 'rubocop' + gem 'rubocop', '~> 1.60' gem 'rubocop-rspec' gem 'rubyzip' - gem 'simplecov' gem 'tee' + gem 'terminal-table' gem 'webmock' gem 'yard' end diff --git a/Gemfile.lock b/Gemfile.lock index fcac40ed69..9206946216 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,70 +1,99 @@ GEM remote: https://rubygems.org/ specs: - addressable (2.3.6) - ast (2.0.0) - astrolabe (1.3.0) - parser (>= 2.2.0.pre.3, < 3.0) - codeclimate-test-reporter (0.4.3) - simplecov (>= 0.7.1, < 1.0.0) - crack (0.4.2) - safe_yaml (~> 1.0.0) - diff-lcs (1.2.5) - docile (1.1.5) - multi_json (1.10.1) - parser (2.2.0.pre.8) - ast (>= 1.1, < 3.0) - slop (~> 3.4, >= 3.4.5) - powerpack (0.0.9) - rainbow (2.0.0) - rake (10.4.2) - redcarpet (3.2.2) - rspec (3.1.0) - rspec-core (~> 3.1.0) - rspec-expectations (~> 3.1.0) - rspec-mocks (~> 3.1.0) - rspec-core (3.1.7) - rspec-support (~> 3.1.0) - rspec-expectations (3.1.2) + addressable (2.8.0) + public_suffix (>= 2.0.2, < 5.0) + ast (2.4.3) + base64 (0.3.0) + bigdecimal (3.3.1) + crack (0.4.5) + rexml + diff-lcs (1.5.0) + digest (3.2.1) + fileutils (1.8.0) + hashdiff (1.0.1) + json (2.16.0) + language_server-protocol (3.17.0.5) + lint_roller (1.1.0) + parallel (1.27.0) + parser (3.3.10.0) + ast (~> 2.4.1) + racc + prism (1.6.0) + public_suffix (4.0.7) + racc (1.8.1) + rainbow (3.1.1) + rake (13.0.6) + redcarpet (3.5.1) + regexp_parser (2.11.3) + rexml (3.4.4) + rspec (3.11.0) + rspec-core (~> 3.11.0) + rspec-expectations (~> 3.11.0) + rspec-mocks (~> 3.11.0) + rspec-core (3.11.0) + rspec-support (~> 3.11.0) + rspec-expectations (3.11.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.1.0) - rspec-mocks (3.1.3) - rspec-support (~> 3.1.0) - rspec-support (3.1.2) - rubocop (0.28.0) - astrolabe (~> 1.3) - parser (>= 2.2.0.pre.7, < 3.0) - powerpack (~> 0.0.6) - rainbow (>= 1.99.1, < 3.0) - ruby-progressbar (~> 1.4) - rubocop-rspec (1.2.1) - ruby-progressbar (1.7.0) - rubyzip (1.1.6) - safe_yaml (1.0.4) - simplecov (0.9.1) - docile (~> 1.1.0) - multi_json (~> 1.0) - simplecov-html (~> 0.8.0) - simplecov-html (0.8.0) - slop (3.6.0) + rspec-support (~> 3.11.0) + rspec-mocks (3.11.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.11.0) + rspec-support (3.11.0) + rubocop (1.81.7) + json (~> 2.3) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.47.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.48.0) + parser (>= 3.3.7.2) + prism (~> 1.4) + rubocop-rspec (3.8.0) + lint_roller (~> 1.1) + rubocop (~> 1.81) + ruby-progressbar (1.13.0) + rubyzip (2.3.2) + set (1.1.2) tee (1.0.0) - webmock (1.20.4) - addressable (>= 2.3.6) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + tmpdir (0.3.1) + fileutils + unicode-display_width (2.6.0) + webmock (3.14.0) + addressable (>= 2.8.0) crack (>= 0.3.2) - yard (0.8.7.6) + hashdiff (>= 0.4.0, < 2.0.0) + webrick (1.7.0) + yard (0.9.27) + webrick (~> 1.7.0) PLATFORMS ruby DEPENDENCIES - codeclimate-test-reporter + base64 + bigdecimal + digest + racc rake redcarpet rspec - rubocop + rubocop (~> 1.60) rubocop-rspec rubyzip - simplecov + set tee + terminal-table + tmpdir webmock yard + +BUNDLED WITH + 2.3.12 diff --git a/NOTICE b/NOTICE index 8d3e4c02a9..0bc6e64605 100644 --- a/NOTICE +++ b/NOTICE @@ -1,2 +1,15 @@ - Cloud Foundry Java Buildpack - Copyright 2013 the original author or authors. +Cloud Foundry Java Buildpack + +Copyright (c) 2013-Present CloudFoundry.org Foundation, Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README.md b/README.md index 0385565fe5..e562ea1a34 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,18 @@ # Cloud Foundry Java Buildpack -[![Build Status](https://travis-ci.org/cloudfoundry/java-buildpack.svg?branch=master)](https://travis-ci.org/cloudfoundry/java-buildpack) -[![Dependency Status](https://gemnasium.com/cloudfoundry/java-buildpack.svg)](https://gemnasium.com/cloudfoundry/java-buildpack) -[![Code Climate](https://codeclimate.com/repos/5224adaec7f3a3415107004c/badges/bc49f7d7f8dfc47057c8/gpa.svg)](https://codeclimate.com/repos/5224adaec7f3a3415107004c/feed) -[![Code Climate](https://codeclimate.com/repos/5224adaec7f3a3415107004c/badges/bc49f7d7f8dfc47057c8/coverage.svg)](https://codeclimate.com/repos/5224adaec7f3a3415107004c/feed) -The `java-buildpack` is a [Cloud Foundry][] buildpack for running JVM-based applications. It is designed to run many JVM-based applications ([Grails][], [Groovy][], Java Main, [Play Framework][], [Spring Boot][], and Servlet) with no additional configuration, but supports configuration of the standard components, and extension to add custom components. +The `java-buildpack` is a [Cloud Foundry][] buildpack for running JVM-based applications. It is designed to run many JVM-based applications ([Grails][], [Groovy][], Java Main, [Play Framework][], [Spring Boot][], and Servlet) with no additional configuration, but supports configuration of the standard components, and extension to add custom components. ## Usage To use this buildpack specify the URI of the repository when pushing an application to Cloud Foundry: ```bash -cf push -p -b https://github.com/cloudfoundry/java-buildpack.git +$ cf push -p -b https://github.com/cloudfoundry/java-buildpack.git ``` ## Examples The following are _very_ simple examples for deploying the artifact types that we support. +* [Embedded web server](docs/example-embedded-web-server.md) * [Grails](docs/example-grails.md) * [Groovy](docs/example-groovy.md) * [Java Main](docs/example-java_main.md) @@ -24,91 +21,230 @@ The following are _very_ simple examples for deploying the artifact types that w * [Spring Boot CLI](docs/example-spring_boot_cli.md) ## Configuration and Extension -The buildpack supports configuration and extension through the use of Git repository forking. The easiest way to accomplish this is to use [GitHub's forking functionality][] to create a copy of this repository. Make the required configuration and extension changes in the copy of the repository. Then specify the URL of the new repository when pushing Cloud Foundry applications. If the modifications are generally applicable to the Cloud Foundry community, please submit a [pull request][] with the changes. -To learn how to configure various properties of the buildpack, follow the "Configuration" links below. More information on extending the buildpack is available [here](docs/extending.md). +The buildpack default configuration can be overridden with an environment variable matching the configuration file you wish to override minus the `.yml` extension. It is not possible to add new configuration properties and properties with `nil` or empty values will be ignored by the buildpack (in this case you will have to extend the buildpack, see below). The value of the variable should be valid inline yaml, referred to as "flow style" in the yaml spec ([Wikipedia][] has a good description of this yaml syntax). + +There are two levels of overrides: operator and application developer. + + - If you are an operator that wishes to override configuration across a foundation, you may do this by setting environment variable group entries that begin with a prefix of `JBP_DEFAULT`. + - If you are an application developer that wishes to override configuration for an individual application, you may do this by setting environment variables that begin with a prefix of `JBP_CONFIG`. + +Here are some examples: + +### Operator + +1. To change the default version of Java to 11 across all applications on a foundation. + +```bash +$ cf set-staging-environment-variable-group '{"JBP_DEFAULT_OPEN_JDK_JRE":"{jre: {version: 11.+ }}"}' +``` + +2. To change the default repository root across all applications on a foundation. Be careful to ensure that your JSON is properly escaped. + +```bash +$ cf set-staging-environment-variable-group '{"JBP_DEFAULT_REPOSITORY": "{default_repository_root: \"http://repo.example.io\" }"}' +``` + +3. To change the default JVM vendor across all applications on a foundation. Be careful to ensure that your JSON is properly escaped. + +```bash +$ cf set-staging-environment-variable-group '{"JBP_DEFAULT_COMPONENTS": "{jres: [\"JavaBuildpack::Jre::ZuluJRE\"]}"}' +``` + +### Application Developer + +1. To change the default version of Java to 11 and adjust the memory heuristics then apply this environment variable to the application. + +```bash +$ cf set-env my-application JBP_CONFIG_OPEN_JDK_JRE '{ jre: { version: 11.+ }, memory_calculator: { stack_threads: 25 } }' +``` + +2. If the key or value contains a special character such as `:` it should be escaped with double quotes. For example, to change the default repository path for the buildpack. + +```bash +$ cf set-env my-application JBP_CONFIG_REPOSITORY '{ default_repository_root: "http://repo.example.io" }' +``` + +3. If the key or value contains an environment variable that you want to bind at runtime you need to escape it from your shell. For example, to add command line arguments containing an environment variable to a [Java Main](docs/container-java_main.md) application. + +```bash +$ cf set-env my-application JBP_CONFIG_JAVA_MAIN '{ arguments: "--server.port=9090 --foo=bar" }' +``` + +4. An example of configuration is to specify a `javaagent` that is packaged within an application. + +```bash +$ cf set-env my-application JAVA_OPTS '-javaagent:app/META-INF/myagent.jar -Dmyagent.config_file=app/META-INF/my_agent.conf' +``` + +5. Environment variable can also be specified in the applications `manifest` file. For example, to specify an environment variable in an applications manifest file that disables Auto-reconfiguration. + +```bash +env: + JBP_CONFIG_SPRING_AUTO_RECONFIGURATION: '{ enabled: false }' +``` + +6. This final example shows how to change the version of Tomcat that is used by the buildpack with an environment variable specified in the applications manifest file. + +```bash +env: + JBP_CONFIG_TOMCAT: '{ tomcat: { version: 8.0.+ } }' +``` + +See the [Environment Variables][] documentation for more information. + +To learn how to configure various properties of the buildpack, follow the "Configuration" links below. + +The buildpack supports extension through the use of Git repository forking. The easiest way to accomplish this is to use [GitHub's forking functionality][] to create a copy of this repository. Make the required extension changes in the copy of the repository. Then specify the URL of the new repository when pushing Cloud Foundry applications. If the modifications are generally applicable to the Cloud Foundry community, please submit a [pull request][] with the changes. More information on extending the buildpack is available [here](docs/extending.md). ## Additional Documentation * [Design](docs/design.md) * [Security](docs/security.md) * Standard Containers - * [Dist ZIP](docs/container-dist_zip.md) - * [Groovy](docs/container-groovy.md) ([Configuration](docs/container-groovy.md#configuration)) - * [Java Main](docs/container-java_main.md) ([Configuration](docs/container-java_main.md#configuration)) - * [Play Framework](docs/container-play_framework.md) - * [Ratpack](docs/container-ratpack.md) - * [Spring Boot](docs/container-spring_boot.md) - * [Spring Boot CLI](docs/container-spring_boot_cli.md) ([Configuration](docs/container-spring_boot_cli.md#configuration)) - * [Tomcat](docs/container-tomcat.md) ([Configuration](docs/container-tomcat.md#configuration)) + * [Dist ZIP](docs/container-dist_zip.md) + * [Groovy](docs/container-groovy.md) ([Configuration](docs/container-groovy.md#configuration)) + * [Java Main](docs/container-java_main.md) ([Configuration](docs/container-java_main.md#configuration)) + * [Play Framework](docs/container-play_framework.md) + * [Ratpack](docs/container-ratpack.md) + * [Spring Boot](docs/container-spring_boot.md) + * [Spring Boot CLI](docs/container-spring_boot_cli.md) ([Configuration](docs/container-spring_boot_cli.md#configuration)) + * [Tomcat](docs/container-tomcat.md) ([Configuration](docs/container-tomcat.md#configuration)) * Standard Frameworks - * [AppDynamics Agent](docs/framework-app_dynamics_agent.md) ([Configuration](docs/framework-app_dynamics_agent.md#configuration)) - * [Java Options](docs/framework-java_opts.md) ([Configuration](docs/framework-java_opts.md#configuration)) - * [MariaDB JDBC](docs/framework-maria_db_jdbc.md) ([Configuration](docs/framework-maria_db_jdbc.md#configuration)) - * [New Relic Agent](docs/framework-new_relic_agent.md) ([Configuration](docs/framework-new_relic_agent.md#configuration)) - * [Play Framework Auto Reconfiguration](docs/framework-play_framework_auto_reconfiguration.md) ([Configuration](docs/framework-play_framework_auto_reconfiguration.md#configuration)) - * [Play Framework JPA Plugin](docs/framework-play_framework_jpa_plugin.md) ([Configuration](docs/framework-play_framework_jpa_plugin.md#configuration)) - * [PostgreSQL JDBC](docs/framework-postgresql_jdbc.md) ([Configuration](docs/framework-postgresql_jdbc.md#configuration)) - * [Spring Auto Reconfiguration](docs/framework-spring_auto_reconfiguration.md) ([Configuration](docs/framework-spring_auto_reconfiguration.md#configuration)) - * [Spring Insight](docs/framework-spring_insight.md) + * [AppDynamics Agent](docs/framework-app_dynamics_agent.md) ([Configuration](docs/framework-app_dynamics_agent.md#configuration)) + * [AspectJ Weaver Agent](docs/framework-aspectj_weaver_agent.md) ([Configuration](docs/framework-aspectj_weaver_agent.md#configuration)) + * [Azure Application Insights Agent](docs/framework-azure_application_insights_agent.md) ([Configuration](docs/framework-azure_application_insights_agent.md#configuration)) + * [Checkmarx IAST Agent](docs/framework-checkmarx_iast_agent.md) ([Configuration](docs/framework-checkmarx_iast_agent.md#configuration)) + * [Client Certificate Mapper](docs/framework-client_certificate_mapper.md) ([Configuration](docs/framework-client_certificate_mapper.md#configuration)) + * [Container Customizer](docs/framework-container_customizer.md) ([Configuration](docs/framework-container_customizer.md#configuration)) + * [Container Security Provider](docs/framework-container_security_provider.md) ([Configuration](docs/framework-container_security_provider.md#configuration)) + * [Contrast Security Agent](docs/framework-contrast_security_agent.md) ([Configuration](docs/framework-contrast_security_agent.md#configuration)) + * [DataDog](docs/framework-datadog_javaagent.md) ([Configuration](docs/framework-datadog_javaagent.md#configuration) + * [Debug](docs/framework-debug.md) ([Configuration](docs/framework-debug.md#configuration)) + * [Elastic APM Agent](docs/framework-elastic_apm_agent.md) ([Configuration](docs/framework-elastic_apm_agent.md#configuration)) + * [Dynatrace SaaS/Managed OneAgent](docs/framework-dynatrace_one_agent.md) ([Configuration](docs/framework-dynatrace_one_agent.md#configuration)) + * [Google Stackdriver Debugger](docs/framework-google_stackdriver_debugger.md) ([Configuration](docs/framework-google_stackdriver_debugger.md#configuration)) + * [Google Stackdriver Profiler](docs/framework-google_stackdriver_profiler.md) ([Configuration](docs/framework-google_stackdriver_profiler.md#configuration)) + * [Introscope Agent](docs/framework-introscope_agent.md) ([Configuration](docs/framework-introscope_agent.md#configuration)) + * [JaCoCo Agent](docs/framework-jacoco_agent.md) ([Configuration](docs/framework-jacoco_agent.md#configuration)) + * [Java CfEnv](docs/framework-java-cfenv.md) ([Configuration](docs/framework-java-cfenv.md#configuration)) + * [Java Memory Assistant](docs/framework-java_memory_assistant.md) ([Configuration](docs/framework-java_memory_assistant.md#configuration)) + * [Java Options](docs/framework-java_opts.md) ([Configuration](docs/framework-java_opts.md#configuration)) + * [JProfiler Profiler](docs/framework-jprofiler_profiler.md) ([Configuration](docs/framework-jprofiler_profiler.md#configuration)) + * [JRebel Agent](docs/framework-jrebel_agent.md) ([Configuration](docs/framework-jrebel_agent.md#configuration)) + * [JMX](docs/framework-jmx.md) ([Configuration](docs/framework-jmx.md#configuration)) + * [Luna Security Provider](docs/framework-luna_security_provider.md) ([Configuration](docs/framework-luna_security_provider.md#configuration)) + * [MariaDB JDBC](docs/framework-maria_db_jdbc.md) ([Configuration](docs/framework-maria_db_jdbc.md#configuration)) (also supports MySQL) + * [Multiple Buildpack](docs/framework-multi_buildpack.md) + * [Metric Writer](docs/framework-metric_writer.md) ([Configuration](docs/framework-metric_writer.md#configuration)) + * [New Relic Agent](docs/framework-new_relic_agent.md) ([Configuration](docs/framework-new_relic_agent.md#configuration)) + * [PostgreSQL JDBC](docs/framework-postgresql_jdbc.md) ([Configuration](docs/framework-postgresql_jdbc.md#configuration)) + * [ProtectApp Security Provider](docs/framework-protect_app_security_provider.md) ([Configuration](docs/framework-protect_app_security_provider.md#configuration)) + * [Riverbed AppInternals Agent](docs/framework-riverbed_appinternals_agent.md) ([Configuration](docs/framework-riverbed_appinternals_agent.md#configuration)) + * [Sealights Agent](docs/framework-sealights_agent.md) ([Configuration](docs/framework-sealights_agent.md#configuration)) + * [Seeker Security Provider](docs/framework-seeker_security_provider.md) ([Configuration](docs/framework-seeker_security_provider.md#configuration)) + * [Splunk Observability Cloud](docs/framework-splunk_otel_java_agent.md) ([Configuration](docs/framework-splunk_otel_java_agent.md#user-provided-service)) + * [Spring Auto Reconfiguration](docs/framework-spring_auto_reconfiguration.md) ([Configuration](docs/framework-spring_auto_reconfiguration.md#configuration)) + * [Spring Insight](docs/framework-spring_insight.md) + * [SkyWalking Agent](docs/framework-sky_walking_agent.md) ([Configuration](docs/framework-sky_walking_agent.md#configuration)) + * [Takipi Agent](docs/framework-takipi_agent.md) ([Configuration](docs/framework-takipi_agent.md#configuration)) + * [YourKit Profiler](docs/framework-your_kit_profiler.md) ([Configuration](docs/framework-your_kit_profiler.md#configuration)) * Standard JREs - * [OpenJDK](docs/jre-open_jdk_jre.md) ([Configuration](docs/jre-open_jdk_jre.md#configuration)) - * [Oracle](docs/jre-oracle_jre.md) ([Configuration](docs/jre-oracle_jre.md#configuration)) + * [Azul Zulu](docs/jre-zulu_jre.md) ([Configuration](docs/jre-zulu_jre.md#configuration)) + * [Azul Platform Prime](docs/jre-zing_jre.md) ([Configuration](docs/jre-zing_jre.md#configuration)) + * [GraalVM](docs/jre-graal_vm_jre.md) ([Configuration](docs/jre-graal_vm_jre.md#configuration)) + * [IBM® SDK, Java™ Technology Edition](docs/jre-ibm_jre.md) ([Configuration](docs/jre-ibm_jre.md#configuration)) + * [OpenJDK](docs/jre-open_jdk_jre.md) ([Configuration](docs/jre-open_jdk_jre.md#configuration)) + * [Oracle](docs/jre-oracle_jre.md) ([Configuration](docs/jre-oracle_jre.md#configuration)) + * [SapMachine](docs/jre-sap_machine_jre.md) ([Configuration](docs/jre-sap_machine_jre.md#configuration)) * [Extending](docs/extending.md) - * [Application](docs/extending-application.md) - * [Droplet](docs/extending-droplet.md) - * [BaseComponent](docs/extending-base_component.md) - * [VersionedDependencyComponent](docs/extending-versioned_dependency_component.md) - * [ModularComponent](docs/extending-modular_component.md) - * [Caches](docs/extending-caches.md) ([Configuration](docs/extending-caches.md#configuration)) - * [Logging](docs/extending-logging.md) ([Configuration](docs/extending-logging.md#configuration)) - * [Repositories](docs/extending-repositories.md) ([Configuration](docs/extending-repositories.md#configuration)) - * [Utilities](docs/extending-utilities.md) + * [Application](docs/extending-application.md) + * [Droplet](docs/extending-droplet.md) + * [BaseComponent](docs/extending-base_component.md) + * [VersionedDependencyComponent](docs/extending-versioned_dependency_component.md) + * [ModularComponent](docs/extending-modular_component.md) + * [Caches](docs/extending-caches.md) ([Configuration](docs/extending-caches.md#configuration)) + * [Logging](docs/extending-logging.md) ([Configuration](docs/extending-logging.md#configuration)) + * [Repositories](docs/extending-repositories.md) ([Configuration](docs/extending-repositories.md#configuration)) + * [Utilities](docs/extending-utilities.md) * [Debugging the Buildpack](docs/debugging-the-buildpack.md) * [Buildpack Modes](docs/buildpack-modes.md) * Related Projects - * [Java Buildpack Dependency Builder](https://github.com/cloudfoundry/java-buildpack-dependency-builder) - * [Java Test Applications](https://github.com/cloudfoundry/java-test-applications) - * [Java Buildpack System Tests](https://github.com/cloudfoundry/java-buildpack-system-test) + * [Java Buildpack Dependency Builder](https://github.com/cloudfoundry/java-buildpack-dependency-builder) + * [Java Buildpack Memory Calculator](https://github.com/cloudfoundry/java-buildpack-memory-calculator) + * [Java Test Applications](https://github.com/cloudfoundry/java-test-applications) + * [Java Buildpack System Tests](https://github.com/cloudfoundry/java-buildpack-system-test) + * [jvmkill](https://github.com/cloudfoundry/jvmkill) ## Building Packages -The buildpack can be packaged up so that it can be uploaded to Cloud Foundry using the `cf create-buildpack` and `cf update-buildpack` commands. In order to create these packages, the rake `package` task is used. +The buildpack can be packaged up so that it can be uploaded to Cloud Foundry using the `cf create-buildpack` and `cf update-buildpack` commands. In order to create these packages, the rake `package` task is used. + +Note that this process is not currently supported on Windows. It is possible it will work, but it is not tested, and no additional functionality has been added to make it work. ### Online Package -The online package is a version of the buildpack that is as minimal as possible and is configured to connect to the network for all dependencies. This package is about 50K in size. To create the online package, run: +The online package is a version of the buildpack that is as minimal as possible and is configured to connect to the network for all dependencies. This package is about 250K in size. To create the online package, run: ```bash -bundle install -bundle exec rake package +$ bundle install +$ bundle exec rake clean package ... Creating build/java-buildpack-cfd6b17.zip ``` ### Offline Package -The offline package is a version of the buildpack designed to run without access to a network. It packages the latest version of each dependency (as configured in the [`config/` directory][]) and [disables `remote_downloads`][]. This package is about 180M in size. To create the offline package, use the `OFFLINE=true` argument: +The offline package is a version of the buildpack designed to run without access to a network. It packages the latest version of each dependency (as configured in the [`config/` directory][]) and [disables `remote_downloads`][]. To create the offline package, use the `OFFLINE=true` argument: +To pin the version of dependencies used by the buildpack to the ones currently resolvable use the `PINNED=true` argument. This will update the [`config/` directory][] to contain exact version of each dependency instead of version ranges. ```bash -bundle install -bundle exec rake package OFFLINE=true +$ bundle install +$ bundle exec rake clean package OFFLINE=true PINNED=true +... +Creating build/java-buildpack-offline-cfd6b17.zip +``` + +If you would rather specify the exact version to which the buildpack should bundle, you may manually edit the [`config/` file](`config/`) for the component and indicate the specific version to use. For most components, there is a single `version` property which defaults to a pattern to match the latest version. By setting the `version` property to a fixed version, the buildpack will install that exact version when you run the package command. For JRE config files, like [`config/open_jdk_jre.yml`](config/open_jdk_jre.yml), you need to set the `version_lines` array to include the specific version you'd like to install. By default, the `version_lines` array is going to have a pattern entry for each major version line that matches to the latest patch version. You can override the items in the `version_lines` array to set a specific versions to use when packaging the buildpack. For a JRE, the `jre.version` property is used to set the default version line and must match one of the entries in the `version_lines` property. + +This package size will vary depending on what dependencies are included. You can reduce the size by removing unused components, because only packages referenced in the [`config/components.yml` file](config/components.yml) will be cached. In addition, you can remove entries from the `version_lines` array in JRE configuration files, this removes that JRE version line, to further reduce the file size. + +Additional packages may be added using the `ADD_TO_CACHE` argument. The value of `ADD_TO_CACHE` should be set to the name of a `.yml` file in the [`config/` directory][] with the `.yml` file extension omitted (e.g. `sap_machine_jre`). Multiple file names may be separated by commas. This is useful to add additional JREs. These additional components will not be enabled by default and must be explicitly enabled in the application with the `JBP_CONFIG_COMPONENTS` environment variable. + +```bash +$ bundle install +$ bundle exec rake clean package OFFLINE=true ADD_TO_CACHE=sap_machine_jre,ibm_jre +... +Caching https://public.dhe.ibm.com/ibmdl/export/pub/systems/cloud/runtimes/java/8.0.6.26/linux/x86_64/ibm-java-jre-8.0-6.26-x86_64-archive.bin +Caching https://github.com/SAP/SapMachine/releases/download/sapmachine-11.0.10/sapmachine-jre-11.0.10_linux-x64_bin.tar.gz ... Creating build/java-buildpack-offline-cfd6b17.zip ``` ### Package Versioning -Keeping track of different versions of the buildpack can be difficult. To help with this, the rake `package` task puts a version discriminator in the name of the created package file. The default value for this discriminator is the current Git hash (e.g. `cfd6b17`). To change the version when creating a package, use the `VERSION=` argument: +Keeping track of different versions of the buildpack can be difficult. To help with this, the rake `package` task puts a version discriminator in the name of the created package file. The default value for this discriminator is the current Git hash (e.g. `cfd6b17`). To change the version when creating a package, use the `VERSION=` argument: ```bash -bundle install -bundle exec rake package VERSION=2.1 +$ bundle install +$ bundle exec rake clean package VERSION=2.1 ... Creating build/java-buildpack-2.1.zip ``` +### Packaging Caveats + +1. Prior to version 4.51 when pinning versions, only the default JRE version is pinned. There is [special handling to package additional versions of a JRE](https://github.com/cloudfoundry/java-buildpack/blob/main/rakelib/dependency_cache_task.rb#L128-L144) and the way this works, it will pick the latest version at the time you package not at the time of the version's release. Starting with version 4.51, the version number for all JRE version lines is tracked in the `config/` file. + +2. The `index.yml` file for a dependency is packaged in the buildpack cache when building offline buildpacks. The `index.yml` file isn't versioned with the release, so if you package an offline buildpack later after the release was tagged, it will pull the current `index.yml`, not the one from the time of the release. This can result in errors at build time if a user tells the buildpack to install the latest version of a dependency because the latest version is calculated from the `index.yml` file which has more recent versions than what are packaged in the offline buildpack. For example, if the user says give me Java `11._+` and the buildpack is pinned to Java `11.0.13_8` but at the time you packaged the buildpack the latest version in `index.yml` is `11.0.15_10` then the user will get an error. The buildpack will want to install `11.0.15_10` but it won't be present because `11.0.13_8` is all that's in the buildpack. + + Because of #1 for versions prior to 4.51, this only impacts the default JRE. Non-default JREs always package the most recent version, which is also the most recent version in `index.yml` at the time you package the offline buildpack. For 4.51 and up, this can impact all versions. + +4. Because of #1 and #2, it is not possible to accurately reproduce packages of the buildpack, after releases have been cut. If building pinned or offline buildpacks, it is suggested to build them as soon as possible after a release is cut and save the produced artifact. Alternatively, you would need to maintain your own buildpack dependency repository and keep snapshots of the buildpack dependency repository for each buildpack release you'd like to be able to rebuild. + +See [#892](https://github.com/cloudfoundry/java-buildpack/issues/892#issuecomment-880212806) for additional details. + ## Running Tests To run the tests, do the following: ```bash -bundle install -bundle exec rake +$ bundle install +$ bundle exec rake ``` [Running Cloud Foundry locally][] is useful for privately testing new features. @@ -121,14 +257,16 @@ This buildpack is released under version 2.0 of the [Apache License][]. [`config/` directory]: config [Apache License]: http://www.apache.org/licenses/LICENSE-2.0 -[Cloud Foundry]: http://www.cloudfoundry.com +[Cloud Foundry]: http://www.cloudfoundry.org [contributor guidelines]: CONTRIBUTING.md [disables `remote_downloads`]: docs/extending-caches.md#configuration +[Environment Variables]: http://docs.cloudfoundry.org/devguide/deploy-apps/manifest.html#env-block [GitHub's forking functionality]: https://help.github.com/articles/fork-a-repo [Grails]: http://grails.org [Groovy]: http://groovy.codehaus.org [Play Framework]: http://www.playframework.com [pull request]: https://help.github.com/articles/using-pull-requests [Pull requests]: http://help.github.com/send-pull-requests -[Running Cloud Foundry locally]: http://docs.cloudfoundry.org/deploying/run-local.html +[Running Cloud Foundry locally]: https://github.com/cloudfoundry/cf-deployment/tree/master/iaas-support/bosh-lite [Spring Boot]: http://projects.spring.io/spring-boot/ +[Wikipedia]: https://en.wikipedia.org/wiki/YAML#Basic_components_of_YAML diff --git a/Rakefile b/Rakefile index 899e4bc7a6..d5a096ac18 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -33,13 +34,15 @@ task :check_api_doc do abort "\nFailed due to undocumented public API:\n\n#{output}" if output !~ /100.00% documented/ end -$LOAD_PATH.unshift File.expand_path('..', __FILE__) +$LOAD_PATH.unshift File.expand_path(__dir__) require 'rakelib/dependency_cache_task' require 'rakelib/stage_buildpack_task' require 'rakelib/package_task' +require 'rakelib/versions_task' Package::DependencyCacheTask.new -Package::StageBuildpackTask.new(Dir['bin/**/*', 'config/**/*', 'lib/**/*', 'resources/**/*'] +Package::StageBuildpackTask.new(Dir['bin/**/*', 'config/**/*', 'lib/**/*', 'resources/**/*', 'LICENSE', 'NOTICE'] .reject { |f| File.directory? f }) Package::PackageTask.new +Package::VersionsTask.new -task default: %w(rubocop check_api_doc spec) +task default: %w[rubocop check_api_doc spec] diff --git a/bin/compile b/bin/compile deleted file mode 100755 index 1d74294fa6..0000000000 --- a/bin/compile +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env ruby -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -$stdout.sync = true -$stderr.sync = true -$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) - -require 'java_buildpack/buildpack' - -build_dir = ARGV[0] - -JavaBuildpack::Buildpack.with_buildpack(build_dir, 'Compile failed with exception %s') do |buildpack| - buildpack.compile -end diff --git a/bin/compile b/bin/compile new file mode 120000 index 0000000000..84db910f22 --- /dev/null +++ b/bin/compile @@ -0,0 +1 @@ +./run \ No newline at end of file diff --git a/bin/detect b/bin/detect deleted file mode 100755 index 0af538a042..0000000000 --- a/bin/detect +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env ruby -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -$stdout.sync = true -$stderr.sync = true -$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) - -require 'java_buildpack/buildpack' - -build_dir = ARGV[0] - -components = JavaBuildpack::Buildpack.with_buildpack(build_dir, 'Detect failed with exception %s') do |buildpack| - buildpack.detect -end.compact - -if components.empty? - abort -else - str = components.join(' ') - puts str.length > 255 ? str.slice(0..251) + '...' : str -end diff --git a/bin/detect b/bin/detect new file mode 120000 index 0000000000..84db910f22 --- /dev/null +++ b/bin/detect @@ -0,0 +1 @@ +./run \ No newline at end of file diff --git a/bin/finalize b/bin/finalize new file mode 120000 index 0000000000..84db910f22 --- /dev/null +++ b/bin/finalize @@ -0,0 +1 @@ +./run \ No newline at end of file diff --git a/bin/release b/bin/release deleted file mode 100755 index eeecd019af..0000000000 --- a/bin/release +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env ruby -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -$stdout.sync = true -$stderr.sync = true -$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) - -require 'java_buildpack/buildpack' - -build_dir = ARGV[0] - -output = JavaBuildpack::Buildpack.with_buildpack(build_dir, 'Release failed with exception %s') do |buildpack| - buildpack.release -end - -puts output diff --git a/bin/release b/bin/release new file mode 120000 index 0000000000..84db910f22 --- /dev/null +++ b/bin/release @@ -0,0 +1 @@ +./run \ No newline at end of file diff --git a/bin/ruby-run b/bin/ruby-run new file mode 100755 index 0000000000..c8188ae68b --- /dev/null +++ b/bin/ruby-run @@ -0,0 +1,50 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +$stdout.sync = true +$stderr.sync = true +$LOAD_PATH.unshift File.expand_path('../lib', __dir__) +require 'java_buildpack/buildpack' + +case ARGV[0] +when 'compile' + app_dir = ARGV[1] + JavaBuildpack::Buildpack.with_buildpack(app_dir, nil, nil, 'Compile failed with exception %s', &:compile) + +when 'detect' + app_dir = ARGV[1] + components = JavaBuildpack::Buildpack.with_buildpack(app_dir, nil, nil, 'Detect failed with exception %s', + &:detect).compact + if components.empty? + abort + else + str = components.join(' ') + puts str.length > 255 ? str.slice(0..251) + '...' : str + end + +when 'finalize' + app_dir = ARGV[1] + deps_dir = ARGV[3] + index = ARGV[4] + JavaBuildpack::Buildpack.with_buildpack(app_dir, deps_dir, index, 'Finalize failed with exception %s', &:compile) + +when 'release' + app_dir = ARGV[1] + output = JavaBuildpack::Buildpack.with_buildpack(app_dir, nil, nil, 'Release failed with exception %s', &:release) + puts output +end diff --git a/bin/run b/bin/run new file mode 100755 index 0000000000..f7ea7c9633 --- /dev/null +++ b/bin/run @@ -0,0 +1,109 @@ +#!/usr/bin/env bash + +set -e +set -u +set -o pipefail + +BUILDPACK_DIR="$(cd "$(dirname "${0}")/.." && pwd)" +readonly BUILDPACK_DIR + +RUBY_DIR="/tmp/java-buildpack/ruby" +readonly RUBY_DIR + +function util::config::lookup() { + sed '/^#/d' < "${BUILDPACK_DIR}/config/ruby.yml" +} + +function util::cache::present() { + if [[ -e "${BUILDPACK_DIR}/resources/cache" ]]; then + return 0 + else + return 1 + fi +} + +function util::index::lookup() { + local repository_root + repository_root="$(grep "repository_root" <<< "$(util::config::lookup)" | cut -d' ' -f2)" + + local uri + uri="${repository_root}/index.yml" + + if util::cache::present; then + local sha + sha="$(printf "%s" "${uri}" | shasum -a 256 | cut -d' ' -f1)" + cat "${BUILDPACK_DIR}/resources/cache/${sha}.cached" + else + curl -ssL "${uri}" + fi +} + +function util::semver::parse() { + local version major minor patch + version="$(grep "version" <<< "$(util::config::lookup)" | cut -d' ' -f2)" + major="$(cut -d'.' -f1 <<< "${version}")" + minor="$(cut -d'.' -f2 <<< "${version}")" + patch="$(cut -d'.' -f3 <<< "${version}")" + + printf "%s" "${major/+/*}\\.${minor/+/*}\\.${patch/+/*}" +} + +function util::ruby::stream() { + local uri + uri="${1}" + + if util::cache::present; then + local sha + sha="$(printf "%s" "${uri}" | shasum -a 256 | cut -d' ' -f1)" + cat "${BUILDPACK_DIR}/resources/cache/${sha}.cached" + else + curl -ssL "${uri}" + fi +} + +function util::install() { + local index semver + index="$(util::index::lookup)" + semver="$(util::semver::parse)" + + local uri + uri="$(grep "${semver}" <<< "${index}" | head -n 1 | awk '{print $2}')" + + util::ruby::stream "${uri}" | tar xz -C "${RUBY_DIR}" +} + +function util::print::error() { + local message red reset + message="${1}" + red="\033[0;31m" + reset="\033[0;39m" + + echo -e "${red}${message}${reset}" >&2 + exit 1 +} + +function util::environment::setup() { + export PATH="${RUBY_DIR}/bin:${PATH:-}" + export LIBRARY_PATH="${RUBY_DIR}/lib:${LIBRARY_PATH:-}" + export LD_LIBRARY_PATH="${RUBY_DIR}/lib:${LIBRARY_PATH:-}" + export CPATH="${RUBY_DIR}/include:${CPATH:-}" +} + +function main() { + local phase + phase="$(basename "${0}")" + + # Always install and use buildpack Ruby to ensure consistent version + # regardless of system Ruby availability + mkdir -p "${RUBY_DIR}" + + if [ ! -d "${RUBY_DIR}/bin" ]; then + util::install + fi + + util::environment::setup + + exec "${BUILDPACK_DIR}/bin/ruby-run" "${phase}" "${@-}" +} + +main "${@:-}" diff --git a/ci/Dockerfile b/ci/Dockerfile new file mode 100644 index 0000000000..0b9bc77e52 --- /dev/null +++ b/ci/Dockerfile @@ -0,0 +1,41 @@ +ARG base_image=ubuntu:jammy +FROM ${base_image} + +RUN apt-get update && apt-get install -y wget gnupg + +RUN wget -q -O - https://download.bell-sw.com/pki/GPG-KEY-bellsoft | apt-key add - +RUN echo "deb [arch=amd64] https://apt.bell-sw.com/ stable main" | tee /etc/apt/sources.list.d/bellsoft.list + +RUN apt-get update && apt-get install -y \ + build-essential \ + curl \ + git \ + libffi-dev \ + libssl-dev \ + libreadline-dev \ + libyaml-dev \ + lsb-release \ + locales \ + python3 \ + zip \ + zlib1g-dev \ + bellsoft-java17 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +RUN locale-gen en_US.UTF-8 \ + && /usr/sbin/update-locale LANG=en_US.UTF-8 \ + && dpkg-reconfigure -f noninteractive locales + +RUN git clone https://github.com/rbenv/rbenv.git $HOME/.rbenv && ln -s $HOME/.rbenv/libexec/rbenv /usr/local/bin + +RUN eval "$(rbenv init -)" \ + && git clone https://github.com/rbenv/ruby-build.git $(rbenv root)/plugins/ruby-build + +RUN eval "$(rbenv init -)" \ + && git clone https://github.com/sstephenson/rbenv-default-gems.git $(rbenv root)/plugins/rbenv-default-gems \ + && echo 'bundler 2.3.12' >> $(rbenv root)/default-gems + +RUN eval "$(rbenv init -)" \ + && rbenv install 3.4.7 \ + && rbenv global 3.4.7 diff --git a/resources/open_jdk_jre/bin/killjava.sh b/ci/auto-merge.sh similarity index 52% rename from resources/open_jdk_jre/bin/killjava.sh rename to ci/auto-merge.sh index 168c701038..7437d2654c 100755 --- a/resources/open_jdk_jre/bin/killjava.sh +++ b/ci/auto-merge.sh @@ -1,7 +1,6 @@ #!/usr/bin/env bash -# Encoding: utf-8 # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,36 +14,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Kill script for use as the parameter of OpenJDK's -XX:OnOutOfMemoryError +set -euo pipefail -set -e +pushd upstream + COMMIT=$(git rev-parse HEAD) +popd -echo " -Process Status (Before) -======================= -$(ps -ef) +git clone downstream merged -ulimit (Before) -=============== -$(ulimit -a) +pushd merged + git config --local user.name "$GIT_USER_NAME" + git config --local user.email $GIT_USER_EMAIL -Free Disk Space (Before) -======================== -$(df -h) -" + git remote add upstream ../upstream + git fetch upstream --no-tags -pkill -9 -f .*-XX:OnOutOfMemoryError=.*killjava.* - -echo " -Process Status (After) -====================== -$(ps -ef) - -ulimit (After) -============== -$(ulimit -a) - -Free Disk Space (After) -======================= -$(df -h) -" + git merge --no-ff --log --no-edit $COMMIT +popd diff --git a/ci/create-release.sh b/ci/create-release.sh new file mode 100755 index 0000000000..0ab439ffde --- /dev/null +++ b/ci/create-release.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +set -euo pipefail + +RELEASE=$1 + +echo "---" > config/version.yml +echo "version: v$RELEASE" >> config/version.yml + +bundle exec rake clobber package +mv build/*-buildpack-v$RELEASE.zip $HOME/Desktop + +bundle exec rake clobber package OFFLINE=true PINNED=true +mv build/*-buildpack-offline-v$RELEASE.zip $HOME/Desktop + +bundle exec rake versions:markdown versions:json + +git add . +git commit --message "v$RELEASE Release" +git tag "v$RELEASE" +git reset --hard HEAD^1 diff --git a/resources/oracle_jre/bin/killjava.sh b/ci/package-test.sh similarity index 52% rename from resources/oracle_jre/bin/killjava.sh rename to ci/package-test.sh index 168c701038..e50addeb73 100755 --- a/resources/oracle_jre/bin/killjava.sh +++ b/ci/package-test.sh @@ -1,7 +1,6 @@ #!/usr/bin/env bash -# Encoding: utf-8 # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,36 +14,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Kill script for use as the parameter of OpenJDK's -XX:OnOutOfMemoryError +set -euo pipefail -set -e +export GEM_HOME=$PWD/gems +export LANG=en_US.UTF-8 +export LANGUAGE=en_US.UTF-8 +export LC_ALL=en_US.UTF-8 -echo " -Process Status (Before) -======================= -$(ps -ef) +eval "$(rbenv init -)" -ulimit (Before) -=============== -$(ulimit -a) - -Free Disk Space (Before) -======================== -$(df -h) -" - -pkill -9 -f .*-XX:OnOutOfMemoryError=.*killjava.* - -echo " -Process Status (After) -====================== -$(ps -ef) - -ulimit (After) -============== -$(ulimit -a) - -Free Disk Space (After) -======================= -$(df -h) -" +pushd java-buildpack + bundle install --quiet + bundle exec rake package +popd diff --git a/ci/unit-test.sh b/ci/unit-test.sh new file mode 100755 index 0000000000..bcfd1c4e9e --- /dev/null +++ b/ci/unit-test.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +export GEM_HOME=$PWD/gems +export LANG=en_US.UTF-8 +export LANGUAGE=en_US.UTF-8 +export LC_ALL=en_US.UTF-8 + +eval "$(rbenv init -)" + +pushd java-buildpack + bundle install --quiet + bundle exec rake +popd diff --git a/ci/versions-json.sh b/ci/versions-json.sh new file mode 100755 index 0000000000..ae6b06888e --- /dev/null +++ b/ci/versions-json.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +export GEM_HOME=$PWD/gems +export LANG=en_US.UTF-8 +export LANGUAGE=en_US.UTF-8 +export LC_ALL=en_US.UTF-8 + +eval "$(rbenv init -)" + +pushd java-buildpack + bundle install --quiet + bundle exec rake versions:json +popd diff --git a/ci/versions-markdown.sh b/ci/versions-markdown.sh new file mode 100755 index 0000000000..f4d377a639 --- /dev/null +++ b/ci/versions-markdown.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +export GEM_HOME=$PWD/gems +export LANG=en_US.UTF-8 +export LANGUAGE=en_US.UTF-8 +export LC_ALL=en_US.UTF-8 + +eval "$(rbenv init -)" + +pushd java-buildpack + bundle install --quiet + bundle exec rake versions:markdown +popd diff --git a/ci/versions-yaml.sh b/ci/versions-yaml.sh new file mode 100755 index 0000000000..14edffeb4c --- /dev/null +++ b/ci/versions-yaml.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +export GEM_HOME=$PWD/gems +export LANG=en_US.UTF-8 +export LANGUAGE=en_US.UTF-8 +export LC_ALL=en_US.UTF-8 + +eval "$(rbenv init -)" + +pushd java-buildpack + bundle install --quiet + bundle exec rake versions:yaml +popd diff --git a/ci/versions.sh b/ci/versions.sh new file mode 100755 index 0000000000..9c12787162 --- /dev/null +++ b/ci/versions.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +export GEM_HOME=$PWD/gems +export LANG=en_US.UTF-8 +export LANGUAGE=en_US.UTF-8 +export LC_ALL=en_US.UTF-8 + +eval "$(rbenv init -)" + +pushd java-buildpack + bundle install --quiet + bundle exec rake versions +popd diff --git a/config/app_dynamics_agent.yml b/config/app_dynamics_agent.yml index 593fa9e72e..3c55e187a6 100644 --- a/config/app_dynamics_agent.yml +++ b/config/app_dynamics_agent.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,8 +13,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Configuration for the New Relic framework +# Configuration for the AppDynamics framework --- -version: 3.9.+ -repository_root: "{default.repository.root}/app-dynamics" -default_tier_name: CloudFoundry +version: + +repository_root: "{default.repository.root}/appdynamics" +default_application_name: $(jq -r -n "$VCAP_APPLICATION | .space_name + \":\" + .application_name | @sh") +default_node_name: $(jq -r -n "\"$APPD_CF_NODE_PREFIX\" + ($VCAP_APPLICATION | .application_name) + \":$CF_INSTANCE_INDEX\" | @sh") +default_tier_name: +default_unique_host_name: $(jq -r -n "$VCAP_APPLICATION | .application_id + \":$CF_INSTANCE_INDEX\" | @sh") diff --git a/config/play_framework_jpa_plugin.yml b/config/aspectj_weaver_agent.yml similarity index 77% rename from config/play_framework_jpa_plugin.yml rename to config/aspectj_weaver_agent.yml index 876dba0f99..5fdc234969 100644 --- a/config/play_framework_jpa_plugin.yml +++ b/config/aspectj_weaver_agent.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,8 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Configuration for the Play JPA Plugin framework +# AspectJ Weaver Agent configuration --- -version: 1.+ -repository_root: "{default.repository.root}/play-jpa-plugin" enabled: true diff --git a/config/azure_application_insights_agent.yml b/config/azure_application_insights_agent.yml new file mode 100644 index 0000000000..4199801339 --- /dev/null +++ b/config/azure_application_insights_agent.yml @@ -0,0 +1,19 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the Azure Application Insights framework. +--- +version: 3.+ +repository_root: "{default.repository.root}/azure-application-insights" diff --git a/config/cache.yml b/config/cache.yml index 61b451bf13..9f2be8f19f 100644 --- a/config/cache.yml +++ b/config/cache.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,4 +15,8 @@ # Download cache configuration --- -remote_downloads: enabled \ No newline at end of file +remote_downloads: enabled +client_authentication: + certificate_location: + private_key_location: + private_key_password: diff --git a/config/client_certificate_mapper.yml b/config/client_certificate_mapper.yml new file mode 100644 index 0000000000..e32fbedc82 --- /dev/null +++ b/config/client_certificate_mapper.yml @@ -0,0 +1,21 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Container security provider configuration +--- +version: 2.+ +version_lines: + - 2.+ +repository_root: "{default.repository.root}/client-certificate-mapper" \ No newline at end of file diff --git a/config/components.yml b/config/components.yml index db5a732ae7..90a12ec28e 100644 --- a/config/components.yml +++ b/config/components.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -25,19 +25,58 @@ containers: - "JavaBuildpack::Container::SpringBootCLI" - "JavaBuildpack::Container::Tomcat" -# In order to use Oracle JREs instead of OpenJDK, you must comment out the OpenJDK line and uncomment the Oracle line. +# List of available JREs. In order to use a different one either comment out the OpenJDK line and uncomment the desired one or use environment variables to change this configuration. +# Example: cf set-env JBP_CONFIG_COMPONENTS '{jres: ["JavaBuildpack::Jre::OpenJdkJRE"]}' # Please see the documentation for more detail. jres: +# - "JavaBuildpack::Jre::GraalVmJRE" +# - "JavaBuildpack::Jre::IbmJRE" - "JavaBuildpack::Jre::OpenJdkJRE" # - "JavaBuildpack::Jre::OracleJRE" +# - "JavaBuildpack::Jre::ZuluJRE" +# - "JavaBuildpack::Jre::ZingJRE" +# - "JavaBuildpack::Jre::SapMachineJRE" +# Frameworks are processed in order. +# The MultiBuildpack framework is first in order to allow any framework to override contributions from earlier buildpacks +# The JavaOpts is last in order to allow any user-defined JAVA_OPTS to override contributions from earlier frameworks frameworks: + - "JavaBuildpack::Framework::MultiBuildpack" - "JavaBuildpack::Framework::AppDynamicsAgent" - - "JavaBuildpack::Framework::JavaOpts" + - "JavaBuildpack::Framework::AspectjWeaverAgent" + - "JavaBuildpack::Framework::AzureApplicationInsightsAgent" + - "JavaBuildpack::Framework::CheckmarxIastAgent" + - "JavaBuildpack::Framework::ClientCertificateMapper" + - "JavaBuildpack::Framework::ContainerCustomizer" + - "JavaBuildpack::Framework::ContainerSecurityProvider" + - "JavaBuildpack::Framework::ContrastSecurityAgent" + - "JavaBuildpack::Framework::DatadogJavaagent" + - "JavaBuildpack::Framework::Debug" + - "JavaBuildpack::Framework::DynatraceOneAgent" + - "JavaBuildpack::Framework::ElasticApmAgent" +# - "JavaBuildpack::Framework::GoogleStackdriverDebugger" + - "JavaBuildpack::Framework::GoogleStackdriverProfiler" + - "JavaBuildpack::Framework::IntroscopeAgent" + - "JavaBuildpack::Framework::JacocoAgent" + - "JavaBuildpack::Framework::JavaCfEnv" + - "JavaBuildpack::Framework::JavaMemoryAssistant" + - "JavaBuildpack::Framework::Jmx" + - "JavaBuildpack::Framework::JprofilerProfiler" + - "JavaBuildpack::Framework::JrebelAgent" + - "JavaBuildpack::Framework::LunaSecurityProvider" - "JavaBuildpack::Framework::MariaDbJDBC" + - "JavaBuildpack::Framework::MetricWriter" - "JavaBuildpack::Framework::NewRelicAgent" - - "JavaBuildpack::Framework::PlayFrameworkAutoReconfiguration" - - "JavaBuildpack::Framework::PlayFrameworkJPAPlugin" + - "JavaBuildpack::Framework::OpenTelemetryJavaagent" - "JavaBuildpack::Framework::PostgresqlJDBC" + - "JavaBuildpack::Framework::RiverbedAppinternalsAgent" + - "JavaBuildpack::Framework::SealightsAgent" + - "JavaBuildpack::Framework::SeekerSecurityProvider" - "JavaBuildpack::Framework::SpringAutoReconfiguration" + - "JavaBuildpack::Framework::SplunkOtelJavaAgent" - "JavaBuildpack::Framework::SpringInsight" + - "JavaBuildpack::Framework::SkyWalkingAgent" + - "JavaBuildpack::Framework::YourKitProfiler" + - "JavaBuildpack::Framework::TakipiAgent" + - "JavaBuildpack::Framework::JavaSecurity" + - "JavaBuildpack::Framework::JavaOpts" diff --git a/config/container_customizer.yml b/config/container_customizer.yml new file mode 100644 index 0000000000..cfa6e00b5b --- /dev/null +++ b/config/container_customizer.yml @@ -0,0 +1,19 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the Container Customizer framework +--- +version: 2.+ +repository_root: "{default.repository.root}/container-customizer" diff --git a/config/container_security_provider.yml b/config/container_security_provider.yml new file mode 100644 index 0000000000..f4d88d84fe --- /dev/null +++ b/config/container_security_provider.yml @@ -0,0 +1,21 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Container security provider configuration +--- +version: 1.+ +repository_root: "{default.repository.root}/container-security-provider" +key_manager_enabled: +trust_manager_enabled: diff --git a/config/contrast_security_agent.yml b/config/contrast_security_agent.yml new file mode 100644 index 0000000000..60b5cae8da --- /dev/null +++ b/config/contrast_security_agent.yml @@ -0,0 +1,19 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the ContrastSecurity framework +--- +version: 6.+ +repository_root: https://download.run.pivotal.io/contrast-security diff --git a/config/datadog_javaagent.yml b/config/datadog_javaagent.yml new file mode 100644 index 0000000000..ba5c97a322 --- /dev/null +++ b/config/datadog_javaagent.yml @@ -0,0 +1,19 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2021 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the Datadog APM Javaagent +--- +version: + +repository_root: https://raw.githubusercontent.com/datadog/dd-trace-java/cloudfoundry/ diff --git a/config/debug.yml b/config/debug.yml new file mode 100644 index 0000000000..186de94881 --- /dev/null +++ b/config/debug.yml @@ -0,0 +1,20 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Debug configuration +--- +enabled: false +port: 8000 +suspend: false diff --git a/config/dist_zip.yml b/config/dist_zip.yml new file mode 100644 index 0000000000..6723375a88 --- /dev/null +++ b/config/dist_zip.yml @@ -0,0 +1,18 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the DistZip container +--- +arguments: diff --git a/config/dist_zip_like.yml b/config/dist_zip_like.yml new file mode 100644 index 0000000000..6723375a88 --- /dev/null +++ b/config/dist_zip_like.yml @@ -0,0 +1,18 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the DistZip container +--- +arguments: diff --git a/config/elastic_apm_agent.yml b/config/elastic_apm_agent.yml new file mode 100644 index 0000000000..e9db8ffcf9 --- /dev/null +++ b/config/elastic_apm_agent.yml @@ -0,0 +1,19 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the Elastic Apm Agent framework +--- +version: 1.+ +repository_root: https://raw.githubusercontent.com/elastic/apm-agent-java/master/cloudfoundry diff --git a/config/google_stackdriver_debugger.yml b/config/google_stackdriver_debugger.yml new file mode 100644 index 0000000000..8c4a521661 --- /dev/null +++ b/config/google_stackdriver_debugger.yml @@ -0,0 +1,21 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the Groovy container +--- +version: 2.+ +repository_root: "{default.repository.root}/google-stackdriver-debugger/{platform}/{architecture}" +application_name: +application_version: diff --git a/config/google_stackdriver_profiler.yml b/config/google_stackdriver_profiler.yml new file mode 100644 index 0000000000..470051e04e --- /dev/null +++ b/config/google_stackdriver_profiler.yml @@ -0,0 +1,21 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the Groovy container +--- +version: 0.+ +repository_root: "{default.repository.root}/google-stackdriver-profiler/{platform}/{architecture}" +application_name: +application_version: diff --git a/config/graal_vm_jre.yml b/config/graal_vm_jre.yml new file mode 100644 index 0000000000..94419a9183 --- /dev/null +++ b/config/graal_vm_jre.yml @@ -0,0 +1,34 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# You must specify a the repository root of an GraalVM repository. Please see the documentation for more detail. +# e.g. repository_root: "https://example.com/graalvm-jre/{platform}/{architecture}" +--- +jre: + version: 22.1.+ + version_lines: + - 22.1.+ + - 21.3.+ + repository_root: "" +jvmkill_agent: + version: 1.+ + repository_root: "{default.repository.root}/jvmkill/{platform}/{architecture}" +memory_calculator: + version: 3.+ + repository_root: "{default.repository.root}/memory-calculator/{platform}/{architecture}" + class_count: + headroom: + stack_threads: 250 diff --git a/config/groovy.yml b/config/groovy.yml index 1d70b6cedd..bd697ccd6e 100644 --- a/config/groovy.yml +++ b/config/groovy.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,5 +15,5 @@ # Configuration for the Groovy container --- -version: 2.3.+ +version: 2.5.+ repository_root: "{default.repository.root}/groovy" diff --git a/config/ibm_jre.yml b/config/ibm_jre.yml new file mode 100644 index 0000000000..4446537109 --- /dev/null +++ b/config/ibm_jre.yml @@ -0,0 +1,27 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Configuration for JRE repository +--- +jre: + version: 1.8.+ + version_lines: + - 1.8.+ + repository_root: https://raw.githubusercontent.com/ibmruntimes/ci.docker/master/ibmjava/meta/jre/linux/{architecture} + heap_ratio: 0.75 +jvmkill_agent: + version: 1.+ + repository_root: "https://raw.githubusercontent.com/ibmruntimes/jvmkill/jvmkill-ibmagent/jvmkill-agent/" diff --git a/config/introscope_agent.yml b/config/introscope_agent.yml new file mode 100644 index 0000000000..2675cbf308 --- /dev/null +++ b/config/introscope_agent.yml @@ -0,0 +1,20 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the CA Wily framework +--- +version: + +repository_root: https://packages.broadcom.com/artifactory/apm-agents +default_agent_name: $(jq -r -n "$VCAP_APPLICATION | .application_name") diff --git a/config/jacoco_agent.yml b/config/jacoco_agent.yml new file mode 100644 index 0000000000..d26b0bec05 --- /dev/null +++ b/config/jacoco_agent.yml @@ -0,0 +1,19 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the JaCoco Agent framework +--- +version: 0.+ +repository_root: "{default.repository.root}/jacoco" diff --git a/config/play_framework_auto_reconfiguration.yml b/config/java_cf_env.yml similarity index 64% rename from config/play_framework_auto_reconfiguration.yml rename to config/java_cf_env.yml index feb522ce83..a021138ff7 100644 --- a/config/play_framework_auto_reconfiguration.yml +++ b/config/java_cf_env.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2023 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,10 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Configuration for the Play Auto Reconfiguration framework. -# Note that the repository is shared with the Spring Auto Reconfiguration framework and should be kept in step to -# avoid conflicts. +# Configuration for the Java CfEnv framework. +# See https://github.com/pivotal-cf/java-cfenv for library information + --- -version: 1.+ -repository_root: "{default.repository.root}/auto-reconfiguration" +version: 3.+ +repository_root: "{default.repository.root}/java-cfenv" enabled: true diff --git a/config/java_main.yml b/config/java_main.yml new file mode 100644 index 0000000000..bcd986be62 --- /dev/null +++ b/config/java_main.yml @@ -0,0 +1,19 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the Java Main container +--- +java_main_class: +arguments: diff --git a/config/java_memory_assistant.yml b/config/java_memory_assistant.yml new file mode 100644 index 0000000000..9f4c874933 --- /dev/null +++ b/config/java_memory_assistant.yml @@ -0,0 +1,41 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +--- +enabled: false +agent: + version: 0.+ + repository_root: https://raw.githubusercontent.com/SAP/java-memory-assistant/repository + heap_dump_folder: + check_interval: 5s + max_frequency: 1/1m + log_level: + thresholds: + heap: + code_cache: + metaspace: + perm_gen: + compressed_class: + eden: + survivor: + old_gen: ">600MB" + tenured_gen: + code_heap.non_nmethods: + code_heap.non_profiled_nmethods: + code_heap.profiled_nmethods: + +clean_up: + version: 0.+ + repository_root: https://raw.githubusercontent.com/SAP/java-memory-assistant-tools/repository-cu + max_dump_count: 1 diff --git a/config/java_opts.yml b/config/java_opts.yml index 1de33db86e..60a38d8b7d 100644 --- a/config/java_opts.yml +++ b/config/java_opts.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright (c) 2014 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,4 +16,4 @@ # JAVA_OPTS configuration --- from_environment: true -# java_opts: +java_opts: diff --git a/config/jmx.yml b/config/jmx.yml new file mode 100644 index 0000000000..12b75b24b3 --- /dev/null +++ b/config/jmx.yml @@ -0,0 +1,19 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# JMX configuration +--- +enabled: false +port: 5000 diff --git a/config/jprofiler_profiler.yml b/config/jprofiler_profiler.yml new file mode 100644 index 0000000000..e48294a00c --- /dev/null +++ b/config/jprofiler_profiler.yml @@ -0,0 +1,22 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# JMX configuration +--- +version: 15.+ +repository_root: https://download.run.pivotal.io/jprofiler +enabled: false +nowait: true +port: 8849 diff --git a/config/jrebel_agent.yml b/config/jrebel_agent.yml new file mode 100644 index 0000000000..fd197b69eb --- /dev/null +++ b/config/jrebel_agent.yml @@ -0,0 +1,20 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the JRebel framework +--- +version: + +repository_root: https://dl.zeroturnaround.com/jrebel +enabled: true diff --git a/config/logging.yml b/config/logging.yml index eb83017b59..1bb256393f 100644 --- a/config/logging.yml +++ b/config/logging.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,4 +15,5 @@ # Logging configuration --- -default_log_level: INFO \ No newline at end of file +default_log_level: INFO +enable_log_file: false diff --git a/config/luna_security_provider.yml b/config/luna_security_provider.yml new file mode 100644 index 0000000000..e79f7103a1 --- /dev/null +++ b/config/luna_security_provider.yml @@ -0,0 +1,22 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the Luna Security Provider framework +--- +version: 7.+ +repository_root: "{default.repository.root}/luna-security-provider" +ha_logging_enabled: true +logging_enabled: false +tcp_keep_alive_enabled: false diff --git a/config/maria_db_jdbc.yml b/config/maria_db_jdbc.yml index d294543c03..d13bf8d749 100644 --- a/config/maria_db_jdbc.yml +++ b/config/maria_db_jdbc.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,5 +15,5 @@ # Configuration for the MariaDB JDBC framework --- -version: 1.1.+ +version: 2.+ repository_root: "{default.repository.root}/mariadb-jdbc" diff --git a/config/metric_writer.yml b/config/metric_writer.yml new file mode 100644 index 0000000000..7d833ef384 --- /dev/null +++ b/config/metric_writer.yml @@ -0,0 +1,20 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Metric Writer configuration +--- +version: 3.+ +repository_root: "{default.repository.root}/metric-writer" +enabled: false diff --git a/config/new_relic_agent.yml b/config/new_relic_agent.yml index c87e0e6b8f..e431d0be13 100644 --- a/config/new_relic_agent.yml +++ b/config/new_relic_agent.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,5 +15,8 @@ # Configuration for the New Relic framework --- -version: 3.+ -repository_root: "{default.repository.root}/new-relic" +version: + +repository_root: https://download.run.pivotal.io/new-relic +extensions: + version: 1.+ + repository_root: diff --git a/config/open_jdk_jre.yml b/config/open_jdk_jre.yml index 2a4feb2af6..8e706e2fb6 100644 --- a/config/open_jdk_jre.yml +++ b/config/open_jdk_jre.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,16 +14,21 @@ # limitations under the License. # Configuration for JRE repositories keyed by vendor -# To go back to Java 7, permgen should be used instead of metaspace. Please see the documentation for more detail. --- -repository_root: "{default.repository.root}/openjdk/{platform}/{architecture}" -version: 1.8.0_+ -memory_sizes: - metaspace: 64m.. - # permgen: 64m.. -memory_heuristics: - heap: 75 - metaspace: 10 - # permgen: 10 - stack: 5 - native: 10 +jre: + version: 1.8.0_+ + version_lines: + - 1.8.0_+ + - 11.+ + - 17.+ + - 21.+ + repository_root: "{default.repository.root}/openjdk/{platform}/{architecture}" +jvmkill_agent: + version: 1.+ + repository_root: "{default.repository.root}/jvmkill/{platform}/{architecture}" +memory_calculator: + version: 3.+ + repository_root: "{default.repository.root}/memory-calculator/{platform}/{architecture}" + class_count: + headroom: + stack_threads: 250 diff --git a/config/open_telemetry_javaagent.yml b/config/open_telemetry_javaagent.yml new file mode 100644 index 0000000000..2e45e5713e --- /dev/null +++ b/config/open_telemetry_javaagent.yml @@ -0,0 +1,19 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2023 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the OpenTelemetry Javaagent +--- +version: + +repository_root: https://raw.githubusercontent.com/open-telemetry/opentelemetry-java-instrumentation/cloudfoundry/ diff --git a/config/oracle_jre.yml b/config/oracle_jre.yml index cdb6226dc2..a24848b28c 100644 --- a/config/oracle_jre.yml +++ b/config/oracle_jre.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,18 +15,23 @@ # Configuration for JRE repositories keyed by vendor # Pre Java 1.8, permgen was used instead of metaspace. Please see the documentation for more detail. ---- -# You must specify a the repository root of an Oracle JRE repository. Please see the documentation for more detail. -# e.g. repository_root: "http://example.com/oracle-jre/{platform}/{architecture}" -repository_root: "" -version: 1.8.0_+ -memory_sizes: - metaspace: 64m.. -# permgen: 64m.. -memory_heuristics: - heap: 75 - metaspace: 10 -# permgen: 10 - stack: 5 - native: 10 +# You must specify a the repository root of an Oracle JRE repository. Please see the documentation for more detail. +# e.g. repository_root: "https://example.com/oracle-jre/{platform}/{architecture}" +--- +jre: + version: 1.8.0_+ + version_lines: + - 1.8.0_+ + - 11.+ + - 17.+ + repository_root: "" +jvmkill_agent: + version: 1.+ + repository_root: "{default.repository.root}/jvmkill/{platform}/{architecture}" +memory_calculator: + version: 3.+ + repository_root: "{default.repository.root}/memory-calculator/{platform}/{architecture}" + class_count: + headroom: + stack_threads: 250 diff --git a/config/packaging.yml b/config/packaging.yml new file mode 100644 index 0000000000..db047e5ef9 --- /dev/null +++ b/config/packaging.yml @@ -0,0 +1,212 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2022 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- +access_logging_support: + name: Tomcat Access Logging Support + cve_notes: Included inline above + release_notes: Included inline above + +agent: + name: Java Memory Assistant Agent + +app_dynamics_agent: + name: AppDynamics Agent + release_notes: '[Release Notes](https://docs.appdynamics.com/appd/onprem/24.x/latest/en/product-and-release-announcements/release-notes)' + +azure_application_insights_agent: + name: Azure Application Insights Agent + release_notes: '[Release Notes](https://github.com/Microsoft/ApplicationInsights-Java/releases)' + +clean_up: + name: Java Memory Assistant Clean Up + +client_certificate_mapper: + name: Client Certificate Mapper + cve_notes: 'Included inline above' + release_notes: 'Included inline above' + +container_customizer: + name: Spring Boot Container Customizer + cve_notes: 'Included inline above' + release_notes: 'Included inline above' + +container_security_provider: + name: Container Security Provider + cve_notes: 'Included inline above' + release_notes: 'Included inline above' + +contrast_security_agent: + name: Contrast Security Agent + release_notes: '[Release Notes](https://docs.contrastsecurity.com/en/java-agent-release-notes-and-archive.html)' + +datadog_javaagent: + name: Datadog APM Javaagent + release_notes: '[Release Notes](https://github.com/DataDog/dd-trace-java/releases)' + +dynatrace_one_agent: + name: Dynatrace OneAgent + release_notes: '[Release Notes](https://www.dynatrace.com/support/help/whats-new/release-notes/#oneagent)' + +elastic_apm_agent: + name: Elastic APM Agent + release_notes: '[Release Notes](https://www.elastic.co/guide/en/apm/agent/java/current/release-notes.html)' + +geode_store: + name: Geode Tomcat Session Store + +google_stackdriver_profiler: + name: Google Stackdriver Profiler + release_notes: '[Release Notes](https://cloud.google.com/profiler/docs/release-notes)' + +groovy: + name: Groovy + release_notes: '[Release Notes](http://www.groovy-lang.org/releases.html)' + +introscope_agent: + name: CA Introscope APM Framework + +jacoco_agent: + name: JaCoCo Agent + release_notes: '[Release Notes](https://github.com/jacoco/jacoco/releases)' + +java_cf_env: + name: Java CFEnv + release_notes: '[Release Notes](https://github.com/pivotal-cf/java-cfenv/releases)' + +jprofiler_profiler: + name: JProfiler Profiler + release_notes: '[ChangeLog](https://www.ej-technologies.com/download/jprofiler/changelog.html)' + +jre: + name: OpenJDK JRE 8 + cve_notes: '[Risk Matrix](https://www.oracle.com/security-alerts/cpuapr2025.html#AppendixJAVA)' + release_notes: '[Release Notes](https://docs.bell-sw.com/liberica-jdk/8u452b11/general/release-notes/)' + +jre-11: + name: OpenJDK JRE 11 + cve_notes: '[Risk Matrix](https://www.oracle.com/security-alerts/cpuapr2025.html#AppendixJAVA)' + release_notes: '[Release Notes](https://docs.bell-sw.com/liberica-jdk/11.0.27b9/general/release-notes/)' + +jre-17: + name: OpenJDK JRE 17 + cve_notes: '[Risk Matrix](https://www.oracle.com/security-alerts/cpuapr2025.html#AppendixJAVA)' + release_notes: '[Release Notes](https://docs.bell-sw.com/liberica-jdk/17.0.15b10/general/release-notes/)' + +jre-21: + name: OpenJDK JRE 21 + cve_notes: '[Risk Matrix](https://www.oracle.com/security-alerts/cpuapr2025.html#AppendixJAVA)' + release_notes: '[Release Notes](https://docs.bell-sw.com/liberica-jdk/21.0.7b9/general/release-notes/)' + +jrebel_agent: + name: JRebel Agent + release_notes: '[ChangeLog](https://www.jrebel.com/products/jrebel/changelog)' + +jvmkill_agent: + name: jvmkill Agent + cve_notes: 'Included inline above' + release_notes: 'Included inline above' + +lifecycle_support: + name: Tomcat Lifecycle Support + cve_notes: 'Included inline above' + release_notes: 'Included inline above' + +logging_support: + name: Tomcat Logging Support + cve_notes: 'Included inline above' + release_notes: 'Included inline above' + +luna_security_provider: + name: Gemalto Luna Security Provider + release_notes: '[Release Notes](https://www.thalesdocs.com/gphsm/luna/7/docs/network/Content/CRN/Luna/CRN_Luna.htm)' + +maria_db_jdbc: + name: MariaDB JDBC Driver + release_notes: '[Release Notes](https://mariadb.com/kb/en/mariadb-connector-j-2-7-9-release-notes/)' + +memory_calculator: + name: Memory Calculator + cve_notes: 'Included inline above' + release_notes: 'Included inline above' + +metric_writer: + name: Metric Writer + cve_notes: 'Included inline above' + release_notes: 'Included inline above' + +new_relic_agent: + name: New Relic Agent + release_notes: '[Release Notes](https://docs.newrelic.com/docs/release-notes/agent-release-notes/java-release-notes/)' + +open_telemetry_javaagent: + name: Open Telemetry Agent + release_notes: '[Release Notes](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases)' + +postgresql_jdbc: + name: PostgreSQL JDBC Driver + release_notes: '[ChangeLog](https://jdbc.postgresql.org/documentation/changelog.html)' + +protect_app_security_provider: + name: Gemalto ProtectApp Security Provider + +redis_store: + name: Redis Session Store + cve_notes: 'Included inline above' + release_notes: 'Included inline above' + +riverbed_appinternals_agent: + name: Riverbed Appinternals Agent + +ruby: + name: Ruby + release_notes: 'https://www.ruby-lang.org/en/downloads/releases' + +sealights_agent: + name: SeaLights Agent + +sky_walking_agent: + name: SkyWalking + release_notes: '[ChangeLog](https://github.com/apache/skywalking/tree/master/changes)' + +splunk_otel_java_agent: + name: Splunk OpenTelemetry Java Agent + release_notes: '[Release Notes](https://github.com/signalfx/splunk-otel-java/releases)' + +spring_auto_reconfiguration: + name: Spring Auto-reconfiguration + cve_notes: 'Included inline above' + release_notes: 'Included inline above' + +spring_boot_cli: + name: Spring Boot CLI + +takipi_agent: + name: Takipi Agent + release_notes: '[Release Notes](https://doc.overops.com/docs/whats-new)' + +tomcat: + name: Tomcat 9 + cve_notes: '[Security](https://tomcat.apache.org/security-9.html)' + release_notes: '[ChangeLog](https://tomcat.apache.org/tomcat-9.0-doc/changelog.html)' + +tomcat-10: + name: Tomcat 10.1 + cve_notes: '[Security](https://tomcat.apache.org/security-10.html)' + release_notes: '[ChangeLog](https://tomcat.apache.org/tomcat-10.1-doc/changelog.html)' + +your_kit_profiler: + name: YourKit Profiler + release_notes: '[Release Notes](https://www.yourkit.com/download/yjp_2025_3_builds.jsp)' diff --git a/config/postgresql_jdbc.yml b/config/postgresql_jdbc.yml index 9193fe8f0b..2238a6ba0c 100644 --- a/config/postgresql_jdbc.yml +++ b/config/postgresql_jdbc.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,5 +15,5 @@ # Configuration for the Postgresql JDBC framework --- -version: 9.3.+ +version: 42.+ repository_root: "{default.repository.root}/postgresql-jdbc" diff --git a/config/protect_app_security_provider.yml b/config/protect_app_security_provider.yml new file mode 100644 index 0000000000..04fa1c23b2 --- /dev/null +++ b/config/protect_app_security_provider.yml @@ -0,0 +1,22 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the ProtectApp Security Provider framework + +# You must specify a the repository root of an ProtectApp repository. Please see the documentation for more detail. +# e.g. repository_root: "https://example.com/protectapp-installer/" +--- +version: 8.+ +repository_root: "" diff --git a/config/repository.yml b/config/repository.yml index ab4c37a2fe..8ec03e178a 100644 --- a/config/repository.yml +++ b/config/repository.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,4 +15,4 @@ # Repository configuration --- -default_repository_root: https://download.run.pivotal.io +default_repository_root: https://java-buildpack.cloudfoundry.org diff --git a/config/riverbed_appinternals_agent.yml b/config/riverbed_appinternals_agent.yml new file mode 100644 index 0000000000..c628c5ba8a --- /dev/null +++ b/config/riverbed_appinternals_agent.yml @@ -0,0 +1,20 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the riverbed appinternals agent framework +--- +version: 11.+ +repository_root: https://pcf-instrumentation-download.steelcentral.net/ +rvbd_moniker: $(jq -r -n "$VCAP_APPLICATION | .application_name") diff --git a/config/ruby.yml b/config/ruby.yml new file mode 100644 index 0000000000..0d05199092 --- /dev/null +++ b/config/ruby.yml @@ -0,0 +1,21 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for Ruby +# Note: Ruby 3.2.8+ is required for Psych 5.0.1+ which supports YAML.load_file +# with permitted_classes and aliases keyword arguments used throughout the buildpack +--- +version: 3.2.+ +repository_root: https://raw.githubusercontent.com/cloudfoundry/ruby-buildpack/master/java-index diff --git a/config/sap_machine_jre.yml b/config/sap_machine_jre.yml new file mode 100644 index 0000000000..0c372cc4d7 --- /dev/null +++ b/config/sap_machine_jre.yml @@ -0,0 +1,33 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Configuration for JRE repository +--- +jre: + version: 11.+ + version_lines: + - 11.+ + - 17.+ + repository_root: "https://sapmachine.io/assets/cf/jre/{platform}/{architecture}" +jvmkill_agent: + version: 1.+ + repository_root: "{default.repository.root}/jvmkill/{platform}/{architecture}" +memory_calculator: + version: 3.+ + repository_root: "{default.repository.root}/memory-calculator/{platform}/{architecture}" + class_count: + headroom: + stack_threads: 250 diff --git a/config/sealights_agent.yml b/config/sealights_agent.yml new file mode 100644 index 0000000000..a75881a853 --- /dev/null +++ b/config/sealights_agent.yml @@ -0,0 +1,23 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the SeaLights Agent framework +--- +version: + +repository_root: https://agents.sealights.co/pcf +build_session_id: +lab_id: +proxy: +auto_upgrade: false diff --git a/config/sky_walking_agent.yml b/config/sky_walking_agent.yml new file mode 100644 index 0000000000..dd8f25892c --- /dev/null +++ b/config/sky_walking_agent.yml @@ -0,0 +1,20 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the Sky Walking framework. +--- +version: + +repository_root: "{default.repository.root}/sky-walking" +default_application_name: $(jq -r -n "$VCAP_APPLICATION | .space_name + \":\" + .application_name | @sh") diff --git a/config/splunk_otel_java_agent.yml b/config/splunk_otel_java_agent.yml new file mode 100644 index 0000000000..7a3824cf67 --- /dev/null +++ b/config/splunk_otel_java_agent.yml @@ -0,0 +1,21 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the Splunk Distribution of OpenTelemetry Java Instrumentation +# See https://github.com/signalfx/splunk-otel-java for more information +--- +version: + +repository_root: https://raw.githubusercontent.com/signalfx/splunk-otel-java/main/deployments/cloudfoundry/ + \ No newline at end of file diff --git a/config/spring_auto_reconfiguration.yml b/config/spring_auto_reconfiguration.yml index 5b88fbfa9c..14e976bff7 100644 --- a/config/spring_auto_reconfiguration.yml +++ b/config/spring_auto_reconfiguration.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,6 +17,6 @@ # Note that the repository is shared with the Play Auto Reconfiguration framework and should be kept in step to # avoid conflicts. --- -version: 1.+ +version: 2.+ repository_root: "{default.repository.root}/auto-reconfiguration" enabled: true diff --git a/config/spring_boot_cli.yml b/config/spring_boot_cli.yml index c5f3b52f75..54d4c8c5bd 100644 --- a/config/spring_boot_cli.yml +++ b/config/spring_boot_cli.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,5 +17,5 @@ # Note that the repository is shared with the Play Auto Reconfiguration framework and should be kept in step to # avoid conflicts. --- -version: 1.1.+ +version: 2.+ repository_root: "{default.repository.root}/spring-boot-cli" diff --git a/config/takipi_agent.yml b/config/takipi_agent.yml new file mode 100644 index 0000000000..dd8407034e --- /dev/null +++ b/config/takipi_agent.yml @@ -0,0 +1,21 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the Takipi framework +--- +version: 4.+ +repository_root: https://get.takipi.com/cloudfoundry +node_name_prefix: node +application_name: diff --git a/config/tomcat.yml b/config/tomcat.yml index de37fe52a4..ac2df4789a 100644 --- a/config/tomcat.yml +++ b/config/tomcat.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2021 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,16 +16,24 @@ # Configuration for the Tomcat container --- tomcat: - version: 8.0.+ + version: 9.+ + version_lines: + - 9.+ + - 10.1.+ repository_root: "{default.repository.root}/tomcat" + context_path: + external_configuration_enabled: false +external_configuration: + version: 1.+ + repository_root: lifecycle_support: - version: 2.+ + version: 3.+ repository_root: "{default.repository.root}/tomcat-lifecycle-support" logging_support: - version: 2.+ + version: 3.+ repository_root: "{default.repository.root}/tomcat-logging-support" access_logging_support: - version: 2.+ + version: 3.+ repository_root: "{default.repository.root}/tomcat-access-logging-support" access_logging: disabled redis_store: @@ -34,22 +42,8 @@ redis_store: database: 0 timeout: 2000 connection_pool_size: 2 -gemfire_store: - gemfire: - version: 8.0.+ - repository_root: "{default.repository.root}/gem-fire" - gemfire_modules: - version: 8.0.+ - repository_root: "{default.repository.root}/gem-fire-modules" - gemfire_modules_tomcat7: - version: 8.0.+ - repository_root: "{default.repository.root}/gem-fire-modules-tomcat7" - gemfire_security: - version: 8.0.+ - repository_root: "{default.repository.root}/gem-fire-security" - gemfire_logging: - version: 1.5.8 - repository_root: "{default.repository.root}/slf4j-jdk14" - gemfire_logging_api: - version: 1.5.8 - repository_root: "{default.repository.root}/slf4j-api" +geode_store: + # The version of Geode Store must be less than or equal to your Tanzu Gemfire for VMs version to ensure compatibility. + # The Geode Store version is pinned to 1.12.4 to be compatible with the most commonly used versions of Tanzu Gemfire for VMs. + version: 1.14.9 + repository_root: https://java-buildpack-tomcat-gemfire-store.s3-us-west-2.amazonaws.com diff --git a/config/your_kit_profiler.yml b/config/your_kit_profiler.yml new file mode 100644 index 0000000000..261bdbda91 --- /dev/null +++ b/config/your_kit_profiler.yml @@ -0,0 +1,22 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# JMX configuration +--- +version: + +repository_root: https://download.run.pivotal.io/your-kit/{platform}/{architecture} +enabled: false +port: 10001 +default_session_name: $(jq -r -n "$VCAP_APPLICATION | .application_name + \":$CF_INSTANCE_INDEX\"") diff --git a/config/zing_jre.yml b/config/zing_jre.yml new file mode 100755 index 0000000000..d1631a4843 --- /dev/null +++ b/config/zing_jre.yml @@ -0,0 +1,33 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for JRE repositories keyed by vendor +# Pre Java 1.8, permgen was used instead of metaspace. Please see the documentation for more detail. + +# You must specify a the repository root of a Azul Platform Prime JRE repository. Please see the documentation for more detail. +# e.g. repository_root: "https://example.com/zing-jre/{platform}/{architecture}" +--- +jre: + version: 1.8.0_+ + repository_root: "" +jvmkill_agent: + version: 1.+ + repository_root: "{default.repository.root}/jvmkill/{platform}/{architecture}" +memory_calculator: + version: 3.+ + repository_root: "{default.repository.root}/memory-calculator/{platform}/{architecture}" + class_count: + headroom: + stack_threads: 250 diff --git a/config/zulu_jre.yml b/config/zulu_jre.yml new file mode 100755 index 0000000000..5b8f41e05e --- /dev/null +++ b/config/zulu_jre.yml @@ -0,0 +1,36 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for JRE repositories keyed by vendor +# Pre Java 1.8, permgen was used instead of metaspace. Please see the documentation for more detail. + +# You must specify a the repository root of an Zulu JRE repository. Please see the documentation for more detail. +# e.g. repository_root: "https://example.com/zulu-jre/{platform}/{architecture}" +--- +jre: + version: 1.8.0_+ + version_lines: + - 1.8.0_+ + - 11.+ + repository_root: "https://cdn.azul.com/zulu/bin" +jvmkill_agent: + version: 1.+ + repository_root: "{default.repository.root}/jvmkill/{platform}/{architecture}" +memory_calculator: + version: 3.+ + repository_root: "{default.repository.root}/memory-calculator/{platform}/{architecture}" + class_count: + headroom: + stack_threads: 250 diff --git a/docs/buildpack-modes.md b/docs/buildpack-modes.md index 73a0b858a4..1035504702 100644 --- a/docs/buildpack-modes.md +++ b/docs/buildpack-modes.md @@ -1,8 +1,8 @@ # Buildpack Modes The Java Buildpack has three execution modes as described in the blog post, ['Packaged and Offline Buildpacks'][l]. -* **Easy Mode:** Uses the repository at `https://download.run.pivotal.io`. This does not require any cloning or downloading unless you want to modify the Cloud Foundry provided buildpack. This is the default, and what we recommend to anyone who asks. -* **Expert Mode:** Refers to a repository hosted at a different location, possibly on an internal network. The [structure of the repository][r] is defined as an HTTP-accessible collection of files. The repository root must contain an `index.yml` file that is a mapping of concrete versions to absolute URIs. This repository can be created manually or [creating a replica](#replicating-the-repository-optional) for the repository at `https://download.run.pivotal.io`. This is what we would recommend to any customer that didn’t want to access the Internet. It’s easy to keep applications secure and up-to-date, but requires the expertise to run a web-server and keep it up to date. +* **Easy Mode:** Uses the repository at `https://java-buildpack.cloudfoundry.org`. This does not require any cloning or downloading unless you want to modify the Cloud Foundry provided buildpack. This is the default, and what we recommend to anyone who asks. +* **Expert Mode:** Refers to a repository hosted at a different location, possibly on an internal network. The [structure of the repository][r] is defined as an HTTP-accessible collection of files. The repository root must contain an `index.yml` file that is a mapping of concrete versions to absolute URIs. This repository can be created manually or [creating a replica](#replicating-the-repository-optional) for the repository at `https://java-buildpack.cloudfoundry.org`. This is what we would recommend to any customer that didn't want to access the Internet. It's easy to keep applications secure and up-to-date, but requires the expertise to run a web-server and keep it up to date. * **Offline Mode:** Uses only the packaged internal cache. This is what we recommend if you wanted a single, self-contained artifact. The downside is having to package and keep all your dependencies up to date. @@ -29,7 +29,7 @@ default_repository_root: https:// Once the buildpack has been modified, it needs to be packaged and uploaded to the Cloud Foundry instance. In order to package the modified buildpack, refer to [Building Packages][p]. To add the buildpack to an instance of Cloud Foundry, use the `cf create-buildpack java-buildpack java-buildpack-v.zip` command. For more details refer to the [Cloud Foundry buildpack documentation][b]. ### Replicating the Repository _(Optional)_ -The easiest way to create a fully populated internal repository is to replicate the one found at `https://download.run.pivotal.io`. The [Java Buildpack Dependency Builder][d] contains a `replicate` script that automates this process. To use the script, issue the following commands from the root directory of a clone of this repository: +The easiest way to create a fully populated internal repository is to replicate the one found at `https://java-buildpack.cloudfoundry.org`. The [Java Buildpack Dependency Builder][d] contains a `replicate` script that automates this process. To use the script, issue the following commands from the root directory of a clone of this repository: ```bash $ bundle install diff --git a/docs/container-dist_zip.md b/docs/container-dist_zip.md index 41fe602150..aadd126a96 100644 --- a/docs/container-dist_zip.md +++ b/docs/container-dist_zip.md @@ -5,22 +5,29 @@ The Dist Zip Container allows applications packaged in [`distZip`-style][] to be Detection Criteria
    -
  • A start script in the bin/ subdirectory of the application directory or one of its immediate subdirectories (but not in both), and
  • -
  • A JAR file in the lib/ subdirectory of the application directory or one of its immediate subdirectories (but not in both)
  • +
  • A start script in the bin/ subdirectory of the application directory or one of its immediate subdirectories (but not in both) - for example, a bin folder with: bin/myscript along with bin/myscript.bat, and
  • +
  • A JAR file in the lib/ subdirectory of the application directory or one of its immediate subdirectories (but not in both)
Tags - dist-zip + dist-zip Tags are printed to standard output by the buildpack detect script -If the application uses Spring, [Spring profiles][] can be specified by setting the [`SPRING_PROFILES_ACTIVE`][] environment variable. This is automatically detected and used by Spring. The Spring Auto-reconfiguration Framework will specify the `cloud` profile in addition to any others. +If the application uses Spring, [Spring profiles][] can be specified by setting the [`SPRING_PROFILES_ACTIVE`][] environment variable. This is automatically detected and used by Spring. The Spring Auto-reconfiguration Framework will specify the `cloud` profile in addition to any others. ## Configuration -The Dist Zip Container cannot be configured. +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. +The container can be configured by modifying the `config/dist_zip.yml` file in the buildpack fork. + +| Name | Description +| ---- | ----------- +| `arguments` | Optional command line arguments to be passed to the start script. The arguments are specified as a single YAML scalar in plain style or enclosed in single or double quotes. + +[Configuration and Extension]: ../README.md#configuration-and-extension [`distZip`-style]: http://www.gradle.org/docs/current/userguide/application_plugin.html [Spring profiles]:http://blog.springsource.com/2011/02/14/spring-3-1-m1-introducing-profile/ [`SPRING_PROFILES_ACTIVE`]: http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/core/env/AbstractEnvironment.html#ACTIVE_PROFILES_PROPERTY_NAME diff --git a/docs/container-groovy.md b/docs/container-groovy.md index ae78650ab4..06e763684d 100644 --- a/docs/container-groovy.md +++ b/docs/container-groovy.md @@ -22,7 +22,7 @@ Tags are printed to standard output by the buildpack detect script Any JAR files found in the application are automatically added to the classpath at runtime. ## Configuration -For general information on configuring the buildpack, refer to [Configuration and Extension][]. +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. The container can be configured by modifying the [`config/groovy.yml`][] file in the buildpack fork. The container uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. diff --git a/docs/container-java_main.md b/docs/container-java_main.md index d311e85a2d..62d65f14ec 100644 --- a/docs/container-java_main.md +++ b/docs/container-java_main.md @@ -9,7 +9,8 @@ Command line arguments may optionally be configured. - + + @@ -18,20 +19,21 @@ Command line arguments may optionally be configured.
Detection CriteriaMain-Class attribute set in META-INF/MANIFEST.MF or java_main_class set in config/java_main.ymlDetection CriteriaMain-Class attribute set in META-INF/MANIFEST.MF or java_main_class set in config/java_main.yml
Tags
Tags are printed to standard output by the buildpack detect script -If the application uses Spring, [Spring profiles][] can be specified by setting the [`SPRING_PROFILES_ACTIVE`][] environment variable. This is automatically detected and used by Spring. The Spring Auto-reconfiguration Framework will specify the `cloud` profile in addition to any others. +If the application uses Spring, [Spring profiles][] can be specified by setting the [`SPRING_PROFILES_ACTIVE`][] environment variable. This is automatically detected and used by Spring. The Spring Auto-reconfiguration Framework will specify the `cloud` profile in addition to any others. ## Spring Boot If the main class is Spring Boot's `JarLauncher`, `PropertiesLauncher` or `WarLauncher`, the Java Main Container adds a `--server.port` argument to the command so that the application uses the correct port. + ## Configuration -For general information on configuring the buildpack, refer to [Configuration and Extension][]. +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. -The container can be configured by creating or modifying the `config/java_main.yml` file in the buildpack fork. +The container can be configured by modifying the `config/java_main.yml` file in the buildpack fork. | Name | Description | ---- | ----------- | `arguments` | Optional command line arguments to be passed to the Java main class. The arguments are specified as a single YAML scalar in plain style or enclosed in single or double quotes. -| `java_main_class` | The Java class name to run. Values containing whitespace are rejected with an error, but all others values appear without modification on the Java command line. If not specified, the Java Manifest value of `Main-Class` is used. +| `java_main_class` | Optional Java class name to run. Values containing whitespace are rejected with an error, but all others values appear without modification on the Java command line. If not specified, the Java Manifest value of `Main-Class` is used. [Configuration and Extension]: ../README.md#configuration-and-extension [Spring profiles]:http://blog.springsource.com/2011/02/14/spring-3-1-m1-introducing-profile/ diff --git a/docs/container-spring_boot.md b/docs/container-spring_boot.md index 7e7f030fb8..25d019bb65 100644 --- a/docs/container-spring_boot.md +++ b/docs/container-spring_boot.md @@ -1,5 +1,5 @@ # Spring Boot Container -The Spring Boot Container allows [Spring Boot][s] applications, packaged `distZip`-style to be run. **Note** All styles of Sping Boot can be run (e.g. self-executable JAR, WAR file, `distZip`-style). This is just explicit support for the `distZip` style. +The Spring Boot Container allows [Spring Boot][s] applications, packaged `distZip`-style to be run. **Note** All styles of Spring Boot can be run (e.g. self-executable JAR, WAR file, `distZip`-style). This is just explicit support for the `distZip` style. diff --git a/docs/container-spring_boot_cli.md b/docs/container-spring_boot_cli.md index 4c0d82ac8f..93199f21ca 100644 --- a/docs/container-spring_boot_cli.md +++ b/docs/container-spring_boot_cli.md @@ -22,7 +22,7 @@ Tags are printed to standard output by the buildpack detect script. If the application uses Spring, [Spring profiles][] can be specified by setting the [`SPRING_PROFILES_ACTIVE`][] environment variable. This is automatically detected and used by Spring. The Spring Auto-reconfiguration Framework will specify the `cloud` profile in addition to any others. ## Configuration -For general information on configuring the buildpack, refer to [Configuration and Extension][]. +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. The container can be configured by modifying the [`config/spring_boot_cli.yml`][] file in the buildpack fork. The container uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. diff --git a/docs/container-tomcat.md b/docs/container-tomcat.md index f49bfc0536..2927795cd9 100644 --- a/docs/container-tomcat.md +++ b/docs/container-tomcat.md @@ -3,48 +3,121 @@ The Tomcat Container allows servlet 2 and 3 web applications to be run. These a
- + + - +
Detection CriterionExistence of a WEB-INF/ folder in the application directory and Java Main not detectedDetection CriterionExistence of a WEB-INF/ folder in the application directory and Java Main not detected
Tagstomcat-instance=⟨version⟩, tomcat-lifecycle-support=⟨version⟩, tomcat-logging-support=⟨version⟩ tomcat-redis-store=⟨version⟩ (optional)tomcat-instance=⟨version⟩, tomcat-lifecycle-support=⟨version⟩, tomcat-logging-support=⟨version⟩, tomcat-redis-store=⟨version⟩ (optional), tomcat-external_configuration=⟨version⟩ (optional)
Tags are printed to standard output by the buildpack detect script -If the application uses Spring, [Spring profiles][] can be specified by setting the [`SPRING_PROFILES_ACTIVE`][] environment variable. This is automatically detected and used by Spring. The Spring Auto-reconfiguration Framework will specify the `cloud` profile in addition to any others. +If the application uses Spring, [Spring profiles][] can be specified by setting the [`SPRING_PROFILES_ACTIVE`][] environment variable. This is automatically detected and used by Spring. The Spring Auto-reconfiguration Framework will specify the `cloud` profile in addition to any others. ## Configuration -For general information on configuring the buildpack, refer to [Configuration and Extension][]. +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. The container can be configured by modifying the [`config/tomcat.yml`][] file in the buildpack fork. The container uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. | Name | Description | ---- | ----------- +| `access_logging_support.repository_root` | The URL of the Tomcat Access Logging Support repository index ([details][repositories]). +| `access_logging_support.version` | The version of Tomcat Access Logging Support to use. Candidate versions can be found in [this listing](http://download.pivotal.io.s3.amazonaws.com/tomcat-access-logging-support/index.yml). +| `access_logging_support.access_logging` | Set to `enabled` to turn on the access logging support. Default is `disabled`. +| `geode_store.repository_root` | The URL of the Geode Store repository index ([details][repositories]). +| `geode_store.version` | The version of Geode Store to use. Candidate versions can be found in [this listing](https://java-buildpack-tomcat-gemfire-store.s3-us-west-2.amazonaws.com/index.yml). | `lifecycle_support.repository_root` | The URL of the Tomcat Lifecycle Support repository index ([details][repositories]). | `lifecycle_support.version` | The version of Tomcat Lifecycle Support to use. Candidate versions can be found in [this listing](http://download.pivotal.io.s3.amazonaws.com/tomcat-lifecycle-support/index.yml). | `logging_support.repository_root` | The URL of the Tomcat Logging Support repository index ([details][repositories]). | `logging_support.version` | The version of Tomcat Logging Support to use. Candidate versions can be found in [this listing](http://download.pivotal.io.s3.amazonaws.com/tomcat-logging-support/index.yml). -| `access_logging_support.repository_root` | The URL of the Tomcat Access Logging Support repository index ([details][repositories]). -| `access_logging_support.version` | The version of Tomcat Access Logging Support to use. Candidate versions can be found in [this listing](http://download.pivotal.io.s3.amazonaws.com/tomcat-access-logging-support/index.yml). -| `access_logging_support.access_logging` | Set to `enabled` to turn on the access logging support. Default is `disabled`. | `redis_store.connection_pool_size` | The Redis connection pool size. Note that this is per-instance, not per-application. | `redis_store.database` | The Redis database to connect to. | `redis_store.repository_root` | The URL of the Redis Store repository index ([details][repositories]). | `redis_store.timeout` | The Redis connection timeout (in milliseconds). | `redis_store.version` | The version of Redis Store to use. Candidate versions can be found in [this listing](http://download.pivotal.io.s3.amazonaws.com/redis-store/index.yml). +| `tomcat.context_path` | The context path to expose the application at. | `tomcat.repository_root` | The URL of the Tomcat repository index ([details][repositories]). | `tomcat.version` | The version of Tomcat to use. Candidate versions can be found in [this listing](http://download.pivotal.io.s3.amazonaws.com/tomcat/index.yml). +| `tomcat.external_configuration_enabled` | Set to `true` to be able to supply an external Tomcat configuration. Default is `false`. +| `external_configuration.version` | The version of the External Tomcat Configuration to use. Candidate versions can be found in the the repository that you have created to house the External Tomcat Configuration. Note: It is required the external configuration to allow symlinks. +| `external_configuration.repository_root` | The URL of the External Tomcat Configuration repository index ([details][repositories]). + +### Common configurations +The version of Tomcat can be configured by setting an environment variable. + +``` +$ cf set-env my-application JBP_CONFIG_TOMCAT '{tomcat: { version: 7.0.+ }}' +``` + +The context path that an application is deployed at can be configured by setting an environment variable. + +``` +$ cf set-env my-application JBP_CONFIG_TOMCAT '{tomcat: { context_path: /first-segment/second-segment }}' +``` + ### Additional Resources -The container can also be configured by overlaying a set of resources on the default distribution. To do this, add files to the `resources/tomcat` directory in the buildpack fork. For example, to override the default `logging.properties` add your custom file to `resources/tomcat/conf/logging.properties`. +The container can also be configured by overlaying a set of resources on the default distribution. To do this follow one of the options below. + +#### Buildpack Fork +Add files to the `resources/tomcat` directory in the buildpack fork. For example, to override the default `logging.properties` add your custom file to `resources/tomcat/conf/logging.properties`. + +#### External Tomcat Configuration +Supply a repository with an external Tomcat configuration. + +Example in a manifest.yml + +```yaml +env: + JBP_CONFIG_TOMCAT: '{ tomcat: { external_configuration_enabled: true }, external_configuration: { repository_root: "http://repository..." } }' +``` + +The artifacts that the repository provides must be in TAR format and must follow the Tomcat archive structure: + +``` +tomcat +|- conf + |- context.xml + |- server.xml + |- web.xml + |... +``` + +Notes: +* It is required the external configuration to allow symlinks. For more information check [Tomcat 7 configuration] or [Tomcat 8 configuration]. +* `JasperListener` is removed in Tomcat 8 so you should not add it to the server.xml. ## Session Replication -By default, the Tomcat instance is configured to store all Sessions and their data in memory. Under certain cirmcumstances it my be appropriate to persist the Sessions and their data to a repository. When this is the case (small amounts of data that should survive the failure of any individual instance), the buildpack can automatically configure Tomcat to do so. +By default, the Tomcat instance is configured to store all Sessions and their data in memory. Under certain circumstances it my be appropriate to persist the Sessions and their data to a repository. When this is the case (small amounts of data that should survive the failure of any individual instance), the buildpack can automatically configure Tomcat to do so by binding an appropriate service. ### Redis To enable Redis-based session replication, simply bind a Redis service containing a name, label, or tag that has `session-replication` as a substring. +### Tanzu GemFire for VMs +To enable session state caching on Tanzu GemFire for VMs, bind to a Tanzu GemFire service instance whose name either ends in `-session-replication` or is tagged with `session-replication`. + +Service instances can be created with a tag: + +```sh +$ cf create-service p-cloudcache my-service-instance -t session-replication +``` + +or existing service instances can be given a tag: + +```sh +$ cf update-service new-service-instance -t session-replication +``` + +## Managing Entropy +Entropy from `/dev/random` is used heavily to create session ids, and on startup for initializing `SecureRandom`, which can then cause instances to fail to start in time (see the [Tomcat wiki]). Also, the entropy is shared so it's possible for a single app to starve the DEA of entropy and cause apps in other containers that make use of entropy to be blocked. +If this is an issue then configuring `/dev/urandom` as an alternative source of entropy may help. It is unlikely, but possible, that this may cause some security issues which should be taken in to account. + +Example in a manifest.yml +``` +env: + JAVA_OPTS: -Djava.security.egd=file:///dev/urandom +``` ## Supporting Functionality Additional supporting functionality can be found in the [`java-buildpack-support`][] Git repository. @@ -55,4 +128,7 @@ Additional supporting functionality can be found in the [`java-buildpack-support [repositories]: extending-repositories.md [Spring profiles]:http://blog.springsource.com/2011/02/14/spring-3-1-m1-introducing-profile/ [`SPRING_PROFILES_ACTIVE`]: http://docs.spring.io/spring/docs/4.0.0.RELEASE/javadoc-api/org/springframework/core/env/AbstractEnvironment.html#ACTIVE_PROFILES_PROPERTY_NAME +[Tomcat wiki]: http://wiki.apache.org/tomcat/HowTo/FasterStartUp [version syntax]: extending-repositories.md#version-syntax-and-ordering +[Tomcat 7 configuration]: http://tomcat.apache.org/tomcat-7.0-doc/config/context.html#Standard_Implementation +[Tomcat 8 configuration]: http://tomcat.apache.org/tomcat-8.0-doc/config/resources.html#Common_Attributes diff --git a/docs/debugging-the-buildpack.md b/docs/debugging-the-buildpack.md index 98c9304edf..425386f990 100644 --- a/docs/debugging-the-buildpack.md +++ b/docs/debugging-the-buildpack.md @@ -49,18 +49,18 @@ In either case, the output will look like the following: 2014-04-25T08:06:06.12+0000 [Buildpack] DEBUG Instantiating JavaBuildpack::Framework::SpringAutoReconfiguration 2014-04-25T08:06:06.15+0000 [Buildpack] DEBUG Successfully required JavaBuildpack::Framework::SpringAutoReconfiguration 2014-04-25T08:06:06.15+0000 [ConfigurationUtils] DEBUG Configuration from /tmp/buildpacks/java-buildpack/config/spring_auto_reconfiguration.yml: {"version"=>"0.+", "repository_root"=>"{default.repository.root}/auto-reconfiguration"} -2014-04-25T08:06:06.15+0000 [ConfigurationUtils] DEBUG Configuration from /tmp/buildpacks/java-buildpack/config/repository.yml: {"default_repository_root"=>"http://download.run.pivotal.io"} -2014-04-25T08:06:06.21+0000 [RepositoryIndex] DEBUG {default.repository.root}/auto-reconfiguration expanded to http://download.run.pivotal.io/auto-reconfiguration +2014-04-25T08:06:06.15+0000 [ConfigurationUtils] DEBUG Configuration from /tmp/buildpacks/java-buildpack/config/repository.yml: {"default_repository_root"=>"https://java-buildpack.cloudfoundry.org"} +2014-04-25T08:06:06.21+0000 [RepositoryIndex] DEBUG {default.repository.root}/auto-reconfiguration expanded to https://java-buildpack.cloudfoundry.org/auto-reconfiguration 2014-04-25T08:06:06.21+0000 [ConfigurationUtils] DEBUG Configuration from /tmp/buildpacks/java-buildpack/config/cache.yml: {"remote_downloads"=>"enabled"} 2014-04-25T08:06:06.22+0000 [DownloadCache] DEBUG Proxy: , , , -2014-04-25T08:06:06.24+0000 [DownloadCache] DEBUG HTTP: download.run.pivotal.io, 80, {:read_timeout=>10, :connect_timeout=>10, :open_timeout=>10, :use_ssl=>false} +2014-04-25T08:06:06.24+0000 [DownloadCache] DEBUG HTTP: java-buildpack.cloudfoundry.org, 80, {:read_timeout=>10, :connect_timeout=>10, :open_timeout=>10, :use_ssl=>false} 2014-04-25T08:06:06.24+0000 [DownloadCache] DEBUG Request: /auto-reconfiguration/index.yml, {"accept"=>["*/*"], "user-agent"=>["Ruby"]} 2014-04-25T08:06:06.24+0000 [DownloadCache] DEBUG Status: 200 2014-04-25T08:06:06.24+0000 [DownloadCache] DEBUG Persisting etag: "d093ebc9c94d3050c28898585611701c" 2014-04-25T08:06:06.25+0000 [DownloadCache] DEBUG Persisting last-modified: Thu, 24 Apr 2014 10:47:19 GMT -2014-04-25T08:06:06.25+0000 [DownloadCache] DEBUG Persisting content to /tmp/http:%2F%2Fdownload.run.pivotal.io%2Fauto-reconfiguration%2Findex.yml.cached +2014-04-25T08:06:06.25+0000 [DownloadCache] DEBUG Persisting content to /tmp/http:%2F%2Fjava-buildpack.cloudfoundry.org%2Fauto-reconfiguration%2Findex.yml.cached 2014-04-25T08:06:06.25+0000 [DownloadCache] DEBUG Validated content size 1174 is 1174 -2014-04-25T08:06:06.25+0000 [RepositoryIndex] DEBUG {"0.6.8"=>"http://download.run.pivotal.io/auto-reconfiguration/auto-reconfiguration-0.6.8.jar", "0.7.0"=>"http://download.run.pivotal.io/auto-reconfiguration/auto-reconfiguration-0.7.0.jar", "0.7.1"=>"http://download.run.pivotal.io/auto-reconfiguration/auto-reconfiguration-0.7.1.jar", "0.7.2"=>"http://download.run.pivotal.io/auto-reconfiguration/auto-reconfiguration-0.7.2.jar", "0.8.0"=>"http://download.run.pivotal.io/auto-reconfiguration/auto-reconfiguration-0.8.0.jar", "0.8.1"=>"http://download.run.pivotal.io/auto-reconfiguration/auto-reconfiguration-0.8.1.jar", "0.8.2"=>"http://download.run.pivotal.io/auto-reconfiguration/auto-reconfiguration-0.8.2.jar", "0.8.3"=>"http://download.run.pivotal.io/auto-reconfiguration/auto-reconfiguration-0.8.3.jar", "0.8.4"=>"http://download.run.pivotal.io/auto-reconfiguration/auto-reconfiguration-0.8.4.jar", "0.8.5"=>"http://download.run.pivotal.io/auto-reconfiguration/auto-reconfiguration-0.8.5.jar", "0.8.6"=>"http://download.run.pivotal.io/auto-reconfiguration/auto-reconfiguration-0.8.6.jar", "0.8.7"=>"http://download.run.pivotal.io/auto-reconfiguration/auto-reconfiguration-0.8.7.jar", "0.8.8"=>"http://download.run.pivotal.io/auto-reconfiguration/auto-reconfiguration-0.8.8.jar"} +2014-04-25T08:06:06.25+0000 [RepositoryIndex] DEBUG {"0.6.8"=>"https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-0.6.8.jar", "0.7.0"=>"https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-0.7.0.jar", "0.7.1"=>"https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-0.7.1.jar", "0.7.2"=>"https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-0.7.2.jar", "0.8.0"=>"https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-0.8.0.jar", "0.8.1"=>"https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-0.8.1.jar", "0.8.2"=>"https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-0.8.2.jar", "0.8.3"=>"https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-0.8.3.jar", "0.8.4"=>"https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-0.8.4.jar", "0.8.5"=>"https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-0.8.5.jar", "0.8.6"=>"https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-0.8.6.jar", "0.8.7"=>"https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-0.8.7.jar", "0.8.8"=>"https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-0.8.8.jar"} ... @@ -75,8 +75,7 @@ addons: [] config_vars: {} default_process_types: web: JAVA_HOME=$PWD/.java-buildpack/open_jdk_jre JAVA_OPTS="-Djava.io.tmpdir=$TMPDIR - -XX:OnOutOfMemoryError=$PWD/.java-buildpack/open_jdk_jre/bin/killjava.sh -Xmx382293K - -Xms382293K -XX:MaxPermSize=64M -XX:PermSize=64M -Xss995K -Dalpha=bravo -Dhttp.port=$PORT" + -Xmx382293K -Xms382293K -XX:MaxPermSize=64M -XX:PermSize=64M -Xss995K -Dalpha=bravo -Dhttp.port=$PORT" $PWD/.java-buildpack/tomcat/bin/catalina.sh run ``` @@ -104,15 +103,15 @@ app-dynamics-agent=3.8.4 java-buildpack=0fab02b-https://github.com/cloudfoundry/ $ $BUILDPACK_ROOT/bin/compile . $TMPDIR -----> Java Buildpack Version: 0fab02b | https://github.com/cloudfoundry/java-buildpack.git#0fab02b ------> Downloading Open Jdk JRE 1.7.0_60 from http://download.run.pivotal.io/openjdk/mountainlion/x86_64/openjdk-1.7.0_60.tar.gz (found in cache) +-----> Downloading Open Jdk JRE 1.7.0_60 from https://java-buildpack.cloudfoundry.org/openjdk/mountainlion/x86_64/openjdk-1.7.0_60.tar.gz (found in cache) Expanding Open Jdk JRE to .java-buildpack/open_jdk_jre (0.4s) ------> Downloading App Dynamics Agent 3.8.4 from http://download.run.pivotal.io/app-dynamics/app-dynamics-3.8.4.zip (found in cache) +-----> Downloading App Dynamics Agent 3.8.4 from https://java-buildpack.cloudfoundry.org/app-dynamics/app-dynamics-3.8.4.zip (found in cache) Expanding App Dynamics Agent to .java-buildpack/app_dynamics_agent (0.1s) ------> Downloading Play Framework Auto Reconfiguration 1.4.0_RELEASE from http://download.run.pivotal.io/auto-reconfiguration/auto-reconfiguration-1.4.0_RELEASE.jar (found in cache) ------> Downloading Spring Auto Reconfiguration 1.4.0_RELEASE from http://download.run.pivotal.io/auto-reconfiguration/auto-reconfiguration-1.4.0_RELEASE.jar (found in cache) +-----> Downloading Play Framework Auto Reconfiguration 1.4.0_RELEASE from https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-1.4.0_RELEASE.jar (found in cache) +-----> Downloading Spring Auto Reconfiguration 1.4.0_RELEASE from https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-1.4.0_RELEASE.jar (found in cache) $ $BUILDPACK_ROOT/bin/release . | ruby -e "require \"yaml\"; puts YAML.load(STDIN.read)[\"default_process_types\"][\"web\"]" -PATH=$PWD/.java-buildpack/open_jdk_jre/bin:$PATH JAVA_HOME=$PWD/.java-buildpack/open_jdk_jre $PWD/play-application-1.0.0.BUILD-SNAPSHOT/bin/play-application -J-Djava.io.tmpdir=$TMPDIR -J-XX:OnOutOfMemoryError=$PWD/.java-buildpack/open_jdk_jre/bin/killjava.sh -J-XX:MaxPermSize=64M -J-XX:PermSize=64M -J-javaagent:$PWD/.java-buildpack/app_dynamics_agent/javaagent.jar -J-Dappdynamics.agent.applicationName='' -J-Dappdynamics.agent.tierName='Cloud Foundry' -J-Dappdynamics.agent.nodeName=$(expr "$VCAP_APPLICATION" : '.*instance_id[": ]*"\([a-z0-9]\+\)".*') -J-Dappdynamics.agent.accountAccessKey=[REDACTED] -J-Dappdynamics.agent.accountName=[REDACTED] -J-Dappdynamics.controller.hostName=[REDACTED] -J-Dappdynamics.controller.port=443 -J-Dappdynamics.controller.ssl.enabled=true -J-Dhttp.port=$PORT +PATH=$PWD/.java-buildpack/open_jdk_jre/bin:$PATH JAVA_HOME=$PWD/.java-buildpack/open_jdk_jre $PWD/play-application-1.0.0.BUILD-SNAPSHOT/bin/play-application -J-Djava.io.tmpdir=$TMPDIR -J-XX:MaxPermSize=64M -J-XX:PermSize=64M -J-javaagent:$PWD/.java-buildpack/app_dynamics_agent/javaagent.jar -J-Dappdynamics.agent.applicationName='' -J-Dappdynamics.agent.tierName='Cloud Foundry' -J-Dappdynamics.agent.nodeName=$(expr "$VCAP_APPLICATION" : '.*instance_id[": ]*"\([a-z0-9]\+\)".*') -J-Dappdynamics.agent.accountAccessKey=[REDACTED] -J-Dappdynamics.agent.accountName=[REDACTED] -J-Dappdynamics.controller.hostName=[REDACTED] -J-Dappdynamics.controller.port=443 -J-Dappdynamics.controller.ssl.enabled=true -J-Dhttp.port=$PORT ``` You can trigger different behaviour in the buildpack by setting the `VCAP_SERVICES` environment variable. For example, to fake the binding of a service. @@ -125,14 +124,14 @@ JBP_LOG_LEVEL=DEBUG /bin/compile . $TMPDIR JBP_LOG_LEVEL=DEBUG /bin/release . ``` -##Aliases +## Aliases Running the different stages of the buildpack lifecycle can be made simpler with the use of aliases and an environment variable to point at your local copy of the buildpack. The examples below pass in `.` to the scripts assuming you are calling them from the local working directory. ```bash $ alias detect='$BUILDPACK_ROOT/bin/detect .' $ alias compile='$BUILDPACK_ROOT/bin/compile . $TMPDIR' -$ alias release='$BUILDPACK_ROOT/bin/release . | ruby -e "require \"yaml\"; puts YAML.load(STDIN.read)[\"default_process_types\"][\"web\"]"' +$ alias release='$BUILDPACK_ROOT/bin/release . | ruby -e "require \"yaml\"; puts YAML.load(STDIN.read)[\"default_process_types\"][\"web\"]" | sed "s| exec||"' ``` [d]: extending-logging.md#configuration diff --git a/docs/example-embedded-web-server.md b/docs/example-embedded-web-server.md new file mode 100644 index 0000000000..52d7291a9d --- /dev/null +++ b/docs/example-embedded-web-server.md @@ -0,0 +1,30 @@ +# Embedded web server examples + +The Java Buildpack can run applications which provide their own web server or servlet container, provided as JAR files. + +## Example + +This example uses Jetty as an embedded web server, but should be applicable for other technologies. + +```java + +public static void main(String[] args) { + int port = Integer.parseInt(System.getenv("PORT")); + Server server = new Server(port); + server.setHandler(new AbstractHandler() { + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + response.setContentType("text/html;charset=utf-8"); + response.setStatus(HttpServletResponse.SC_OK); + baseRequest.setHandled(true); + response.getWriter().println("

Hello, world

"); + } + }); + server.start(); + server.join(); +} +``` + +The important takeaway is to note that the port comes from the environment variable `port`. Other variables are detailed in the [Cloud Foundry developer guide](http://docs.cloudfoundry.org/devguide/deploy-apps/environment-variable.html). When the application is built as an executable JAR file, it will be treated as a [Java Main](https://github.com/cloudfoundry/java-buildpack/blob/master/docs/example-java_main.md) application. + + diff --git a/docs/example-servlet.md b/docs/example-servlet.md index d7a212dfd3..4a4932040b 100644 --- a/docs/example-servlet.md +++ b/docs/example-servlet.md @@ -26,7 +26,7 @@ The following example shows how deploy the sample application located in the [Ja ```bash $ mvn package -$ cf push web-servlet-2-application -p target/web-servlet-2-application-1.0.0.BUILD-SNAPSHOT.war -b https://github.com/cloudfoundry/java-buildpack.git -b https://github.com/cloudfoundry/java-buildpack.git +$ cf push web-servlet-2-application -p target/web-servlet-2-application-1.0.0.BUILD-SNAPSHOT.war -b https://github.com/cloudfoundry/java-buildpack.git -----> Downloading Open Jdk JRE 1.7.0_51 from http://.../openjdk/lucid/x86_64/openjdk-1.7.0_51.tar.gz (0.0s) Expanding Open Jdk JRE to .java-buildpack/open_jdk_jre (1.1s) diff --git a/docs/extending-application.md b/docs/extending-application.md index a8c0bb6a1d..af904e05f5 100644 --- a/docs/extending-application.md +++ b/docs/extending-application.md @@ -38,8 +38,11 @@ A helper type (`JavaBuildpack::Component::Services`) that enables querying of th # +filter+ matches exactly one service, +false+ otherwise. # # @param [Regexp, String] filter a +RegExp+ or +String+ to match against the name, label, and tags of the services -# @return [Boolean] +true+ if the +filter+ matches exactly one service, +false+ otherwise. -def one_service?(filter) +# @param [String] required_credentials an optional list of keys or groups of keys, where at least one key from the +# group, must exist in the credentials payload of the candidate service +# @return [Boolean] +true+ if the +filter+ matches exactly one service with the required credentials, +false+ +# otherwise. +def one_service?(filter, *required_credentials) # Compares the name, label, and tags of each service to the given +filter+. The method returns the first service # that the +filter+ matches. If no service matches, returns +nil+ diff --git a/docs/extending-base_component.md b/docs/extending-base_component.md index 1643fadf85..836812a2a8 100644 --- a/docs/extending-base_component.md +++ b/docs/extending-base_component.md @@ -48,7 +48,7 @@ def release # @param [String] uri # @param [String] name an optional name for the download. Defaults to +@component_name+. # @return [Void] -def download(version, uri, name = @component_name, &block) +def download(version, uri, name = @component_name) # Downloads a given JAR file and stores it. # @@ -57,6 +57,7 @@ def download(version, uri, name = @component_name, &block) # @param [String] jar_name the name to save the jar as # @param [Pathname] target_directory the directory to store the JAR file in. Defaults to the component's sandbox. # @param [String] name an optional name for the download. Defaults to +@component_name+. +# @return [Void] def download_jar(version, uri, jar_name, target_directory = @droplet.sandbox, name = @component_name) # Downloads a given TAR file and expands it. @@ -65,6 +66,7 @@ def download_jar(version, uri, jar_name, target_directory = @droplet.sandbox, na # @param [String] uri the uri of the download # @param [Pathname] target_directory the directory to expand the TAR file to. Defaults to the component's sandbox. # @param [String] name an optional name for the download and expansion. Defaults to +@component_name+. +# @return [Void] def download_tar(version, uri, target_directory = @droplet.sandbox, name = @component_name) # Downloads a given ZIP file and expands it. @@ -72,11 +74,13 @@ def download_tar(version, uri, target_directory = @droplet.sandbox, name = @comp # @param [Boolean] strip_top_level whether to strip the top-level directory when expanding. Defaults to +true+. # @param [Pathname] target_directory the directory to expand the ZIP file to. Defaults to the component's sandbox. # @param [String] name an optional name for the download. Defaults to +@component_name+. +# @return [Void] def download_zip(version, uri, strip_top_level = true, target_directory = @droplet.sandbox, name = @component_name) # Wrap the execution of a block with timing information # # @param [String] caption the caption to print when timing starts +# @return [Void] def with_timing(caption) ``` diff --git a/docs/extending-caches.md b/docs/extending-caches.md index 512d8680f1..0489516005 100644 --- a/docs/extending-caches.md +++ b/docs/extending-caches.md @@ -39,32 +39,36 @@ end ``` ## Configuration -For general information on configuring the buildpack, refer to [Configuration and Extension][]. +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. Caching can be configured by modifying the [`config/cache.yml`][] file in the buildpack fork. | Name | Description | ---- | ----------- | `remote_downloads` | This property can take the value `enabled` or `disabled`.

The default value of `enabled` means that the buildpack will check the internet connection and remember the result for the remainder of the buildpack invocation. If the internet is available, it will then be used to download files. If the internet is not available, cache will be consulted instead.

Alternatively, the property may be set to `disabled` which avoids the check for an internet connection, does not attempt downloads, and consults the cache instead. +| `client_authentication.certificate_location` | The path to a PEM or DER encoded certificate to use for SSL client certificate authentication +| `client_authentication.private_key_location` | The path to a PEM or DER encoded DSA or RSA private key to use for SSL client certificate authentication +| `client_authentication.private_key_password` | The password for the private key to use for SSL client certificate authentication ## `JavaBuildpack::Util::Cache::DownloadCache` The [`DownloadCache`][] is the most generic of the two caches. It allows you to create a cache that persists files any that write access is available. The constructor signature looks the following: ```ruby -# Creates an instance of the cache that is backed by the filesystem rooted at +cache_root+ +# Creates an instance of the cache that is backed by a number of filesystem locations. The first argument +# (+mutable_cache_root+) is the only location that downloaded files will be stored in. # -# @param [String] cache_root the filesystem root for downloaded files to be cached in -def initialize(cache_root = Dir.tmpdir) +# @param [Pathname] mutable_cache_root the filesystem location in which find cached files in. This will also be +# the location that all downloaded files are written to. +# @param [Pathname] immutable_cache_roots other filesystem locations to find cached files in. No files will be +# written to these locations. +def initialize(mutable_cache_root = Pathname.new(Dir.tmpdir), *immutable_cache_roots) ``` ## `JavaBuildpack::Util::Cache::ApplicationCache` The [`ApplicationCache`][] is a cache that persists files into the application cache passed to the `compile` script. It examines `ARGV[1]` for the cache location and configures itself accordingly. ```ruby -# Creates an instance that is configured to use the application cache. The application cache location is defined by -# the second argument (ARGV[1]) to the +compile+ script. -# -# @raise if the second argument (ARGV[1]) to the +compile+ script is +nil+ +# Creates an instance of the cache that is backed by the the application cache def initialize ``` diff --git a/docs/extending-droplet.md b/docs/extending-droplet.md index 798bfa0dbe..51988ddf2e 100644 --- a/docs/extending-droplet.md +++ b/docs/extending-droplet.md @@ -32,7 +32,8 @@ attr_reader :sandbox # Copy resources from a components resources directory to a directory # -# @param [Pathname] target_directory the directory to copy to. Default to a component's +sandbox+ +# @param [Pathname] target_directory the directory to copy to. Defaults to the component's +sandbox+. +# @return [Void] def copy_resources(target_directory = @sandbox) ``` @@ -48,6 +49,7 @@ def as_classpath # Symlink the contents of the collection to a destination directory. # # @param [Pathname] destination the destination to link to +# @return [Void] def link_to(destination) ``` @@ -58,7 +60,7 @@ The id of the component, as determined by the buildpack. This is used in variou One of two helper types (`JavaBuildpack::Component::ImmutableJavaHome`, `JavaBuildpack::Component::MutableJavaHome`) that enables the mutation and retrieval of the droplet's `JAVA_HOME`. Components that are JREs will be given the `MutableJavaHome` in order to set the value. All other components will be given the `ImmutableJavaHome` in order to retrieve the value. ```ruby -# Returns the path of +JAVA_HOME+ as an environment variable formatted as +JAVA_HOME="$PWD/"+ +# Returns the path of +JAVA_HOME+ as an environment variable formatted as +JAVA_HOME=$PWD/+ # # @return [String] the path of +JAVA_HOME+ as an environment variable def as_env_var @@ -66,9 +68,10 @@ def as_env_var # Execute a block with the +JAVA_HOME+ environment variable set # # @yield yields to block with the +JAVA_HOME+ environment variable set +# @return [Object] the returned value of the block def do_with -# @return [String] the root of the droplet's +JAVA_HOME+ +# @return [String] the root of the droplet's +JAVA_HOME+ formatted as +$PWD/+ def root # Sets the root of the droplet's +JAVA_HOME+ diff --git a/docs/extending-logging.md b/docs/extending-logging.md index 055554f835..a8f7a26241 100644 --- a/docs/extending-logging.md +++ b/docs/extending-logging.md @@ -24,7 +24,7 @@ logger.debug { "#{costly_method}" } ``` ## Configuration -For general information on configuring the buildpack, refer to [Configuration and Extension][]. +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. The console logging severity filter is set to `DEBUG`, `INFO`, `WARN`, `ERROR`, `FATAL` using the following strategies in descending priority: diff --git a/docs/extending-modular_component.md b/docs/extending-modular_component.md index 8760e930a9..e61e7649e7 100644 --- a/docs/extending-modular_component.md +++ b/docs/extending-modular_component.md @@ -10,11 +10,12 @@ This base class is recommended for use by any component that is sufficiently com # components are expected to return the command required to run the application. def command -# The modules that make up this component +# The sub_components that make up this component # # @param [Hash] context the context of the component -# @return [Array] a collection of +BaseComponent+s that make up the modules of this component -def modules(context) +# @return [Array] a collection of +BaseComponent+s that make up the sub_components of this +# component +def sub_components(_context) # Whether or not this component supports this application # diff --git a/docs/extending-repositories.md b/docs/extending-repositories.md index 84604ef680..9d7854f17d 100644 --- a/docs/extending-repositories.md +++ b/docs/extending-repositories.md @@ -22,15 +22,17 @@ An example filesystem might look like: The main class used when dealing with a repository is [`JavaBuildpack::Repository::ConfiguredItem`][]. It provides a single method that is used to resolve a specific version and its URI. ```ruby -# Finds an instance of the file based on the configuration. +# Finds an instance of the file based on the configuration and wraps any exceptions +# to identify the component. # +# @param [String] component_name the name of the component # @param [Hash] configuration the configuration # @option configuration [String] :repository_root the root directory of the repository # @option configuration [String] :version the version of the file to resolve # @param [Block, nil] version_validator an optional version validation block -# @return [JavaBuildpack::Util::TokenizedVersion] the chosen version of the file # @return [String] the URI of the chosen version of the file -def find_item(configuration, &version_validator) +# @return [JavaBuildpack::Util::TokenizedVersion] the chosen version of the file +def find_item(component_name, configuration) ``` Usage of the class might look like the following: @@ -52,12 +54,12 @@ end | Variable | Description | | -------- | ----------- | -| `{default.repository.root}` | The common root for all repositories. Currently defaults to `http://download.pivotal.io.s3.amazonaws.com`. -| `{platform}` | The platform that the application is running on. Currently detects `centos6`, `lucid`, `mountainlion`, and `precise`. +| `{default.repository.root}` | The common root for all repositories. Currently defaults to `https://java-buildpack.cloudfoundry.org`. +| `{platform}` | The platform that the application is running on. Currently detects `jammy`, `noble`, etc. | `{architecture}` | The architecture of the system as returned by Ruby. The value is typically one of `x86_64` or `x86`. ## Configuration -For general information on configuring the buildpack, refer to [Configuration and Extension][]. +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. Repositories can be configured by modifying the [`config/repository.yml`][] file in the buildpack fork. @@ -93,5 +95,5 @@ In addition to declaring a specific versions to use, you can also specify a boun [`config/repository.yml`]: ../config/repository.yml [`JavaBuildpack::Repository::ConfiguredItem`]: ../lib/java_buildpack/repository/configured_item.rb [Configuration and Extension]: ../README.md#configuration-and-extension -[example]: http://download.pivotal.io.s3.amazonaws.com/openjdk/lucid/x86_64/index.yml +[example]: https://java-buildpack.cloudfoundry.org/openjdk/jammy/x86_64/index.yml diff --git a/docs/extending.md b/docs/extending.md index 9cb95f93ce..d90b05c8a1 100644 --- a/docs/extending.md +++ b/docs/extending.md @@ -1,7 +1,7 @@ # Extending For general information on extending the buildpack, refer to [Configuration and Extension](../README.md#configuration-and-extension). -To add a component, its class name must be added added to [`config/components.yml`][]. It is recommended, but not required, that the class' file be placed in a directory that matches its type. +To add a component, its class name must be added to [`config/components.yml`][]. It is recommended, but not required, that the class' file be placed in a directory that matches its type. | Component Type | Location | -------------- | -------- diff --git a/docs/framework-app_dynamics_agent.md b/docs/framework-app_dynamics_agent.md index 28d7dcaab5..e1ec4a53e0 100644 --- a/docs/framework-app_dynamics_agent.md +++ b/docs/framework-app_dynamics_agent.md @@ -1,9 +1,9 @@ # AppDynamics Agent Framework -The AppDynamics Agent Framework causes an application to be automatically configured to work with a bound [AppDynamics Service][]. +The AppDynamics Agent Framework causes an application to be automatically configured to work with a bound [AppDynamics Service][]. **Note:** This framework is disabled by default. - @@ -13,34 +13,87 @@ The AppDynamics Agent Framework causes an application to be automatically config Tags are printed to standard output by the buildpack detect script ## User-Provided Service -When binding AppDynamics using a user-provided service, it must have name or tag with `app-dynamics` in it. The credential payload can contain the following entries: +When binding AppDynamics using a user-provided service, it must have name or tag with `app-dynamics` or `appdynamics` in it. The credential payload can contain the following entries. | Name | Description | ---- | ----------- -| `account-access-key` | (Optional) The account access key to use when authenticating with the controller -| `account-name` | (Optional) The account name to use when authenticating with the controller +| `account-access-key` | The account access key to use when authenticating with the controller +| `account-name` | The account name to use when authenticating with the controller | `host-name` | The controller host name -| `port` | (Optional) The controller port -| `ssl-enabled` | (Optional) Whether or not to use an SSL connection to the controller +| `port` | The controller port +| `ssl-enabled` | Whether or not to use an SSL connection to the controller +| `application-name` | (Optional) the application's name +| `node-name` | (Optional) the application's node name | `tier-name` | (Optional) the application's tier name +To provide more complex values such as the `tier-name`, using the interactive mode when creating a user-provided service will manage the character escaping automatically. For example, the default `tier-name` could be set with a value of `Tier-$(expr "${VCAP_APPLICATION}" : '.*instance_index[": ]*\([[:digit:]]*\).*')` to calculate a value from the Cloud Foundry instance index. + +**Note:** Some credentials were previously marked as "(Optional)" as requirements have changed across versions of the AppDynamics agent. Please see the [AppDynamics Java Agent Configuration Properties][] for the version of the agent used by your application for more details. + ## Configuration -For general information on configuring the buildpack, refer to [Configuration and Extension][]. +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. -The framework can be configured by modifying the [`config/app_dynamics_agent.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. +The framework can be configured by modifying the [`config/app_dynamics_agent.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. | Name | Description | ---- | ----------- -| `default_tier_name` | The default tier name for this application in the AppDynamics dashboard. This can be overridden with a `tier-name` entry in the credentials payload. +| `default_application_name` | This is omitted by default but can be added to specify the application name in the AppDynamics dashboard. This can be overridden by an `application-name` entry in the credentials payload. If neither are supplied the default is the `application_name` as specified by Cloud Foundry. +| `default_node_name` | The default node name for this application in the AppDynamics dashboard. The default value is an expression that will be evaluated based on the `instance_index` of the application. This can be overridden by a `node-name` entry in the credentials payload. +| `default_tier_name` | This is omitted by default but can be added to specify the tier name for this application in the AppDynamics dashboard. This can be overridden by a `tier-name` entry in the credentials payload. If neither are supplied the default is the `application_name` as specified by Cloud Foundry. | `repository_root` | The URL of the AppDynamics repository index ([details][repositories]). | `version` | The version of AppDynamics to use. Candidate versions can be found in [this listing][]. ### Additional Resources -The framework can also be configured by overlaying a set of resources on the default distribution. To do this, add files to the `resources/app_dynamics_agent` directory in the buildpack fork. For example, to override the default `app-agent-config.xml` add your custom file to `resources/app_dynamics_agent/conf/app-agent-config.xml`. +The framework can also be configured by overlaying a set of resources on the default distribution. To do this follow one of the options below. + +Configuration files are created in this order: + +1. Default AppDynamics configuration +2. Buildpack default configuration is taken from `resources/app_dynamics_agent/default` +3. External Configuration if configured +4. Local Configuration if configured +5. Buildpack Fork if it exists + +#### Buildpack Fork +Add files to the `resources/app_dynamics_agent` directory in the buildpack fork. For example, to override the default `app-agent-config.xml` add your custom file to `resources/app_dynamics_agent//conf/app-agent-config.xml`. + +#### External Configuration +Set `APPD_CONF_HTTP_URL` to an HTTP or HTTPS URL which points to the directory where your configuration files exist. You may also include a user and password in the URL, like `https://user:pass@example.com`. + +The Java buildpack will take the URL to the directory provided and attempt to download the following files from that directory: + +- `logging/log4j2.xml` +- `logging/log4j.xml` +- `app-agent-config.xml` +- `controller-info.xml` +- `service-endpoint.xml` +- `transactions.xml` +- `custom-interceptors.xml` +- `custom-activity-correlation.xml` + +Any file successfully downloaded will be copied to the configuration directory. The buildpack does not fail if files are missing. + +#### Local Configuration +Set `APPD_CONF_DIR` to a relative path which points to the directory in your application files where your custom configuration exists. + +The Java buildpack will take the `app_root` + `APPD_CONF_DIR` directory and attempt to copy the followinig files from that directory: + +- `logging/log4j2.xml` +- `logging/log4j.xml` +- `app-agent-config.xml` +- `controller-info.xml` +- `service-endpoint.xml` +- `transactions.xml` +- `custom-interceptors.xml` +- `custom-activity-correlation.xml` + +Any files that exist will be copied to the configuration directory. The buildpack does not fail if files are missing. + [`config/app_dynamics_agent.yml`]: ../config/app_dynamics_agent.yml +[AppDynamics Java Agent Configuration Properties]: https://docs.appdynamics.com/display/PRO42/Java+Agent+Configuration+Properties [AppDynamics Service]: http://www.appdynamics.com [Configuration and Extension]: ../README.md#configuration-and-extension [repositories]: extending-repositories.md -[this listing]: http://download.pivotal.io.s3.amazonaws.com/app-dynamics/index.yml +[this listing]: https://packages.appdynamics.com/java/index.yml [version syntax]: extending-repositories.md#version-syntax-and-ordering diff --git a/docs/framework-aspectj_weaver_agent.md b/docs/framework-aspectj_weaver_agent.md new file mode 100644 index 0000000000..926315defe --- /dev/null +++ b/docs/framework-aspectj_weaver_agent.md @@ -0,0 +1,26 @@ +# AspectJ Weaver Agent Framework +The AspectJ Weaver Agent Framework configures the AspectJ Runtime Weaving Agent at runtime. + +
Detection CriterionExistence of a single bound AppDynamics service. The existence of an AppDynamics service defined by the VCAP_SERVICES payload containing a service name, label or tag with app-dynamics as a substring. + Detection CriterionExistence of a single bound AppDynamics service. The existence of an AppDynamics service defined by the VCAP_SERVICES payload containing a service name, label or tag with app-dynamics or appdynamics as a substring.
+ + + + + + + + +
Detection Criterionaspectjweaver-*.jar existing and BOOT-INF/classes/META-INF/aop.xml, BOOT-INF/classes/org/aspectj/aop.xml, META-INF/aop.xml, or org/aspectj/aop.xml existing.
Tagsaspectj-weaver-agent=<version>
+Tags are printed to standard output by the buildpack detect script + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by creating or modifying the [`config/aspectj_weaver_agent.yml`][] file in the buildpack fork. + +| Name | Description +| ---- | ----------- +| `enabled` | Whether to enable the AspectJ Runtime Weaving agent. + +[`config/aspectj_weaver_agent.yml`]: ../config/aspect_weaver_agent.yml +[Configuration and Extension]: ../README.md#configuration-and-extension diff --git a/docs/framework-azure_application_insights_agent.md b/docs/framework-azure_application_insights_agent.md new file mode 100644 index 0000000000..ec78d2c921 --- /dev/null +++ b/docs/framework-azure_application_insights_agent.md @@ -0,0 +1,33 @@ +# Azure Application Insights Agent Framework +The Azure Application Insights Agent Framework causes an application to be automatically configured to work with a bound [Azure Application Insights Service][]. **Note:** This framework is disabled by default. + + + + + + + + + +
Detection CriterionExistence of a single bound Azure Application Insights service. +
    +
  • Existence of a Azure Application Insights service is defined as the VCAP_SERVICES payload containing a service who's name, label or tag has azure-application-insights as a substring with at least `connection_string` or `instrumentation_key` set as credentials.
  • +
+
Tagsazure-application-insights=<version>
+Tags are printed to standard output by the buildpack detect script + +## User-Provided Service +Users must provide their own Azure Application Insights service. A user-provided Azure Application Insights service must have a name or tag with `azure-application-insights` in it so that the Azure Application Insights Agent Framework Framework will automatically configure the application to work with the service. + +The credential payload of the service has to contain one of the following entries: + +| Name | Description +| ---- | ----------- +| `connection_string` | With agent version 3.x the connection string is required. You can find your connection string in your Application Insights resource. +| `instrumentation_key` | With agent version 2.x the instrumentation key is required. With version 3.x this configuration is deprecated an it is recommended to switch to a connection string. You can find your instrumentation key in your Application Insights resource. + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +[Configuration and Extension]: ../README.md#configuration-and-extension +[Azure Application Insights Service]: https://learn.microsoft.com/en-us/azure/azure-monitor/app/java-in-process-agent diff --git a/docs/framework-checkmarx_iast_agent.md b/docs/framework-checkmarx_iast_agent.md new file mode 100644 index 0000000000..45a938def6 --- /dev/null +++ b/docs/framework-checkmarx_iast_agent.md @@ -0,0 +1,22 @@ +# Checkmarx IAST Agent Framework +The Checkmarx IAST Agent Framework causes an application to be automatically configured to work with a bound [Checkmarx IAST Service][]. + + + + + +
Detection CriterionExistence of a bound Checkmarx IAST service. The existence of an Checkmarx IAST service is defined by the VCAP_SERVICES payload containing a service named checkmarx-iast. +
+ +## User-Provided Service +When binding Checkmarx IAST using a user-provided service, it must have the name `checkmarx-iast` and the credential payload must include the following entry: + +| Name | Description +| ---- | ----------- +| `server` | The IAST Manager URL + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +[Checkmarx IAST Service]: https://www.checkmarx.com/products/interactive-application-security-testing +[Configuration and Extension]: ../README.md#configuration-and-extension diff --git a/docs/framework-client_certificate_mapper.md b/docs/framework-client_certificate_mapper.md new file mode 100644 index 0000000000..5d3852b5a6 --- /dev/null +++ b/docs/framework-client_certificate_mapper.md @@ -0,0 +1,37 @@ +# Client Certificate Mapper +The Client Certificate Mapper Framework adds a Servlet Filter to applications that will that maps the `X-Forwarded-Client-Cert` to the `javax|jakarta.servlet.request.X509Certificate` Servlet attribute. + +The Client Certificate Mapper Framework will download a helper library, [java-buildpack-client-certificate-mapper][library repository], that will enrich Spring Boot (2 and 3), as well as JEE / JakartaEE applications classpath with a servlet filter. + + + + + + + + + + +
Detection CriterionUnconditional
Tagsclient-certificate-mapper=<version>
+Tags are printed to standard output by the buildpack detect script + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by modifying the [`config/client_certificate_mapper.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +|-------------------| ----------- +| `repository_root` | The URL of the Container Customizer repository index ([details][repositories]). +| `version` | The version of Container Customizer to use. Candidate versions can be found in [this listing][]. + +## Servlet Filter +The [Servlet Filter][] added by this framework maps the `X-Forwarded-Client-Cert` to the `javax.servlet.request.X509Certificate` Servlet attribute for each request. The `X-Forwarded-Client-Cert` header is contributed by the Cloud Foundry Router and contains the any TLS certificate presented by a client for mututal TLS authentication. This certificate can then be used by any standard Java security framework to establish authentication and authorization for a request. + +[`config/client_certificate_mapper.yml`]: ../config/client_certificate_mapper.yml +[Configuration and Extension]: ../README.md#configuration-and-extension +[repositories]: extending-repositories.md +[Servlet Filter]: https://github.com/cloudfoundry/java-buildpack-client-certificate-mapper +[this listing]: http://download.pivotal.io.s3.amazonaws.com/container-security-provider/index.yml +[version syntax]: extending-repositories.md#version-syntax-and-ordering +[library repository]: https://github.com:cloudfoundry/java-buildpack-client-certificate-mapper.git diff --git a/docs/framework-container_customizer.md b/docs/framework-container_customizer.md new file mode 100644 index 0000000000..97538ea05e --- /dev/null +++ b/docs/framework-container_customizer.md @@ -0,0 +1,30 @@ +# Container Customizer Framework +The Container Customizer Framework modifies the configuration of an embedded Tomcat container in a Spring Boot WAR file. + + + + + + + + + + +
Detection CriterionApplication is a Spring Boot WAR file
Tagscontainer-customizer=<version>
+Tags are printed to standard output by the buildpack detect script + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by modifying the [`config/container_customizer.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +| ---- | ----------- +| `repository_root` | The URL of the Container Customizer repository index ([details][repositories]). +| `version` | The version of Container Customizer to use. Candidate versions can be found in [this listing][]. + +[Configuration and Extension]: ../README.md#configuration-and-extension +[`config/container_customizer.yml`]: ../config/container_customizer.yml +[repositories]: extending-repositories.md +[this listing]: http://download.pivotal.io.s3.amazonaws.com/container-customizer/index.yml +[version syntax]: extending-repositories.md#version-syntax-and-ordering diff --git a/docs/framework-container_security_provider.md b/docs/framework-container_security_provider.md new file mode 100644 index 0000000000..bb5f7c4223 --- /dev/null +++ b/docs/framework-container_security_provider.md @@ -0,0 +1,39 @@ +# Container Security Provider +The Container Security Provider Framework adds a Security Provider to the JVM that automatically includes BOSH trusted certificates and Diego identity certificates and private keys. + + + + + + + + + + +
Detection CriterionUnconditional
Tagscontainer-security-provider=<version>
+Tags are printed to standard output by the buildpack detect script + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by modifying the [`config/container_security_provider.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +| ---- | ----------- +| `repository_root` | The URL of the Container Customizer repository index ([details][repositories]). +| `version` | The version of Container Customizer to use. Candidate versions can be found in [this listing][]. +| `key_manager_enabled` | Whether the container `KeyManager` is enabled. Defaults to `true`. +| `trust_manager_enabled` | Whether the container `TrustManager` is enabled. Defaults to `true`. + +## Security Provider +The [security provider][] added by this framework contributes two types, a `TrustManagerFactory` and a `KeyManagerFactory`. The `TrustManagerFactory` adds an additional new `TrustManager` after the configured system `TrustManager` which reads the contents of `/etc/ssl/certs/ca-certificates.crt` which is where [BOSH trusted certificates][] are placed. The `KeyManagerFactory` adds an additional `KeyManager` after the configured system `KeyManager` which reads the contents of the files specified by `$CF_INSTANCE_CERT` and `$CF_INSTANCE_KEY` which are set by Diego to give each container a unique cryptographic identity. These `TrustManager`s and `KeyManager`s are used transparently by any networking library that reads standard system SSL configuration and can be used to enable system-wide trust and [mutual TLS authentication][]. + + +[`config/container_security_provider.yml`]: ../config/container_security_provider.yml +[BOSH trusted certificates]: https://bosh.io/docs/trusted-certs.html +[Configuration and Extension]: ../README.md#configuration-and-extension +[mutual TLS authentication]: https://en.wikipedia.org/wiki/Mutual_authentication +[repositories]: extending-repositories.md +[security provider]: https://github.com/cloudfoundry/java-buildpack-security-provider +[this listing]: http://download.pivotal.io.s3.amazonaws.com/container-security-provider/index.yml +[version syntax]: extending-repositories.md#version-syntax-and-ordering diff --git a/docs/framework-contrast_security_agent.md b/docs/framework-contrast_security_agent.md new file mode 100644 index 0000000000..ab6e5d4ea0 --- /dev/null +++ b/docs/framework-contrast_security_agent.md @@ -0,0 +1,39 @@ +# Contrast Security Agent Framework +The Contrast Security Agent Framework causes an application to be automatically configured to work with a bound [Contrast Security Service][]. + + + + + +
Detection CriterionExistence of a single bound Contrast Security service. The existence of an Contrast Security service defined by the VCAP_SERVICES payload containing a service name, label or tag with contrast-security as a substring. +
+Tags are printed to standard output by the buildpack detect script + +## User-Provided Service +When binding ContrastSecurity using a user-provided service, it must have name or tag with `contrast-security` in it. The credential payload can contain the following entries: + +| Name | Description +| ---- | ----------- +| `api_key` | Your user's api key +| `service_key` | Your user's service key +| `teamserver_url` | The base URL in which your user has access to and the URL to which the Agent will report. ex: https://app.contrastsecurity.com +| `username` | The account name to use when downloading the agent + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by modifying the [`config/contrast_security_agent.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +| ---- | ----------- +| `repository_root` | The URL of the Contrast Security repository index ([details][repositories]). +| `version` | The version of Contrast Security to use. Candidate versions can be found in [this listing][]. + +[Contrast Security]: https://www.contrastsecurity.com +[Configuration and Extension]: ../README.md#configuration-and-extension +[Contrast Security Service]: https://www.contrastsecurity.com +[`config/contrast_security_agent.yml`]: ../config/contrast_security_agent.yml +[Configuration and Extension]: ../README.md#configuration-and-extension +[repositories]: extending-repositories.md +[this listing]: https://artifacts.contrastsecurity.com/agents/java/index.yml +[version syntax]: extending-repositories.md#version-syntax-and-ordering diff --git a/docs/framework-datadog_javaagent.md b/docs/framework-datadog_javaagent.md new file mode 100644 index 0000000000..b5efa52f37 --- /dev/null +++ b/docs/framework-datadog_javaagent.md @@ -0,0 +1,46 @@ +# Datadog APM Javaagent Framework +The [Datadog APM]() Javaagent Framework installs an agent that allows your application to be dynamically instrumented [by][datadog-javaagent] `dd-java-agent.jar`. + +For this functionality to work, you **must** also use this feature in combination with the [Datadog Cloudfoundry Buildpack](). The Datadog Cloudfoundry Buildpack **must** run first, so that it can supply the components to which the Datadog APM agent will talk. Please make sure you follow the instructions on the README for the Datadog Cloudfoundry Buildpack to enable and configure it. + +The framework will configure the Datadog agent for correct use in most situations, however you may adjust its behavior by setting additional environment variables. For a complete list of Datadog Agent configuration options, please see the [Datadog Documentation](https://docs.datadoghq.com/tracing/setup_overview/setup/java/?tab=containers#configuration). + + + + + + + + + +
Detection CriterionAll must be true: +
    +
  • The Datadog Buildpack must be included
  • +
  • DD_API_KEY defined and contain your API key
  • +
+ Optionally, you may set DD_APM_ENABLED to false to force the framework to not contribute the agent. +
Tagsdatadog-javaagent=<version>
+ +Tags are printed to standard output by the buildpack detect script + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. +The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +The javaagent can be configured directly via environment variables or system properties as defined in the [Configuration of Datadog Javaagent][] documentation. + + +| Name | Description +| ---- | ----------- +| `repository_root` | The URL of the Datadog Javaagent repository index ([details][repositories]). +| `version` | The `dd-java-agent` version to use. Candidate versions can be found in [this listing][]. + + +[Configuration and Extension]: ../README.md#configuration-and-extension +[Datadog APM]: https://www.datadoghq.com/product/apm/ +[Datadog Cloudfoundry Builpack]: https://github.com/DataDog/datadog-cloudfoundry-buildpack +[datadog-javaagent]: https://github.com/datadog/dd-trace-java +[Configuration of Datadog Javaagent]: https://docs.datadoghq.com/tracing/setup_overview/setup/java/#configuration +[this listing]: https://raw.githubusercontent.com/datadog/dd-trace-java/cloudfoundry/index.yml +[repositories]: extending-repositories.md +[version syntax]: extending-repositories.md#version-syntax-and-ordering diff --git a/docs/framework-debug-eclipse.png b/docs/framework-debug-eclipse.png new file mode 100644 index 0000000000..e01c28d329 Binary files /dev/null and b/docs/framework-debug-eclipse.png differ diff --git a/docs/framework-debug.md b/docs/framework-debug.md new file mode 100644 index 0000000000..303ce3fe2b --- /dev/null +++ b/docs/framework-debug.md @@ -0,0 +1,41 @@ +# Debug Framework +The Debug Framework contributes Java debug configuration to the application at runtime. **Note:** This framework is only useful in Diego-based containers with SSH access enabled. + + + + + + + + + + +
Detection Criterionenabled set in the config/debug.yml file
Tagsdebug=<port>
+Tags are printed to standard output by the buildpack detect script + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by creating or modifying the [`config/debug.yml`][] file in the buildpack fork. + +| Name | Description +| ---- | ----------- +| `enabled` | Whether to enable Java debugging +| `port` | The port that the debug agent will listen on. Defaults to `8000`. +| `suspend` | Whether to suspend execution until a debugger has attached. Note, you cannot ssh to a container until the container has decided the application is running. Therefore when enabling this setting you must also push the application using the parameter `-u process` which disables container health checking. + +## Creating SSH Tunnel +After starting an application with debugging enabled, an SSH tunnel must be created to the container. To create that SSH container, execute the following command: + +```bash +$ cf ssh -N -T -L :localhost: +``` + +The `REMOTE_PORT` should match the `port` configuration for the application (`8000` by default). The `LOCAL_PORT` can be any open port on your computer, but typically matches the `REMOTE_PORT` where possible. + +Once the SSH tunnel has been created, your IDE should connect to `localhost:` for debugging. + +![Eclipse Configuration](framework-debug-eclipse.png) + +[`config/debug.yml`]: ../config/debug.yml +[Configuration and Extension]: ../README.md#configuration-and-extension diff --git a/docs/framework-dynatrace_one_agent.md b/docs/framework-dynatrace_one_agent.md new file mode 100644 index 0000000000..0cbeaf3866 --- /dev/null +++ b/docs/framework-dynatrace_one_agent.md @@ -0,0 +1,43 @@ +# Dynatrace SaaS/Managed OneAgent Framework +[Dynatrace SaaS/Managed](http://www.dynatrace.com/cloud-foundry/) is your full stack monitoring solution - powered by artificial intelligence. Dynatrace SaaS/Managed allows you insights into all application requests from the users click in the browser down to the database statement and code-level. + +The Dynatrace SaaS/Managed OneAgent Framework causes an application to be automatically configured to work with a bound [Dynatrace SaaS/Managed Service][] instance (Free trials available). + + + + + + + + + +
Detection CriterionExistence of a single bound Dynatrace SaaS/Managed service. +
    +
  • Existence of a Dynatrace SaaS/Managed service is defined as the VCAP_SERVICES payload containing a service who's name, label or tag has dynatrace as a substring with at least `environmentid` and `apitoken` set as credentials.
  • +
+
Tagsdynatrace-one-agent=<version>
+Tags are printed to standard output by the buildpack detect script + +## User-Provided Service +Users must provide their own Dynatrace SaaS/Managed service. A user-provided Dynatrace SaaS/Managed service must have a name or tag with `dynatrace` in it so that the Dynatrace Saas/Managed OneAgent Framework will automatically configure the application to work with the service. + +The credential payload of the service may contain the following entries: + +| Name | Description +| ---- | ----------- +| `apitoken` | The token for integrating your Dynatrace environment with Cloud Foundry. You can find it in the deploy Dynatrace section within your environment. +| `apiurl` | (Optional) The base URL of the Dynatrace API. If you are using Dynatrace Managed you will need to set this property to `https:///e//api`. If you are using Dynatrace SaaS you don't need to set this property. +| `environmentid` | Your Dynatrace environment ID is the unique identifier of your Dynatrace environment. You can find it in the deploy Dynatrace section within your environment. +| `networkzone` | (Optional) Network zones are Dynatrace entities that represent your network structure. They help you to route the traffic efficiently, avoiding unnecessary traffic across data centers and network regions. Enter the network zone you wish to pass to the server during the OneAgent Download. +| `skiperrors` | (Optional) The errors during agent download are skipped and the injection is disabled. Use this option at your own risk. Possible values are 'true' and 'false'. This option is disabled by default! +| `enablefips`| (Optional) Enables the use of [FIPS 140 cryptographic algorithms](https://docs.dynatrace.com/docs/shortlink/oneagentctl#fips-140). Possible values are 'true' and 'false'. This option is disabled by default! +| addtechnologies | (Optional) Adds additional OneAgent code-modules via a comma-separated list. See [supported values](https://docs.dynatrace.com/docs/dynatrace-api/environment-api/deployment/oneagent/download-oneagent-version#parameters) in the "included" row| + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +## Support +This buildpack extension is currently Beta. If you have any questions or problems regarding the build pack itself please don't hesitate to contact Dynatrace on https://answers.ruxit.com/, be sure to use "cloudfoundry" as a topic. + +[Configuration and Extension]: ../README.md#configuration-and-extension +[Dynatrace SaaS/Managed Service]: http://www.dynatrace.com/cloud-foundry/ diff --git a/docs/framework-elastic_apm_agent.md b/docs/framework-elastic_apm_agent.md new file mode 100644 index 0000000000..55f09c5d15 --- /dev/null +++ b/docs/framework-elastic_apm_agent.md @@ -0,0 +1,67 @@ +# Elastic APM Agent Framework + +The Elastic APM Agent Framework causes an application to be automatically configured to work with [Elastic APM][]. + + + + + + + + + +
Detection CriterionExistence of a single bound Elastic APM service. The existence of an Elastic APM service defined by the VCAP_SERVICES payload containing a service name, label or tag with elastic-apm as a substring. +
Tagselastic-apm-agent=<version>
+Tags are printed to standard output by the buildpack detect script + +## User-Provided Service +When binding Elastic APM using a user-provided service, it must have name or tag with `elasticapm` or `elastic-apm` in it. The credential payload can contain the following entries. + +| Name | Description +| ---- | ----------- +| `server_urls` | The URLs for the Elastic APM Server. They must be fully qualified, including protocol (http or https) and port. +| `secret_token` (Optional)| This string is used to ensure that only your agents can send data to your APM server. Both the agents and the APM server have to be configured with the same secret token. Use if APM Server requires a token. +| `***` (Optional) | Any additional entries will be applied as a system property appended to `-Delastic.apm.` to allow full configuration of the agent. See [Configuration of Elastic Agent][]. Values are shell-escaped by default, but do have limited support, use with caution, for incorporating subshells (i.e. `$(some-cmd)`) and accessing environment variables (i.e. `${SOME_VAR}`). + + +### Creating an Elastic APM USer Provided Service +Users must provide their own Elastic APM service. A user-provided Elastic APM service must have a name or tag with `elastic-apm` in it so that the Elastic APM Agent Framework will automatically configure the application to work with the service. + +Example of a minimal configuration: + +``` +cf cups my-elastic-apm-service -p '{"server_urls":"https://my-apm-server:8200","secret_token":"my-secret-token"}' +``` + +Example of a configuration with additional configuration parameters: + +``` +cf cups my-elastic-apm-service -p '{"server_urls":"https://my-apm-server:8200","secret_token":"","server_timeout":"10s","environment":"production"}' +``` + +Bind your application to the service using: + +`cf bind-service my-app-name my-elastic-apm-service` + +or use the `services` block in the application manifest file. + + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by modifying the [`config/elastic_apm_agent.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +| ---- | ----------- +| `service_name` | This can be overridden by a `service_name` entry in the credentials payload. If neither are supplied the default is the application_name as specified by Cloud Foundry. +| `repository_root` | The URL of the Elastic APM repository index ([details][repositories]). +| `version` | The version of Elastic APM to use. Candidate versions can be found in [this listing][]. + + +[Configuration and Extension]: ../README.md#configuration-and-extension +[`config/elastic_apm_agent.yml`]: ../config/elastic_apm_agent.yml +[Elastic APM]: https://www.elastic.co/guide/en/apm/agent/java/current/index.html +[repositories]: extending-repositories.md +[this listing]: https://raw.githubusercontent.com/elastic/apm-agent-java/master/cloudfoundry/index.yml +[version syntax]: extending-repositories.md#version-syntax-and-ordering +[Configuration of Elastic Agent]: https://www.elastic.co/guide/en/apm/agent/java/current/configuration.html diff --git a/docs/framework-google_stackdriver_debugger.md b/docs/framework-google_stackdriver_debugger.md new file mode 100644 index 0000000000..82d1a4b991 --- /dev/null +++ b/docs/framework-google_stackdriver_debugger.md @@ -0,0 +1,43 @@ +# Google Stackdriver Debugger Framework +The Google Stackdriver Debugger Framework causes an application to be automatically configured to work with a bound [Google Stackdriver Debugger Service][]. + + + + + + + + + +
Detection CriterionExistence of a single bound Google Stackdriver Debugger service. +
    +
  • Existence of a Google Stackdriver Debugger service is defined as the VCAP_SERVICES payload containing a service who's name, label or tag has google-stackdriver-debugger as a substring.
  • +
+
Tagsgoogle-stackdriver-debugger=<version>
+Tags are printed to standard output by the buildpack detect script + +## User-Provided Service (Optional) +Users may optionally provide their own Google Stackdriver Debugger service. A user-provided Google Stackdriver Debugger service must have a name or tag with `google-stackdriver-debugger` in it so that the Google Stackdriver Debugger Agent Framework will automatically configure the application to work with the service. + +The credential payload of the service must contain the following entry: + +| Name | Description +| ---- | ----------- +| `PrivateKeyData` | A Base64 encoded Service Account JSON payload + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by modifying the [`config/google_stackdriver_debugger.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +| ---- | ----------- +| `repository_root` | The URL of the Google Stackdriver Debugger repository index ([details][repositories]). +| `version` | The version of Google Stackdriver Debugger to use. Candidate versions can be found in [this listing][]. + +[Configuration and Extension]: ../README.md#configuration-and-extension +[`config/google_stackdriver_debugger.yml`]: ../config/google_stackdriver_debugger.yml +[Google Stackdriver Debugger Service]: https://cloud.google.com/debugger/ +[repositories]: extending-repositories.md +[this listing]: https://java-buildpack.cloudfoundry.org/google-stackdriver-debugger/jammy/x86_64/index.yml +[version syntax]: extending-repositories.md#version-syntax-and-ordering diff --git a/docs/framework-google_stackdriver_profiler.md b/docs/framework-google_stackdriver_profiler.md new file mode 100644 index 0000000000..7bc3fa645f --- /dev/null +++ b/docs/framework-google_stackdriver_profiler.md @@ -0,0 +1,43 @@ +# Google Stackdriver Profiler Framework +The Google Stackdriver Profiler Framework causes an application to be automatically configured to work with a bound [Google Stackdriver Profiler Service][]. + + + + + + + + + +
Detection CriterionExistence of a single bound Google Stackdriver Profiler service. +
    +
  • Existence of a Google Stackdriver Profiler service is defined as the VCAP_SERVICES payload containing a service who's name, label or tag has google-stackdriver-profiler as a substring.
  • +
+
Tagsgoogle-stackdriver-profiler=<version>
+Tags are printed to standard output by the buildpack detect script + +## User-Provided Service (Optional) +Users may optionally provide their own Google Stackdriver Profiler service. A user-provided Google Stackdriver Profiler service must have a name or tag with `google-stackdriver-profiler` in it so that the Google Stackdriver Profiler Agent Framework will automatically configure the application to work with the service. + +The credential payload of the service must contain the following entry: + +| Name | Description +| ---- | ----------- +| `PrivateKeyData` | A Base64 encoded Service Account JSON payload + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by modifying the [`config/google_stackdriver_profiler.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +| ---- | ----------- +| `repository_root` | The URL of the Google Stackdriver Profiler repository index ([details][repositories]). +| `version` | The version of Google Stackdriver Profiler to use. Candidate versions can be found in [this listing][]. + +[Configuration and Extension]: ../README.md#configuration-and-extension +[`config/google_stackdriver_profiler.yml`]: ../config/google_stackdriver_profiler.yml +[Google Stackdriver Profiler Service]: https://cloud.google.com/profiler/ +[repositories]: extending-repositories.md +[this listing]: https://java-buildpack.cloudfoundry.org/google-stackdriver-profiler/jammy/x86_64/index.yml +[version syntax]: extending-repositories.md#version-syntax-and-ordering diff --git a/docs/framework-introscope_agent.md b/docs/framework-introscope_agent.md new file mode 100644 index 0000000000..d63adaeb24 --- /dev/null +++ b/docs/framework-introscope_agent.md @@ -0,0 +1,79 @@ +# CA Introscope APM Framework +The CA Introscope APM Framework causes an application to be automatically configured to work with a bound [Introscope service][]. + + + + + + + + + +
Detection CriterionExistence of a single bound Introscope service. +
    +
  • Existence of a Introscope service is defined as the VCAP_SERVICES payload containing a service who's name, label or tag has introscope as a substring.
  • +
+
Tagsintroscope-agent=<version>
+Tags are printed to standard output by the buildpack detect script + +## User-Provided Service (Optional) +Users may optionally provide their own Introscope service. A user-provided Introscope service must have a name or tag with `introscope` in it so that the Introscope Agent Framework will automatically configure the application to work with the service. + +The credential payload of the service may contain any valid CA APM Java agent property. + +The table below displays a subset of properties that are accepted by the buildpack. +Please refer to CA APM docs for a full list of valid agent properties. + + +| Name | Description +| ---- | ----------- +|`agent_manager_credential`| (Optional) The credential that is used to connect to the Enterprise Manager server. +|`agentManager_url_1` | The url of the Enterprise Manager server. +|`agent_manager_url`| (Deprecated) The url of the Enterprise Manager server. +|`credential`| (Deprecated) The credential that is used to connect to the Enterprise Manager server + + +To provide more complex values such as the `agent_name`, using the interactive mode when creating a user-provided service will manage the character escaping automatically. For example, the default `agent_name` could be set with a value of `agent-$(expr "$VCAP_APPLICATION" : '.*application_name[": ]*\([[:word:]]*\).*')` to calculate a value from the Cloud Foundry application name. + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by modifying the [`config/introscope_agent.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +| ---- | ----------- +| `repository_root` | The URL of the Introscope Agent repository index ([details][repositories]). +| `version` | The version of Introscope Agent to use. + +### Additional Resources +The framework can also be configured by overlaying a set of resources on the default distribution. To do this, add files to the `resources/introscope_agent` directory in the buildpack fork. + +##### Example for 10.x +``` +resources/ + |-introscope_agent/ + |-core/ + |-config/ + |-IntroscopeAgent.profile # place custom Introscope Profile under the config folder + |-hotdeploy/ # place custom pbd files under the hotdeploy folder + |-example.pbd +``` + +##### Example for 11.1.x +``` +resources/ + |-introscope_agent/ + |-releases/ + |-11.1/ + |-core/ + |-config/ + |-IntroscopeAgent.profile # place custom Introscope Profile under the config folder + |-hotdeploy/ # place custom pbd files under the hotdeploy folder + |-example.pbd +``` + +[Configuration and Extension]: ../README.md#configuration-and-extension +[`config/intoscope_agent.yml`]: ../config/intoscope_agent.yml +[Introscope service]: http://www.ca.com/us/opscenter/ca-application-performance-management.aspx +[repositories]: extending-repositories.md +[version syntax]: extending-repositories.md#version-syntax-and-ordering diff --git a/docs/framework-jacoco_agent.md b/docs/framework-jacoco_agent.md new file mode 100644 index 0000000000..c97d75e08a --- /dev/null +++ b/docs/framework-jacoco_agent.md @@ -0,0 +1,50 @@ +# JaCoco Agent Framework +The JaCoCo Agent Framework causes an application to be automatically configured to work with a bound [JaCoCo Service][]. + + + + + + + + + +
Detection CriterionExistence of a single bound JaCoCo service. +
    +
  • Existence of a JaCoCo service is defined as the VCAP_SERVICES payload containing a service who's name, label or tag has jacoco as a substring.
  • +
+
Tagsjacoco-agent=<version>
+Tags are printed to standard output by the buildpack detect script + +## User-Provided Service (Optional) +Users may optionally provide their own JaCoCo service. A user-provided JaCoCo service must have a name or tag with `jacoco` in it so that the JaCoCo Agent Framework will automatically configure the application to work with the service. + +The credential payload of the service may contain the following entries: + +| Name | Description +| ---- | ----------- +| `address` | The host for the agent to connect to or listen on +| `excludes` | (Optional) A list of class names that should be excluded from execution analysis. The list entries are separated by a colon (:) and may use wildcard characters (* and ?). +| `includes` | (Optional) A list of class names that should be included in execution analysis. The list entries are separated by a colon (:) and may use wildcard characters (* and ?). +| `port` | (Optional) The port for the agent to connect to or listen on +| `output` | (Optional) The mode for the agent. Possible values are either tcpclient (default) or tcpserver. + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by modifying the [`config/jacoc_agent.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +| ---- | ----------- +| `repository_root` | The URL of the JaCoCo repository index ([details][repositories]). +| `version` | The version of JaCoCo to use. Candidate versions can be found in [this listing][]. + +### Additional Resources +The framework can also be configured by overlaying a set of resources on the default distribution. To do this, add files to the `resources/jacoco_agent` directory in the buildpack fork. For example, to override the default `jacoco.yml` add your custom file to `resources/jacoco_agent/jacoco.yml`. + +[Configuration and Extension]: ../README.md#configuration-and-extension +[`config/jacoco_agent.yml`]: ../config/jacoco_agent.yml +[JaCoCo Service]: http://www.jacoco.org/jacoco/ +[repositories]: extending-repositories.md +[this listing]: https://java-buildpack.cloudfoundry.org/jacoco/index.yml +[version syntax]: extending-repositories.md#version-syntax-and-ordering diff --git a/docs/framework-java-cfenv.md b/docs/framework-java-cfenv.md new file mode 100644 index 0000000000..e9bbb31c71 --- /dev/null +++ b/docs/framework-java-cfenv.md @@ -0,0 +1,19 @@ +# Java CfEnv Framework +The Java CfEnv Framework provides the `java-cfenv` library for Spring Boot 3+ applications. This library sets various Spring Boot properties by parsing CloudFoundry variables such as `VCAP_SERVICES`, allowing Spring Boot's autoconfiguration to kick in. + +This is the recommended replacement for Spring AutoReconfiguration library which is deprecated. See the `java-cfenv` repostitory for more detail. + +It also sets the 'cloud' profile for Spring Boot applications, as the Spring AutoReconfiguration framework did. + + + + + + + + + + + +
Detection CriterionExistence of a `Spring-Boot-Version: 3.*` manifest entryNo existing `java-cfenv` library found
Tagsjava-cf-env=<version>
+Tags are printed to standard output by the buildpack detect script diff --git a/docs/framework-java_memory_assistant.md b/docs/framework-java_memory_assistant.md new file mode 100644 index 0000000000..f8a2f60594 --- /dev/null +++ b/docs/framework-java_memory_assistant.md @@ -0,0 +1,125 @@ +# Java Memory Assistant Framework +The Java Memory Assistant is a Java agent (as in `-javaagent`) that creats heap dumps of your application automatically based on preconfigured conditions of memory usage. +The heap dumps created by the Java Memory Assistant can be analyzed using Java memory profilers that support the `.hprof` format (i.e., virtually all profilers). + + + + + + + + +
Detection Criterionenabled set in the config/java_memory_assistant.yml
Tagsjava-memory-assistant=<version>
+Tags are printed to standard output by the buildpack detect script. + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by modifying the [`config/java_memory_assistant.yml`][] file in the buildpack fork. + +| Name | Description +| ---- | ----------- +| `enabled` | Whether to enable the Java Memory Assistant framework. By default the agent is turned off. +| `agent.heap_dump_folder` | The folder on the container's filesystem where heap dumps are created. Default value: `$PWD` +| `agent.thresholds.` | This configuration allows to define thresholds for every memory area of the JVM. Thresholds can be defined in absolute percentages, e.g., `75%` creates a heap dump at 75% of the selected memory area. It is also possible to specify relative increases and decreases of memory usage: for example, `+5%/2m` will triggera heap dumpo if the particular memory area has increased by `5%` or more over the last two minutes. See below to check which memory areas are supported. Since version `0.3.0`, thresholds can also be specified in terms of absolute values, e.g., `>400MB` (more than 400 MB) or `<=30KB` (30 KB or less); supported memory size units are `KB`, `MB` and `GB`. +| `agent.check_interval` | The interval between checks. Examples: `1s` (once a second), `3m` (every three minutes), `1h` (once every hour). Default: `5s` (check every five seconds). +| `agent.max_frequency` | Maximum amount of heap dumps that the Java Memory Assistant is allowed to create in a given amount of time. Examples: `1/30s` (no more than one heap dump every thirty seconds), `2/3m` (up to two heap dumps every three minutes), `1/2h` (one heap dump every two hours). The time interval is checked every time one heap dump *should* be created (based on the specified thresholds), and compared with the timestamps of the previously created heap dumps to make sure that the maximum frequency is not exceeded. Default: `1/1m` (one heap dump per minute). | +| `agent.log_level` | The log level used by the Java Memory Assistant. Supported values are the same as the Java buildpack's: `DEBUG`, `WARN`, `INFO`, `ERROR` and `FATAL` (the latter is equivalent to `ERROR`). If the `agent.log_level` is not specified, the Java buildpack's log level will be used. | +| `clean_up.max_dump_count` | Maximum amount of heap dumps that can be stored in the filesystem of the container; when the creation of a new heap dump would cause the threshold to be surpassed, the oldest heap dumps are removed from the file system. Default value: `1` | + +### Heap Dump Names + +The heap dump filenames will be generated according to the following name pattern: + +`-%ts:yyyyMMdd'T'mmssSSSZ%-.hprof` + +The timestamp pattern `%ts:yyyyMMdd'T'mmssSSSZ%` is equivalent to the `%FT%T%z` pattern of [strftime](http://www.cplusplus.com/reference/ctime/strftime/) for [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601). The default naming convention matches the [`jvmkill`][] naming convention. + +### Supported Memory Areas + +| Memory Area | Property Name | +|------------------------|------------------| +| Heap | `heap` | +| Code Cache | `code_cache` | +| Metaspace | `metaspace` | +| Compressed Class Space | `compressed_class` | +| Eden | `eden` | +| Survivor | `survivor` | +| Old Generation | `old_gen` | +| Tenured Gen | `tenured_gen` | +| CodeHeap 'non-nmethods' | `code_heap.non_nmethods` | +| CodeHeap 'profiled nmethods' | `code_heap.profiled_nmethods` | +| CodeHeap 'non-profiled nmethods' | `code_heap.non_profiled_nmethods` | + +Different builds and versions of Java Virtual Machines offer different memory areas. +The list of supported Java Virtual Machines and the respective memory areas can be found in the [Java Memory Assistant documentation](https://github.com/SAP/java-memory-assistant#supported-jvms). + +The default values can be found in the [`config/java_memory_assistant.yml`][] file. + +### Examples + +Enable the Java Memory Assistant with its default settings: + +```yaml +JBP_CONFIG_JAVA_MEMORY_ASSISTANT: '{enabled : true}' +``` + +Create heap dumps when the old generation memory pool exceeds 800 MB: + +```yaml +JBP_CONFIG_JAVA_MEMORY_ASSISTANT: '{enabled : true, agent: { thresholds : { old_gen : ">800MB" } } }' +``` + +Create heap dumps when the old generation grows by more than 20% in two minutes: + +```yaml +JBP_CONFIG_JAVA_MEMORY_ASSISTANT: '{enabled : true, agent : { thresholds : { old_gen : +20%/2m } } }' +``` + +### What are the right thresholds for your application? + +Well, it depends. +The way applications behave in terms of memory management is a direct result of how they are implemented. +This is much more then case when the applications are under heavy load. +Thus, there is no "silver bullet" configuration that will serve all applications equally well, and Java Memory Assistant configurations should result from profiling the application under load and then encode the expected memory usage patterns (plus a margin upwards) to detect anomalies. + +Nevertheless, a memory area that tends to be particularly interesting to monitor is the so called "old generation" (`old_gen`). +When instantiated, bjects in the Java heap are allocated in an area called `eden`. +As garbage collections occur, objects that are not reclaimed become first "survivors" (and belong to the namesake `survivor` memory area) and then eventually become `old_gen`. +In other words, `old_gen` objects are those that survived multiple garbage collections. +In contrast, `eden` and `survivor` objects are collectively called "young generation". + +Application-wide singletons and pooled objects (threads, connections) are examples of "legitimate" `old_gen` candidates. +But memory leaks, by their very nature or surviving multiple garbage collections, end up in `old_gen` too. +Under load that is not too high for the application (and you should find out what it is with load tests and avoid it via rate limiting, e.g., using [route services](https://docs.cloudfoundry.org/services/route-services.html) in front of your application), Java code that allows the JVM to perform efficient memory management tends to have a rather consistent baseline of `old_gen` objects, with most objects being reclaimed as they are still young generation. +That is, when the `old_gen` grows large with respect to the overall heap, this often signifies some sort of memory leak or, at the very least, suboptimal memory management. +Notable exceptions to this rule of thumb are applications that use large local caches. + +### Making sure heap dumps can be created + +The Java Virtual Machine must create heap dumps on a file. +Unless you are using a `volume service`, it pretty much means that, even if you are uploading the heap dump somewhere else, the heap dump must first land on the ephemeral disk of the container. +Ephemeral disks have quotas and, if all the space is taken by heap dumps (even incomplete ones!), horrible things are bound to happen to your app. + +The maximum size of a heap dump depends on the maximum size of the heap of the Java Virtual Machine. +Consider increasing the disk quota of your warden/garden container via the `cf scale -k [new size]` using as `new size` to the outcome of the following calculation: + +`[max heap size] * [max heap dump count] + 200MB` + +The aditional `200MB` is a rule-of-thumb, generous over-approximation of the amount of disk the buildpack and the application therein needs to run. +If your application requires more filesystem than just a few tens of megabytes, you must increase the additional portion of the disk amount calculation accordingly. + +### Where to best store heap dumps? + +Heap dumps are created by the Java Virtual Machine on a file on the filesystem mounted by the garden container. +Normally, the filesystem of a container is ephemeral. +That is, if your app crashes or it is shut down, the filesystem of its container is gone with it and so are your heap dumps. + +To prevent heap dumps from "going down" with the container, you should consider storing them on a `volume service`. + +#### Container-mounted volumes + +If you are using a filesystem service that mounts persistent volumes to the container, it is enough to name one of the volume services `heap-dump` or tag one volume with `heap-dump`, and the path specified as the `heap_dump_folder` configuration will be resolved against `/-/-`. The default directory convention matches the [`jvmkill`][] directory convention. + +[`config/java_memory_assistant.yml`]: ../config/java_memory_assistant.yml +[`jvmkill`]: jre-open_jdk_jre.md#jvmkill diff --git a/docs/framework-java_opts.md b/docs/framework-java_opts.md index 02e8237761..69dea716d6 100644 --- a/docs/framework-java_opts.md +++ b/docs/framework-java_opts.md @@ -14,9 +14,8 @@ The Java Options Framework contributes arbitrary Java options to the application Tags are printed to standard output by the buildpack detect script - ## Configuration -For general information on configuring the buildpack, refer to [Configuration and Extension][]. +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. The framework can be configured by creating or modifying the [`config/java_opts.yml`][] file in the buildpack fork. @@ -25,7 +24,38 @@ The framework can be configured by creating or modifying the [`config/java_opts. | `from_environment` | Whether to append the value of the `JAVA_OPTS` environment variable to the collection of Java options | `java_opts` | The Java options to use when running the application. All values are used without modification when invoking the JVM. The options are specified as a single YAML scalar in plain style or enclosed in single or double quotes. -Any `JAVA_OPTS` from either the config file or environment variables that configure memory options will cause deployment to fail as they're not allowed. Memory options are configured by the buildpack and may not be modified. +Any `JAVA_OPTS` from either the config file or environment variables will be specified in the start command after any Java Opts added by other frameworks. + +## Escaping strings + +Java options will have special characters escaped when used in the shell command that starts the Java application but the `$` and `\` characters will not be escaped. This is to allow Java options to include environment variables when the application starts. + +```bash +cf set-env my-application JAVA_OPTS '-Dexample.port=$PORT' +``` + +If an escaped `$` or `\` character is needed in the Java options they will have to be escaped manually. For example, to obtain this output in the start command. + +```bash +-Dexample.other=something.\$dollar.\\slash +``` + +From the command line use; +```bash +cf set-env my-application JAVA_OPTS '-Dexample.other=something.\\\\\$dollar.\\\\\\\slash' +``` + +From the [`config/java_opts.yml`][] file use; +```yaml +from_environment: true +java_opts: '-Dexample.other=something.\\$dollar.\\\\slash' +``` + +Finally, from the applications manifest use; +```yaml + env: + JAVA_OPTS: '-Dexample.other=something.\\\\\$dollar.\\\\\\\slash' +``` ## Example ```yaml @@ -35,44 +65,32 @@ from_environment: false java_opts: -Xloggc:$PWD/beacon_gc.log -verbose:gc ``` -## Memory Settings - -The following `JAVA_OPTS` are restricted and will cause the application to fail deployment. - -* `-Xms` -* `-Xmx` -* `-Xss` -* `-XX:MaxMetaspaceSize` -* `-XX:MaxPermSize` -* `-XX:MetaspaceSize` -* `-XX:PermSize` - -### Allowed Memory Settings - -Setting any of the allowed memory settings may require a change to the [Memory Weightings]. Where a value is shown it is the default value for that setting. +## Allowed Memory Settings | Argument| Description | ------- | ----------- -| `-Xmn ` | Maximum size of young generation, known as the eden region. **This could effect the total heap size [Memory Weightings].** +| `-Xms` | Minimum or initial size of heap. +| `-Xss` | Size of each thread's stack. **This could effect the total heap size. [JRE Memory]** +| `-XX:MaxMetaspaceSize` | The maximum size Metaspace can grow to. **This could effect the total heap size. [JRE Memory]** +| `-XX:MaxPermSize` | The maximum size Permgen can grow to. Only applies to Java 7. **This could effect the total heap size. [JRE Memory]** +| `-Xmn ` | Maximum size of young generation, known as the eden region. | `-XX:+UseGCOverheadLimit` | Use a policy that limits the proportion of the VM's time that is spent in GC before an `java.lang.OutOfMemoryError` error is thrown. | `-XX:+UseLargePages` | Use large page memory. For details, see [Java Support for Large Memory Pages]. | `-XX:-HeapDumpOnOutOfMemoryError` | Dump heap to file when `java.lang.OutOfMemoryError` is thrown. | `-XX:HeapDumpPath=` | Path to directory or filename for heap dump. | `-XX:LargePageSizeInBytes=` | Sets the large page size used for the Java heap. -| `-XX:MaxDirectMemorySize=` | Upper limit on the maximum amount of allocatable direct buffer memory. **This could effect the [Memory Weightings].** +| `-XX:MaxDirectMemorySize=` | Upper limit on the maximum amount of allocatable direct buffer memory. **This could effect the total heap size. [JRE Memory]** | `-XX:MaxHeapFreeRatio=` | Maximum percentage of heap free after GC to avoid shrinking. -| `-XX:MaxNewSize=` | Maximum size of new generation. Since `1.4`, `MaxNewSize` is computed as a function of `NewRatio`. **This could effect the total heap size [Memory Weightings].** +| `-XX:MaxNewSize=` | Maximum size of new generation. Since `1.4`, `MaxNewSize` is computed as a function of `NewRatio`. | `-XX:MinHeapFreeRatio=` | Minimum percentage of heap free after GC to avoid expansion. | `-XX:NewRatio=` | Ratio of old/new generation sizes. 2 is equal to approximately 66%. -| `-XX:NewSize=` | Default size of new generation. **This could effect the total heap size [Memory Weightings].** +| `-XX:NewSize=` | Default size of new generation. | `-XX:OnError=";"` | Run user-defined commands on fatal error. -| `-XX:OnOutOfMemoryError=";"` | Run user-defined commands when an `java.lang.OutOfMemoryError` is first thrown. -| `-XX:ReservedCodeCacheSize=` | _Java 8 Only_ Maximum code cache size. Also know as `-Xmaxjitcodesize`. **This could effect the [Memory Weightings].** +| `-XX:ReservedCodeCacheSize=` | _Java 8 Only_ Maximum code cache size. Also know as `-Xmaxjitcodesize`. **This could effect the total heap size. [JRE Memory]** | `-XX:SurvivorRatio=` | Ratio of eden/survivor space. Solaris only. | `-XX:TargetSurvivorRatio=` | Desired ratio of survivor space used after scavenge. -| `-XX:ThreadStackSize=` | Thread stack size. (0 means use default stack size). [`config/java_opts.yml`]: ../config/java_opts.yml [Configuration and Extension]: ../README.md#configuration-and-extension [Java Support for Large Memory Pages]: http://www.oracle.com/technetwork/java/javase/tech/largememory-jsp-137182.html -[Memory Weightings]: jre-open_jdk_jre.md#memory-weightings +[JRE Memory]: jre-open_jdk_jre.md#memory diff --git a/docs/framework-jmx-jconsole.png b/docs/framework-jmx-jconsole.png new file mode 100644 index 0000000000..ed0a52ebb1 Binary files /dev/null and b/docs/framework-jmx-jconsole.png differ diff --git a/docs/framework-jmx.md b/docs/framework-jmx.md new file mode 100644 index 0000000000..8d122ccca8 --- /dev/null +++ b/docs/framework-jmx.md @@ -0,0 +1,40 @@ +# JMX Framework +The JMX Framework contributes Java JMX configuration to the application at runtime. **Note:** This framework is only useful in Diego-based containers with SSH access enabled. + + + + + + + + + + +
Detection Criterionenabled set in the config/jmx.yml file
Tagsjmx=<port>
+Tags are printed to standard output by the buildpack detect script + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by creating or modifying the [`config/jmx.yml`][] file in the buildpack fork. + +| Name | Description +| ---- | ----------- +| `enabled` | Whether to enable JMX +| `port` | The port that the debug agent will listen on. Defaults to `5000`. + +## Creating SSH Tunnel +After starting an application with JMX enabled, an SSH tunnel must be created to the container. To create that SSH container, execute the following command: + +```bash +$ cf ssh -N -T -L :localhost: +``` + +The `REMOTE_PORT` should match the `port` configuration for the application (`5000` by default). The `LOCAL_PORT` must match the `REMOTE_PORT`. + +Once the SSH tunnel has been created, your JConsole should connect to `localhost:` for JMX access. + +![JConsole Configuration](framework-jmx-jconsole.png) + +[`config/jmx.yml`]: ../config/jmx.yml +[Configuration and Extension]: ../README.md#configuration-and-extension diff --git a/docs/framework-jprofiler_profiler.md b/docs/framework-jprofiler_profiler.md new file mode 100644 index 0000000000..42902ac42f --- /dev/null +++ b/docs/framework-jprofiler_profiler.md @@ -0,0 +1,46 @@ +# JProfiler Profiler Framework +The JProfiler Profiler Framework contributes JProfiler configuration to the application at runtime. + + + + + + + + + + +
Detection Criterionenabled set in the config/jprofiler_profiler.yml file
Tagsjprofiler-profiler=<version>
+Tags are printed to standard output by the buildpack detect script + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by creating or modifying the [`config/jprofiler_profiler.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +| ---- | ----------- +| `enabled` | Whether to enable the JProfiler Profiler +| `port` | The port that the JProfiler Profiler will listen on. Defaults to `8849`. +| `nowait` | Whether to start process without waiting for JProfiler to connect first. Defaults to `true`. +| `repository_root` | The URL of the JProfiler Profiler repository index ([details][repositories]). +| `version` | The version of the JProfiler Profiler to use. Candidate versions can be found in [this listing][]. + +## Creating SSH Tunnel +After starting an application with the JPorfiler Profiler enabled, an SSH tunnel must be created to the container. To create that SSH container, execute the following command: + +```bash +$ cf ssh -N -T -L :localhost: +``` + +The `REMOTE_PORT` should match the `port` configuration for the application (`8849` by default). The `LOCAL_PORT` can be any open port on your computer, but typically matches the `REMOTE_PORT` where possible. + +Once the SSH tunnel has been created, your JProfiler Profiler should connect to `localhost:` for debugging. + +![JProfiler Configuration](framework-jprofiler_profiler.png) + +[`config/jprofiler_profiler.yml`]: ../config/jprofiler_profiler.yml +[Configuration and Extension]: ../README.md#configuration-and-extension +[this listing]: http://download.pivotal.io.s3.amazonaws.com/jprofiler/index.yml +[repositories]: extending-repositories.md +[version syntax]: extending-repositories.md#version-syntax-and-ordering diff --git a/docs/framework-jprofiler_profiler.png b/docs/framework-jprofiler_profiler.png new file mode 100644 index 0000000000..e7d34c898b Binary files /dev/null and b/docs/framework-jprofiler_profiler.png differ diff --git a/docs/framework-jrebel_agent.md b/docs/framework-jrebel_agent.md new file mode 100644 index 0000000000..a8923c3058 --- /dev/null +++ b/docs/framework-jrebel_agent.md @@ -0,0 +1,37 @@ +# JRebel Agent Framework + +The JRebel Agent Framework causes an application to be automatically configured to work with [JRebel][]. Pushing any [JRebel Cloud/Remote][] enabled application (containing `rebel-remote.xml`) will automatically download the latest version of [JRebel][] and set it up for use. + + + + + + + + + + +
Detection CriterionExistence of a rebel-remote.xml file inside the application archive. This file is present in every application that is configured to use JRebel Cloud/Remote.
Tagsjrebel-agent=<version>
+Tags are printed to standard output by the buildpack detect script + +For more information regarding setup and configuration, please refer to the [JRebel with Pivotal Cloud Foundry tutorial][pivotal]. + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by modifying the [`config/jrebel_agent.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +| ---- | ----------- +| `repository_root` | The URL of the JRebel repository index ([details][repositories]). +| `version` | The version of JRebel to use. Candidate versions can be found in [this listing][]. +| `enabled` | Whether to activate JRebel (upon the presence of `rebel-remote.xml`) or not. + +[Configuration and Extension]: ../README.md#configuration-and-extension +[`config/jrebel_agent.yml`]: ../config/jrebel_agent.yml +[JRebel Cloud/Remote]: http://manuals.zeroturnaround.com/jrebel/remoteserver/index.html +[JRebel]: http://zeroturnaround.com/software/jrebel/ +[pivotal]: http://manuals.zeroturnaround.com/jrebel/remoteserver/pivotal.html +[repositories]: extending-repositories.md +[this listing]: http://dl.zeroturnaround.com/jrebel/index.yml +[version syntax]: extending-repositories.md#version-syntax-and-ordering diff --git a/docs/framework-luna_security_provider.md b/docs/framework-luna_security_provider.md new file mode 100644 index 0000000000..7fd8ad0cda --- /dev/null +++ b/docs/framework-luna_security_provider.md @@ -0,0 +1,127 @@ +# Luna Security Provider Framework +The Luna Security Provider Framework causes an application to be automatically configured to work with a bound [Luna Security Service][]. + + + + + + + + + + +
Detection CriterionExistence of a single bound Luna Security Provider service. The existence of an Luna Security service defined by the VCAP_SERVICES payload containing a service name, label or tag with luna as a substring. +
Tagsluna-security-provider=<version>
+Tags are printed to standard output by the buildpack detect script + +## User-Provided Service +When binding to the Luna Security Provider using a user-provided service, it must have name or tag with `luna` in it. The credential payload can contain the following entries: + +| Name | Description +| ---- | ----------- +| `client` | A hash containing client configuration +| `servers` | An array of hashes containing server configuration +| `groups` | An array of hashes containing group configuration + +#### Client Configuration +| Name | Description +| ---- | ----------- +| `certificate` | A PEM encoded client certificate +| `private-key` | A PEM encoded client private key + +#### Server Configuration +| Name | Description +| ---- | ----------- +| `certificate` | A PEM encoded server certificate +| `name` | A host name or address + +#### Group Configuration +| Name | Description +| ---- | ----------- +| `label` | The label for the group +| `members` | An array of group member serial numbers + +### Example Credentials Payload +``` +{ + "client": { + "certificate": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----", + "private-key": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" + }, + "servers": [ + { + "name": "test-host-1", + "certificate": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + }, + { + "name": "test-host-2", + "certificate": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + } + ], + "groups": [ + { + "label": "test-group-1", + "members": [ + "test-serial-number-1", + "test-serial-number-2" + ] + }, + { + "label": "test-group-2", + "members": [ + "test-serial-number-3", + "test-serial-number-4" + ] + } + ] +} +``` + +### Creating Credential Payload +In order to create the credentials payload, you should collapse the JSON payload to a single line and set it like the following + +``` +$ cf create-user-provided-service luna -p '{"client":{"certificate":"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----","private-key":"-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"},"servers":[{"name":"test-host-1","certificate":"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"},{"name":"test-host-2","certificate":"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"}],"groups":[{"label":"test-group-1","members":["test-serial-number-1","test-serial-number-2"]},{"label":"test-group-2","members":["test-serial-number-3","test-serial-number-4"]}]}' +``` + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by modifying the [`config/luna_security_provider.yml`][] file in the buildpack. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +| ---- | ----------- +| `ha_logging_enabled` | Whether to enable HA logging for the Luna Security Provider. Defaults to `true`. +| `logging_enabled` | Whether to enable the logging wrapper for the Luna Security Provider. Defaults to `false`. +| `tcp_keep_alive_enabled` | Whether to enable the client TCP keep alive setting for the Luna Security Provider. Defaults to `false`. +| `repository_root` | The URL of the Luna Security Provider repository index ([details][repositories]). +| `version` | Version of the Luna Security Provider to use. + +### Additional Resources +The framework can also be configured by overlaying a set of resources on the default distribution. To do this follow one of the options below. + +Configuration files are created in this order: + +1. Default configuration +2. Buildpack fork +3. Buildpack generated configuration if the bound service has both a `servers` and `groups` key +4. External configuration if configured + +#### Buildpack Fork +Add files to the `resources/luna_security_provider` directory in the buildpack fork. For example, to override the default `Chrystoki.conf` add your custom file to `resources/luna_security_provider/Chrystoki.conf`. + +#### External Configuration +Set `LUNA_CONF_HTTP_URL` to an HTTP or HTTPS URL which points to the directory where your configuration files exist. You may also include a user and password in the URL, like `https://user:pass@example.com`. + +The Java buildpack will take the URL to the directory provided and attempt to download the following files from that directory: + +- `Chrystoki.conf` +- `server-certificates.pem` + +Any file successfully downloaded will be copied to the configuration directory. The buildpack does not fail if files are missing. + +[`config/luna_security_provider.yml`]: ../config/luna_security_provider.yml +[Luna Security Service]: http://www.safenet-inc.com/data-encryption/hardware-security-modules-hsms/ +[Configuration and Extension]: ../README.md#configuration-and-extension +[repositories]: extending-repositories.md +[version syntax]: extending-repositories.md#version-syntax-and-ordering diff --git a/docs/framework-maria_db_jdbc.md b/docs/framework-maria_db_jdbc.md index cd90ced0f9..635767f92f 100644 --- a/docs/framework-maria_db_jdbc.md +++ b/docs/framework-maria_db_jdbc.md @@ -4,18 +4,18 @@ The MariaDB JDBC Framework causes a JDBC driver JAR to be automatically download - - +
Detection CriterionExistence of a single bound MariaDB or MySQL service and no provided MariaDB or MySQL JDBC JAR. + Existence of a single bound MariaDB or MySQL service and NO provided MariaDB or MySQL JDBC jar.
    -
  • Existence of a MariaDB service is defined as the VCAP_SERVICES payload containing a service who's name, label or tag has mariadb as a substring.
  • -
  • Existence of a MySQL service is defined as the VCAP_SERVICES payload containing a service who's name, label or tag has mysql as a substring.
  • -
  • Existence of a MariaDB JDBC JAR is defined as the application containing a JAR who's name matches mariadb-java-client*.jar
  • -
  • Existence of a MySQL JDBC JAR is defined as the application containing a JAR who's name matches mysql-connector-java*.jar
  • +
  • Existence of a MariaDB service is defined as the VCAP_SERVICES payload containing a service whose name, label or tag has mariadb as a substring.
  • +
  • Existence of a MySQL service is defined as the VCAP_SERVICES payload containing a service whose name, label or tag has mysql as a substring.
  • +
  • Existence of a MariaDB JDBC jar is defined as the application containing a JAR whose name matches mariadb-java-client*.jar
  • +
  • Existence of a MySQL JDBC jar is defined as the application containing a JAR whose name matches mysql-connector-j*.jar
Tagsmaria-db-jdbc=<version>maria-db-jdbc=<version>
Tags are printed to standard output by the buildpack detect script @@ -24,7 +24,7 @@ Tags are printed to standard output by the buildpack detect script Users may optionally provide their own MariaDB or MySQL service. A user-provided MariaDB or MySQL service must have a name or tag with `mariadb` or `mysql` in it so that the MariaDB JDBC Framework will automatically download the JDBC driver JAR and place it on the classpath. ## Configuration -For general information on configuring the buildpack, refer to [Configuration and Extension][]. +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. The framework can be configured by modifying the [`config/maria_db_jdbc.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. diff --git a/docs/framework-metric_writer.md b/docs/framework-metric_writer.md new file mode 100644 index 0000000000..aeaaff0a5a --- /dev/null +++ b/docs/framework-metric_writer.md @@ -0,0 +1,44 @@ +# Metric Writer Framework +The Metric Writer Framework causes an application to be automatically configured to add Cloud Foundry-specific Micrometer tags. + + + + + + + + + + +
Detection CriterionExistence of a micrometer-core*.jar file in the application directory
Tagsmetric-writer-reconfiguration=<version>
+Tags are printed to standard output by the buildpack detect script + +The Metric Writer Framework adds a set of CloudFoundry-specific Micrometer tags to any Micrometer metric that does not already contain the keys. The values of these tags can be explicitly configured via environment variables otherwise they default to values extracted from the standard Cloud Foundry runtime environment. + +| Tag | Environment Variable | Default +| --- | ---------------------| ----------- +| `cf.account` | `CF_APP_ACCOUNT` | `$VCAP_APPLICATION / cf_api` +| `cf.application` | `CF_APP_APPLICATION`| `$VCAP_APPLICATION / application_name / frigga:name` +| `cf.cluster` | `CF_APP_CLUSTER` | `$VCAP_APPLICATION / application_name / frigga:cluster` +| `cf.version` | `CF_APP_VERSION` | `$VCAP_APPLICATION / application_name / frigga:revision` +| `cf.instance.index` | `CF_APP_INSTANCE_INDEX` | `$CF_INSTANCE_INDEX` +| `cf.organization` | `CF_APP_ORGANIZATION` | `$VCAP_APPLICATION / organization_name` +| `cf.space` | `CF_APP_SPACE` | `$VCAP_APPLICATION / space_name` + + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by modifying the [`config/metric_writer.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +| ---- | ----------- +| `enabled` | Whether to attempt metric augmentation +| `repository_root` | The URL of the Metric Writer repository index ([details][repositories]). +| `version` | The version of Metric Writer to use. Candidate versions can be found in [this listing][]. + +[Configuration and Extension]: ../README.md#configuration-and-extension +[`config/metric_writer.yml`]: ../config/metric_writer.yml +[repositories]: extending-repositories.md +[this listing]: https://java-buildpack.cloudfoundry.org/metric-writer/index.yml +[version syntax]: extending-repositories.md#version-syntax-and-ordering diff --git a/docs/framework-multi_buildpack.md b/docs/framework-multi_buildpack.md new file mode 100644 index 0000000000..29f165c9d0 --- /dev/null +++ b/docs/framework-multi_buildpack.md @@ -0,0 +1,40 @@ +# Multiple Buildpack Framework +The Multiple Buildpack Framework enables the Java Buildpack to act as the final buildpack in a multiple buildpack deployment. It reads the contributions of other, earlier buildpacks and incorporates them into its standard staging. + +The Java Options Framework contributes arbitrary Java options to the application at runtime. + + + + + + + + + + +
Detection CriterionExistence of buildpack contribution directories (typically /tmp/<RANDOM>/deps/<INDEX> containing a config.yml file.
Tagsmulti-buildpack=<BUILDPACK_NAME>,...
+Tags are printed to standard output by the buildpack detect script + +## Multiple Buildpack Integration API +When the Java Buildpack acts as the final buildpack in a multiple buildpack deployment it honors the following core contract integration points. + +| Integration Point | Buildpack Usage +| ----------------- | --------------- +| `/bin` | An existing `/bin` directory contributed by a non-final buildpack will be added to the `$PATH` of the application as it executes +| `/lib` | An existing `/lib` directory contributed by a non-final buildpack will be added to the `$LD_LIBRARY_PATH` of the application as it executes + +In addition to the core contract, the Java Buildpack defines the following keys in `config.yml` as extension points for contributing to the application. **All keys are optional, and all paths are absolute.** + +| Key | Type | Description +| --- | ---- | ----------- +| `additional_libraries` | `[ path ]` | An array of absolute paths to libraries will be added to the application's classpath +| `environment_variables` | `{ string, ( path \| string ) }` | A hash of string keys to absolute path or string values that will be added as environment variables +| `extension_directories` | `[ path ]` | An array of absolute paths to directories containing JRE extensions +| `java_opts.agentpaths` | `[ path ]` | An array of absolute paths to libraries that will be added as agents +| `java_opts.agentpaths_with_props` | `{ path, { string, string } }` | A nested hash with absolute paths keys and hashes of string keys and string values as a value that will be added as agents with properties +| `java_opts.bootclasspath_ps` | `[ path ]` | An array of absolute paths that will be added to the application's bootclasspath +| `java_opts.javaagents` | `[ path ]` | An array of absolute paths that will be added as javaagents +| `java_opts.preformatted_options` | `[ string ]` | An array of strings that will be added as options without modification +| `java_opts.options` | `{ string, ( path \| string ) }` | A hash of string keys to absolute path or string values that will be added as options +| `java_opts.system_properties` | `{ string , ( path \| string ) }` | A hash of string keys to absolute path or string values that will be added as system properties +| `security_providers` | `[ string ]` | An array of strings to be added to list of security providers diff --git a/docs/framework-new_relic_agent.md b/docs/framework-new_relic_agent.md index 815781cd70..b30336d3fd 100644 --- a/docs/framework-new_relic_agent.md +++ b/docs/framework-new_relic_agent.md @@ -23,10 +23,12 @@ The credential payload of the service may contain the following entries: | Name | Description | ---- | ----------- -| `licenseKey` | The license key to use when authenticating +| `license_key` | (Optional) Either this credential or `licenseKey` must be provided. If both are provided then the value for `license_key` will always win. The license key to use when authenticating. +| `licenseKey` | (Optional) As above. +| `***` | (Optional) Any additional entries will be applied as a system property appended to `-Dnewrelic.config.` to allow full configuration of the agent. ## Configuration -For general information on configuring the buildpack, refer to [Configuration and Extension][]. +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. The framework can be configured by modifying the [`config/new_relic_agent.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. @@ -34,6 +36,28 @@ The framework can be configured by modifying the [`config/new_relic_agent.yml`][ | ---- | ----------- | `repository_root` | The URL of the New Relic repository index ([details][repositories]). | `version` | The version of New Relic to use. Candidate versions can be found in [this listing][]. +| `extensions.repository_root` | The URL of the Extensions repository index ([details][repositories]). +| `extensions.version` | The version of the Extensions to use. Candidate versions can be found in the the repository that you have created to house the Extensions. + +### Extensions + +Custom New Relic instrumentation in the form of [Extension XML Files][] (or JARs) may be provided via a custom repository. + +Example in a manifest.yml + +```yaml +env: + JBP_CONFIG_NEW_RELIC_AGENT: '{ extensions: { repository_root: "http://repository..." } }' +``` + +The artifacts that the repository provides must be in TAR format and must include the extension files in a directory, with a structure like: + +``` +extensions +|- my-extension.xml +|- my-other-extension.jar +|... +``` ### Additional Resources The framework can also be configured by overlaying a set of resources on the default distribution. To do this, add files to the `resources/new_relic_agent` directory in the buildpack fork. For example, to override the default `new_relic.yml` add your custom file to `resources/new_relic_agent/newrelic.yml`. @@ -42,5 +66,6 @@ The framework can also be configured by overlaying a set of resources on the def [`config/new_relic_agent.yml`]: ../config/new_relic_agent.yml [New Relic Service]: https://newrelic.com [repositories]: extending-repositories.md -[this listing]: http://download.pivotal.io.s3.amazonaws.com/new-relic/index.yml +[this listing]: https://download.run.pivotal.io/new-relic/index.yml [version syntax]: extending-repositories.md#version-syntax-and-ordering +[Extension XML Files]: https://docs.newrelic.com/docs/agents/java-agent/custom-instrumentation/java-instrumentation-xml diff --git a/docs/framework-open_telemetry_javaagent.md b/docs/framework-open_telemetry_javaagent.md new file mode 100644 index 0000000000..26706700cd --- /dev/null +++ b/docs/framework-open_telemetry_javaagent.md @@ -0,0 +1,50 @@ +# OpenTelemetry Javaagent + +The OpenTelemetry Javaagent buildpack framework will cause an application to be automatically instrumented +with the [OpenTelemetry Javaagent Instrumentation](https://github.com/open-telemetry/opentelemetry-java-instrumentation). + +Data will be sent directly to the OpenTelemetry Collector. + + + + + + + + + + +
Detection CriterionExistence of a bound service containing the string otel-collector
Tagsopentelemetry-javaagent=<version>
+ +Tags are printed to standard output by the buildpack detect script + +## User-Provided Service + +Users are currently expected to `create-user-provided-service` (cups) of the collector +and bind it to their application. The service MUST contain the string `otel-collector`. + +For example, to create a service named `otel-collector` that represents an environment named `cf-demo`, you could use the following commands: + +``` +$ cf cups otel-collector -p '{"otel.exporter.otlp.endpoint" : "https://my-collector-endpoint", "otel.exporter.otlp.headers" : "authorization=Basic SOMEBAS64STRING","otel.exporter.otlp.protocol" : "grpc", "otel.traces.exporter" : "otlp", "otel.metrics.exporter" : "otlp", "otel.resource.attributes": "deployment.environment=cf-demo"}' +$ cf bind-service myApp otel-collector +$ cf restage myApp +``` + +Additional configuration options for the Agent can be found [here](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/#configuring-with-environment-variables) + +### Choosing a version + +Most users should skip this and simply use the latest version of the agent available (the default). +To override the default and choose a specific version, you can use the `JBP_CONFIG_*` mechanism +and set the `JBP_CONFIG_OPENTELEMETRY_JAVAAGENT` environment variable for your application. + +For example, to use version 1.27.0 of the OpenTelemetry Javaagent Instrumentation, you +could run: +``` +$ cf set-env testapp JBP_CONFIG_OPENTELEMETRY_JAVAAGENT '{version: 1.27.0}' +``` + +# Additional Resources + +* [OpenTelemetry Javaagent Instrumentation](https://github.com/open-telemetry/opentelemetry-java-instrumentation) on GitHub diff --git a/docs/framework-play_framework_auto_reconfiguration.md b/docs/framework-play_framework_auto_reconfiguration.md deleted file mode 100644 index a56a5d2b55..0000000000 --- a/docs/framework-play_framework_auto_reconfiguration.md +++ /dev/null @@ -1,32 +0,0 @@ -# Play Framework Auto-reconfiguration Framework -The Play Framework Auto-reconfiguration Framework causes an application to be automatically reconfigured to work with configured cloud services. - - - - - - - - - - -
Detection CriterionAn application is a Play Framework application
Tagsplay-framework-auto-reconfiguration=<version>
-Tags are printed to standard output by the buildpack detect script - -## Configuration -For general information on configuring the buildpack, refer to [Configuration and Extension][]. - -The framework can be configured by modifying the [`config/play_framework_auto_reconfiguration.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. - - -| Name | Description -| ---- | ----------- -| `enabled` | Whether to attempt auto-reconfiguration -| `repository_root` | The URL of the Auto-reconfiguration repository index ([details][repositories]). -| `version` | The version of Auto-reconfiguration to use. Candidate versions can be found in [this listing][]. - -[Configuration and Extension]: ../README.md#configuration-and-extension -[`config/play_framework_auto_reconfiguration.yml`]: ../config/config/play_framework_auto_reconfiguration.yml -[repositories]: extending-repositories.md -[this listing]: http://download.pivotal.io.s3.amazonaws.com/auto-reconfiguration/index.yml -[version syntax]: extending-repositories.md#version-syntax-and-ordering diff --git a/docs/framework-play_framework_jpa_plugin.md b/docs/framework-play_framework_jpa_plugin.md deleted file mode 100644 index f5ab7331fa..0000000000 --- a/docs/framework-play_framework_jpa_plugin.md +++ /dev/null @@ -1,36 +0,0 @@ -# Play Framework JPA Plugin Framework -The Play Framework JPA Plugin Framework causes an application to be automatically reconfigured to work with configured cloud services. - - - - - - - - - - -
Detection Criterion -
    -
  • An application is a Play Framework 2.0 application
  • -
  • An application uses the play-java-jpa plugin
  • -
-
Tagsplay-framework-jpa-plugin=<version>
-Tags are printed to standard output by the buildpack detect script - -## Configuration -For general information on configuring the buildpack, refer to [Configuration and Extension][]. - -The framework can be configured by modifying the [`config/play_framework_jpa_plugin.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. - -| Name | Description -| ---- | ----------- -| `enabled` | Whether to attempt reconfiguration -| `repository_root` | The URL of the Play Framework JPA Plugin repository index ([details][repositories]). -| `version` | The version of the Play Framework JPA Plugin to use. Candidate versions can be found in [this listing][]. - -[Configuration and Extension]: ../README.md#configuration-and-extension -[`config/play_framework_jpa_plugin.yml`]: ../config/play_framework_jpa_plugin.yml -[repositories]: extending-repositories.md -[this listing]: http://download.pivotal.io.s3.amazonaws.com/play-jpa-plugin/index.yml -[version syntax]: extending-repositories.md#version-syntax-and-ordering diff --git a/docs/framework-postgresql_jdbc.md b/docs/framework-postgresql_jdbc.md index 17352ecabf..bff4a17844 100644 --- a/docs/framework-postgresql_jdbc.md +++ b/docs/framework-postgresql_jdbc.md @@ -22,7 +22,7 @@ Tags are printed to standard output by the buildpack detect script Users may optionally provide their own PostgreSQL service. A user-provided PostgreSQL service must have a name or tag with `postgres` in it so that the PostgreSQL JDBC Framework will automatically download the JDBC driver JAR and place it on the classpath. ## Configuration -For general information on configuring the buildpack, refer to [Configuration and Extension][]. +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. The framework can be configured by modifying the [`config/postgresql_jdbc.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. diff --git a/docs/framework-protect_app_security_provider.md b/docs/framework-protect_app_security_provider.md new file mode 100644 index 0000000000..b1dd5d6d4e --- /dev/null +++ b/docs/framework-protect_app_security_provider.md @@ -0,0 +1,96 @@ +# ProtectApp Security Provider Framework +The ProtectApp Security Provider Framework causes an application to be automatically configured to work with a bound [ProtectApp Security Service][]. + + + + + + + + + + +
Detection CriterionExistence of a single bound ProtectApp Security Provider service. The existence of an ProtectApp Security service defined by the VCAP_SERVICES payload containing a service name, label or tag with protectapp as a substring. +
Tagsprotect-app-security-provider=<version>
+Tags are printed to standard output by the buildpack detect script + +## User-Provided Service +When binding to the ProtectApp Security Provider using a user-provided service, it must have name or tag with `protectapp` in it. The credential payload can contain the following entries: + +| Name | Description +| ---- | ----------- +| `client` | The client configuration +| `trusted_certificates` | An array of certs containing trust information +| `NAE_IP.1` | A list of KeySecure server ips or hostnames to be used +| `***` | (Optional) Any additional entries will be applied as a system property appended to `-Dcom.ingrian.security.nae.` to allow full configuration of the library. + +#### Client Configuration +| Name | Description +| ---- | ----------- +| `certificate` | A PEM encoded client certificate +| `private_key` | A PEM encoded client private key + +#### Trusted Certs Configuration +One or more PEM encoded certificate + +### Example Credentials Payload +``` +{ + "client": { + "certificate": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----", + "private_key": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" + }, + "trusted_certificates": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----", + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], + "NAE_IP.1": "192.168.1.25:192.168.1.26" +} +``` + +### Creating Credential Payload +In order to create the credentials payload, you should collapse the JSON payload to a single line and set it like the following + +``` +$ cf create-user-provided-service protectapp -p '{"client":{"certificate":"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----","private_key":"-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"},"trusted_certificates":["-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----","-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"],"NAE_IP.1":"192.168.1.25:192.168.1.26"}' +``` + +You may want to use a file for this + +Note the client portion is very exacting and needs line breaks in the body every 64 characters. + +1. The file must contain: +`-----BEGIN CERTIFICATE-----` +on a separate line (i.e. it must be terminated with a newline). +1. Each line of "gibberish" must be 64 characters wide. +1. The file must end with: +`-----END CERTIFICATE-----` +and also be terminated with a newline. +1. Don't save the cert text with Word. It must be in ASCII. +1. Don't mix DOS and UNIX style line terminations. + +So, here are a few steps you can take to normalize your certificate: + +1. Run it through `dos2unix` +`$ dos2unix cert.pem` +1. Run it through `fold` +`$ fold -w 64 cert.pem` + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by modifying the [`config/protect_app_security_provider.yml`][] file in the buildpack. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +| ---- | ----------- +| `repository_root` | The URL of the ProtectApp Security Provider repository index ([details][repositories]). +| `version` | Version of the ProtectApp Security Provider to use. + +### Additional Resources +The framework can also be configured by overlaying a set of resources on the default distribution. To do this, add files to the `resources/protect_app_security_provider` directory in the buildpack fork. + +[`config/protect_app_security_provider.yml`]: ../config/protect_app_security_provider.yml +[ProtectApp Security Service]: https://safenet.gemalto.com/data-encryption/protectapp-application-protection/ +[Configuration and Extension]: ../README.md#configuration-and-extension +[repositories]: extending-repositories.md +[version syntax]: extending-repositories.md#version-syntax-and-ordering diff --git a/docs/framework-riverbed_appinternals_agent.md b/docs/framework-riverbed_appinternals_agent.md new file mode 100644 index 0000000000..f0012a01f6 --- /dev/null +++ b/docs/framework-riverbed_appinternals_agent.md @@ -0,0 +1,58 @@ +# Riverbed Appinternals Agent Framework +The Riverbed Appinternals Agent Framework causes an application to be bound with a Riverbed Appinternals service instance. + + + + + + + + + +
Detection CriterionExistence of a single bound Riverbed Appinternals agent service. The existence of an agent service is defined by the VCAP_SERVICES payload containing a service name, label or tag with appinternals as a substring. +
Tagsriverbed-appinternals-agent=<version>
+Tags are printed to standard output by the buildpack detect script + +## User-Provided Service +When binding Appinternals using a user-provided service, it must have appinternals as substring. The credential payload can contain the following entries: + +| Name | Description +| ---- | ----------- +| `rvbd_dsa_port` | (Optional)The AppInternals agent (DSA) port (default 2111). +| `rvbd_agent_port` | (Optional) The AppInternals agent socket port (default 7073). +| `rvbd_moniker` | (Optional) A custom name for the application (default supplied by agent process discovery). + +**NOTE** + +Change `rvbd_dsa_port` and `rvbd_agent_port` only if there is a port conflict + +### Example: Creating Riverbed Appinternals User-Provided Service Payload + +``` +cf cups spring-music-appinternals -p '{"rvbd_dsa_port":"9999","rvbd_moniker":"my_app"}' +cf bind-service spring-music spring-music-appinternals +``` + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by modifying the [`config/riverbed_appinternals_agent.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +| ---- | ----------- +| `repository_root` | The URL of the Riverbed Appinternals agent repository index ([details][repositories]). +| `version` | The version of the Riverbed Appinternals agent to use. + +[Configuration and Extension]: ../README.md#configuration-and-extension +[repositories]: extending-repositories.md +[version syntax]: extending-repositories.md#version-syntax-and-ordering +[`config/riverbed_appinternals_agent.yml`]: ../config/riverbed_appinternals_agent.yml + + +**NOTE** + +If the Riverbed Service Broker's version is greater than or equal to 10.20, the buildpack will instead download Riverbed AppInternals agent from Riverbed Service Broker and will fall back to using `repository_root` in [`config/riverbed_appinternals_agent.yml`][] only if Service Broker failed to serve the Agent artifact. + +**NOTE** + +If the Rivered verstion is 10.21.9 or later, the buildpack will load the profiler normally, instead of from the Service Broker. This allows for creating multiple offline buildpacks containing different versions. diff --git a/docs/framework-sealights_agent.md b/docs/framework-sealights_agent.md new file mode 100644 index 0000000000..235254b8a9 --- /dev/null +++ b/docs/framework-sealights_agent.md @@ -0,0 +1,54 @@ +# Sealights Agent Framework +The Sealights Agent Framework causes an application to be automatically configured to work with [Sealights Service][]. + + + + + + + + +
Detection CriterionExistence of a single bound sealights service. The existence of a sealights service defined by the VCAP_SERVICES payload containing a service name, label or tag with sealights as a substring. +
Tagssealights-agent=<version>
+Tags are printed to standard output by the buildpack detect script + +## User-Provided Service +When binding Sealights using a user-provided service, it must have name or tag with `sealights` in it. +The credential payload can contain the following entries. + +| Name | Description +| ---- | ----------- +| `token` | A Sealights Agent token +| `proxy` | Specify a HTTP proxy used to communicate with the Sealights backend. Required when a corporate network prohibits communication to cloud services. The default is to have no proxy configured. This does not inherit from `http_proxy`/`https_proxy` or `http.proxyHost/https.proxyHost`, you must set this specifically if a proxy is needed. +| `lab_id` | Specify a Sealights [Lab ID][] + +All fields above except the agent token may be also specified in the [Configuration Section](#configuration) below. + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by modifying the [`config/sealights_agent.yml`][] file. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +| ---- | ----------- +| `build_session_id` | Sealights [Build Session ID][] for the application. Leave blank to use the value embedded in the jar/war artifacts +| `proxy` | Specify a HTTP proxy used to communicate with the Sealights backend. Required when a corporate network prohibits communication to cloud services. The default is to have no proxy configured. This does not inherit from `http_proxy`/`https_proxy` or `http.proxyHost/https.proxyHost`, you must set this specifically if a proxy is needed. +| `lab_id` | Specify a Sealights [Lab ID][] +| `auto_upgrade` | Enable/disable agent auto-upgrade. Off by default +| `version` | The version of Auto-reconfiguration to use. Candidate versions can be found in [this listing][]. If auto_upgrade is turned on, a different version may be downloaded and used at runtime + +Configuration settings will take precedence over the ones specified in the [User-Provided Service](#user-provided-service), if those are defined. + +## Troubleshooting and Support + +For additional documentation and support, visit the official [Sealights Java agents documentation] page + +[`config/sealights_agent.yml`]: ../config/sealights_agent.yml +[Configuration and Extension]: ../README.md#configuration-and-extension +[repositories]: extending-repositories.md +[version syntax]: extending-repositories.md#version-syntax-and-ordering +[Sealights Service]: https://www.sealights.io +[Build Session ID]: https://sealights.atlassian.net/wiki/spaces/SUP/pages/3473472/Using+Java+Agents+-+Generating+a+session+ID +[Lab ID]: https://sealights.atlassian.net/wiki/spaces/SUP/pages/762413124/Using+Java+Agents+-+Running+Tests+in+Parallel+Lab+Id +[this listing]: https://agents.sealights.co/pcf/index.yml +[Sealights Java agents documentation]: https://sealights.atlassian.net/wiki/spaces/SUP/pages/3014685/SeaLights+Java+agents diff --git a/docs/framework-seeker_security_provider.md b/docs/framework-seeker_security_provider.md new file mode 100644 index 0000000000..d4af7dd408 --- /dev/null +++ b/docs/framework-seeker_security_provider.md @@ -0,0 +1,24 @@ +# Seeker Security Provider Framework +The Seeker Security Provider Framework causes an application to be bound with a [Seeker Security Provider][s] service instance. + + + + + + + + + +
Detection CriterionExistence of a single bound Seeker Security Provider service. The existence of a provider service is defined by the VCAP_SERVICES payload containing a service name, label or tag with seeker as a substring. +
Tagsseeker-service-provider
+Tags are printed to standard output by the buildpack detect script + +## User-Provided Service +When binding Appinternals using a user-provided service, it must have seeker as substring. The credential payload must contain the following entries: + +| Name | Description +| ---- | ----------- +| `seeker_server_url` | The fully qualified URL of a Synopsys Seeker Server (e.g. `https://seeker.example.com`) + +**NOTE** +In order to use this integration, the Seeker Server version must be at least `2019.08` or later. diff --git a/docs/framework-sky_walking_agent.md b/docs/framework-sky_walking_agent.md new file mode 100644 index 0000000000..1f32898601 --- /dev/null +++ b/docs/framework-sky_walking_agent.md @@ -0,0 +1,48 @@ +# SkyWalking Agent Framework +The SkyWalking Agent Framework causes an application to be automatically configured to work with a bound [SkyWalking Service][] **Note:** This framework is disabled by default. + + + + + + + + +
Detection CriterionExistence of a single bound SkyWalking service. The existence of an SkyWalking service defined by the VCAP_SERVICES payload containing a service name, label or tag with sky-walking or skywalking as a substring. +
Tagssky-walking-agent=<version>
+Tags are printed to standard output by the buildpack detect script + +## User-Provided Service +When binding SkyWalking using a user-provided service, it must have name or tag with `sky-walking` or `skywalking` in it. The credential payload can contain the following entries. **Note:** Credentials marked as "(Optional)" may be required for some versions of the SkyWalking agent. Please see the [SkyWalking Java Agent Configuration Properties][] for the version of the agent used by your application for more details. + +| Name | Description +| ---- | ----------- +| `application-name` | (Optional) The application's name +| `sample-n-per-3-secs` | (Optional) The number of sampled traces per 3 seconds. Negative number means sample traces as many as possible, most likely 100% +| `span-limit-per-segment` | (Optional) The max amount of spans in a single segment +| `ignore-suffix` | (Optional) Ignore the segments if their operation names start with these suffix +| `open-debugging-class` | (Optional) If true, skywalking agent will save all instrumented classes files in `/debugging` folder.Skywalking team may ask for these files in order to resolve compatible problem +| `servers` | Server addresses .Examples: Single collector:servers="127.0.0.1:8080",Collector cluster:servers="10.2.45.126:8080,10.2.45.127:7600" +| `logging-level` | (Optional) Logging level + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by modifying the [`config/sky_walking_agent.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +| ---- | ----------- +| `default_application_name` | This is omitted by default but can be added to specify the application name in the SkyWalking dashboard. This can be overridden by an `application-name` entry in the credentials payload. If neither are supplied the default is the `application_name` as specified by Cloud Foundry. +| `repository_root` | The URL of the SkyWalking repository index ([details][repositories]). +| `version` | The version of SkyWalking to use. Candidate versions can be found in [this listing][]. + +### Additional Resources +The framework can also be configured by overlaying a set of resources on the default distribution. To do this, add files to the `resources/sky_walking_agent` directory in the buildpack fork. + +[`config/sky_walking_agent.yml`]: ../config/sky_walking_agent.yml +[SkyWalking Java Agent Configuration Properties]: https://github.com/apache/incubator-skywalking/blob/master/docs/en/Deploy-skywalking-agent.md +[SkyWalking Service]: http://skywalking.io +[Configuration and Extension]: ../README.md#configuration-and-extension +[repositories]: extending-repositories.md +[this listing]: https://download.run.pivotal.io/sky-walking/index.yml +[version syntax]: extending-repositories.md#version-syntax-and-ordering diff --git a/docs/framework-splunk_otel_java_agent.md b/docs/framework-splunk_otel_java_agent.md new file mode 100644 index 0000000000..f6c0bf2a5f --- /dev/null +++ b/docs/framework-splunk_otel_java_agent.md @@ -0,0 +1,64 @@ +# Splunk Distribution of OpenTelemetry Java Instrumentation + +This buildpack framework automatically instruments your Java application +with the [Splunk distribution of OpenTelemetry Java Instrumentation](https://github.com/signalfx/splunk-otel-java) +to send trace data to Splunk Observability Cloud. + + + + + + + + + + +
Detection CriterionExistence of a bound service containing the string splunk-o11y
Tagssplunk-otel-java-agent=<version>
+ +The buildpack detect script prints tags to standard output. + +## User-Provided Service + + +Provide your own "user provided service" (cups) instance and bind +it to your application. + +The service name MUST contain the string `splunk-o11y`. + +For example, to create a service named `splunk-o11y` that represents Observability Cloud +realm `us0` and represents a user environment named `cf-demo`, use the following +commands: + +``` +$ cf cups splunk-o11y -p \ + '{"splunk.realm": "us0", "splunk.access.token": "", "otel.resource.attributes": "deployment.environment=cf-demo"}' +$ cf bind-service myApp splunk-o11y +$ cf restage myApp +``` + +Provide the following values using the `credential` field of the service: + +| Name | Required? | Description +|------------------------|-----------| ----------- +| `splunk.access.token` | Yes | Splunk [org access token](https://docs.splunk.com/observability/admin/authentication-tokens/org-tokens.html). +| `splunk.realm` | Yes | Splunk realm where data will be sent. This is commonly `us0`, `eu0`, and so on. See [Available regions or realms](https://docs.splunk.com/observability/en/get-started/service-description.html#available-regions-or-realms) for more information. +| `otel.*` or `splunk.*` | Optional | All additional credentials starting with these prefixes are appended to the application's JVM arguments as system properties. + +### Choosing a version + +To override the default and choose a specific version, use the `JBP_CONFIG_*` mechanism +and set the `JBP_CONFIG_SPLUNK_OTEL_JAVA_AGENT` environment variable for your application. + +For example, to use version 1.16.0 of the Splunk OpenTelemetry Java Instrumentation, run: + +``` +$ cf set-env testapp JBP_CONFIG_SPLUNK_OTEL_JAVA_AGENT '{version: 1.16.0}' +``` + +In most cases you can use the latest or default version of the agent available. + +# Additional Resources + +* [Splunk Observability](https://www.splunk.com/en_us/products/observability.html) +* [Official documentation of the Splunk Java agent](https://docs.splunk.com/observability/en/gdi/get-data-in/application/java/get-started.html) +* [Splunk Distribution of OpenTelemetry Java](https://github.com/signalfx/splunk-otel-java) on GitHub diff --git a/docs/framework-spring_auto_reconfiguration.md b/docs/framework-spring_auto_reconfiguration.md index 86b0708e65..c13280acde 100644 --- a/docs/framework-spring_auto_reconfiguration.md +++ b/docs/framework-spring_auto_reconfiguration.md @@ -13,10 +13,10 @@ The Spring Auto-reconfiguration Framework causes an application to be automatica Tags are printed to standard output by the buildpack detect script -If a `/WEB-INF/web.xml` file exists, the framework will modify it in addition to making the auto-reconfiguration JAR available on the classpath. This modification consists of adding `org.cloudfoundry.reconfiguration.spring.CloudProfileApplicationContextInitializer`, `org.cloudfoundry.reconfiguration.spring.CloudPropertySourceApplicationContextInitializer`, and `org.cloudfoundry.reconfiguration.spring.CloudAutoReconfigurationApplicationContextInitializer` to the collection of `contextInitializerClasses`. The Spring Auto-reconfiguration Framework also adds the `cloud` profile to any existing Spring profiles such as those defined in the [`SPRING_PROFILES_ACTIVE`][] environment variable. +The Spring Auto-reconfiguration Framework adds the `cloud` profile to any existing Spring profiles such as those defined in the [`SPRING_PROFILES_ACTIVE`][] environment variable. It also uses the [Spring Cloud Cloud Foundry Connector][] to replace any bean of a candidate type with one mapped to a bound service instance. Please see the [Auto-Reconfiguration][] project for more details. ## Configuration -For general information on configuring the buildpack, refer to [Configuration and Extension][]. +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. The framework can be configured by modifying the [`config/spring_auto_reconfiguration.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. @@ -26,9 +26,11 @@ The framework can be configured by modifying the [`config/spring_auto_reconfigur | `repository_root` | The URL of the Auto-reconfiguration repository index ([details][repositories]). | `version` | The version of Auto-reconfiguration to use. Candidate versions can be found in [this listing][]. +[Auto-Reconfiguration]: https://github.com/cloudfoundry/java-buildpack-auto-reconfiguration [Configuration and Extension]: ../README.md#configuration-and-extension [`config/spring_auto_reconfiguration.yml`]: ../config/spring_auto_reconfiguration.yml [repositories]: extending-repositories.md +[Spring Cloud Cloud Foundry Connector]: https://cloud.spring.io/spring-cloud-connectors/spring-cloud-cloud-foundry-connector.html [this listing]: http://download.pivotal.io.s3.amazonaws.com/auto-reconfiguration/index.yml [version syntax]: extending-repositories.md#version-syntax-and-ordering [`SPRING_PROFILES_ACTIVE`]: http://docs.spring.io/spring/docs/4.0.0.RELEASE/javadoc-api/org/springframework/core/env/AbstractEnvironment.html#ACTIVE_PROFILES_PROPERTY_NAME diff --git a/docs/framework-takipi_agent.md b/docs/framework-takipi_agent.md new file mode 100644 index 0000000000..c14805f1be --- /dev/null +++ b/docs/framework-takipi_agent.md @@ -0,0 +1,65 @@ +# Takipi Agent Framework +The Takipi Agent Framework causes an application to be automatically configured to work with [OverOps Service][]. + + + + + + + + +
Detection CriterionExistence of a single bound Takipi service. The existence of an Takipi service defined by the VCAP_SERVICES payload containing a service name, label or tag with app-dynamics or takipi as a substring. +
Tagstakipi-agent=<version>
+Tags are printed to standard output by the buildpack detect script + +## User-Provided Service +When binding Takipi using a user-provided service, it must have name or tag with `takipi` in it. +The credential payload can contain the following entries. + +| Name | Description +| ---- | ----------- +| `collector_host` | The remote collector hostname or IP +| `collector_port` | the remote collector port (TCP) +| `secret_key` | (DEPRECATED) The agent installation key for running collector alongside agent + +Setting `collector_host` and `collector_port` will connect to a remote collector. More information can be found in [OverOps Remote Collector][] + +(DEPRECATED)Setting `secret_key` will run a local collector alongside the agent. + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by modifying the [`config/takipi_agent.yml`][] file. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +| ---- | ----------- +| `node_name_prefix` | Node name prefix, will be concatenated with `-` and instance index +| `application_name` | Override the CloudFoundry default application name + +### Remarks +In case **Java 9+** is being used, 2 JVM flags will be added to the execution: +| Name | Description +| ---- | ----------- +| `-XX:-UseTypeSpeculation` | Disable type speculation optimization of the JVM which might not work properly in some situations where an agent is present. +| `-Xshare:off` | Disable class sharing as it might affect the agent's bytecode manipulation work. + +These two flags are needs as otherwise the agent or the JVM might not work properly together. + +## Logs + +Currently, you can view the Takipi agent logs using the `cf ssh` command: +``` +cf ssh app_name +cat ~/app/.java-buildpack/takipi_agent/log/agents/*.log +``` + +## Troubleshooting + +If your container is running out of memory and exited with status 137, then you should setup and use a remote collector as explained in the `User-Provided Service` above section. + +[`config/takipi_agent.yml`]: ../config/takipi_agent.yml +[Configuration and Extension]: ../README.md#configuration-and-extension +[repositories]: extending-repositories.md +[version syntax]: extending-repositories.md#version-syntax-and-ordering +[OverOps Remote Collector]: https://doc.overops.com/docs/install-collector +[OverOps Service]: https://www.overops.com diff --git a/docs/framework-your_kit_profiler.md b/docs/framework-your_kit_profiler.md new file mode 100644 index 0000000000..8c56095116 --- /dev/null +++ b/docs/framework-your_kit_profiler.md @@ -0,0 +1,46 @@ +# YourKit Profiler Framework +The YourKit Profiler Framework contributes YourKit Profiler configuration to the application at runtime. + + + + + + + + + + +
Detection Criterionenabled set in the config/your_kit_profiler.yml file
Tagsyour-kit-profiler=<version>
+Tags are printed to standard output by the buildpack detect script + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The framework can be configured by creating or modifying the [`config/your_kit_profiler.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +| Name | Description +| ---- | ----------- +| `default_session_name` | The session name to display in the YourKit Profiler UI. Defaults to `:`. +| `enabled` | Whether to enable the YourKit Profiler +| `port` | The port that the YourKit Profiler will listen on. Defaults to `10001`. +| `repository_root` | The URL of the YourKit Profiler repository index ([details][repositories]). +| `version` | The version of the YourKit Profiler to use. Candidate versions can be found in the listings for [jammy][]. + +## Creating SSH Tunnel +After starting an application with the YourKit Profiler enabled, an SSH tunnel must be created to the container. To create that SSH container, execute the following command: + +```bash +$ cf ssh -N -T -L :localhost: +``` + +The `REMOTE_PORT` should match the `port` configuration for the application (`10001` by default). The `LOCAL_PORT` can be any open port on your computer, but typically matches the `REMOTE_PORT` where possible. + +Once the SSH tunnel has been created, your YourKit Profiler should connect to `localhost:` for debugging. + +![YourKit Configuration](framework-your_kit_profiler.png) + +[`config/your_kit_profiler.yml`]: ../config/your_kit_profiler.yml +[jammy]: https://download.run.pivotal.io/your-kit/bioni/x86_64/index.yml +[Configuration and Extension]: ../README.md#configuration-and-extension +[repositories]: extending-repositories.md +[version syntax]: extending-repositories.md#version-syntax-and-ordering diff --git a/docs/framework-your_kit_profiler.png b/docs/framework-your_kit_profiler.png new file mode 100644 index 0000000000..3a036436be Binary files /dev/null and b/docs/framework-your_kit_profiler.png differ diff --git a/docs/jre-graal_vm_jre.md b/docs/jre-graal_vm_jre.md new file mode 100644 index 0000000000..5ec9a60144 --- /dev/null +++ b/docs/jre-graal_vm_jre.md @@ -0,0 +1,178 @@ +# GraalVM JRE +The GraalVM JRE provides Java runtimes from [GraalVM][] project. No versions of the JRE are available be default due to licensing restrictions. Instead you will need to create a repository with the GraalVM JREs in it and configure the buildpack to use that repository. Unless otherwise configured, the version of Java that will be used is specified in [`config/graal_vm_jre.yml`][]. + + + + + + + + + + +
Detection CriterionUnconditional. Existence of a single bound Volume Service will result in Terminal heap dumps being written. +
    +
  • Existence of a Volume Service service is defined as the VCAP_SERVICES payload containing a service who's name, label or tag has heap-dump as a substring.
  • +
+
Tagsopen-jdk-like-jre=⟨version⟩, open-jdk-like-memory-calculator=⟨version⟩, jvmkill=⟨version⟩
+Tags are printed to standard output by the buildpack detect script + +**NOTE:** Unlike the [OpenJDK JRE][], this JRE does not connect to a pre-populated repository. Instead you will need to create your own repository by: + +1. Downloading the GraalVM JRE binary (in TAR format) to an HTTP-accesible location +1. Uploading an `index.yml` file with a mapping from the version of the JRE to its location to the same HTTP-accessible location +1. Configuring the [`config/graal_vm_jre.yml`][] file to point to the root of the repository holding both the index and JRE binary +1. Configuring the [`config/components.yml`][] file to disable the OpenJDK JRE and enable the GraalVM JRE + +For details on the repository structure, see the [repository documentation][repositories]. + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The JRE can be configured by modifying the [`config/graal_vm_jre.yml`][] file in the buildpack fork. The JRE uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +To use GraalVM JRE instead of OpenJDK without forking java-buildpack, set environment variable and restage: + +```bash +cf set-env JBP_CONFIG_COMPONENTS '{ jres: [ "JavaBuildpack::Jre::GraalVmJRE" ] }' +cf set-env JBP_CONFIG_GRAAL_VM_JRE '{ jre: { repository_root: "" } }' +cf restage +``` + +| Name | Description +| ---- | ----------- +| `jre.repository_root` | The URL of the GraalVM repository index ([details][repositories]). +| `jre.version` | The version of Java runtime to use. Candidate versions can be found in the the repository that you have created to house the JREs. +| `jvmkill.repository_root` | The URL of the `jvmkill` repository index ([details][repositories]). +| `jvmkill.version` | The version of `jvmkill` to use. Candidate versions can be found in the listings for [jammy][jvmkill-jammy]. +| `memory_calculator` | Memory calculator defaults, described below under "Memory". + +### Additional Resources +The JRE can also be configured by overlaying a set of resources on the default distribution. To do this, add files to the `resources/graal_vm_jre` directory in the buildpack fork. + +#### Custom CA Certificates +To add custom SSL certificates, add your `cacerts` file to `resources/graal_vm_jre/lib/security/cacerts`. This file will be overlayed onto the GraalVM distribution. + +### `jvmkill` +The `jvmkill` agent runs when an application has experience a resource exhaustion event. When this event occurs, the agent will print out a histogram of the first 100 largest types by total number of bytes. + +```plain +Resource exhaustion event: the JVM was unable to allocate memory from the heap. +ResourceExhausted! (1/0) +| Instance Count | Total Bytes | Class Name | +| 18273 | 313157136 | [B | +| 47806 | 7648568 | [C | +| 14635 | 1287880 | Ljava/lang/reflect/Method; | +| 46590 | 1118160 | Ljava/lang/String; | +| 8413 | 938504 | Ljava/lang/Class; | +| 28573 | 914336 | Ljava/util/concurrent/ConcurrentHashMap$Node; | +``` + +It will also print out a summary of all of the memory spaces in the JVM. + +```plain +Memory usage: + Heap memory: init 65011712, used 332392888, committed 351797248, max 351797248 + Non-heap memory: init 2555904, used 63098592, committed 64815104, max 377790464 +Memory pool usage: + Code Cache: init 2555904, used 14702208, committed 15007744, max 251658240 + PS Eden Space: init 16252928, used 84934656, committed 84934656, max 84934656 + PS Survivor Space: init 2621440, used 0, committed 19398656, max 19398656 + Compressed Class Space: init 0, used 5249512, committed 5505024, max 19214336 + Metaspace: init 0, used 43150616, committed 44302336, max 106917888 + PS Old Gen: init 43515904, used 247459792, committed 247463936, max 247463936 +``` + +If a [Volume Service][] with the string `heap-dump` in its name or tag is bound to the application, terminal heap dumps will be written with the pattern `/-/-/--.hprof` + +```plain +Heapdump written to /var/vcap/data/9ae0b817-1446-4915-9990-74c1bb26f147/pcfdev-space-e91c5c39/java-main-application-892f20ab/0-2017-06-13T18:31:29+0000-7b23124e.hprof +``` + +### Memory +The total available memory for the application's container is specified when an application is pushed. +The Java buildpack uses this value to control the JRE's use of various +regions of memory and logs the JRE memory settings when the application starts or restarts. +These settings can be influenced by configuring +the `stack_threads` and/or `class_count` mappings (both part of the `memory_calculator` mapping), +and/or Java options relating to memory. + +Note: If the total available memory is scaled up or down, the Java buildpack will re-calculate the JRE memory settings the next time the application is started. + +#### Total Memory + +The user can change the container's total memory available to influence the JRE memory settings. +Unless the user specifies the heap size Java option (`-Xmx`), increasing or decreasing the total memory +available results in the heap size setting increasing or decreasing by a corresponding amount. + +#### Loaded Classes + +The amount of memory that is allocated to metaspace and compressed class space (or, on Java 7, the permanent generation) is calculated from an estimate of the number of classes that will be loaded. The default behaviour is to estimate the number of loaded classes as a fraction of the number of class files in the application. +If a specific number of loaded classes should be used for calculations, then it should be specified as in the following example: + +```yaml +class_count: 500 +``` + +#### Headroom + +A percentage of the total memory allocated to the container to be left as headroom and excluded from the memory calculation. + +```yaml +headroom: 10 +``` + +#### Stack Threads + +The amount of memory that should be allocated to stacks is given as an amount of memory per thread with the Java option `-Xss`. If an explicit number of threads should be used for the calculation of stack memory, then it should be specified as in the following example: + +```yaml +stack_threads: 500 +``` + +Note that the default value of 250 threads is optimized for a default Tomcat configuration. If you are using another container, especially something non-blocking like Netty, it's more appropriate to use a significantly smaller value. Typically 25 threads would cover the needs of both the server (Netty) and the threads started by the JVM itself. + +#### Java Options + +If the JRE memory settings need to be fine-tuned, the user can set one or more Java memory options to +specific values. The heap size can be set explicitly, but changing the value of options other +than the heap size can also affect the heap size. For example, if the user increases +the maximum direct memory size from its default value of 10 Mb to 20 Mb, then this will +reduce the calculated heap size by 10 Mb. + +#### Memory Calculation +Memory calculation happens before every `start` of an application and is performed by an external program, the [Java Buildpack Memory Calculator]. There is no need to `restage` an application after scaling the memory as restarting will cause the memory settings to be recalculated. + +The container's total available memory is allocated into heap, metaspace and compressed class space (or permanent generation for Java 7), +direct memory, and stack memory settings. + +The memory calculation is described in more detail in the [Memory Calculator's README]. + +The inputs to the memory calculation, except the container's total memory (which is unknown at staging time), are logged during staging, for example: +``` +Loaded Classes: 13974, Threads: 300, JAVA_OPTS: '' +``` + +The container's total memory is logged during `cf push` and `cf scale`, for example: +``` + state since cpu memory disk details +#0 running 2017-04-10 02:20:03 PM 0.0% 896K of 1G 1.3M of 1G +``` + +The JRE memory settings are logged when the application is started or re-started, for example: +``` +JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -XX:MaxMetaspaceSize=99199K \ + -XX:ReservedCodeCacheSize=240M -XX:CompressedClassSpaceSize=18134K -Xss1M -Xmx368042K +``` + +[`config/components.yml`]: ../config/components.yml +[`config/graal_vm_jre.yml`]: ../config/graal_vm_jre.yml +[Configuration and Extension]: ../README.md#configuration-and-extension +[Java Buildpack Memory Calculator]: https://github.com/cloudfoundry/java-buildpack-memory-calculator +[jvmkill-jammy]: https://java-buildpack.cloudfoundry.org/jvmkill/jammy/x86_64/index.yml +[Memory Calculator's README]: https://github.com/cloudfoundry/java-buildpack-memory-calculator +[OpenJDK JRE]: jre-open_jdk_jre.md +[GraalVM]: https://www.graalvm.org/ +[repositories]: extending-repositories.md +[version syntax]: extending-repositories.md#version-syntax-and-ordering +[Volume Service]: https://docs.cloudfoundry.org/devguide/services/using-vol-services.html diff --git a/docs/jre-ibm_jre.md b/docs/jre-ibm_jre.md new file mode 100644 index 0000000000..a4fbe44706 --- /dev/null +++ b/docs/jre-ibm_jre.md @@ -0,0 +1,60 @@ +## IBM JRE +IBM JRE provides IBM® SDK, Java™ Technology Edition, Version 8. Unless otherwise configured, the version of Java that will be used is specified in [`config/ibm_jre.yml`][]. See the license section for restrictions that relate to the use of this image. For more information about IBM® SDK, Java™ Technology Edition and API documentation, see the [IBM Knowledge Center][]. + +### License +Licenses for the products installed within the buildpack: + +IBM® SDK, Java™ Technology Edition, Version 8: [International License Agreement for Non-Warranted Programs][]. + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The JRE can be configured by modifying the [`config/ibm_jre.yml`][] file in the buildpack fork. The JRE uses the [`Repository` utility support][repositories] and so, it supports the [version syntax][] defined there. + +To use IBM JRE instead of OpenJDK without forking java-buildpack, set environment variable and restage: + +```bash +cf set-env JBP_CONFIG_COMPONENTS '{jres: ["JavaBuildpack::Jre::IbmJRE"]}' +cf restage +``` + +| Name | Description +| ---- | ----------- +| `repository_root` | The URL of the IBM JRE repository index ([details][repositories]). +| `version` | The version of Java runtime to use. + +### TLS Options +It is recommended to use the following Transport Layer Security (TLS) options for IBM JRE version 8 and above: + +`cf set-env JAVA_OPTS '-Dcom.ibm.jsse2.overrideDefaultTLS=true'` + +### Additional Resources +The JRE can also be configured by overlaying a set of resources on the default distribution. To do this, add files to the `resources/ibm_jre` directory in the buildpack fork. + +#### Custom CA Certificates +To add custom SSL certificates, add your `cacerts` file to `resources/ibm_jre/jre/lib/security/cacerts`. This file will be overlayed onto the IBM JRE distribution. + +### Memory +The total available memory for the application's container is specified when an application is pushed.The Java buildpack uses this value to control the JRE's use of various regions of memory and logs the JRE memory settings when the application starts or restarts. + +Note: If the total available memory is scaled up or down, the Java buildpack will re-calculate the JRE memory settings the next time the application is started. + +#### Total Memory +The user can change the container's total memory available to influence the JRE memory settings. Unless the user specifies the heap size Java option (`-Xmx`), increasing or decreasing the total memory available results in the heap size setting increasing or decreasing by a corresponding amount. + +#### Memory Calculation +The user can configure the desired heap ratio (`-Xmx`) by changing the `heap_ratio` attribute under `jre` in [`config/ibm_jre.yml`][] and the buildpack calculates the `-Xmx Memory Setting` based on the total memory available. + +The container's total memory is logged during `cf push` and `cf scale`, for example: +``` + state since cpu memory disk details +#0 running 2017-04-10 02:20:03 PM 0.0% 896K of 1G 1.3M of 1G +``` + +[`config/components.yml`]: ../config/components.yml +[`config/ibm_jre.yml`]: ../config/ibm_jre.yml +[Configuration and Extension]: ../README.md#configuration-and-extension +[repositories]: extending-repositories.md +[version syntax]: extending-repositories.md#version-syntax-and-ordering +[IBM Knowledge Center]: http://www.ibm.com/support/knowledgecenter/SSYKE2/welcome_javasdk_family.html +[International License Agreement for Non-Warranted Programs]: http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?la_formnum=&li_formnum=L-PMAA-A3Z8P2&title=IBM%AE+SDK%2C+Java%99+Technology+Edition%2C+Version+8.0&l=en diff --git a/docs/jre-open_jdk_jre.md b/docs/jre-open_jdk_jre.md index ecb3c72ad8..f5cac3083e 100644 --- a/docs/jre-open_jdk_jre.md +++ b/docs/jre-open_jdk_jre.md @@ -1,86 +1,163 @@ # OpenJDK JRE -The OpenJDK JRE provides Java runtimes from the [OpenJDK][] project. Versions of Java from the `1.6`, `1.7`, and `1.8` lines are available. Unless otherwise configured, the version of Java that will be used is specified in [`config/open_jdk_jre.yml`][]. +The OpenJDK JRE provides Java runtimes from the [OpenJDK][] project. Unless otherwise configured, the version of Java that will be used is specified in [`config/open_jdk_jre.yml`][]. - + - +
Detection CriterionUnconditionalUnconditional. Existence of a single bound Volume Service will result in Terminal heap dumps being written. +
    +
  • Existence of a Volume Service service is defined as the VCAP_SERVICES payload containing a service who's name, label or tag has heap-dump as a substring.
  • +
+
Tagsopen-jdk=⟨version⟩open-jdk=⟨version⟩, open-jdk-like-memory-calculator=⟨version⟩, jvmkill=⟨version⟩
Tags are printed to standard output by the buildpack detect script ## Configuration -For general information on configuring the buildpack, refer to [Configuration and Extension][]. +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. The JRE can be configured by modifying the [`config/open_jdk_jre.yml`][] file in the buildpack fork. The JRE uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. | Name | Description | ---- | ----------- -| `memory_sizes` | Optional memory sizes, described below under "Memory Sizes". -| `memory_heuristics` | Default memory size weightings, described below under "Memory Weightings. -| `repository_root` | The URL of the OpenJDK repository index ([details][repositories]). -| `version` | The version of Java runtime to use. Candidate versions can be found in the listings for [centos6][], [lucid][], [mountainlion][], and [precise][]. Note: version 1.8.0 and higher require the `memory_sizes` and `memory_heuristics` mappings to specify `metaspace` rather than `permgen`. +| `jre.repository_root` | The URL of the OpenJDK repository index ([details][repositories]). +| `jre.version` | The version of Java runtime to use. Candidate versions can be found in the listings for [jammy][]. Note: version 1.8.0 and higher require the `memory_sizes` and `memory_heuristics` mappings to specify `metaspace` rather than `permgen`. +| `jvmkill.repository_root` | The URL of the `jvmkill` repository index ([details][repositories]). +| `jvmkill.version` | The version of `jvmkill` to use. Candidate versions can be found in the listings for [jammy][jvmkill-jammy]. +| `memory_calculator` | Memory calculator defaults, described below under "Memory". ### Additional Resources -The JRE can also be configured by overlaying a set of resources on the default distribution. To do this, add files to the `resources/open_jdk_jre` directory in the buildpack fork. For example, to add the JCE Unlimited Strength `local_policy.jar` add your file to `resources/open_jdk_jre/lib/security/local_policy.jar`. +The JRE can also be configured by overlaying a set of resources on the default distribution. To do this, add files to the `resources/open_jdk_jre` directory in the buildpack fork. + +#### JCE Unlimited Strength +To add the JCE Unlimited Strength `local_policy.jar`, add your file to `resources/open_jdk_jre/lib/security/local_policy.jar`. This file will be overlayed onto the OpenJDK distribution. + +#### Custom CA Certificates +To add custom SSL certificates, add your `cacerts` file to `resources/open_jdk_jre/lib/security/cacerts`. This file will be overlayed onto the OpenJDK distribution. + +### `jvmkill` +The `jvmkill` agent runs when an application has experience a resource exhaustion event. When this event occurs, the agent will print out a histogram of the first 100 largest types by total number of bytes. + +```plain +Resource exhaustion event: the JVM was unable to allocate memory from the heap. +ResourceExhausted! (1/0) +| Instance Count | Total Bytes | Class Name | +| 18273 | 313157136 | [B | +| 47806 | 7648568 | [C | +| 14635 | 1287880 | Ljava/lang/reflect/Method; | +| 46590 | 1118160 | Ljava/lang/String; | +| 8413 | 938504 | Ljava/lang/Class; | +| 28573 | 914336 | Ljava/util/concurrent/ConcurrentHashMap$Node; | +``` + +It will also print out a summary of all of the memory spaces in the JVM. + +```plain +Memory usage: + Heap memory: init 65011712, used 332392888, committed 351797248, max 351797248 + Non-heap memory: init 2555904, used 63098592, committed 64815104, max 377790464 +Memory pool usage: + Code Cache: init 2555904, used 14702208, committed 15007744, max 251658240 + PS Eden Space: init 16252928, used 84934656, committed 84934656, max 84934656 + PS Survivor Space: init 2621440, used 0, committed 19398656, max 19398656 + Compressed Class Space: init 0, used 5249512, committed 5505024, max 19214336 + Metaspace: init 0, used 43150616, committed 44302336, max 106917888 + PS Old Gen: init 43515904, used 247459792, committed 247463936, max 247463936 +``` + +If a [Volume Service][] with the string `heap-dump` in its name or tag is bound to the application, terminal heap dumps will be written with the pattern `/-/-/--.hprof` + +```plain +Heapdump written to /var/vcap/data/9ae0b817-1446-4915-9990-74c1bb26f147/pcfdev-space-e91c5c39/java-main-application-892f20ab/0-2017-06-13T18:31:29+0000-7b23124e.hprof +``` ### Memory -The total available memory is specified when an application is pushed as part of it's configuration. The Java buildpack uses this value to control the JRE's use of various regions of memory. The JRE memory settings can be influenced by configuring the `memory_sizes` and/or `memory_heuristics` mappings. +The total available memory for the application's container is specified when an application is pushed. +The Java buildpack uses this value to control the JRE's use of various +regions of memory and logs the JRE memory settings when the application starts or restarts. +These settings can be influenced by configuring +the `stack_threads` and/or `class_count` mappings (both part of the `memory_calculator` mapping), +and/or Java options relating to memory. -Note: if the total available memory is scaled up or down, the Java buildpack does not re-calculate the JRE memory settings until the next time the appication is pushed. +Note: If the total available memory is scaled up or down, the Java buildpack will re-calculate the JRE memory settings the next time the application is started. -#### Memory Sizes -The following optional properties may be specified in the `memory_sizes` mapping. +#### Total Memory -| Name | Description -| ---- | ----------- -| `heap` | The maximum heap size to use. It may be a single value such as `64m` or a range of acceptable values such as `128m..256m`. It is used to calculate the value of the Java command line options `-Xmx` and `-Xms`. -| `metaspace` | The maximum Metaspace size to use. It is applicable to versions of OpenJDK from 1.8 onwards. It may be a single value such as `64m` or a range of acceptable values such as `128m..256m`. It is used to calculate the value of the Java command line options `-XX:MaxMetaspaceSize=` and `-XX:MetaspaceSize=`. -| `native` | The amount of memory to reserve for native memory allocation. It should normally be omitted or specified as a range with no upper bound such as `100m..`. It does not correspond to a switch on the Java command line. -| `permgen` | The maximum PermGen size to use. It is applicable to versions of OpenJDK earlier than 1.8. It may be a single value such as `64m` or a range of acceptable values such as `128m..256m`. It is used to calculate the value of the Java command line options `-XX:MaxPermSize=` and `-XX:PermSize=`. -| `stack` | The stack size to use. It may be a single value such as `2m` or a range of acceptable values such as `2m..4m`. It is used to calculate the value of the Java command line option `-Xss`. +The user can change the container's total memory available to influence the JRE memory settings. +Unless the user specifies the heap size Java option (`-Xmx`), increasing or decreasing the total memory +available results in the heap size setting increasing or decreasing by a corresponding amount. -Memory sizes together with _memory weightings_ (described in the next section) are used to calculate the amount of memory for each memory type. The calculation is described later. +#### Loaded Classes -Memory sizes consist of a non-negative integer followed by a unit (`k` for kilobytes, `m` for megabytes, `g` for gigabytes; the case is not significant). Only the memory size `0` may be specified without a unit. +The amount of memory that is allocated to metaspace and compressed class space (or, on Java 7, the permanent generation) is calculated from an estimate of the number of classes that will be loaded. The default behaviour is to estimate the number of loaded classes as a fraction of the number of class files in the application. +If a specific number of loaded classes should be used for calculations, then it should be specified as in the following example: -The above memory size properties may be omitted, specified as a single value, or specified as a range. Ranges use the syntax `..`, although either bound may be omitted in which case the defaults of zero and the total available memory are used for the lower bound and upper bound, respectively. Examples of ranges are `100m..200m` (any value between 100 and 200 megabytes, inclusive) and `100m..` (any value greater than or equal to 100 megabytes). +```yaml +class_count: 500 +``` -Each form of memory size is equivalent to a range. Omitting a memory size is equivalent to specifying the range `0..`. Specifying a single value is equivalent to specifying the range with that value as both the lower and upper bound, for example `128m` is equivalent to the range `128m..128m`. +#### Headroom -#### Memory Weightings -Memory weightings are configured in the `memory_heuristics` mapping of [`config/open_jdk_jre.yml`][]. Each weighting is a non-negative number and represents a proportion of the total available memory (represented by the sum of all the weightings). For example, the following weightings: +A percentage of the total memory allocated to the container to be left as headroom and excluded from the memory calculation. ```yaml -memory_heuristics: - heap: 15 - permgen: 5 - stack: 1 - native: 2 +headroom: 10 ``` -represent a maximum heap size three times as large as the maximum PermGen size, and so on. +#### Stack Threads -Memory weightings are used together with memory ranges to calculate the amount of memory for each memory type, as follows. +The amount of memory that should be allocated to stacks is given as an amount of memory per thread with the Java option `-Xss`. If an explicit number of threads should be used for the calculation of stack memory, then it should be specified as in the following example: + +```yaml +stack_threads: 500 +``` + +Note that the default value of 250 threads is optimized for a default Tomcat configuration. If you are using another container, especially something non-blocking like Netty, it's more appropriate to use a significantly smaller value. Typically 25 threads would cover the needs of both the server (Netty) and the threads started by the JVM itself. + +#### Java Options + +If the JRE memory settings need to be fine-tuned, the user can set one or more Java memory options to +specific values. The heap size can be set explicitly, but changing the value of options other +than the heap size can also affect the heap size. For example, if the user increases +the maximum direct memory size from its default value of 10 Mb to 20 Mb, then this will +reduce the calculated heap size by 10 Mb. #### Memory Calculation -The total available memory is allocated into heap, Metaspace or PermGen (depending on the version of OpenJDK), stack, and native memory types. +Memory calculation happens before every `start` of an application and is performed by an external program, the [Java Buildpack Memory Calculator]. There is no need to `restage` an application after scaling the memory as restarting will cause the memory settings to be recalculated. + +The container's total available memory is allocated into heap, metaspace and compressed class space (or permanent generation for Java 7), +direct memory, and stack memory settings. + +The memory calculation is described in more detail in the [Memory Calculator's README]. + +The inputs to the memory calculation, except the container's total memory (which is unknown at staging time), are logged during staging, for example: +``` +Loaded Classes: 13974, Threads: 300, JAVA_OPTS: '' +``` -The total available memory is allocated to each memory type in proportion to its weighting. If the resultant size of a memory type lies outside its range, the size is constrained to -the range, the constrained size is excluded from the remaining memory, and no further calculation is required for the memory type. If the resultant size of a memory size lies within its range, the size is included in the remaining memory. The remaining memory is then allocated to the remaining memory types in a similar fashion. Allocation terminates when none of the sizes of the remaining memory types is constrained by the corresponding range. +The container's total memory is logged during `cf push` and `cf scale`, for example: +``` + state since cpu memory disk details +#0 running 2017-04-10 02:20:03 PM 0.0% 896K of 1G 1.3M of 1G +``` -Termination is guaranteed since there is a finite number of memory types and in each iteration either none of the remaining memory sizes is constrained by the corresponding range and allocation terminates or at least one memory size is constrained by the corresponding range and is omitted from the next iteration. +The JRE memory settings are logged when the application is started or re-started, for example: +``` +JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -XX:MaxMetaspaceSize=99199K \ + -XX:ReservedCodeCacheSize=240M -XX:CompressedClassSpaceSize=18134K -Xss1M -Xmx368042K +``` [`config/open_jdk_jre.yml`]: ../config/open_jdk_jre.yml +[jammy]: https://java-buildpack.cloudfoundry.org/openjdk/jammy/x86_64/index.yml [Configuration and Extension]: ../README.md#configuration-and-extension -[centos6]: http://download.pivotal.io.s3.amazonaws.com/openjdk/centos6/x86_64/index.yml -[lucid]: http://download.pivotal.io.s3.amazonaws.com/openjdk/lucid/x86_64/index.yml -[mountainlion]: http://download.pivotal.io.s3.amazonaws.com/openjdk/mountainlion/x86_64/index.yml +[Java Buildpack Memory Calculator]: https://github.com/cloudfoundry/java-buildpack-memory-calculator +[jvmkill-jammy]: https://java-buildpack.cloudfoundry.org/jvmkill/jammy/x86_64/index.yml +[Memory Calculator's README]: https://github.com/cloudfoundry/java-buildpack-memory-calculator [OpenJDK]: http://openjdk.java.net -[precise]: http://download.pivotal.io.s3.amazonaws.com/openjdk/precise/x86_64/index.yml [repositories]: extending-repositories.md [version syntax]: extending-repositories.md#version-syntax-and-ordering +[Volume Service]: https://docs.cloudfoundry.org/devguide/services/using-vol-services.html diff --git a/docs/jre-oracle_jre.md b/docs/jre-oracle_jre.md index 1b5612eb29..2b0635d3d0 100644 --- a/docs/jre-oracle_jre.md +++ b/docs/jre-oracle_jre.md @@ -4,11 +4,15 @@ The Oracle JRE provides Java runtimes from [Oracle][] project. No versions of t - + - +
Detection CriterionUnconditionalUnconditional. Existence of a single bound Volume Service will result in Terminal heap dumps being written. +
    +
  • Existence of a Volume Service service is defined as the VCAP_SERVICES payload containing a service who's name, label or tag has heap-dump as a substring.
  • +
+
Tagsoracle=⟨version⟩oracle=⟨version⟩, open-jdk-like-memory-calculator=⟨version⟩, jvmkill=⟨version⟩
Tags are printed to standard output by the buildpack detect script @@ -23,71 +27,155 @@ Tags are printed to standard output by the buildpack detect script For details on the repository structure, see the [repository documentation][repositories]. ## Configuration -For general information on configuring the buildpack, refer to [Configuration and Extension][]. +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. The JRE can be configured by modifying the [`config/oracle_jre.yml`][] file in the buildpack fork. The JRE uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. +To use Oracle JRE instead of OpenJDK without forking java-buildpack, set environment variable and restage: + +```bash +cf set-env JBP_CONFIG_COMPONENTS '{ jres: [ "JavaBuildpack::Jre::OracleJRE" ] }' +cf set-env JBP_CONFIG_ORACLE_JRE '{ jre: { repository_root: "" } }' +cf restage +``` + | Name | Description | ---- | ----------- -| `memory_sizes` | Optional memory sizes, described below under "Memory Sizes". -| `memory_heuristics` | Default memory size weightings, described below under "Memory Weightings. -| `repository_root` | The URL of the Oracle repository index ([details][repositories]). -| `version` | The version of Java runtime to use. Candidate versions can be found in the the repository that you have created to house the JREs. Note: version 1.8.0 and higher require the `memory_sizes` and `memory_heuristics` mappings to specify `metaspace` rather than `permgen`. +| `jre.repository_root` | The URL of the Oracle repository index ([details][repositories]). +| `jre.version` | The version of Java runtime to use. Candidate versions can be found in the the repository that you have created to house the JREs. Note: version 1.8.0 and higher require the `memory_sizes` and `memory_heuristics` mappings to specify `metaspace` rather than `permgen`. +| `jvmkill.repository_root` | The URL of the `jvmkill` repository index ([details][repositories]). +| `jvmkill.version` | The version of `jvmkill` to use. Candidate versions can be found in the listings for [jammy][jvmkill-jammy]. +| `memory_calculator` | Memory calculator defaults, described below under "Memory". ### Additional Resources -The JRE can also be configured by overlaying a set of resources on the default distribution. To do this, add files to the `resources/oracle_jre` directory in the buildpack fork. For example, to add the JCE Unlimited Strength `local_policy.jar` add your file to `resources/oracle_jre/lib/security/local_policy.jar`. +The JRE can also be configured by overlaying a set of resources on the default distribution. To do this, add files to the `resources/oracle_jre` directory in the buildpack fork. + +#### JCE Unlimited Strength +To add the JCE Unlimited Strength `local_policy.jar`, add your file to `resources/oracle_jre/lib/security/local_policy.jar`. In case you you'r using the 'server jre', then the file should go to `resources/oracle_jre/jre/lib/security/local_policy.jar`. This file will be overlayed onto the Oracle distribution. + +#### Custom CA Certificates +To add custom SSL certificates, add your `cacerts` file to `resources/oracle_jre/lib/security/cacerts`. This file will be overlayed onto the Oracle distribution. + +### `jvmkill` +The `jvmkill` agent runs when an application has experience a resource exhaustion event. When this event occurs, the agent will print out a histogram of the first 100 largest types by total number of bytes. + +```plain +Resource exhaustion event: the JVM was unable to allocate memory from the heap. +ResourceExhausted! (1/0) +| Instance Count | Total Bytes | Class Name | +| 18273 | 313157136 | [B | +| 47806 | 7648568 | [C | +| 14635 | 1287880 | Ljava/lang/reflect/Method; | +| 46590 | 1118160 | Ljava/lang/String; | +| 8413 | 938504 | Ljava/lang/Class; | +| 28573 | 914336 | Ljava/util/concurrent/ConcurrentHashMap$Node; | +``` + +It will also print out a summary of all of the memory spaces in the JVM. + +```plain +Memory usage: + Heap memory: init 65011712, used 332392888, committed 351797248, max 351797248 + Non-heap memory: init 2555904, used 63098592, committed 64815104, max 377790464 +Memory pool usage: + Code Cache: init 2555904, used 14702208, committed 15007744, max 251658240 + PS Eden Space: init 16252928, used 84934656, committed 84934656, max 84934656 + PS Survivor Space: init 2621440, used 0, committed 19398656, max 19398656 + Compressed Class Space: init 0, used 5249512, committed 5505024, max 19214336 + Metaspace: init 0, used 43150616, committed 44302336, max 106917888 + PS Old Gen: init 43515904, used 247459792, committed 247463936, max 247463936 +``` + +If a [Volume Service][] with the string `heap-dump` in its name or tag is bound to the application, terminal heap dumps will be written with the pattern `/-/-/--.hprof` + +```plain +Heapdump written to /var/vcap/data/9ae0b817-1446-4915-9990-74c1bb26f147/pcfdev-space-e91c5c39/java-main-application-892f20ab/0-2017-06-13T18:31:29+0000-7b23124e.hprof +``` ### Memory -The total available memory is specified when an application is pushed as part of it's configuration. The Java buildpack uses this value to control the JRE's use of various regions of memory. The JRE memory settings can be influenced by configuring the `memory_sizes` and/or `memory_heuristics` mappings. +The total available memory for the application's container is specified when an application is pushed. +The Java buildpack uses this value to control the JRE's use of various +regions of memory and logs the JRE memory settings when the application starts or restarts. +These settings can be influenced by configuring +the `stack_threads` and/or `class_count` mappings (both part of the `memory_calculator` mapping), +and/or Java options relating to memory. -Note: if the total available memory is scaled up or down, the Java buildpack does not re-calculate the JRE memory settings until the next time the appication is pushed. +Note: If the total available memory is scaled up or down, the Java buildpack will re-calculate the JRE memory settings the next time the application is started. -#### Memory Sizes -The following optional properties may be specified in the `memory_sizes` mapping. +#### Total Memory -| Name | Description -| ---- | ----------- -| `heap` | The maximum heap size to use. It may be a single value such as `64m` or a range of acceptable values such as `128m..256m`. It is used to calculate the value of the Java command line options `-Xmx` and `-Xms`. -| `metaspace` | The maximum Metaspace size to use. It is applicable to versions of Oracle from 1.8 onwards. It may be a single value such as `64m` or a range of acceptable values such as `128m..256m`. It is used to calculate the value of the Java command line options `-XX:MaxMetaspaceSize=` and `-XX:MetaspaceSize=`. -| `native` | The amount of memory to reserve for native memory allocation. It should normally be omitted or specified as a range with no upper bound such as `100m..`. It does not correspond to a switch on the Java command line. -| `permgen` | The maximum PermGen size to use. It is applicable to versions of Oracle earlier than 1.8. It may be a single value such as `64m` or a range of acceptable values such as `128m..256m`. It is used to calculate the value of the Java command line options `-XX:MaxPermSize=` and `-XX:PermSize=`. -| `stack` | The stack size to use. It may be a single value such as `2m` or a range of acceptable values such as `2m..4m`. It is used to calculate the value of the Java command line option `-Xss`. +The user can change the container's total memory available to influence the JRE memory settings. +Unless the user specifies the heap size Java option (`-Xmx`), increasing or decreasing the total memory +available results in the heap size setting increasing or decreasing by a corresponding amount. + +#### Loaded Classes + +The amount of memory that is allocated to metaspace and compressed class space (or, on Java 7, the permanent generation) is calculated from an estimate of the number of classes that will be loaded. The default behaviour is to estimate the number of loaded classes as a fraction of the number of class files in the application. +If a specific number of loaded classes should be used for calculations, then it should be specified as in the following example: + +```yaml +class_count: 500 +``` -Memory sizes together with _memory weightings_ (described in the next section) are used to calculate the amount of memory for each memory type. The calculation is described later. +#### Headroom -Memory sizes consist of a non-negative integer followed by a unit (`k` for kilobytes, `m` for megabytes, `g` for gigabytes; the case is not significant). Only the memory size `0` may be specified without a unit. +A percentage of the total memory allocated to the container to be left as headroom and excluded from the memory calculation. -The above memory size properties may be omitted, specified as a single value, or specified as a range. Ranges use the syntax `..`, although either bound may be omitted in which case the defaults of zero and the total available memory are used for the lower bound and upper bound, respectively. Examples of ranges are `100m..200m` (any value between 100 and 200 megabytes, inclusive) and `100m..` (any value greater than or equal to 100 megabytes). +```yaml +headroom: 10 +``` -Each form of memory size is equivalent to a range. Omitting a memory size is equivalent to specifying the range `0..`. Specifying a single value is equivalent to specifying the range with that value as both the lower and upper bound, for example `128m` is equivalent to the range `128m..128m`. +#### Stack Threads -#### Memory Weightings -Memory weightings are configured in the `memory_heuristics` mapping of [`config/oracle_jre.yml`][]. Each weighting is a non-negative number and represents a proportion of the total available memory (represented by the sum of all the weightings). For example, the following weightings: +The amount of memory that should be allocated to stacks is given as an amount of memory per thread with the Java option `-Xss`. If an explicit number of threads should be used for the calculation of stack memory, then it should be specified as in the following example: ```yaml -memory_heuristics: - heap: 15 - permgen: 5 - stack: 1 - native: 2 +stack_threads: 500 ``` -represent a maximum heap size three times as large as the maximum PermGen size, and so on. +Note that the default value of 250 threads is optimized for a default Tomcat configuration. If you are using another container, especially something non-blocking like Netty, it's more appropriate to use a significantly smaller value. Typically 25 threads would cover the needs of both the server (Netty) and the threads started by the JVM itself. -Memory weightings are used together with memory ranges to calculate the amount of memory for each memory type, as follows. +#### Java Options + +If the JRE memory settings need to be fine-tuned, the user can set one or more Java memory options to +specific values. The heap size can be set explicitly, but changing the value of options other +than the heap size can also affect the heap size. For example, if the user increases +the maximum direct memory size from its default value of 10 Mb to 20 Mb, then this will +reduce the calculated heap size by 10 Mb. #### Memory Calculation -The total available memory is allocated into heap, Metaspace or PermGen (depending on the version of Oracle), stack, and native memory types. +Memory calculation happens before every `start` of an application and is performed by an external program, the [Java Buildpack Memory Calculator]. There is no need to `restage` an application after scaling the memory as restarting will cause the memory settings to be recalculated. + +The container's total available memory is allocated into heap, metaspace and compressed class space (or permanent generation for Java 7), +direct memory, and stack memory settings. + +The memory calculation is described in more detail in the [Memory Calculator's README]. -The total available memory is allocated to each memory type in proportion to its weighting. If the resultant size of a memory type lies outside its range, the size is constrained to -the range, the constrained size is excluded from the remaining memory, and no further calculation is required for the memory type. If the resultant size of a memory size lies within its range, the size is included in the remaining memory. The remaining memory is then allocated to the remaining memory types in a similar fashion. Allocation terminates when none of the sizes of the remaining memory types is constrained by the corresponding range. +The inputs to the memory calculation, except the container's total memory (which is unknown at staging time), are logged during staging, for example: +``` +Loaded Classes: 13974, Threads: 300, JAVA_OPTS: '' +``` -Termination is guaranteed since there is a finite number of memory types and in each iteration either none of the remaining memory sizes is constrained by the corresponding range and allocation terminates or at least one memory size is constrained by the corresponding range and is omitted from the next iteration. +The container's total memory is logged during `cf push` and `cf scale`, for example: +``` + state since cpu memory disk details +#0 running 2017-04-10 02:20:03 PM 0.0% 896K of 1G 1.3M of 1G +``` + +The JRE memory settings are logged when the application is started or re-started, for example: +``` +JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -XX:MaxMetaspaceSize=99199K \ + -XX:ReservedCodeCacheSize=240M -XX:CompressedClassSpaceSize=18134K -Xss1M -Xmx368042K +``` [`config/components.yml`]: ../config/components.yml [`config/oracle_jre.yml`]: ../config/oracle_jre.yml [Configuration and Extension]: ../README.md#configuration-and-extension +[Java Buildpack Memory Calculator]: https://github.com/cloudfoundry/java-buildpack-memory-calculator +[jvmkill-jammy]: https://java-buildpack.cloudfoundry.org/jvmkill/jammy/x86_64/index.yml +[Memory Calculator's README]: https://github.com/cloudfoundry/java-buildpack-memory-calculator [OpenJDK JRE]: jre-open_jdk_jre.md [Oracle]: http://www.oracle.com/technetwork/java/index.html [repositories]: extending-repositories.md [version syntax]: extending-repositories.md#version-syntax-and-ordering +[Volume Service]: https://docs.cloudfoundry.org/devguide/services/using-vol-services.html diff --git a/docs/jre-sap_machine_jre.md b/docs/jre-sap_machine_jre.md new file mode 100644 index 0000000000..2aee4e2fb3 --- /dev/null +++ b/docs/jre-sap_machine_jre.md @@ -0,0 +1,166 @@ +# SapMachine JRE +The SapMachine JRE provides Java runtimes from the [SapMachine][] project. Versions of Java from the `10` line are available. Unless otherwise configured, the version of Java that will be used is specified in [`config/sap_machine_jre.yml`][]. + + + + + + + + + + +
Detection CriterionUnconditional. Existence of a single bound Volume Service will result in Terminal heap dumps being written. +
    +
  • Existence of a Volume Service service is defined as the VCAP_SERVICES payload containing a service who's name, label or tag has heap-dump as a substring.
  • +
+
Tagsopen-jdk-like-jre=⟨version⟩, open-jdk-like-memory-calculator=⟨version⟩, jvmkill=⟨version⟩
+Tags are printed to standard output by the buildpack detect script + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The JRE can be configured by modifying the [`config/sap_machine_jre.yml`][] file in the buildpack fork. The JRE uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there. + +To use SapMachine JRE instead of OpenJDK without forking java-buildpack, set environment variable and restage: + +```bash +cf set-env JBP_CONFIG_COMPONENTS '{jres: ["JavaBuildpack::Jre::SapMachineJRE"]}' +cf restage +``` + +| Name | Description +| ---- | ----------- +| `jre.repository_root` | The URL of the SapMachine repository index ([details][repositories]). +| `jre.version` | The version of Java runtime to use. Candidate versions can be found in the listings for [jammy][]. Note: version 1.8.0 and higher require the `memory_sizes` and `memory_heuristics` mappings to specify `metaspace` rather than `permgen`. +| `jvmkill.repository_root` | The URL of the `jvmkill` repository index ([details][repositories]). +| `jvmkill.version` | The version of `jvmkill` to use. Candidate versions can be found in the listings for [jammy][jvmkill-jammy]. +| `memory_calculator` | Memory calculator defaults, described below under "Memory". + +### Additional Resources +The JRE can also be configured by overlaying a set of resources on the default distribution. To do this, add files to the `resources/sap_machine_jre` directory in the buildpack fork. + +#### Custom CA Certificates +To add custom SSL certificates, add your `cacerts` file to `resources/sap_machine_jre/lib/security/cacerts`. This file will be overlayed onto the SapMachine distribution. + +### `jvmkill` +The `jvmkill` agent runs when an application has experience a resource exhaustion event. When this event occurs, the agent will print out a histogram of the first 100 largest types by total number of bytes. + +```plain +Resource exhaustion event: the JVM was unable to allocate memory from the heap. +ResourceExhausted! (1/0) +| Instance Count | Total Bytes | Class Name | +| 18273 | 313157136 | [B | +| 47806 | 7648568 | [C | +| 14635 | 1287880 | Ljava/lang/reflect/Method; | +| 46590 | 1118160 | Ljava/lang/String; | +| 8413 | 938504 | Ljava/lang/Class; | +| 28573 | 914336 | Ljava/util/concurrent/ConcurrentHashMap$Node; | +``` + +It will also print out a summary of all of the memory spaces in the JVM. + +```plain +Memory usage: + Heap memory: init 65011712, used 332392888, committed 351797248, max 351797248 + Non-heap memory: init 2555904, used 63098592, committed 64815104, max 377790464 +Memory pool usage: + Code Cache: init 2555904, used 14702208, committed 15007744, max 251658240 + PS Eden Space: init 16252928, used 84934656, committed 84934656, max 84934656 + PS Survivor Space: init 2621440, used 0, committed 19398656, max 19398656 + Compressed Class Space: init 0, used 5249512, committed 5505024, max 19214336 + Metaspace: init 0, used 43150616, committed 44302336, max 106917888 + PS Old Gen: init 43515904, used 247459792, committed 247463936, max 247463936 +``` + +If a [Volume Service][] with the string `heap-dump` in its name or tag is bound to the application, terminal heap dumps will be written with the pattern `/-/-/--.hprof` + +```plain +Heapdump written to /var/vcap/data/9ae0b817-1446-4915-9990-74c1bb26f147/pcfdev-space-e91c5c39/java-main-application-892f20ab/0-2017-06-13T18:31:29+0000-7b23124e.hprof +``` + +### Memory +The total available memory for the application's container is specified when an application is pushed. +The Java buildpack uses this value to control the JRE's use of various +regions of memory and logs the JRE memory settings when the application starts or restarts. +These settings can be influenced by configuring +the `stack_threads` and/or `class_count` mappings (both part of the `memory_calculator` mapping), +and/or Java options relating to memory. + +Note: If the total available memory is scaled up or down, the Java buildpack will re-calculate the JRE memory settings the next time the application is started. + +#### Total Memory + +The user can change the container's total memory available to influence the JRE memory settings. +Unless the user specifies the heap size Java option (`-Xmx`), increasing or decreasing the total memory +available results in the heap size setting increasing or decreasing by a corresponding amount. + +#### Loaded Classes + +The amount of memory that is allocated to metaspace and compressed class space (or, on Java 7, the permanent generation) is calculated from an estimate of the number of classes that will be loaded. The default behaviour is to estimate the number of loaded classes as a fraction of the number of class files in the application. +If a specific number of loaded classes should be used for calculations, then it should be specified as in the following example: + +```yaml +class_count: 500 +``` + +#### Headroom + +A percentage of the total memory allocated to the container to be left as headroom and excluded from the memory calculation. + +```yaml +headroom: 10 +``` + +#### Stack Threads + +The amount of memory that should be allocated to stacks is given as an amount of memory per thread with the Java option `-Xss`. If an explicit number of threads should be used for the calculation of stack memory, then it should be specified as in the following example: + +```yaml +stack_threads: 500 +``` + +Note that the default value of 250 threads is optimized for a default Tomcat configuration. If you are using another container, especially something non-blocking like Netty, it's more appropriate to use a significantly smaller value. Typically 25 threads would cover the needs of both the server (Netty) and the threads started by the JVM itself. + +#### Java Options + +If the JRE memory settings need to be fine-tuned, the user can set one or more Java memory options to +specific values. The heap size can be set explicitly, but changing the value of options other +than the heap size can also affect the heap size. For example, if the user increases +the maximum direct memory size from its default value of 10 Mb to 20 Mb, then this will +reduce the calculated heap size by 10 Mb. + +#### Memory Calculation +Memory calculation happens before every `start` of an application and is performed by an external program, the [Java Buildpack Memory Calculator]. There is no need to `restage` an application after scaling the memory as restarting will cause the memory settings to be recalculated. + +The container's total available memory is allocated into heap, metaspace and compressed class space, direct memory, and stack memory settings. + +The memory calculation is described in more detail in the [Memory Calculator's README]. + +The inputs to the memory calculation, except the container's total memory (which is unknown at staging time), are logged during staging, for example: +``` +Loaded Classes: 13974, Threads: 300, JAVA_OPTS: '' +``` + +The container's total memory is logged during `cf push` and `cf scale`, for example: +``` + state since cpu memory disk details +#0 running 2017-04-10 02:20:03 PM 0.0% 896K of 1G 1.3M of 1G +``` + +The JRE memory settings are logged when the application is started or re-started, for example: +``` +JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -XX:MaxMetaspaceSize=99199K \ + -XX:ReservedCodeCacheSize=240M -XX:CompressedClassSpaceSize=18134K -Xss1M -Xmx368042K +``` + +[`config/sap_machine_jre.yml`]: ../config/sap_machine_jre.yml +[jammy]: https://java-buildpack.cloudfoundry.org/openjdk/jammy/x86_64/index.yml +[Configuration and Extension]: ../README.md#configuration-and-extension +[Java Buildpack Memory Calculator]: https://github.com/cloudfoundry/java-buildpack-memory-calculator +[jvmkill-jammy]: https://java-buildpack.cloudfoundry.org/jvmkill/jammy/x86_64/index.yml +[Memory Calculator's README]: https://github.com/cloudfoundry/java-buildpack-memory-calculator +[repositories]: extending-repositories.md +[SapMachine]: https://sapmachine.io +[version syntax]: extending-repositories.md#version-syntax-and-ordering +[Volume Service]: https://docs.cloudfoundry.org/devguide/services/using-vol-services.html diff --git a/docs/jre-zing_jre.md b/docs/jre-zing_jre.md new file mode 100644 index 0000000000..c29adb9595 --- /dev/null +++ b/docs/jre-zing_jre.md @@ -0,0 +1,146 @@ +# Azul Platform Prime JRE +Azul Platform Prime JRE provides Java runtimes developed by Azul. No versions of the JRE are available by default due to licensing restrictions. Instead you will need to create a repository with the Prime JREs in it and configure the buildpack to use that repository. Unless otherwise configured, the version of Java that will be used is specified in [`config/zing_jre.yml`][]. + + + + + + + + + + +
Detection CriterionUnconditional. Existence of a single bound Volume Service will result in Terminal heap dumps being written. +
    +
  • Existence of a Volume Service service is defined as the VCAP_SERVICES payload containing a service who's name, label or tag has heap-dump as a substring.
  • +
+
Tagsopen-jdk-like-jre=⟨version⟩, open-jdk-like-memory-calculator=⟨version⟩, jvmkill=⟨version⟩
+Tags are printed to standard output by the buildpack detect script. + + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The JRE can be configured by modifying the [`config/zing_jre.yml`][] file in the buildpack fork. The JRE uses the [`Repository` utility support][repositories] and so, it supports the [version syntax][] defined there. + +To use Azul Platform Prime JRE instead of OpenJDK without forking java-buildpack, set environment variable and restage: + +```bash +cf set-env JBP_CONFIG_COMPONENTS '{jres: ["JavaBuildpack::Jre::ZingJRE"]}' +cf set-env JBP_CONFIG_ZING_JRE '{ jre: { repository_root: "" } }' +cf restage +``` + +| Name | Description +| ---- | ----------- +| `jre.repository_root` | The URL of the Azul Platform Prime repository index ([details][repositories]). +| `jre.version` | The version of Java runtime to use. Note: version 1.8.0 and higher require the `memory_sizes` and `memory_heuristics` mappings to specify `metaspace` rather than `permgen`. +| `jvmkill.repository_root` | The URL of the `jvmkill` repository index ([details][repositories]). +| `jvmkill.version` | The version of `jvmkill` to use. Candidate versions can be found in the listings for [jammy][jvmkill-jammy]. +| `memory_calculator` | Memory calculator defaults, described below under "Memory". + +### Additional Resources +The JRE can also be configured by overlaying a set of resources on the default distribution. To do this, add files to the `resources/zing_jre` directory in the buildpack fork. + +#### JCE Unlimited Strength +To add the JCE Unlimited Strength `local_policy.jar`, add your file to `resources/zing_jre/lib/security/local_policy.jar`. This file will be overlayed onto the Azul Platform Prime distribution. + +#### Custom CA Certificates +To add custom SSL certificates, add your `cacerts` file to `resources/zing_jre/lib/security/cacerts`. This file will be overlayed onto the Azul Platform Prime distribution. + +### `jvmkill` +Azul Platform Prime JRE does not use the jvmkill agent instead by default uses the -XX:ExitOnOutOfMemoryError flag which terminates the JVM process when an out-of-memory error occurs. + +If a [Volume Service][] with the string `heap-dump` in its name or tag is bound to the application, terminal heap dumps will be written with the pattern `/-/-/--.hprof` + +```plain +Heapdump written to /var/vcap/data/9ae0b817-1446-4915-9990-74c1bb26f147/pcfdev-space-e91c5c39/java-main-application-892f20ab/0-2017-06-13T18:31:29+0000-7b23124e.hprof +``` + +### Memory +The total available memory for the application's container is specified when an application is pushed. +The Java buildpack uses this value to control the JRE's use of various +regions of memory and logs the JRE memory settings when the application starts or restarts. +These settings can be influenced by configuring +the `stack_threads` and/or `class_count` mappings (both part of the `memory_calculator` mapping), +and/or Java options relating to memory. + +Note: If the total available memory is scaled up or down, the Java buildpack will re-calculate the JRE memory settings the next time the application is started. + +#### Total Memory + +The user can change the container's total memory available to influence the JRE memory settings. +Unless the user specifies the heap size Java option (`-Xmx`), increasing or decreasing the total memory +available results in the heap size setting increasing or decreasing by a corresponding amount. + +#### Loaded Classes + +The amount of memory that is allocated to metaspace and compressed class space (or, on Java 7, the permanent generation) is calculated from an estimate of the number of classes that will be loaded. The default behaviour is to estimate the number of loaded classes as a fraction of the number of class files in the application. +If a specific number of loaded classes should be used for calculations, then it should be specified as in the following example: + +```yaml +class_count: 500 +``` + +#### Headroom + +A percentage of the total memory allocated to the container to be left as headroom and excluded from the memory calculation. + +```yaml +headroom: 10 +``` + +#### Stack Threads + +The amount of memory that should be allocated to stacks is given as an amount of memory per thread with the Java option `-Xss`. If an explicit number of threads should be used for the calculation of stack memory, then it should be specified as in the following example: + +```yaml +stack_threads: 500 +``` + +Note that the default value of 250 threads is optimized for a default Tomcat configuration. If you are using another container, especially something non-blocking like Netty, it's more appropriate to use a significantly smaller value. Typically 25 threads would cover the needs of both the server (Netty) and the threads started by the JVM itself. + +#### Java Options + +If the JRE memory settings need to be fine-tuned, the user can set one or more Java memory options to +specific values. The heap size can be set explicitly, but changing the value of options other +than the heap size can also affect the heap size. For example, if the user increases +the maximum direct memory size from its default value of 10 Mb to 20 Mb, then this will +reduce the calculated heap size by 10 Mb. + +#### Memory Calculation +Memory calculation happens before every `start` of an application and is performed by an external program, the [Java Buildpack Memory Calculator]. There is no need to `restage` an application after scaling the memory as restarting will cause the memory settings to be recalculated. + +The container's total available memory is allocated into heap, metaspace and compressed class space (or permanent generation for Java 7), +direct memory, and stack memory settings. + +The memory calculation is described in more detail in the [Memory Calculator's README]. + +The inputs to the memory calculation, except the container's total memory (which is unknown at staging time), are logged during staging, for example: +``` +Loaded Classes: 13974, Threads: 300, JAVA_OPTS: '' +``` + +The container's total memory is logged during `cf push` and `cf scale`, for example: +``` + state since cpu memory disk details +#0 running 2017-04-10 02:20:03 PM 0.0% 896K of 1G 1.3M of 1G +``` + +The JRE memory settings are logged when the application is started or re-started, for example: +``` +JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -XX:MaxMetaspaceSize=99199K \ + -XX:ReservedCodeCacheSize=240M -XX:CompressedClassSpaceSize=18134K -Xss1M -Xmx368042K +``` + +[`config/components.yml`]: ../config/components.yml +[`config/zing_jre.yml`]: ../config/zing_jre.yml +[Azul Platform Prime]: https://www.azul.com/products/prime/ +[Configuration and Extension]: ../README.md#configuration-and-extension +[Java Buildpack Memory Calculator]: https://github.com/cloudfoundry/java-buildpack-memory-calculator +[jvmkill-jammy]: https://java-buildpack.cloudfoundry.org/jvmkill/jammy/x86_64/index.yml +[Memory Calculator's README]: https://github.com/cloudfoundry/java-buildpack-memory-calculator +[repositories]: extending-repositories.md +[version syntax]: extending-repositories.md#version-syntax-and-ordering +[Volume Service]: https://docs.cloudfoundry.org/devguide/services/using-vol-services.html +[Azul Platform Prime JRE]: jre-zing_jre.md diff --git a/docs/jre-zulu_jre.md b/docs/jre-zulu_jre.md new file mode 100644 index 0000000000..8d5bbb5bdd --- /dev/null +++ b/docs/jre-zulu_jre.md @@ -0,0 +1,172 @@ +# Azul Zulu JRE +Azul Zulu JRE provides Java runtimes developed by Azul team. Unless otherwise configured, the version of Java that will be used is specified in [`config/zulu_jre.yml`][]. + + + + + + + + + + +
Detection CriterionUnconditional. Existence of a single bound Volume Service will result in Terminal heap dumps being written. +
    +
  • Existence of a Volume Service service is defined as the VCAP_SERVICES payload containing a service who's name, label or tag has heap-dump as a substring.
  • +
+
Tagsopen-jdk-like-jre=⟨version⟩, open-jdk-like-memory-calculator=⟨version⟩, jvmkill=⟨version⟩
+Tags are printed to standard output by the buildpack detect script. + + +## Configuration +For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][]. + +The JRE can be configured by modifying the [`config/zulu_jre.yml`][] file in the buildpack fork. The JRE uses the [`Repository` utility support][repositories] and so, it supports the [version syntax][] defined there. + +To use Zulu JRE instead of OpenJDK without forking java-buildpack, set environment variable and restage: + +```bash +cf set-env JBP_CONFIG_COMPONENTS '{jres: ["JavaBuildpack::Jre::ZuluJRE"]}' +cf restage +``` + +| Name | Description +| ---- | ----------- +| `jre.repository_root` | The URL of the Zulu repository index ([details][repositories]). +| `jre.version` | The version of Java runtime to use. Note: version 1.8.0 and higher require the `memory_sizes` and `memory_heuristics` mappings to specify `metaspace` rather than `permgen`. +| `jvmkill.repository_root` | The URL of the `jvmkill` repository index ([details][repositories]). +| `jvmkill.version` | The version of `jvmkill` to use. Candidate versions can be found in the listings for [jammy][jvmkill-jammy]. +| `memory_calculator` | Memory calculator defaults, described below under "Memory". + +### Additional Resources +The JRE can also be configured by overlaying a set of resources on the default distribution. To do this, add files to the `resources/zulu_jre` directory in the buildpack fork. + +#### JCE Unlimited Strength +To add the JCE Unlimited Strength `local_policy.jar`, add your file to `resources/zulu_jre/lib/security/local_policy.jar`. This file will be overlayed onto the Zulu distribution. + +#### Custom CA Certificates +To add custom SSL certificates, add your `cacerts` file to `resources/zulu_jre/lib/security/cacerts`. This file will be overlayed onto the Zulu distribution. + +### `jvmkill` +The `jvmkill` agent runs when an application has experience a resource exhaustion event. When this event occurs, the agent will print out a histogram of the first 100 largest types by total number of bytes. + +```plain +Resource exhaustion event: the JVM was unable to allocate memory from the heap. +ResourceExhausted! (1/0) +| Instance Count | Total Bytes | Class Name | +| 18273 | 313157136 | [B | +| 47806 | 7648568 | [C | +| 14635 | 1287880 | Ljava/lang/reflect/Method; | +| 46590 | 1118160 | Ljava/lang/String; | +| 8413 | 938504 | Ljava/lang/Class; | +| 28573 | 914336 | Ljava/util/concurrent/ConcurrentHashMap$Node; | +``` + +It will also print out a summary of all of the memory spaces in the JVM. + +```plain +Memory usage: + Heap memory: init 65011712, used 332392888, committed 351797248, max 351797248 + Non-heap memory: init 2555904, used 63098592, committed 64815104, max 377790464 +Memory pool usage: + Code Cache: init 2555904, used 14702208, committed 15007744, max 251658240 + PS Eden Space: init 16252928, used 84934656, committed 84934656, max 84934656 + PS Survivor Space: init 2621440, used 0, committed 19398656, max 19398656 + Compressed Class Space: init 0, used 5249512, committed 5505024, max 19214336 + Metaspace: init 0, used 43150616, committed 44302336, max 106917888 + PS Old Gen: init 43515904, used 247459792, committed 247463936, max 247463936 +``` + +If a [Volume Service][] with the string `heap-dump` in its name or tag is bound to the application, terminal heap dumps will be written with the pattern `/-/-/--.hprof` + +```plain +Heapdump written to /var/vcap/data/9ae0b817-1446-4915-9990-74c1bb26f147/pcfdev-space-e91c5c39/java-main-application-892f20ab/0-2017-06-13T18:31:29+0000-7b23124e.hprof +``` + +### Memory +The total available memory for the application's container is specified when an application is pushed. +The Java buildpack uses this value to control the JRE's use of various +regions of memory and logs the JRE memory settings when the application starts or restarts. +These settings can be influenced by configuring +the `stack_threads` and/or `class_count` mappings (both part of the `memory_calculator` mapping), +and/or Java options relating to memory. + +Note: If the total available memory is scaled up or down, the Java buildpack will re-calculate the JRE memory settings the next time the application is started. + +#### Total Memory + +The user can change the container's total memory available to influence the JRE memory settings. +Unless the user specifies the heap size Java option (`-Xmx`), increasing or decreasing the total memory +available results in the heap size setting increasing or decreasing by a corresponding amount. + +#### Loaded Classes + +The amount of memory that is allocated to metaspace and compressed class space (or, on Java 7, the permanent generation) is calculated from an estimate of the number of classes that will be loaded. The default behaviour is to estimate the number of loaded classes as a fraction of the number of class files in the application. +If a specific number of loaded classes should be used for calculations, then it should be specified as in the following example: + +```yaml +class_count: 500 +``` + +#### Headroom + +A percentage of the total memory allocated to the container to be left as headroom and excluded from the memory calculation. + +```yaml +headroom: 10 +``` + +#### Stack Threads + +The amount of memory that should be allocated to stacks is given as an amount of memory per thread with the Java option `-Xss`. If an explicit number of threads should be used for the calculation of stack memory, then it should be specified as in the following example: + +```yaml +stack_threads: 500 +``` + +Note that the default value of 250 threads is optimized for a default Tomcat configuration. If you are using another container, especially something non-blocking like Netty, it's more appropriate to use a significantly smaller value. Typically 25 threads would cover the needs of both the server (Netty) and the threads started by the JVM itself. + +#### Java Options + +If the JRE memory settings need to be fine-tuned, the user can set one or more Java memory options to +specific values. The heap size can be set explicitly, but changing the value of options other +than the heap size can also affect the heap size. For example, if the user increases +the maximum direct memory size from its default value of 10 Mb to 20 Mb, then this will +reduce the calculated heap size by 10 Mb. + +#### Memory Calculation +Memory calculation happens before every `start` of an application and is performed by an external program, the [Java Buildpack Memory Calculator]. There is no need to `restage` an application after scaling the memory as restarting will cause the memory settings to be recalculated. + +The container's total available memory is allocated into heap, metaspace and compressed class space (or permanent generation for Java 7), +direct memory, and stack memory settings. + +The memory calculation is described in more detail in the [Memory Calculator's README]. + +The inputs to the memory calculation, except the container's total memory (which is unknown at staging time), are logged during staging, for example: +``` +Loaded Classes: 13974, Threads: 300, JAVA_OPTS: '' +``` + +The container's total memory is logged during `cf push` and `cf scale`, for example: +``` + state since cpu memory disk details +#0 running 2017-04-10 02:20:03 PM 0.0% 896K of 1G 1.3M of 1G +``` + +The JRE memory settings are logged when the application is started or re-started, for example: +``` +JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -XX:MaxMetaspaceSize=99199K \ + -XX:ReservedCodeCacheSize=240M -XX:CompressedClassSpaceSize=18134K -Xss1M -Xmx368042K +``` + +[`config/components.yml`]: ../config/components.yml +[`config/zulu_jre.yml`]: ../config/zulu_jre.yml +[Azul Zulu]: https://www.azul.com/products/zulu/ +[Configuration and Extension]: ../README.md#configuration-and-extension +[Java Buildpack Memory Calculator]: https://github.com/cloudfoundry/java-buildpack-memory-calculator +[jvmkill-jammy]: https://java-buildpack.cloudfoundry.org/jvmkill/jammy/x86_64/index.yml +[Memory Calculator's README]: https://github.com/cloudfoundry/java-buildpack-memory-calculator +[repositories]: extending-repositories.md +[version syntax]: extending-repositories.md#version-syntax-and-ordering +[Volume Service]: https://docs.cloudfoundry.org/devguide/services/using-vol-services.html +[Zulu JRE]: jre-zulu_jre.md diff --git a/java-buildpack.iml b/java-buildpack.iml index a0bcb4d298..2b50d9c2eb 100644 --- a/java-buildpack.iml +++ b/java-buildpack.iml @@ -1,261 +1,380 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -263,44 +382,305 @@ - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/java_buildpack.rb b/lib/java_buildpack.rb index fe2c1860bf..aa7f6e6e60 100644 --- a/lib/java_buildpack.rb +++ b/lib/java_buildpack.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/buildpack.rb b/lib/java_buildpack/buildpack.rb index 0250d9fbbf..66c7154944 100644 --- a/lib/java_buildpack/buildpack.rb +++ b/lib/java_buildpack/buildpack.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,10 +20,17 @@ require 'java_buildpack/component/additional_libraries' require 'java_buildpack/component/application' require 'java_buildpack/component/droplet' +require 'java_buildpack/component/environment_variables' +require 'java_buildpack/component/extension_directories' require 'java_buildpack/component/immutable_java_home' require 'java_buildpack/component/java_opts' require 'java_buildpack/component/mutable_java_home' +require 'java_buildpack/component/networking' +require 'java_buildpack/component/root_libraries' +require 'java_buildpack/component/security_providers' require 'java_buildpack/logging/logger_factory' +require 'java_buildpack/util/cache/application_cache' +require 'java_buildpack/util/colorize' require 'java_buildpack/util/configuration_utils' require 'java_buildpack/util/constantize' require 'java_buildpack/util/snake_case' @@ -57,11 +65,22 @@ def compile puts BUILDPACK_MESSAGE % @buildpack_version container = component_detection('container', @containers, true).first - fail 'No container can run this application' unless container + no_container unless container component_detection('JRE', @jres, true).first.compile component_detection('framework', @frameworks, false).each(&:compile) + container.compile + + log_cache_contents + + return if @deps_dir.nil? || @index.nil? + + FileUtils.mkdir_p File.join(@deps_dir, @index) + File.write( + File.join(@deps_dir, @index, 'config.yml'), + { 'name' => 'java', 'config' => {}, 'version' => @buildpack_version.to_s(false) }.to_yaml + ) end # Generates the payload required to run the application. The payload format is defined by the @@ -70,16 +89,22 @@ def compile # @return [String] The payload required to run the application. def release container = component_detection('container', @containers, true).first - fail 'No container can run this application' unless container + no_container unless container + + commands = [] + commands << component_detection('JRE', @jres, true).first.release + + component_detection('framework', @frameworks, false).map(&:release) + + commands << container.release - component_detection('JRE', @jres, true).first.release - component_detection('framework', @frameworks, false).each(&:release) - command = container.release + commands.insert 0, @java_opts.as_env_var + command = commands.flatten.compact.join(' && ') payload = { - 'addons' => [], - 'config_vars' => {}, - 'default_process_types' => { 'web' => command } + 'addons' => [], + 'config_vars' => {}, + 'default_process_types' => { 'web' => command, 'task' => command } }.to_yaml @logger.debug { "Release Payload:\n#{payload}" } @@ -91,36 +116,49 @@ def release private - BUILDPACK_MESSAGE = '-----> Java Buildpack Version: %s'.freeze + BUILDPACK_MESSAGE = "#{'----->'.red.bold} #{'Java Buildpack'.blue.bold} %s".freeze LOAD_ROOT = (Pathname.new(__FILE__).dirname + '..').freeze private_constant :BUILDPACK_MESSAGE, :LOAD_ROOT - def initialize(app_dir, application) - @logger = Logging::LoggerFactory.instance.get_logger Buildpack + def initialize(app_dir, deps_dir, index, application) + @logger = Logging::LoggerFactory.instance.get_logger Buildpack @buildpack_version = BuildpackVersion.new + @deps_dir = deps_dir + @index = index + log_arguments log_environment_variables - - additional_libraries = Component::AdditionalLibraries.new app_dir - mutable_java_home = Component::MutableJavaHome.new - immutable_java_home = Component::ImmutableJavaHome.new mutable_java_home, app_dir - java_opts = Component::JavaOpts.new app_dir - - instantiate_components(additional_libraries, app_dir, application, immutable_java_home, java_opts, - mutable_java_home) + log_application_contents application + log_cache_contents + + @java_opts = Component::JavaOpts.new(app_dir) + + mutable_java_home = Component::MutableJavaHome.new + immutable_java_home = Component::ImmutableJavaHome.new mutable_java_home, app_dir + + component_info = { + 'additional_libraries' => Component::AdditionalLibraries.new(app_dir), + 'app_dir' => app_dir, + 'application' => application, + 'env_vars' => Component::EnvironmentVariables.new(app_dir), + 'extension_directories' => Component::ExtensionDirectories.new(app_dir), + 'java_opts' => @java_opts, + 'networking' => Component::Networking.new, + 'root_libraries' => Component::RootLibraries.new(app_dir), + 'security_providers' => Component::SecurityProviders.new + } + + instantiate_components(mutable_java_home, immutable_java_home, component_info) end - def instantiate_components(additional_libraries, app_dir, application, immutable_java_home, java_opts, - mutable_java_home) - components = JavaBuildpack::Util::ConfigurationUtils.load 'components' - @jres = instantiate(components['jres'], additional_libraries, application, mutable_java_home, java_opts, - app_dir) - @frameworks = instantiate(components['frameworks'], additional_libraries, application, immutable_java_home, - java_opts, app_dir) - @containers = instantiate(components['containers'], additional_libraries, application, immutable_java_home, - java_opts, app_dir) + def instantiate_components(mutable_java_home, immutable_java_home, component_info) + components = JavaBuildpack::Util::ConfigurationUtils.load 'components' + + @jres = instantiate(components['jres'], mutable_java_home, component_info) + @frameworks = instantiate(components['frameworks'], immutable_java_home, component_info) + @containers = instantiate(components['containers'], immutable_java_home, component_info) end def component_detection(type, components, unique) @@ -130,7 +168,7 @@ def component_detection(type, components, unique) def detection(type, components, unique) detected = [] - tags = [] + tags = [] components.each do |component| result = component.detect @@ -141,27 +179,58 @@ def detection(type, components, unique) tags << result end - fail "Application can be run by more than one #{type}: #{names detected}" if unique && detected.size > 1 + raise "Application can be run by more than one #{type}: #{names detected}" if unique && detected.size > 1 + [detected, tags] end - def instantiate(components, additional_libraries, application, java_home, java_opts, root) + def instantiate(components, java_home, component_info) components.map do |component| @logger.debug { "Instantiating #{component}" } require_component(component) component_id = component.split('::').last.snake_case - context = { - application: application, + + context = { + application: component_info['application'], configuration: Util::ConfigurationUtils.load(component_id), - droplet: Component::Droplet.new(additional_libraries, component_id, java_home, java_opts, root) + droplet: Component::Droplet.new(component_info['additional_libraries'], component_id, + component_info['env_vars'], component_info['extension_directories'], + java_home, component_info['java_opts'], component_info['networking'], + component_info['app_dir'], component_info['root_libraries'], + component_info['security_providers']) } - component.constantize.new(context) end end + def log_application_contents(application) + @logger.debug do + paths = [] + application.root.find { |f| paths << f.relative_path_from(application.root).to_s } + + "Application Contents (#{application.root}): #{paths}" + end + end + + def log_arguments + @logger.debug { "Arguments: #{$PROGRAM_NAME} #{ARGV}" } + end + + def log_cache_contents + return unless JavaBuildpack::Util::Cache::ApplicationCache.available? + + @logger.debug do + cache_root = Pathname.new JavaBuildpack::Util::Cache::ApplicationCache.application_cache_directory + + paths = [] + cache_root.find { |f| paths << f.relative_path_from(cache_root).to_s } + + "Cache Contents (#{cache_root}): #{paths}" + end + end + def log_environment_variables @logger.debug { "Environment Variables: #{ENV.to_hash}" } end @@ -170,6 +239,12 @@ def names(components) components.map { |component| component.class.to_s.space_case }.join(', ') end + def no_container + raise 'No container can run this application. Please ensure that you\'ve pushed a valid JVM artifact or ' \ + 'artifacts using the -p command line argument or path manifest entry. Information about valid JVM ' \ + 'artifacts can be found at https://github.com/cloudfoundry/java-buildpack#additional-documentation. ' + end + def require_component(component) file = LOAD_ROOT + "#{component.snake_case}.rb" @@ -195,13 +270,13 @@ class << self # @param [String] message an error message with an insert for the reason for failure # @yield [Buildpack] the buildpack to work with # @return [Object] the return value from the given block - def with_buildpack(app_dir, message) - app_dir = Pathname.new(File.expand_path(app_dir)) - application = Component::Application.new(app_dir) + def with_buildpack(app_dir, deps_dir, index, message) + app_dir = Pathname.new(File.expand_path(app_dir)) Logging::LoggerFactory.instance.setup app_dir + application = Component::Application.new(app_dir) - yield new(app_dir, application) if block_given? - rescue => e + yield new(app_dir, deps_dir, index, application) if block_given? + rescue StandardError => e handle_error(e, message) end diff --git a/lib/java_buildpack/buildpack_version.rb b/lib/java_buildpack/buildpack_version.rb index 4a9017de37..0bf17b6c25 100644 --- a/lib/java_buildpack/buildpack_version.rb +++ b/lib/java_buildpack/buildpack_version.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013-2015 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +16,7 @@ # limitations under the License. require 'java_buildpack' +require 'java_buildpack/util/colorize' require 'java_buildpack/util/configuration_utils' require 'java_buildpack/util/to_b' @@ -45,11 +47,11 @@ class BuildpackVersion # Creates a new instance def initialize(should_log = true) - configuration = JavaBuildpack::Util::ConfigurationUtils.load 'version', should_log - @hash = configuration['hash'] || hash - @offline = configuration['offline'] || ENV['OFFLINE'].to_b - @remote = configuration['remote'] || remote - @version = configuration['version'] || ENV['VERSION'] || @hash + configuration = JavaBuildpack::Util::ConfigurationUtils.load('version', true, should_log) + @hash = configuration['hash'] || calculate_hash + @offline = configuration['offline'] || ENV.fetch('OFFLINE', nil).to_b + @remote = configuration['remote'] || calculate_remote + @version = configuration['version'] || ENV.fetch('VERSION', nil) || @hash return unless should_log @@ -62,7 +64,6 @@ def initialize(should_log = true) # @return [Hash] a representation of the buildpack version def to_hash h = {} - h['hash'] = @hash if @hash h['offline'] = @offline if @offline h['remote'] = @remote if @remote @@ -84,26 +85,30 @@ def to_hash # @return [String] a +String+ representation of the version def to_s(human_readable = true) s = [] - s << @version if @version - s << (human_readable ? '(offline)' : 'offline') if @offline + s << (human_readable ? @version.blue : @version) if @version + s << (human_readable ? '(offline)'.blue : 'offline') if @offline if remote_string s << '|' if @version && human_readable s << remote_string end - s << 'unknown' if s.empty? + s << 'unknown'.yellow if s.empty? s.join(human_readable ? ' ' : '-') end private - GIT_DIR = (Pathname.new(__FILE__).dirname.join('..', '..', '.git')).freeze + GIT_DIR = Pathname.new(__FILE__).dirname.join('..', '..', '.git').freeze private_constant :GIT_DIR - def remote_string - "#{@remote}##{@hash}" if @remote && !@remote.empty? && @hash && !@hash.empty? + def calculate_hash + git 'rev-parse --short HEAD' + end + + def calculate_remote + git 'config --get remote.origin.url' end def git(command) @@ -122,12 +127,8 @@ def git_dir? GIT_DIR.exist? end - def hash - git 'rev-parse --short HEAD' - end - - def remote - git 'config --get remote.origin.url' + def remote_string + "#{@remote}##{@hash}" if @remote && !@remote.empty? && @hash && !@hash.empty? end end diff --git a/lib/java_buildpack/component.rb b/lib/java_buildpack/component.rb index 353f67c5f4..4dce9ef091 100644 --- a/lib/java_buildpack/component.rb +++ b/lib/java_buildpack/component.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/component/additional_libraries.rb b/lib/java_buildpack/component/additional_libraries.rb index 567a252951..81ae6fc231 100644 --- a/lib/java_buildpack/component/additional_libraries.rb +++ b/lib/java_buildpack/component/additional_libraries.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,6 +15,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +require 'fileutils' require 'java_buildpack/component' require 'java_buildpack/util/qualify_path' @@ -26,11 +28,10 @@ module Component class AdditionalLibraries < Array include JavaBuildpack::Util - # Creates an instance of the +JAVA_OPTS+ abstraction. + # Creates an instance of the +AdditionalLibraries+ abstraction. # # @param [Pathname] droplet_root the root directory of the droplet def initialize(droplet_root) - @paths = [] @droplet_root = droplet_root end diff --git a/lib/java_buildpack/component/application.rb b/lib/java_buildpack/component/application.rb index cb3428e3f8..8dfb78409a 100644 --- a/lib/java_buildpack/component/application.rb +++ b/lib/java_buildpack/component/application.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,7 +18,7 @@ require 'java_buildpack/component' require 'java_buildpack/component/services' require 'java_buildpack/util/filtering_pathname' -require 'yaml' +require 'json' module JavaBuildpack module Component @@ -39,7 +40,7 @@ class Application attr_reader :environment # @!attribute [r] root - # @return [JavaBuildpack::Util::FilteringPathname] the root of the application's fileystem filtered so that it + # @return [JavaBuildpack::Util::FilteringPathname] the root of the application's filesystem filtered so that it # only shows files that have been uploaded by the user attr_reader :root @@ -52,7 +53,13 @@ class Application # @param [Pathname] root the root of the application def initialize(root) initial = children(root) - @root = JavaBuildpack::Util::FilteringPathname.new(root, ->(path) { initial.member? path }, false) + + if Logging::LoggerFactory.instance.initialized + log_file = JavaBuildpack::Logging::LoggerFactory.instance.log_file + initial.delete(log_file) + end + + @root = JavaBuildpack::Util::FilteringPathname.new(root, ->(path) { initial.member? path }, false) @environment = ENV.to_hash @details = parse(@environment.delete('VCAP_APPLICATION')) @@ -68,7 +75,7 @@ def children(root, s = Set.new) end def parse(input) - input ? YAML.load(input) : {} + input ? JSON.parse(input) : {} end end diff --git a/lib/java_buildpack/component/base_component.rb b/lib/java_buildpack/component/base_component.rb index f73292ac1b..2f43e3d565 100644 --- a/lib/java_buildpack/component/base_component.rb +++ b/lib/java_buildpack/component/base_component.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,7 +17,8 @@ require 'fileutils' require 'java_buildpack/component' -require 'java_buildpack/util/cache/application_cache' +require 'java_buildpack/util/cache/cache_factory' +require 'java_buildpack/util/colorize' require 'java_buildpack/util/format_duration' require 'java_buildpack/util/shell' require 'java_buildpack/util/space_case' @@ -50,7 +52,7 @@ def initialize(context) # an +Array+ that uniquely identifies the component (e.g. # +open_jdk=1.7.0_40+). Otherwise, +nil+. def detect - fail "Method 'detect' must be defined" + raise "Method 'detect' must be defined" end # Modifies the application's file system. The component is expected to transform the application's file system in @@ -59,7 +61,7 @@ def detect # # @return [Void] def compile - fail "Method 'compile' must be defined" + raise "Method 'compile' must be defined" end # Modifies the application's runtime configuration. The component is expected to transform members of the @@ -69,10 +71,11 @@ def compile # Container components are also expected to create the command required to run the application. These components # are expected to read the +context+ values and take them into account when creating the command. # - # @return [void, String] components other than containers are not expected to return any value. Container - # components are expected to return the command required to run the application. + # @return [void, String] components other than containers and JREs are not expected to return any value. + # Container and JRE components are expected to return a command required to run the + # application. def release - fail "Method 'release' must be defined" + raise "Method 'release' must be defined" end protected @@ -86,10 +89,15 @@ def release # @return [Void] def download(version, uri, name = @component_name) download_start_time = Time.now - print "-----> Downloading #{name} #{version} from #{uri.sanitize_uri} " + print "#{'----->'.red.bold} Downloading #{name.blue.bold} #{version.to_s.blue} from #{uri.sanitize_uri} " + + JavaBuildpack::Util::Cache::CacheFactory.create.get(uri) do |file, downloaded| + if downloaded + puts "(#{(Time.now - download_start_time).duration})".green.italic + else + puts '(found in cache)'.green.italic + end - JavaBuildpack::Util::Cache::ApplicationCache.new.get(uri) do |file, downloaded| - puts downloaded ? "(#{(Time.now - download_start_time).duration})" : '(found in cache)' yield file end end @@ -113,21 +121,26 @@ def download_jar(version, uri, jar_name, target_directory = @droplet.sandbox, na # # @param [String] version the version of the download # @param [String] uri the uri of the download + # @param [Boolean] strip_top_level whether to strip the top-level directory when expanding. Defaults to +true+. # @param [Pathname] target_directory the directory to expand the TAR file to. Defaults to the component's # sandbox. # @param [String] name an optional name for the download and expansion. Defaults to +@component_name+. # @return [Void] - def download_tar(version, uri, target_directory = @droplet.sandbox, name = @component_name) + def download_tar(version, uri, strip_top_level = true, target_directory = @droplet.sandbox, + name = @component_name) download(version, uri, name) do |file| with_timing "Expanding #{name} to #{target_directory.relative_path_from(@droplet.root)}" do FileUtils.mkdir_p target_directory - shell "tar xzf #{file.path} -C #{target_directory} --strip 1 2>&1" + shell "tar x#{compression_flag(file)}f #{file.path} -C #{target_directory} " \ + "#{'--strip 1' if strip_top_level} 2>&1" end end end # Downloads a given ZIP file and expands it. # + # @param [String] version the version of the download + # @param [String] uri the uri of the download # @param [Boolean] strip_top_level whether to strip the top-level directory when expanding. Defaults to +true+. # @param [Pathname] target_directory the directory to expand the ZIP file to. Defaults to the component's # sandbox. @@ -156,13 +169,33 @@ def download_zip(version, uri, strip_top_level = true, target_directory = @dropl # # @param [String] caption the caption to print when timing starts # @return [Void] - def with_timing(caption) + def with_timing(caption, include_arrow = false) start_time = Time.now - print " #{caption} " + print "#{include_arrow ? '----->'.red.bold : ' '} #{caption} " yield - puts "(#{(Time.now - start_time).duration})" + puts "(#{(Time.now - start_time).duration})".green.italic + end + + private + + def gzipped?(file) + file.path.end_with? '.gz' + end + + def bzipped?(file) + file.path.end_with? '.bz2' + end + + def compression_flag(file) + if gzipped?(file) + 'z' + elsif bzipped?(file) + 'j' + else + '' + end end end diff --git a/lib/java_buildpack/component/droplet.rb b/lib/java_buildpack/component/droplet.rb index cb5512e746..d570f2e49e 100644 --- a/lib/java_buildpack/component/droplet.rb +++ b/lib/java_buildpack/component/droplet.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -38,6 +39,14 @@ class Droplet # @return [String] the id of component using this droplet attr_reader :component_id + # @!attribute [r] environment_variables + # @return [EnvironmentVariables] the shared +EnvironmentVariables+ instance for all components + attr_reader :environment_variables + + # @!attribute [r] extension_directories + # @return [ExtensionDirectories] the shared +ExtensionDirectories+ instance for all components + attr_reader :extension_directories + # @!attribute [r] java_home # @return [ImmutableJavaHome, MutableJavaHome] the shared +JavaHome+ instance for all components. If the # component using this instance is a jre, then this will be an @@ -49,39 +58,73 @@ class Droplet # @return [JavaOpts] the shared +JavaOpts+ instance for all components attr_reader :java_opts + # @!attribute [r] networking + # @return [Networking] the shared +Networking+ instance for all components + attr_reader :networking + # @!attribute [r] root # @return [JavaBuildpack::Util::FilteringPathname] the root of the droplet's fileystem filtered so that it # excludes files in the sandboxes of other components attr_reader :root + # @!attribute [r] root_libraries + # @return [RootLibraries] the shared +RootLibraries+ instance for all components + attr_reader :root_libraries + # @!attribute [r] sandbox # @return [Pathname] the root of the component's sandbox attr_reader :sandbox + # @!attribute [r] security_providers + # @return [SecurityProviders] the shared +SecurityProviders+ instance for all components + attr_reader :security_providers + # Creates a new instance of the droplet abstraction # # @param [AdditionalLibraries] additional_libraries the shared +AdditionalLibraries+ instance for all # components # @param [String] component_id the id of the component that will use this +Droplet+ + # @param [EnvironmentVariables] env_vars the shared +EnvironmentVariables+ instance for all + # components + # @param [ExtensionDirectories] extension_directories the shared +ExtensionDirectories+ instance for all + # components # @param [ImmutableJavaHome, MutableJavaHome] java_home the shared +JavaHome+ instance for all components. If the # component using this instance is a jre, then this should # be an instance of +MutableJavaHome+. Otherwise it should # be an instance of +ImmutableJavaHome+. # @param [JavaOpts] java_opts the shared +JavaOpts+ instance for all components + # @param [Networking] networking the shared +Networking+ instance for all components # @param [Pathname] root the root of the droplet - def initialize(additional_libraries, component_id, java_home, java_opts, root) - @additional_libraries = additional_libraries - @component_id = component_id - @java_home = java_home - @java_opts = java_opts - @logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger Droplet + # @param [RootLibraries] root_libraries the shared +RootLibraries+ instance for all components + # @param [SecurityProviders] security_providers the shared +SecurityProviders+ instance for all components + def initialize(additional_libraries, component_id, env_vars, extension_directories, java_home, java_opts, + networking, root, root_libraries, security_providers) + + @additional_libraries = additional_libraries + @component_id = component_id + @environment_variables = env_vars + @extension_directories = extension_directories + @java_home = java_home + @java_opts = java_opts + @logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger Droplet buildpack_root = root + '.java-buildpack' sandbox_root = buildpack_root + component_id - @sandbox = JavaBuildpack::Util::FilteringPathname.new(sandbox_root, ->(path) { in?(path, sandbox_root) }, true) - @root = JavaBuildpack::Util::FilteringPathname.new( - root, ->(path) { !in?(path, buildpack_root) || in?(path, @sandbox) }, true) + @logger.debug { "Droplet root: #{root}" } + @logger.debug { "Buildpack root: #{buildpack_root}" } + @logger.debug { "Sandbox root: #{sandbox_root}" } + + @sandbox = JavaBuildpack::Util::FilteringPathname.new(sandbox_root, + ->(path) { in?(path, sandbox_root) }, true) + @root = JavaBuildpack::Util::FilteringPathname.new( + root, + ->(path) { !in?(path, buildpack_root) || in?(path, @sandbox) }, + true + ) + @root_libraries = root_libraries + @networking = networking + @security_providers = security_providers end # Copy resources from a components resources directory to a directory @@ -102,7 +145,7 @@ def copy_resources(target_directory = @sandbox) private - RESOURCES_DIRECTORY = Pathname.new(File.expand_path('../../../../resources', __FILE__)).freeze + RESOURCES_DIRECTORY = Pathname.new(File.expand_path('../../../resources', __dir__)).freeze private_constant :RESOURCES_DIRECTORY diff --git a/lib/java_buildpack/component/environment_variables.rb b/lib/java_buildpack/component/environment_variables.rb new file mode 100644 index 0000000000..a8ca815b53 --- /dev/null +++ b/lib/java_buildpack/component/environment_variables.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component' +require 'java_buildpack/util/qualify_path' + +module JavaBuildpack + module Component + + # An abstraction encapsulating the Environment Variables of an application. + # + # A new instance of this type should be created once for the application. + class EnvironmentVariables < Array + include JavaBuildpack::Util + + # Creates an instance of the Environment Variables abstraction. + # + # @param [Pathname] droplet_root the root directory of the droplet + def initialize(droplet_root) + @droplet_root = droplet_root + end + + # Adds an environment variable. Prepends +$PWD+ to any variable values that are + # paths (relative to the droplet root) to ensure that the path is always accurate. + # + # @param [String] key the variable name + # @param [String] value the variable value + # @return [EnvironmentVariables] +self+ for chaining + def add_environment_variable(key, value) + self << "#{key}=#{qualify_value(value)}" + end + + # Returns the contents as an environment variable formatted as +=+ + # + # @return [String] the contents as an environment variable + def as_env_vars + join(' ') + end + + private + + def qualify_value(value) + value.respond_to?(:relative_path_from) ? qualify_path(value) : value + end + + end + + end +end diff --git a/lib/java_buildpack/component/extension_directories.rb b/lib/java_buildpack/component/extension_directories.rb new file mode 100644 index 0000000000..836ee8d097 --- /dev/null +++ b/lib/java_buildpack/component/extension_directories.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component' +require 'java_buildpack/util/qualify_path' + +module JavaBuildpack + module Component + + # An abstraction around the extension directories provided to a droplet by components. + # + # A new instance of this type should be created once for the application. + class ExtensionDirectories < Array + include JavaBuildpack::Util + + # Creates an instance of the +JAVA_OPTS+ abstraction. + # + # @param [Pathname] droplet_root the root directory of the droplet + def initialize(droplet_root) + @droplet_root = droplet_root + end + + # Returns the contents of the collection as a colon-delimited paths formatted as +:+ + # + # @return [String] the contents of the collection as a colon-delimited collection of paths + def as_paths + qualified_paths = sort.map { |path| qualify_path path } + qualified_paths.join ':' unless empty? + end + + end + + end +end diff --git a/lib/java_buildpack/component/immutable_java_home.rb b/lib/java_buildpack/component/immutable_java_home.rb index 3218731b4b..7619dc3f08 100644 --- a/lib/java_buildpack/component/immutable_java_home.rb +++ b/lib/java_buildpack/component/immutable_java_home.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -39,29 +40,36 @@ def initialize(delegate, droplet_root) # # @return [String] the path of +JAVA_HOME+ as an environment variable def as_env_var - "JAVA_HOME=#{root}" + "JAVA_HOME=#{qualify_path root}" + end + + # Whether or not the version of Java is 8 or later + # + # @return [Boolean] +true+ iff the version is 1.8.0 or later + def java_8_or_later? + @delegate.java_8_or_later? + end + + # Whether or not the version of Java is 9 or later + # + # @return [Boolean] +true+ iff the version is 9.0.0 or later + def java_9_or_later? + @delegate.java_9_or_later? end - # Execute a block with the +JAVA_HOME+ environment variable set + # Whether or not the version of Java is 10 or later # - # @yield yields to block with the +JAVA_HOME+ environment variable set - # @return [Object] the returned value of the block - def do_with - previous_value = ENV['JAVA_HOME'] - begin - ENV['JAVA_HOME'] = @delegate.root.cleanpath.to_s - yield - ensure - ENV['JAVA_HOME'] = previous_value - end + # @return [Boolean] +true+ iff the version is 10.0.0 or later + def java_10_or_later? + @delegate.java_10_or_later? end - # @return [String] the root of the droplet's +JAVA_HOME+ formatted as +$PWD/+ + # @return [Pathname] the root of the droplet's +JAVA_HOME+ def root - qualify_path @delegate.root + @delegate.root end - # @return [String] the version of Java being used by the droplet + # @return # @return [JavaBuildpack::Util::TokenizedVersion] the tokenized droplet's +VERSION+ def version @delegate.version end diff --git a/lib/java_buildpack/component/java_opts.rb b/lib/java_buildpack/component/java_opts.rb index 6435c1c07c..1dee29c665 100644 --- a/lib/java_buildpack/component/java_opts.rb +++ b/lib/java_buildpack/component/java_opts.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -33,17 +34,54 @@ def initialize(droplet_root) @droplet_root = droplet_root end - # Adds a +javaagent+ entry to the +JAVA_OPTS+. Prepends +$PWD+ to the path (relative to the droplet root) to + # Adds a +javaagent+ entry to the +JAVA_OPTS+. Prepends +$PWD+ to the path (relative to the droplet root) to # ensure that the path is always accurate. # # @param [Pathname] path the path to the +javaagent+ JAR # @return [JavaOpts] +self+ for chaining def add_javaagent(path) - self << "-javaagent:#{qualify_path path}" - self + add_preformatted_options "-javaagent:#{qualify_path path}" + end + + # Adds a +javaagent+ entry to the +JAVA_OPTS+. Prepends +$PWD+ to the path (relative to the droplet root) to + # ensure that the path is always accurate. + # + # @param [Pathname] path the path to the +javaagent+ JAR + # @param [Properties] props to append to the +javaagent+ entry + # @return [JavaOpts] +self+ for chaining + def add_javaagent_with_props(path, props) + add_preformatted_options "-javaagent:#{qualify_path path}=" + props.map { |k, v| "#{k}=#{v}" }.join(',') end - # Adds a system property to the +JAVA_OPTS+. Ensures that the key is prepended with +-D+. If the value is a + # Adds a +agentpath+ entry to the +JAVA_OPTS+. Prepends +$PWD+ to the path (relative to the droplet root) to + # ensure that the path is always accurate. + # + # @param [Pathname] path the path to the +agentpath+ shared library + # @param [Properties] props to append to the +agentpath+ entry + # @return [JavaOpts] +self+ for chaining + def add_agentpath_with_props(path, props) + add_preformatted_options "-agentpath:#{qualify_path path}=" + props.map { |k, v| v ? "#{k}=#{v}" : k }.join(',') + end + + # Adds an +agentpath+ entry to the +JAVA_OPTS+. Prepends +$PWD+ to the path (relative to the droplet root) to + # ensure that the path is always accurate. + # + # @param [Pathname] path the path to the +native+ +agent+ + # @return [JavaOpts] +self+ for chaining + def add_agentpath(path) + add_preformatted_options "-agentpath:#{qualify_path path}" + end + + # Adds a +bootclasspath/p+ entry to the +JAVA_OPTS+. Prepends +$PWD+ to the path (relative to the droplet root) to + # ensure that the path is always accurate. + # + # @param [Pathname] path the path to the +javaagent+ JAR + # @return [JavaOpts] +self+ for chaining + def add_bootclasspath_p(path) + add_preformatted_options "-Xbootclasspath/p:#{qualify_path path}" + end + + # Adds a system property to the +JAVA_OPTS+. Ensures that the key is prepended with +-D+. If the value is a # +Pathname+, then prepends +$PWD+ to the path (relative to the droplet root) to ensure that the path is always # accurate. Otherwise, uses the value as-is. # @@ -51,19 +89,26 @@ def add_javaagent(path) # @param [Pathname, String] value the value of the system property # @return [JavaOpts] +self+ for chaining def add_system_property(key, value) - self << "-D#{key}=#{qualify_value(value)}" - self + add_preformatted_options "-D#{key}=#{qualify_value(value)}" end - # Adds an option to the +JAVA_OPTS+. Nothing is prepended to the key. If the value is a +Pathname+, then + # Adds an option to the +JAVA_OPTS+. Nothing is prepended to the key. If the value is a +Pathname+, then # prepends +$PWD+ to the path (relative to the droplet root) to ensure that the path is always accurate. # Otherwise, uses the value as-is. # # @param [String] key the key of the option - # @param [Pathname, String] value the value of the system property + # @param [Pathname, String] value the value of the option # @return [JavaOpts] +self+ for chaining def add_option(key, value) - self << "#{key}=#{qualify_value(value)}" + add_preformatted_options "#{key}=#{qualify_value(value)}" + end + + # Adds a preformatted option to the +JAVA_OPTS+ + # + # @param [String] value the value of options + # @return [JavaOpts] +self+ for chaining + def add_preformatted_options(value) + self << value self end diff --git a/lib/java_buildpack/component/modular_component.rb b/lib/java_buildpack/component/modular_component.rb index e19d602fd7..e718603867 100644 --- a/lib/java_buildpack/component/modular_component.rb +++ b/lib/java_buildpack/component/modular_component.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -62,7 +63,7 @@ def release # @return [void, String] components other than containers are not expected to return any value. Container # components are expected to return the command required to run the application. def command - fail "Method 'command' must be defined" + raise "Method 'command' must be defined" end # The sub_components that make up this component @@ -71,7 +72,7 @@ def command # @return [Array] a collection of +BaseComponent+s that make up the sub_components of this # component def sub_components(_context) - fail "Method 'sub_components' must be defined" + raise "Method 'sub_components' must be defined" end # Returns a copy of the context, but with a subset of the original configuration @@ -89,7 +90,7 @@ def sub_configuration_context(context, key) # # @return [Boolean] whether or not this component supports this application def supports? - fail "Method 'supports?' must be defined" + raise "Method 'supports?' must be defined" end end diff --git a/lib/java_buildpack/component/mutable_java_home.rb b/lib/java_buildpack/component/mutable_java_home.rb index 03c8ecf804..ddd4c0993f 100644 --- a/lib/java_buildpack/component/mutable_java_home.rb +++ b/lib/java_buildpack/component/mutable_java_home.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +16,7 @@ # limitations under the License. require 'java_buildpack/component' +require 'java_buildpack/util/tokenized_version' module JavaBuildpack module Component @@ -30,9 +32,35 @@ class MutableJavaHome attr_accessor :root # @!attribute [rw] version - # @return [Array] the major, minor, micro and qualifier of the droplet's +VERSION+ + # @return [JavaBuildpack::Util::TokenizedVersion] the tokenized droplet's +VERSION+ attr_accessor :version + # Whether or not the version of Java is 8 or later + # @return [Boolean] +true+ if and only if the version is 1.8.0 or later + def java_8_or_later? + @version >= VERSION8 + end + + # Whether or not the version of Java is 9 or later + # @return [Boolean] +true+ if and only if the version is 9.0.0 or later + def java_9_or_later? + @version >= VERSION9 + end + + # Whether or not the version of Java is 10 or later + # @return [Boolean] +true+ if and only if the version is 10.0.0 or later + def java_10_or_later? + @version >= VERSION10 + end + + VERSION8 = JavaBuildpack::Util::TokenizedVersion.new('1.8.0').freeze + + VERSION9 = JavaBuildpack::Util::TokenizedVersion.new('9.0.0').freeze + + VERSION10 = JavaBuildpack::Util::TokenizedVersion.new('10.0.0').freeze + + private_constant :VERSION8, :VERSION9, :VERSION10 + end end diff --git a/lib/java_buildpack/component/networking.rb b/lib/java_buildpack/component/networking.rb new file mode 100644 index 0000000000..92b29f7779 --- /dev/null +++ b/lib/java_buildpack/component/networking.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/component' + +module JavaBuildpack + module Component + + # An abstraction around the networking configuration provided to a droplet by components. + # + # A new instance of this type should be created once for the application. + class Networking + + # @!attribute [rw] networkaddress_cache_ttl + # @return [Integer] the number of seconds to cache the successful lookup + attr_accessor :networkaddress_cache_ttl + + # @!attribute [rw] networkaddress_cache_negative_ttl + # @return [Integer] the number of seconds to cache the failure for un-successful lookups + attr_accessor :networkaddress_cache_negative_ttl + + # Write the networking configuration to a destination file + # + # @param [Pathname] destination the destination to write to + # @return [Void] + def write_to(destination) + FileUtils.mkdir_p destination.parent + + destination.open(File::CREAT | File::APPEND | File::WRONLY) do |f| + f.write "networkaddress.cache.ttl=#{@networkaddress_cache_ttl}\n" if @networkaddress_cache_ttl + + if @networkaddress_cache_negative_ttl + f.write "networkaddress.cache.negative.ttl=#{networkaddress_cache_negative_ttl}\n" + end + end + end + + end + + end +end diff --git a/lib/java_buildpack/component/root_libraries.rb b/lib/java_buildpack/component/root_libraries.rb new file mode 100644 index 0000000000..dfd44d20e2 --- /dev/null +++ b/lib/java_buildpack/component/root_libraries.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/component' +require 'java_buildpack/util/qualify_path' + +module JavaBuildpack + module Component + + # An abstraction around the root libraries provided to a droplet by components. + # + # A new instance of this type should be created once for the application. + class RootLibraries < Array + include JavaBuildpack::Util + + # Creates an instance of the +RootLibraries+ abstraction. + # + # @param [Pathname] droplet_root the root directory of the droplet + def initialize(droplet_root) + @droplet_root = droplet_root + end + + # Returns the collection as a collection of paths qualified to the +droplet_root+. + # + # @return [Array] the contents of the collection as paths qualified to +droplet_root+ + def qualified_paths + sort.map { |path| qualify_path path } + end + + # Symlink the contents of the collection to a destination directory. + # + # @param [Pathname] destination the destination to link to + # @return [Void] + def link_to(destination) + FileUtils.mkdir_p destination + each { |path| (destination + path.basename).make_symlink(path.relative_path_from(destination)) } + end + + end + + end +end diff --git a/lib/java_buildpack/component/security_providers.rb b/lib/java_buildpack/component/security_providers.rb new file mode 100644 index 0000000000..f2e98503ef --- /dev/null +++ b/lib/java_buildpack/component/security_providers.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/component' + +module JavaBuildpack + module Component + + # An abstraction around the security providers provided to a droplet by components. + # + # A new instance of this type should be created once for the application. + class SecurityProviders < Array + + # Write the contents of the collection to a destination file + # + # @param [Pathname] destination the destination to write to + # @return [Void] + def write_to(destination) + FileUtils.mkdir_p destination.parent + + destination.open(File::CREAT | File::APPEND | File::WRONLY) do |f| + each_with_index { |security_provider, index| f.write "security.provider.#{index + 1}=#{security_provider}\n" } + end + end + + end + + end +end diff --git a/lib/java_buildpack/component/services.rb b/lib/java_buildpack/component/services.rb index 943f1e6e6f..c5759df462 100644 --- a/lib/java_buildpack/component/services.rb +++ b/lib/java_buildpack/component/services.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,7 +16,6 @@ # limitations under the License. require 'java_buildpack/component' -require 'java_buildpack/logging/logger_factory' module JavaBuildpack module Component @@ -29,6 +29,28 @@ def initialize(raw) concat raw.values.flatten end + # Compares the name, label, and tags of each service to the given +filter+. The method returns the first service + # that the +filter+ matches. If no service matches, returns +nil+. + # + # @param [Regexp, String] filter a +RegExp+ or +String+ to match against the name, label, and tags of the services + # @param [String] required_credentials an optional list of keys or groups of keys, where at least one key from the + # group, must exist in the credentials payload of the candidate service + # @return [Hash, nil] the first service that +filter+ matches. If no service matches, returns +nil+. + def find_service(filter, *required_credentials) + select(&service?(filter)) + .find(&credentials?(required_credentials)) + end + + # Compares the name, label, and tags of each service to the given +filter+. The method returns the first service + # that +filter+ matches. If no service matches, returns +nil+. + # + # @param [Regexp, String] filter a +RegExp+ or +String+ to match against the name, label, and tags of the services + # @return [Hash, nil] the first service that +filter+ matches. If no service matches, returns +nil+. + def find_volume_service(filter) + select(&service?(filter)) + .find(&volume_mount?) + end + # Compares the name, label, and tags of each service to the given +filter+. The method returns +true+ if the # +filter+ matches exactly one service, +false+ otherwise. # @@ -38,42 +60,46 @@ def initialize(raw) # @return [Boolean] +true+ if the +filter+ matches exactly one service with the required credentials, +false+ # otherwise. def one_service?(filter, *required_credentials) - candidates = select(&matcher(filter)) - - match = false - if candidates.one? - if credentials?(candidates.first['credentials'], required_credentials) - match = true - else - logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger Services - logger.warn do - "A service with a name label or tag matching #{filter} was found, but was missing one of the required" \ - " credentials #{required_credentials}" - end - end - end - - match + select(&service?(filter)) + .select(&credentials?(required_credentials)) + .one? end - # Compares the name, label, and tags of each service to the given +filter+. The method returns the first service - # that the +filter+ matches. If no service matches, returns +nil+ + # Compares the name, label, and tags of each service to the given +filter+. The method returns +true+ if the + # +filter+ matches exactly one volume service, +false+ otherwise. # # @param [Regexp, String] filter a +RegExp+ or +String+ to match against the name, label, and tags of the services - # @return [Hash, nil] the first service that +filter+ matches. If no service matches, returns +nil+. - def find_service(filter) - find(&matcher(filter)) + # @return [Boolean] +true+ if the +filter+ matches exactly one volume service with the required credentials, + # +false+ otherwise. + def one_volume_service?(filter) + select(&service?(filter)) + .select(&volume_mount?) + .one? end private - def credentials?(candidate, required_keys) - required_keys.all? do |k| - k.is_a?(Array) ? k.any? { |g| candidate.key?(g) } : candidate.key?(k) + def credentials?(required_keys) + lambda do |service| + credentials = service['credentials'] + return false if credentials.nil? + + required_keys.all? do |k| + k.is_a?(Array) ? k.any? { |g| credentials.key?(g) } : credentials.key?(k) + end + end + end + + def volume_mount? + lambda do |service| + volume_mounts = service['volume_mounts'] + return false if volume_mounts.nil? + + volume_mounts.one? end end - def matcher(filter) + def service?(filter) filter = Regexp.new(filter) unless filter.is_a?(Regexp) lambda do |service| diff --git a/lib/java_buildpack/component/versioned_dependency_component.rb b/lib/java_buildpack/component/versioned_dependency_component.rb index dc2e58eb40..545390574f 100644 --- a/lib/java_buildpack/component/versioned_dependency_component.rb +++ b/lib/java_buildpack/component/versioned_dependency_component.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -56,7 +57,7 @@ def detect # # @return [Boolean] whether or not this component supports this application def supports? - fail "Method 'supports?' must be defined" + raise "Method 'supports?' must be defined" end # Downloads a given JAR file and stores it. @@ -65,18 +66,19 @@ def supports? # @param [Pathname] target_directory the directory to store the JAR file in. Defaults to the component's sandbox. # @param [String] name an optional name for the download. Defaults to +@component_name+. # @return [Void] - def download_jar(jar_name = jar_name, target_directory = @droplet.sandbox, name = @component_name) + def download_jar(jar_name = self.jar_name, target_directory = @droplet.sandbox, name = @component_name) super(@version, @uri, jar_name, target_directory, name) end # Downloads a given TAR file and expands it. # + # @param [Boolean] strip_top_level whether to strip the top-level directory when expanding. Defaults to +true+. # @param [Pathname] target_directory the directory to expand the TAR file to. Defaults to the component's # sandbox. # @param [String] name an optional name for the download and expansion. Defaults to +@component_name+. # @return [Void] - def download_tar(target_directory = @droplet.sandbox, name = @component_name) - super(@version, @uri, target_directory, name) + def download_tar(strip_top_level = true, target_directory = @droplet.sandbox, name = @component_name) + super(@version, @uri, strip_top_level, target_directory, name) end # Downloads a given ZIP file and expands it. diff --git a/lib/java_buildpack/container.rb b/lib/java_buildpack/container.rb index 6a3fcff59b..ae680f7768 100644 --- a/lib/java_buildpack/container.rb +++ b/lib/java_buildpack/container.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/container/dist_zip.rb b/lib/java_buildpack/container/dist_zip.rb index f455733104..0a2c3190a1 100644 --- a/lib/java_buildpack/container/dist_zip.rb +++ b/lib/java_buildpack/container/dist_zip.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -45,18 +46,17 @@ def id # (see JavaBuildpack::Container::DistZipLike#supports?) def supports? - start_script(root) && - start_script(root).exist? && + start_script(root)&.exist? && jars? && !@ratpack_utils.is?(@application) && - !@spring_boot_utils.is?(@application) && - !JavaBuildpack::Util::Play::Factory.create(@droplet) + !@spring_boot_utils.is?(@application) && + !JavaBuildpack::Util::Play::Factory.create(@droplet) end private def jars? - (lib_dir + '*.jar').glob.any? + (lib_dir + '**/*.jar').glob.any? end def lib_dir diff --git a/lib/java_buildpack/container/dist_zip_like.rb b/lib/java_buildpack/container/dist_zip_like.rb index 7ded68752d..208d395f06 100644 --- a/lib/java_buildpack/container/dist_zip_like.rb +++ b/lib/java_buildpack/container/dist_zip_like.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -34,16 +35,20 @@ def detect # (see JavaBuildpack::Component::BaseComponent#compile) def compile - start_script(root).chmod 0755 + start_script(root).chmod 0o755 augment_classpath_content end # (see JavaBuildpack::Component::BaseComponent#release) def release + @droplet.environment_variables.add_environment_variable 'JAVA_OPTS', '$JAVA_OPTS' + [ + @droplet.environment_variables.as_env_vars, @droplet.java_home.as_env_var, - @droplet.java_opts.as_env_var, - qualify_path(start_script(root), @droplet.root) + 'exec', + qualify_path(start_script(root), @droplet.root), + arguments ].flatten.compact.join(' ') end @@ -53,7 +58,7 @@ def release # # @return [String] the id of this container def id - fail "Method 'id' must be defined" + raise "Method 'id' must be defined" end # The root directory of the application @@ -67,20 +72,26 @@ def root # # @return [Boolean] whether or not this component supports this application def supports? - fail "Method 'supports?' must be defined" + raise "Method 'supports?' must be defined" end private - PATTERN_APP_CLASSPATH = /^declare -r app_classpath=\"(.*)\"$/ + ARGUMENTS_PROPERTY = 'arguments' + + PATTERN_APP_CLASSPATH = /^declare -r app_classpath="(.*)"$/.freeze PATTERN_CLASSPATH = /^CLASSPATH=(.*)$/.freeze - private_constant :PATTERN_APP_CLASSPATH, :PATTERN_CLASSPATH + private_constant :ARGUMENTS_PROPERTY, :PATTERN_APP_CLASSPATH, :PATTERN_CLASSPATH + + def arguments + @configuration[ARGUMENTS_PROPERTY] + end def augment_app_classpath(content) - additional_classpath = @droplet.additional_libraries.sort.map do |additional_library| - "$app_home/#{additional_library.relative_path_from(start_script(root).dirname)}" + additional_classpath = (@droplet.additional_libraries + @droplet.root_libraries).sort.map do |library| + "$app_home/#{library.relative_path_from(start_script(root).dirname)}" end update_file start_script(root), content, @@ -88,8 +99,8 @@ def augment_app_classpath(content) end def augment_classpath(content) - additional_classpath = @droplet.additional_libraries.sort.map do |additional_library| - "$APP_HOME/#{additional_library.relative_path_from(root)}" + additional_classpath = (@droplet.additional_libraries + @droplet.root_libraries).sort.map do |library| + "$APP_HOME/#{library.relative_path_from(root)}" end update_file start_script(root), content, diff --git a/lib/java_buildpack/container/groovy.rb b/lib/java_buildpack/container/groovy.rb index dbe8d9eb48..a60de4040f 100644 --- a/lib/java_buildpack/container/groovy.rb +++ b/lib/java_buildpack/container/groovy.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -50,13 +51,15 @@ def compile # (see JavaBuildpack::Component::BaseComponent#release) def release + @droplet.environment_variables.add_environment_variable 'JAVA_OPTS', '$JAVA_OPTS' add_libs [ + @droplet.environment_variables.as_env_vars, @droplet.java_home.as_env_var, - @droplet.java_opts.as_env_var, + 'exec', qualify_path(@droplet.sandbox + 'bin/groovy', @droplet.root), - @droplet.additional_libraries.as_classpath, + classpath, relative_main_groovy, relative_other_groovy ].flatten.compact.join(' ') @@ -76,6 +79,10 @@ def add_libs (@droplet.root + '**/*.jar').glob.each { |jar| @droplet.additional_libraries << jar } end + def classpath + ([@droplet.additional_libraries.as_classpath] + @droplet.root_libraries.qualified_paths).join(':') + end + def main_groovy candidates = JavaBuildpack::Util::GroovyUtils.groovy_files(@application) diff --git a/lib/java_buildpack/container/java_main.rb b/lib/java_buildpack/container/java_main.rb index 3811992091..dc00ee1fab 100644 --- a/lib/java_buildpack/container/java_main.rb +++ b/lib/java_buildpack/container/java_main.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,6 +19,8 @@ require 'java_buildpack/container' require 'java_buildpack/util/dash_case' require 'java_buildpack/util/java_main_utils' +require 'java_buildpack/util/qualify_path' +require 'java_buildpack/util/spring_boot_utils' module JavaBuildpack module Container @@ -26,6 +29,15 @@ module Container # method. This isn't a _container_ in the traditional sense, but contains the functionality to manage the lifecycle # of Java +main()+ applications. class JavaMain < JavaBuildpack::Component::BaseComponent + include JavaBuildpack::Util + + # Creates an instance + # + # @param [Hash] context a collection of utilities used the component + def initialize(context) + super(context) + @spring_boot_utils = JavaBuildpack::Util::SpringBootUtils.new + end # (see JavaBuildpack::Component::BaseComponent#detect) def detect @@ -34,30 +46,52 @@ def detect # (see JavaBuildpack::Component::BaseComponent#compile) def compile + return unless @spring_boot_utils.is?(@application) + + if @spring_boot_utils.thin?(@application) + with_timing 'Caching Spring Boot Thin Launcher Dependencies', true do + @spring_boot_utils.cache_thin_dependencies @droplet.java_home.root, @application.root, thin_root + end + end + + @droplet.additional_libraries.link_to(@spring_boot_utils.lib(@droplet)) end # (see JavaBuildpack::Component::BaseComponent#release) def release - @droplet.additional_libraries.insert 0, @application.root manifest_class_path.each { |path| @droplet.additional_libraries << path } - release_text + if @spring_boot_utils.is?(@application) + @droplet.environment_variables.add_environment_variable 'SERVER_PORT', '$PORT' + + if @spring_boot_utils.thin?(@application) + @droplet.java_opts + .add_system_property('thin.offline', true) + .add_system_property('thin.root', thin_root) + end + else + @droplet.additional_libraries.insert 0, @application.root + end + + release_text(classpath) end private - ARGUMENTS_PROPERTY = 'arguments'.freeze + ARGUMENTS_PROPERTY = 'arguments' - CLASS_PATH_PROPERTY = 'Class-Path'.freeze + CLASS_PATH_PROPERTY = 'Class-Path' private_constant :ARGUMENTS_PROPERTY, :CLASS_PATH_PROPERTY - def release_text + def release_text(classpath) [ - port, - "#{@droplet.java_home.root}/bin/java", - @droplet.additional_libraries.as_classpath, - @droplet.java_opts.join(' '), + @droplet.environment_variables.as_env_vars, + 'eval', + 'exec', + "#{qualify_path @droplet.java_home.root, @droplet.root}/bin/java", + '$JAVA_OPTS', + classpath, main_class, arguments ].flatten.compact.join(' ') @@ -67,17 +101,22 @@ def arguments @configuration[ARGUMENTS_PROPERTY] end + def classpath + cp = @spring_boot_utils.is?(@application) ? '-cp $PWD/.' : @droplet.additional_libraries.as_classpath + ([cp] + @droplet.root_libraries.qualified_paths).join(':') + end + def main_class JavaBuildpack::Util::JavaMainUtils.main_class(@application, @configuration) end def manifest_class_path values = JavaBuildpack::Util::JavaMainUtils.manifest(@application)[CLASS_PATH_PROPERTY] - values.nil? ? [] : values.split(' ').map { |value| @droplet.root + value } + values.nil? ? [] : values.split.map { |value| @droplet.root + value } end - def port - main_class =~ /^org\.springframework\.boot\.loader\.(?:[JW]ar|Properties)Launcher$/ ? 'SERVER_PORT=$PORT' : nil + def thin_root + @droplet.sandbox + 'repository' end end diff --git a/lib/java_buildpack/container/play_framework.rb b/lib/java_buildpack/container/play_framework.rb index 18679a4aa9..cdb3bd26b3 100644 --- a/lib/java_buildpack/container/play_framework.rb +++ b/lib/java_buildpack/container/play_framework.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -40,12 +41,12 @@ def detect # (see JavaBuildpack::Component::BaseComponent#compile) def compile - @delegate.compile if @delegate + @delegate&.compile end # (see JavaBuildpack::Component::BaseComponent#release) def release - @delegate.release if @delegate + @delegate&.release end private diff --git a/lib/java_buildpack/container/ratpack.rb b/lib/java_buildpack/container/ratpack.rb index b065e0dccb..8f6858f4f0 100644 --- a/lib/java_buildpack/container/ratpack.rb +++ b/lib/java_buildpack/container/ratpack.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -42,7 +43,7 @@ def id # (see JavaBuildpack::Container::DistZipLike#supports?) def supports? - start_script(root) && start_script(root).exist? && @ratpack_utils.is?(@application) + start_script(root)&.exist? && @ratpack_utils.is?(@application) end private diff --git a/lib/java_buildpack/container/spring_boot.rb b/lib/java_buildpack/container/spring_boot.rb index 3695bcb312..ada6321aac 100644 --- a/lib/java_buildpack/container/spring_boot.rb +++ b/lib/java_buildpack/container/spring_boot.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -35,7 +36,8 @@ def initialize(context) # (see JavaBuildpack::Container::DistZipLike#release) def release - "SERVER_PORT=$PORT #{super}" + @droplet.environment_variables.add_environment_variable 'SERVER_PORT', '$PORT' + super end protected @@ -47,7 +49,7 @@ def id # (see JavaBuildpack::Container::DistZipLike#supports?) def supports? - start_script(root) && start_script(root).exist? && @spring_boot_utils.is?(@application) + start_script(root)&.exist? && @spring_boot_utils.is?(@application) end private diff --git a/lib/java_buildpack/container/spring_boot_cli.rb b/lib/java_buildpack/container/spring_boot_cli.rb index 0bd4b9241d..54a78e4519 100644 --- a/lib/java_buildpack/container/spring_boot_cli.rb +++ b/lib/java_buildpack/container/spring_boot_cli.rb @@ -1,9 +1,10 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this candidate except in compliance with the License. +# you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 @@ -45,13 +46,17 @@ def compile # (see JavaBuildpack::Component::BaseComponent#release) def release + @droplet.environment_variables + .add_environment_variable('JAVA_OPTS', '$JAVA_OPTS') + .add_environment_variable('SERVER_PORT', '$PORT') + [ + @droplet.environment_variables.as_env_vars, @droplet.java_home.as_env_var, - @droplet.java_opts.as_env_var, - 'SERVER_PORT=$PORT', + 'exec', qualify_path(@droplet.sandbox + 'bin/spring', @droplet.root), 'run', - @droplet.additional_libraries.as_classpath, + classpath, relative_groovy_files ].flatten.compact.join(' ') end @@ -60,18 +65,26 @@ def release # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) def supports? - gf = JavaBuildpack::Util::GroovyUtils.groovy_files(@application) - gf.length > 0 && all_pogo_or_configuration(gf) && no_main_method(gf) && no_shebang(gf) && !web_inf? + gf = JavaBuildpack::Util::GroovyUtils.groovy_files(@application).reject { |file| logback_file? file } + !gf.empty? && all_pogo_or_configuration(gf) && no_main_method(gf) && no_shebang(gf) && !web_inf? end private + def classpath + ([@droplet.additional_libraries.as_classpath] + @droplet.root_libraries.qualified_paths).join(':') + end + def relative_groovy_files JavaBuildpack::Util::GroovyUtils.groovy_files(@application).map do |gf| gf.relative_path_from(@application.root) end end + def logback_file?(path) + %r{ch/qos/logback/.*\.groovy$} =~ path.to_s + end + def no_main_method(groovy_files) none?(groovy_files) { |file| JavaBuildpack::Util::GroovyUtils.main_method? file } end diff --git a/lib/java_buildpack/container/tomcat.rb b/lib/java_buildpack/container/tomcat.rb index 8b07276b5e..ac35237ec9 100644 --- a/lib/java_buildpack/container/tomcat.rb +++ b/lib/java_buildpack/container/tomcat.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2021 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,13 +17,16 @@ require 'java_buildpack/component/modular_component' require 'java_buildpack/container' +require 'java_buildpack/container/tomcat/tomcat_access_logging_support' +require 'java_buildpack/container/tomcat/tomcat_external_configuration' +require 'java_buildpack/container/tomcat/tomcat_geode_store' require 'java_buildpack/container/tomcat/tomcat_insight_support' require 'java_buildpack/container/tomcat/tomcat_instance' require 'java_buildpack/container/tomcat/tomcat_lifecycle_support' require 'java_buildpack/container/tomcat/tomcat_logging_support' -require 'java_buildpack/container/tomcat/tomcat_access_logging_support' require 'java_buildpack/container/tomcat/tomcat_redis_store' -require 'java_buildpack/container/tomcat/tomcat_gemfire_store' +require 'java_buildpack/container/tomcat/tomcat_setenv' +require 'java_buildpack/util/java_main_utils' module JavaBuildpack module Container @@ -34,11 +38,13 @@ class Tomcat < JavaBuildpack::Component::ModularComponent # (see JavaBuildpack::Component::ModularComponent#command) def command + @droplet.environment_variables.add_environment_variable 'JAVA_OPTS', '$JAVA_OPTS' @droplet.java_opts.add_system_property 'http.port', '$PORT' [ + @droplet.environment_variables.as_env_vars, @droplet.java_home.as_env_var, - @droplet.java_opts.as_env_var, + 'exec', "$PWD/#{(@droplet.sandbox + 'bin/catalina.sh').relative_path_from(@droplet.root)}", 'run' ].flatten.compact.join(' ') @@ -46,15 +52,26 @@ def command # (see JavaBuildpack::Component::ModularComponent#sub_components) def sub_components(context) - [ - TomcatInstance.new(sub_configuration_context(context, 'tomcat')), + instance = TomcatInstance.new(sub_configuration_context(context, 'tomcat')) + # pass Tomcat major version to geode_store so we can verify compatibility. + tomcat_version = instance.instance_variable_get(:@version)[0] + + components = [ + instance, + TomcatAccessLoggingSupport.new(sub_configuration_context(context, 'access_logging_support')), + TomcatGeodeStore.new(sub_configuration_context(context, 'geode_store'), tomcat_version), + TomcatInsightSupport.new(context), TomcatLifecycleSupport.new(sub_configuration_context(context, 'lifecycle_support')), TomcatLoggingSupport.new(sub_configuration_context(context, 'logging_support')), - TomcatAccessLoggingSupport.new(sub_configuration_context(context, 'access_logging_support')), TomcatRedisStore.new(sub_configuration_context(context, 'redis_store')), - TomcatGemfireStore.new(sub_configuration_context(context, 'gemfire_store')), - TomcatInsightSupport.new(context) + TomcatSetenv.new(context) ] + + tomcat_configuration = @configuration['tomcat'] + components << TomcatExternalConfiguration.new(sub_configuration_context(context, 'external_configuration')) if + tomcat_configuration['external_configuration_enabled'] + + components end # (see JavaBuildpack::Component::ModularComponent#supports?) diff --git a/lib/java_buildpack/container/tomcat/gemfire/gemfire_modules_tomcat7.rb b/lib/java_buildpack/container/tomcat/gemfire/gemfire_modules_tomcat7.rb deleted file mode 100644 index b851e94f4a..0000000000 --- a/lib/java_buildpack/container/tomcat/gemfire/gemfire_modules_tomcat7.rb +++ /dev/null @@ -1,53 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'java_buildpack/component/versioned_dependency_component' -require 'java_buildpack/container' -require 'java_buildpack/container/tomcat/tomcat_utils' -require 'java_buildpack/logging/logger_factory' - -module JavaBuildpack - module Container - - # Encapsulates the detect, compile, and release functionality for Tomcat Redis support. - class GemFireModulesTomcat7 < JavaBuildpack::Component::VersionedDependencyComponent - include JavaBuildpack::Container - - # (see JavaBuildpack::Component::BaseComponent#compile) - def compile - download_jar(jar_name, tomcat_lib) - end - - # (see JavaBuildpack::Component::BaseComponent#release) - def release - end - - protected - - # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) - def supports? - true - end - - private - - def jar_name - "gemfire-modules-tomcat7-#{@version}.jar" - end - end - - end -end diff --git a/lib/java_buildpack/container/tomcat/gemfire/gemfire_security.rb b/lib/java_buildpack/container/tomcat/gemfire/gemfire_security.rb deleted file mode 100644 index 341c72995c..0000000000 --- a/lib/java_buildpack/container/tomcat/gemfire/gemfire_security.rb +++ /dev/null @@ -1,53 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'java_buildpack/component/versioned_dependency_component' -require 'java_buildpack/container' -require 'java_buildpack/container/tomcat/tomcat_utils' -require 'java_buildpack/logging/logger_factory' - -module JavaBuildpack - module Container - - # Encapsulates the detect, compile, and release functionality for Tomcat Redis support. - class GemFireSecurity < JavaBuildpack::Component::VersionedDependencyComponent - include JavaBuildpack::Container - - # (see JavaBuildpack::Component::BaseComponent#compile) - def compile - download_jar(jar_name, tomcat_lib) - end - - # (see JavaBuildpack::Component::BaseComponent#release) - def release - end - - protected - - # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) - def supports? - true - end - - private - - def jar_name - "gemfire-security-#{@version}.jar" - end - end - - end -end diff --git a/lib/java_buildpack/container/tomcat/tomcat_access_logging_support.rb b/lib/java_buildpack/container/tomcat/tomcat_access_logging_support.rb index 303afecfd2..96e910da1f 100644 --- a/lib/java_buildpack/container/tomcat/tomcat_access_logging_support.rb +++ b/lib/java_buildpack/container/tomcat/tomcat_access_logging_support.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -44,7 +45,7 @@ def supports? private - KEY_ENABLED = 'access_logging'.freeze + KEY_ENABLED = 'access_logging' def jar_name "tomcat_access_logging_support-#{@version}.jar" diff --git a/lib/java_buildpack/container/tomcat/gemfire/gemfire_logging_api.rb b/lib/java_buildpack/container/tomcat/tomcat_external_configuration.rb similarity index 58% rename from lib/java_buildpack/container/tomcat/gemfire/gemfire_logging_api.rb rename to lib/java_buildpack/container/tomcat/tomcat_external_configuration.rb index bbf50a0cec..8cec475e6f 100644 --- a/lib/java_buildpack/container/tomcat/gemfire/gemfire_logging_api.rb +++ b/lib/java_buildpack/container/tomcat/tomcat_external_configuration.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,24 +17,32 @@ require 'java_buildpack/component/versioned_dependency_component' require 'java_buildpack/container' -require 'java_buildpack/container/tomcat/tomcat_utils' -require 'java_buildpack/logging/logger_factory' module JavaBuildpack module Container - # Encapsulates the detect, compile, and release functionality for Tomcat Redis support. - class GemFireLoggingApi < JavaBuildpack::Component::VersionedDependencyComponent + # Encapsulates the detect, compile, and release functionality for Tomcat external configuration. + class TomcatExternalConfiguration < JavaBuildpack::Component::VersionedDependencyComponent include JavaBuildpack::Container + # (see JavaBuildpack::Component::VersionedDependencyComponent#initialize) + def initialize(context, &version_validator) + JavaBuildpack::Util::Cache::InternetAvailability.instance.available( + true, 'The Tomcat External Configuration download location is always accessible' + ) do + super(context, &version_validator) + end + end + # (see JavaBuildpack::Component::BaseComponent#compile) def compile - download_jar(jar_name, tomcat_lib) + JavaBuildpack::Util::Cache::InternetAvailability.instance.available( + true, 'The Tomcat External Configuration download location is always accessible', &method(:download_tar) + ) end # (see JavaBuildpack::Component::BaseComponent#release) - def release - end + def release; end protected @@ -42,11 +51,6 @@ def supports? true end - private - - def jar_name - "slf4j-api-#{@version}.jar" - end end end diff --git a/lib/java_buildpack/container/tomcat/tomcat_gemfire_store.rb b/lib/java_buildpack/container/tomcat/tomcat_gemfire_store.rb deleted file mode 100644 index 2a728600b6..0000000000 --- a/lib/java_buildpack/container/tomcat/tomcat_gemfire_store.rb +++ /dev/null @@ -1,181 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'java_buildpack/component/versioned_dependency_component' -require 'java_buildpack/container' -require 'java_buildpack/container/tomcat/tomcat_utils' -require 'java_buildpack/container/tomcat/gemfire/gemfire' -require 'java_buildpack/container/tomcat/gemfire/gemfire_logging_api' -require 'java_buildpack/container/tomcat/gemfire/gemfire_logging' -require 'java_buildpack/container/tomcat/gemfire/gemfire_modules' -require 'java_buildpack/container/tomcat/gemfire/gemfire_modules_tomcat7' -require 'java_buildpack/container/tomcat/gemfire/gemfire_security' -require 'java_buildpack/container/tomcat/tomcat_utils' -require 'java_buildpack/logging/logger_factory' - -module JavaBuildpack - module Container - - # Encapsulates the detect, compile, and release functionality for Tomcat gemfire support. - class TomcatGemfireStore < JavaBuildpack::Component::ModularComponent - include JavaBuildpack::Container - - # (see JavaBuildpack::Component::ModularComponent#command) - def compile - super - return unless supports? - mutate_context - mutate_server - create_client_cache_config - end - - protected - - # (see JavaBuildpack::Component::ModularComponent#sub_components) - def sub_components(context) - [ - GemFire.new(sub_configuration_context(context, 'gemfire')), - GemFireLoggingApi.new(sub_configuration_context(context, 'gemfire_logging_api')), - GemFireLogging.new(sub_configuration_context(context, 'gemfire_logging')), - GemFireModules.new(sub_configuration_context(context, 'gemfire_modules')), - GemFireModulesTomcat7.new(sub_configuration_context(context, 'gemfire_modules_tomcat7')), - GemFireSecurity.new(sub_configuration_context(context, 'gemfire_security')) - ] - end - - # (see JavaBuildpack::Component::ModularComponent#command) - def command - return unless supports? - credentials = @application.services.find_service(FILTER)['credentials'] - @droplet.java_opts.add_system_property 'gemfire.security-username', credentials[KEY_USERNAME] - @droplet.java_opts.add_system_property 'gemfire.security-password', credentials[KEY_PASSWORD] - @droplet.java_opts.add_system_property 'gemfire.security-client-auth-init', - 'templates.security.UserPasswordAuthInit.create' - end - - # (see JavaBuildpack::Component::ModularComponent#supports?) - def supports? - @application.services.one_service? FILTER, KEY_LOCATORS, KEY_USERNAME, KEY_PASSWORD - end - - private - - FILTER = /session_replication/.freeze - - FLUSH_VALVE_CLASS_NAME = 'com.gopivotal.manager.SessionFlushValve'.freeze - - GEMFIRE_LISTENER_CLASS = 'com.gemstone.gemfire.modules.session.catalina.ClientServerCacheLifecycleListener'.freeze - - KEY_LOCATORS = 'locators'.freeze - - KEY_PASSWORD = 'password'.freeze - - KEY_USERNAME = 'username'.freeze - - PERSISTENT_MANAGER_CLASS = 'com.gemstone.gemfire.modules.session.catalina.Tomcat7DeltaSessionManager'.freeze - - private_constant :FILTER, :FLUSH_VALVE_CLASS_NAME, :KEY_LOCATORS, :KEY_PASSWORD, :KEY_USERNAME, - :PERSISTENT_MANAGER_CLASS, :GEMFIRE_LISTENER_CLASS - - def mutate_context - document = read_xml context_xml - context = REXML::XPath.match(document, '/Context').first - add_manager context - write_xml context_xml, document - end - - def mutate_server - document = read_xml server_xml - server = REXML::XPath.match(document, '/Server').first - - add_listener server - write_xml server_xml, document - end - - def create_client_cache_config - document = REXML::Document.new - document << REXML::XMLDecl.new('1.0', 'UTF-8') - document << REXML::DocType.new('client-cache PUBLIC', - '"-//GemStone Systems, Inc.//GemFire Declarative Caching 7.0//EN" ' \ - '"http://www.gemstone.com/dtd/cache7_0.dtd"') - add_client_pool document - - write_xml client_cache_xml_path, document - end - - def add_manager(context) - context.add_element 'Manager', - 'className' => PERSISTENT_MANAGER_CLASS, - 'enableDebugListener' => 'false', - 'enableGatewayReplication' => 'false', - 'enableLocalCache' => 'true', - 'enableCommitValve' => 'true', - 'preferDeserializedForm' => 'true', - 'regionAttributesId' => 'PARTITION_REDUNDANT_PERSISTENT_OVERFLOW', - 'regionName' => 'sessions' - end - - def add_listener(server) - server.add_element 'Listener', - 'className' => GEMFIRE_LISTENER_CLASS, - 'cache-xml-file' => client_cache_xml_name, - 'criticalHeapPercentage' => '0.0', - 'evictionHeapPercentage' => '80.0', - 'log-file' => gemfire_log_file, - 'statistic-archive-file' => gemfire_statistics_file, - 'statistic-sampling-enabled' => 'false' - end - - def add_client_pool(document) - client = document.add_element 'client-cache' - - pool = client.add_element 'pool', - 'name' => 'sessions', - 'subscription-enabled' => 'true' - apply_locators_to_cache_client pool - end - - def apply_locators_to_cache_client(pool) - credentials = @application.services.find_service(FILTER)['credentials'] - - credentials[KEY_LOCATORS].each do |locator| - captures = locator.match(/([\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3})\[([\d]{1,6})\]/) - pool.add_element 'locator', - 'host' => captures[1], - 'port' => captures[2] - end - end - - def client_cache_xml_path - @droplet.sandbox + 'conf' + client_cache_xml_name - end - - def client_cache_xml_name - 'cache-client.xml' - end - - def gemfire_log_file - 'gemfire_modules.log' - end - - def gemfire_statistics_file - 'gemfire_modules.gfs' - end - - end - - end -end diff --git a/lib/java_buildpack/container/tomcat/tomcat_geode_store.rb b/lib/java_buildpack/container/tomcat/tomcat_geode_store.rb new file mode 100644 index 0000000000..c3b8c69fd9 --- /dev/null +++ b/lib/java_buildpack/container/tomcat/tomcat_geode_store.rb @@ -0,0 +1,198 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2021 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/container' +require 'java_buildpack/container/tomcat/tomcat_utils' +require 'java_buildpack/logging/logger_factory' + +module JavaBuildpack + module Container + + # Encapsulates the detect, compile, and release functionality for Tomcat Tanzu GemFire for VMs support. + class TomcatGeodeStore < JavaBuildpack::Component::VersionedDependencyComponent + include JavaBuildpack::Container + + # Creates an instance. In addition to the functionality inherited from +VersionedDependencyComponent+ + # +@tomcat_version+ instance variable is exposed. + # + # @param [Hash] context a collection of utilities used by components + # @param [String] tomcat_version is the major version of tomcat + def initialize(context, tomcat_version) + super(context) + @tomcat_version = tomcat_version + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + return unless supports? + + download_tar(false, tomcat_lib, tar_name) + detect_geode_tomcat_version + mutate_context + mutate_server + create_cache_client_xml + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + return unless supports? + + @droplet.java_opts.add_system_property 'gemfire.security-client-auth-init', + 'io.pivotal.cloudcache.ClientAuthInitialize.create' + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.one_service? FILTER, KEY_LOCATORS, KEY_USERS + end + + private + + FILTER = /session-replication/.freeze + KEY_LOCATORS = 'locators' + KEY_USERS = 'users' + + REGION_ATTRIBUTES_ID = 'PARTITION_REDUNDANT_HEAP_LRU' + CACHE_CLIENT_LISTENER_CLASS_NAME = + 'org.apache.geode.modules.session.catalina.ClientServerCacheLifecycleListener' + SCHEMA_URL = 'http://geode.apache.org/schema/cache' + SCHEMA_INSTANCE_URL = 'http://www.w3.org/2001/XMLSchema-instance' + SCHEMA_LOCATION = 'http://geode.apache.org/schema/cache http://geode.apache.org/schema/cache/cache-1.0.xsd' + LOCATOR_REGEXP = Regexp.new('([^\\[]+)\\[([^\\]]+)\\]').freeze + + private_constant :FILTER, :KEY_LOCATORS, :KEY_USERS, :REGION_ATTRIBUTES_ID, + :CACHE_CLIENT_LISTENER_CLASS_NAME, :SCHEMA_URL, :SCHEMA_INSTANCE_URL, :SCHEMA_LOCATION, + :LOCATOR_REGEXP + + def add_client_cache(document) + client_cache = document.add_element 'client-cache', + 'xmlns' => SCHEMA_URL, + 'xmlns:xsi' => SCHEMA_INSTANCE_URL, + 'xsi:schemaLocation' => SCHEMA_LOCATION, + 'version' => '1.0' + + add_pool client_cache + end + + def add_listener(server) + server.add_element 'Listener', + 'className' => CACHE_CLIENT_LISTENER_CLASS_NAME + end + + def add_locators(pool) + service = @application.services.find_service FILTER, KEY_LOCATORS, KEY_USERS + service['credentials']['locators'].each do |locator| + match_info = LOCATOR_REGEXP.match(locator) + pool.add_element 'locator', + 'host' => match_info[1], + 'port' => match_info[2] + end + end + + def add_manager(context) + context.add_element 'Manager', + 'className' => @session_manager_classname, + 'enableLocalCache' => 'true', + 'regionAttributesId' => REGION_ATTRIBUTES_ID + end + + def add_pool(client_cache) + pool = client_cache.add_element 'pool', + 'name' => 'sessions', + 'subscription-enabled' => 'true' + add_locators pool + end + + def cache_client_xml + 'cache-client.xml' + end + + def cache_client_xml_path + @droplet.sandbox + 'conf' + cache_client_xml + end + + def create_cache_client_xml + document = REXML::Document.new + document << REXML::XMLDecl.new('1.0', 'UTF-8') + add_client_cache document + write_xml cache_client_xml_path, document + end + + def detect_geode_tomcat_version + geode_tomcat_version = nil + + geode_modules_tomcat_pattern = /ge.*-modules-tomcat(?[0-9]*).*.jar/.freeze + Dir.foreach(@droplet.sandbox + 'lib') do |file| + if geode_modules_tomcat_pattern.match(file) + unless geode_tomcat_version.nil? + raise('Multiple versions of geode-modules-tomcat jar found. ' \ + 'Please verify your geode_store tar only contains one geode-modules-tomcat jar.') + end + + geode_tomcat_version = geode_modules_tomcat_pattern.match(file).named_captures['version'] + end + end + + if geode_tomcat_version.nil? + raise('Geode Tomcat module not found. ' \ + 'Please verify your geode_store tar contains a geode-modules-tomcat jar.') + end + + puts " Detected Geode Tomcat #{geode_tomcat_version} module" + + # leave possibility for generic jar/session manager class that is compatible with all tomcat versions + if !geode_tomcat_version.empty? && geode_tomcat_version != @tomcat_version + puts " WARNING: Tomcat version #{@tomcat_version} " \ + "does not match Geode Tomcat #{geode_tomcat_version} module. " \ + 'If you encounter compatibility issues, please make sure these versions match.' + end + + @session_manager_classname = + "org.apache.geode.modules.session.catalina.Tomcat#{geode_tomcat_version}DeltaSessionManager" + end + + def mutate_context + puts ' Adding Geode-based Session Replication' + document = read_xml context_xml + context = REXML::XPath.match(document, '/Context').first + + add_manager context + + write_xml context_xml, document + end + + def mutate_server + document = read_xml server_xml + + server = REXML::XPath.match(document, '/Server').first + + add_listener server + + write_xml server_xml, document + end + + def tar_name + "geode-store-#{@version}.tar.gz" + end + + end + + end +end diff --git a/lib/java_buildpack/container/tomcat/tomcat_insight_support.rb b/lib/java_buildpack/container/tomcat/tomcat_insight_support.rb index 35aec690af..fe6bbe0a68 100644 --- a/lib/java_buildpack/container/tomcat/tomcat_insight_support.rb +++ b/lib/java_buildpack/container/tomcat/tomcat_insight_support.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,8 +29,7 @@ class TomcatInsightSupport < JavaBuildpack::Component::BaseComponent include JavaBuildpack::Container # (see JavaBuildpack::Component::BaseComponent#detect) - def detect - end + def detect; end # (see JavaBuildpack::Component::BaseComponent#compile) def compile @@ -37,8 +37,7 @@ def compile end # (see JavaBuildpack::Component::BaseComponent#release) - def release - end + def release; end private diff --git a/lib/java_buildpack/container/tomcat/tomcat_instance.rb b/lib/java_buildpack/container/tomcat/tomcat_instance.rb index d16abbec0e..a046c3533a 100644 --- a/lib/java_buildpack/container/tomcat/tomcat_instance.rb +++ b/lib/java_buildpack/container/tomcat/tomcat_instance.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -43,8 +44,7 @@ def compile end # (see JavaBuildpack::Component::BaseComponent#release) - def release - end + def release; end protected @@ -53,14 +53,19 @@ def supports? true end - private + TOMCAT8 = JavaBuildpack::Util::TokenizedVersion.new('8.0.0').freeze - TOMCAT_8 = JavaBuildpack::Util::TokenizedVersion.new('8.0.0').freeze + private_constant :TOMCAT8 - private_constant :TOMCAT_8 + # Checks whether Tomcat instance is Tomcat 7 compatible + def tomcat_7_compatible + @version < TOMCAT8 + end + + private def configure_jasper - return unless @version < TOMCAT_8 + return unless tomcat_7_compatible document = read_xml server_xml server = REXML::XPath.match(document, '/Server').first @@ -77,7 +82,7 @@ def configure_linking document = read_xml context_xml context = REXML::XPath.match(document, '/Context').first - if @version < TOMCAT_8 + if tomcat_7_compatible context.add_attribute 'allowLinking', true else context.add_element 'Resources', 'allowLinking' => true @@ -87,7 +92,7 @@ def configure_linking end def expand(file) - with_timing "Expanding Tomcat to #{@droplet.sandbox.relative_path_from(@droplet.root)}" do + with_timing "Expanding #{@component_name} to #{@droplet.sandbox.relative_path_from(@droplet.root)}" do FileUtils.mkdir_p @droplet.sandbox shell "tar xzf #{file.path} -C #{@droplet.sandbox} --strip 1 --exclude webapps 2>&1" @@ -98,7 +103,8 @@ def expand(file) end def root - tomcat_webapps + 'ROOT' + context_path = (@configuration['context_path'] || 'ROOT').sub(%r{^/}, '').gsub(%r{/}, '#') + tomcat_webapps + context_path end def tomcat_datasource_jar diff --git a/lib/java_buildpack/container/tomcat/tomcat_lifecycle_support.rb b/lib/java_buildpack/container/tomcat/tomcat_lifecycle_support.rb index 66cd8c94a7..c3c7c09c78 100644 --- a/lib/java_buildpack/container/tomcat/tomcat_lifecycle_support.rb +++ b/lib/java_buildpack/container/tomcat/tomcat_lifecycle_support.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -31,8 +32,7 @@ def compile end # (see JavaBuildpack::Component::BaseComponent#release) - def release - end + def release; end protected diff --git a/lib/java_buildpack/container/tomcat/tomcat_logging_support.rb b/lib/java_buildpack/container/tomcat/tomcat_logging_support.rb index 2680a61ce4..93c4c1e9ed 100644 --- a/lib/java_buildpack/container/tomcat/tomcat_logging_support.rb +++ b/lib/java_buildpack/container/tomcat/tomcat_logging_support.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,12 +25,12 @@ class TomcatLoggingSupport < JavaBuildpack::Component::VersionedDependencyCompon # (see JavaBuildpack::Component::BaseComponent#compile) def compile - download_jar(jar_name, endorsed) + download_jar(jar_name, bin) + @droplet.root_libraries << (bin + jar_name) end # (see JavaBuildpack::Component::BaseComponent#release) - def release - end + def release; end protected @@ -40,8 +41,8 @@ def supports? private - def endorsed - @droplet.sandbox + 'endorsed' + def bin + @droplet.sandbox + 'bin' end def jar_name diff --git a/lib/java_buildpack/container/tomcat/tomcat_redis_store.rb b/lib/java_buildpack/container/tomcat/tomcat_redis_store.rb index 5ecc1b5ca0..a60d1633c3 100644 --- a/lib/java_buildpack/container/tomcat/tomcat_redis_store.rb +++ b/lib/java_buildpack/container/tomcat/tomcat_redis_store.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -35,8 +36,7 @@ def compile end # (see JavaBuildpack::Component::BaseComponent#release) - def release - end + def release; end protected @@ -49,19 +49,19 @@ def supports? FILTER = /session-replication/.freeze - FLUSH_VALVE_CLASS_NAME = 'com.gopivotal.manager.SessionFlushValve'.freeze + FLUSH_VALVE_CLASS_NAME = 'com.gopivotal.manager.SessionFlushValve' - KEY_HOST_NAME = 'hostname'.freeze + KEY_HOST_NAME = 'hostname' - KEY_HOST = 'host'.freeze + KEY_HOST = 'host' - KEY_PASSWORD = 'password'.freeze + KEY_PASSWORD = 'password' - KEY_PORT = 'port'.freeze + KEY_PORT = 'port' - PERSISTENT_MANAGER_CLASS_NAME = 'org.apache.catalina.session.PersistentManager'.freeze + PERSISTENT_MANAGER_CLASS_NAME = 'org.apache.catalina.session.PersistentManager' - REDIS_STORE_CLASS_NAME = 'com.gopivotal.manager.redis.RedisStore'.freeze + REDIS_STORE_CLASS_NAME = 'com.gopivotal.manager.redis.RedisStore' private_constant :FILTER, :FLUSH_VALVE_CLASS_NAME, :KEY_HOST_NAME, :KEY_PASSWORD, :KEY_PORT, :PERSISTENT_MANAGER_CLASS_NAME, :REDIS_STORE_CLASS_NAME @@ -72,15 +72,16 @@ def add_manager(context) end def add_store(manager) - credentials = @application.services.find_service(FILTER)['credentials'] + credentials = @application.services.find_service(FILTER, [KEY_HOST_NAME, KEY_HOST], KEY_PORT, + KEY_PASSWORD)['credentials'] manager.add_element 'Store', - 'className' => REDIS_STORE_CLASS_NAME, - 'host' => credentials[KEY_HOST_NAME] || credentials[KEY_HOST], - 'port' => credentials[KEY_PORT], - 'database' => @configuration['database'], - 'password' => credentials[KEY_PASSWORD], - 'timeout' => @configuration['timeout'], + 'className' => REDIS_STORE_CLASS_NAME, + 'host' => credentials[KEY_HOST_NAME] || credentials[KEY_HOST], + 'port' => credentials[KEY_PORT], + 'database' => @configuration['database'], + 'password' => credentials[KEY_PASSWORD], + 'timeout' => @configuration['timeout'], 'connectionPoolSize' => @configuration['connection_pool_size'] end @@ -89,7 +90,7 @@ def add_valve(context) end def formatter - formatter = REXML::Formatters::Pretty.new(4) + formatter = REXML::Formatters::Pretty.new(4) formatter.compact = true formatter end @@ -102,7 +103,7 @@ def mutate_context puts ' Adding Redis-based Session Replication' document = read_xml context_xml - context = REXML::XPath.match(document, '/Context').first + context = REXML::XPath.match(document, '/Context').first add_valve context add_manager context diff --git a/lib/java_buildpack/container/tomcat/gemfire/gemfire_logging.rb b/lib/java_buildpack/container/tomcat/tomcat_setenv.rb similarity index 63% rename from lib/java_buildpack/container/tomcat/gemfire/gemfire_logging.rb rename to lib/java_buildpack/container/tomcat/tomcat_setenv.rb index b9c2298d02..a91e9e9824 100644 --- a/lib/java_buildpack/container/tomcat/gemfire/gemfire_logging.rb +++ b/lib/java_buildpack/container/tomcat/tomcat_setenv.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,25 +16,32 @@ # limitations under the License. require 'java_buildpack/component/versioned_dependency_component' -require 'java_buildpack/container' -require 'java_buildpack/container/tomcat/tomcat_utils' -require 'java_buildpack/logging/logger_factory' module JavaBuildpack module Container - # Encapsulates the detect, compile, and release functionality for Tomcat Redis support. - class GemFireLogging < JavaBuildpack::Component::VersionedDependencyComponent - include JavaBuildpack::Container + # Encapsulates the detect, compile, and release functionality for Tomcat logging support. + class TomcatSetenv < JavaBuildpack::Component::BaseComponent + + # (see JavaBuildpack::Component::BaseComponent#detect) + def detect + self.class.to_s.dash_case + end # (see JavaBuildpack::Component::BaseComponent#compile) def compile - download_jar(jar_name, tomcat_lib) + FileUtils.mkdir_p bin + setenv.open('w') do |f| + f.write <<~SH + #!/bin/sh + + CLASSPATH=$CLASSPATH:#{@droplet.root_libraries.qualified_paths.join(':')} + SH + end end # (see JavaBuildpack::Component::BaseComponent#release) - def release - end + def release; end protected @@ -44,9 +52,14 @@ def supports? private - def jar_name - "slf4j-jdk14-#{@version}.jar" + def bin + @droplet.sandbox + 'bin' + end + + def setenv + bin + 'setenv.sh' end + end end diff --git a/lib/java_buildpack/container/tomcat/tomcat_utils.rb b/lib/java_buildpack/container/tomcat/tomcat_utils.rb index f322e9616b..a7ee0ad48c 100644 --- a/lib/java_buildpack/container/tomcat/tomcat_utils.rb +++ b/lib/java_buildpack/container/tomcat/tomcat_utils.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/framework.rb b/lib/java_buildpack/framework.rb index 648f5b193b..43da670f97 100644 --- a/lib/java_buildpack/framework.rb +++ b/lib/java_buildpack/framework.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/framework/app_dynamics_agent.rb b/lib/java_buildpack/framework/app_dynamics_agent.rb index fe4fa5e361..9cd9778380 100644 --- a/lib/java_buildpack/framework/app_dynamics_agent.rb +++ b/lib/java_buildpack/framework/app_dynamics_agent.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,38 +16,60 @@ # limitations under the License. require 'fileutils' +require 'shellwords' require 'java_buildpack/component/versioned_dependency_component' require 'java_buildpack/framework' +require 'java_buildpack/util/external_config' module JavaBuildpack module Framework # Encapsulates the functionality for enabling zero-touch AppDynamics support. class AppDynamicsAgent < JavaBuildpack::Component::VersionedDependencyComponent + include JavaBuildpack::Util::ExternalConfig + + # Full list of configuration files that can be downloaded remotely + CONFIG_FILES = %w[logging/log4j2.xml logging/log4j.xml app-agent-config.xml controller-info.xml + service-endpoint.xml transactions.xml custom-interceptors.xml + custom-activity-correlation.xml].freeze + + # Prefix to be used with external configuration environment variable + CONFIG_PREFIX = 'APPD' + + def initialize(context) + super(context) + @logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger AppDynamicsAgent + end # (see JavaBuildpack::Component::BaseComponent#compile) def compile - download_zip false + download_zip(false, @droplet.sandbox, 'AppDynamics Agent') + + # acessor for resources dir through @droplet? + resources_dir = Pathname.new(File.expand_path('../../../resources', __dir__)).freeze + default_conf_dir = resources_dir + @droplet.component_id + 'defaults' + + copy_appd_default_configuration(default_conf_dir) + override_default_config_remote(&method(:save_cfg_file)) + override_default_config_local @droplet.copy_resources end # (see JavaBuildpack::Component::BaseComponent#release) def release - credentials = @application.services.find_service(FILTER)['credentials'] + credentials = @application.services.find_service(FILTER, 'host-name')['credentials'] java_opts = @droplet.java_opts - - java_opts - .add_javaagent(@droplet.sandbox + 'javaagent.jar') - .add_system_property('appdynamics.agent.applicationName', "'#{application_name}'") - .add_system_property('appdynamics.agent.tierName', "'#{tier_name(credentials)}'") - .add_system_property('appdynamics.agent.nodeName', - "$(expr \"$VCAP_APPLICATION\" : '.*instance_index[\": ]*\\([[:digit:]]*\\).*')") - - account_access_key(java_opts, credentials) - account_name(java_opts, credentials) - host_name(java_opts, credentials) - port(java_opts, credentials) - ssl_enabled(java_opts, credentials) + java_opts.add_javaagent(@droplet.sandbox + 'javaagent.jar') + + application_name java_opts, credentials + tier_name java_opts, credentials + node_name java_opts, credentials + account_access_key java_opts, credentials + account_name java_opts, credentials + host_name java_opts, credentials + port java_opts, credentials + ssl_enabled java_opts, credentials + unique_host_name java_opts end protected @@ -58,32 +81,42 @@ def supports? private - FILTER = /app-dynamics/.freeze + FILTER = /app-?dynamics/.freeze private_constant :FILTER - def tier_name(credentials) - credentials.key?('tier-name') ? credentials['tier-name'] : @configuration['default_tier_name'] - end + def application_name(java_opts, credentials) + name = escape(@application.details['application_name']) + name = @configuration['default_application_name'] if @configuration['default_application_name'] + name = escape(credentials['application-name']) if credentials['application-name'] - def application_name - @application.details['application_name'] + java_opts.add_system_property('appdynamics.agent.applicationName', name.to_s) end def account_access_key(java_opts, credentials) - account_access_key = credentials['account-access-key'] + account_access_key = credentials['account-access-key'] || credentials.dig('account-access-secret', 'secret') + account_access_key = escape(account_access_key) + java_opts.add_system_property 'appdynamics.agent.accountAccessKey', account_access_key if account_access_key end def account_name(java_opts, credentials) account_name = credentials['account-name'] - java_opts.add_system_property 'appdynamics.agent.accountName', account_name if account_name + java_opts.add_system_property 'appdynamics.agent.accountName', escape(account_name) if account_name end def host_name(java_opts, credentials) host_name = credentials['host-name'] - fail "'host-name' credential must be set" unless host_name - java_opts.add_system_property 'appdynamics.controller.hostName', host_name + raise "'host-name' credential must be set" unless host_name + + java_opts.add_system_property 'appdynamics.controller.hostName', escape(host_name) + end + + def node_name(java_opts, credentials) + name = @configuration['default_node_name'] + name = escape(credentials['node-name']) if credentials['node-name'] + + java_opts.add_system_property('appdynamics.agent.nodeName', name.to_s) end def port(java_opts, credentials) @@ -96,7 +129,65 @@ def ssl_enabled(java_opts, credentials) java_opts.add_system_property 'appdynamics.controller.ssl.enabled', ssl_enabled if ssl_enabled end - end + def tier_name(java_opts, credentials) + name = escape(@application.details['application_name']) + name = @configuration['default_tier_name'] if @configuration['default_tier_name'] + name = escape(credentials['tier-name']) if credentials['tier-name'] + + java_opts.add_system_property('appdynamics.agent.tierName', name.to_s) + end + def unique_host_name(java_opts) + name = escape(@application.details['application_name']) + name = @configuration['default_unique_host_name'] if @configuration['default_unique_host_name'] + + java_opts.add_system_property('appdynamics.agent.uniqueHostId', name.to_s) + end + + # Copy default configuration present in resources folder of app_dynamics_agent ver* directories present in sandbox + # + # @param [Pathname] default_conf_dir the 'defaults' directory present in app_dynamics_agent resources. + # @return [Void] + def copy_appd_default_configuration(default_conf_dir) + return unless default_conf_dir.exist? + + Dir.glob(@droplet.sandbox + 'ver*') do |target_directory| + FileUtils.cp_r "#{default_conf_dir}/.", target_directory + end + end + + # Check for configuration files locally. If found, copy to conf dir under each ver* dir + # @return [Void] + def override_default_config_local + return unless @application.environment['APPD_CONF_DIR'] + + app_conf_dir = @application.root + @application.environment['APPD_CONF_DIR'] + + raise "AppDynamics configuration source dir #{app_conf_dir} does not exist" unless Dir.exist?(app_conf_dir) + + @logger.info { "Copy override configuration files from #{app_conf_dir}" } + CONFIG_FILES.each do |conf_file| + conf_file_path = app_conf_dir + conf_file + + next unless File.file?(conf_file_path) + + save_cfg_file(conf_file_path, conf_file) + end + end + + def save_cfg_file(file, conf_file) + Dir.glob(@droplet.sandbox + 'ver*') do |target_directory| + FileUtils.cp_r file, target_directory + '/conf/' + conf_file + end + end + + def escape(value) + if /\$[({][^)}]+[)}]/ =~ value + "\\\"#{value}\\\"" + else + Shellwords.escape(value) + end + end + end end end diff --git a/lib/java_buildpack/framework/aspectj_weaver_agent.rb b/lib/java_buildpack/framework/aspectj_weaver_agent.rb new file mode 100644 index 0000000000..11a4641453 --- /dev/null +++ b/lib/java_buildpack/framework/aspectj_weaver_agent.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component/base_component' +require 'java_buildpack/framework' +require 'java_buildpack/util/dash_case' +require 'java_buildpack/util/jar_finder' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for contributing AspectJ Runtime Weaving configuration an application. + class AspectjWeaverAgent < JavaBuildpack::Component::BaseComponent + + # Creates an instance. In addition to the functionality inherited from +BaseComponent+, +@version+ and +@uri+ + # instance variables are exposed. + # + # @param [Hash] context a collection of utilities used by components + def initialize(context) + super(context) + + @jar_finder = JavaBuildpack::Util::JarFinder.new(/.*aspectjweaver-(\d.*)\.jar/) + end + + # (see JavaBuildpack::Component::BaseComponent#detect) + def detect + supports? ? "#{self.class.to_s.dash_case}=#{@jar_finder.version(@application)}" : nil + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + puts "#{'----->'.red.bold} #{'AspectJ'.blue.bold} #{version.to_s.blue} Runtime Weaving enabled" + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + @droplet.java_opts.add_javaagent @jar_finder.is?(@application) + end + + private + + def aop_xml_exist? + (@application.root + 'BOOT-INF/classes/META-INF/aop.xml').exist? || + (@application.root + 'BOOT-INF/classes/org/aspectj/aop.xml').exist? || + (@application.root + 'META-INF/aop.xml').exist? || + (@application.root + 'org/aspectj/aop.xml').exist? + end + + def enabled? + @configuration['enabled'] + end + + def supports? + enabled? && @jar_finder.is?(@application) && aop_xml_exist? + end + + def version + @jar_finder.version(@application) + end + + end + + end +end diff --git a/lib/java_buildpack/framework/azure_application_insights_agent.rb b/lib/java_buildpack/framework/azure_application_insights_agent.rb new file mode 100644 index 0000000000..7ee630fe8f --- /dev/null +++ b/lib/java_buildpack/framework/azure_application_insights_agent.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for enabling Azure Application Insights support. + class AzureApplicationInsightsAgent < JavaBuildpack::Component::VersionedDependencyComponent + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_jar + @droplet.copy_resources + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + credentials = @application.services.find_service(FILTER, [CONNECTION_STRING, + INSTRUMENTATION_KEY])['credentials'] + + if credentials.key?(CONNECTION_STRING) + @droplet.java_opts.add_system_property('applicationinsights.connection.string', + credentials[CONNECTION_STRING]) + end + if credentials.key?(INSTRUMENTATION_KEY) + @droplet.java_opts.add_system_property('APPLICATION_INSIGHTS_IKEY', + credentials[INSTRUMENTATION_KEY]) + # add environment variable for compatibility with agent version 3.x + # this triggers a warning message to switch to connection string + @droplet.environment_variables.add_environment_variable('APPINSIGHTS_INSTRUMENTATIONKEY', + credentials[INSTRUMENTATION_KEY]) + end + @droplet.java_opts.add_javaagent(@droplet.sandbox + jar_name) + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.one_service?(FILTER, [CONNECTION_STRING, INSTRUMENTATION_KEY]) + end + + FILTER = /azure-application-insights/.freeze + + CONNECTION_STRING = 'connection_string' + INSTRUMENTATION_KEY = 'instrumentation_key' + + private_constant :FILTER, :CONNECTION_STRING, :INSTRUMENTATION_KEY + + end + + end +end diff --git a/lib/java_buildpack/framework/checkmarx_iast_agent.rb b/lib/java_buildpack/framework/checkmarx_iast_agent.rb new file mode 100644 index 0000000000..219826edd7 --- /dev/null +++ b/lib/java_buildpack/framework/checkmarx_iast_agent.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for running with Checkmarx IAST Agent + class CheckmarxIastAgent < JavaBuildpack::Component::VersionedDependencyComponent + include JavaBuildpack::Util + + # Creates an instance. In addition to the functionality inherited from +BaseComponent+, +@version+ and +@uri+ + # instance variables are exposed. + # + # @param [Hash] context a collection of utilities used by components + def initialize(context) + @application = context[:application] + @component_name = self.class.to_s.space_case + @configuration = context[:configuration] + @droplet = context[:droplet] + + if supports? + @version = '' + @uri = @application.services.find_service(FILTER, 'server')['credentials']['server'].chomp + + '/iast/compilation/download/JAVA' + end + + @logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger CheckmarxIastAgent + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + JavaBuildpack::Util::Cache::InternetAvailability.instance.available( + true, 'The Checkmarx IAST download location is always accessible' + ) do + download_zip(false) + end + + # Disable cache (no point, when running in a container) + File.open(@droplet.sandbox + 'cx_agent.override.properties', 'a') do |f| + f.write("\nenableWeavedClassCache=false\n") + end + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + # Default cxAppTag to application name if not set as an env var + app_tag = ENV.fetch('cxAppTag', nil) || application_name + # Default team to CxServer if not set as env var + team = ENV.fetch('cxTeam', nil) || 'CxServer' + + @droplet.java_opts + .add_javaagent(@droplet.sandbox + 'cx-launcher.jar') + .add_preformatted_options('-Xverify:none') + .add_system_property('cx.logToConsole', 'true') + .add_system_property('cx.appName', application_name) + .add_system_property('cxAppTag', app_tag) + .add_system_property('cxTeam', team) + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.find_service(FILTER, 'server') + end + + private + + FILTER = /^checkmarx-iast$/.freeze + + private_constant :FILTER + + def application_name + @application.details['application_name'] || 'ROOT' + end + + end + + end + +end diff --git a/lib/java_buildpack/framework/play_framework_auto_reconfiguration.rb b/lib/java_buildpack/framework/client_certificate_mapper.rb similarity index 65% rename from lib/java_buildpack/framework/play_framework_auto_reconfiguration.rb rename to lib/java_buildpack/framework/client_certificate_mapper.rb index f2768ec231..599d3a6ba2 100644 --- a/lib/java_buildpack/framework/play_framework_auto_reconfiguration.rb +++ b/lib/java_buildpack/framework/client_certificate_mapper.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,15 +17,12 @@ require 'java_buildpack/component/versioned_dependency_component' require 'java_buildpack/framework' -require 'java_buildpack/util/play/factory' module JavaBuildpack module Framework - # Encapsulates the functionality for enabling cloud auto-reconfiguration in Play applications. Note that Spring - # auto- reconfiguration is covered by the SpringAutoReconfiguration framework. The reconfiguration performed here is - # to override Play application configuration to bind a Play application to cloud resources. - class PlayFrameworkAutoReconfiguration < JavaBuildpack::Component::VersionedDependencyComponent + # Encapsulates the functionality for contributing an mTLS client certificate mapper to the application. + class ClientCertificateMapper < JavaBuildpack::Component::VersionedDependencyComponent # (see JavaBuildpack::Component::BaseComponent#compile) def compile @@ -41,7 +39,7 @@ def release # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) def supports? - @configuration['enabled'] && JavaBuildpack::Util::Play::Factory.create(@droplet) + true end end diff --git a/lib/java_buildpack/container/tomcat/gemfire/gemfire_modules.rb b/lib/java_buildpack/framework/container_customizer.rb similarity index 59% rename from lib/java_buildpack/container/tomcat/gemfire/gemfire_modules.rb rename to lib/java_buildpack/framework/container_customizer.rb index 1a55ae08d4..044079877e 100644 --- a/lib/java_buildpack/container/tomcat/gemfire/gemfire_modules.rb +++ b/lib/java_buildpack/framework/container_customizer.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,38 +16,43 @@ # limitations under the License. require 'java_buildpack/component/versioned_dependency_component' -require 'java_buildpack/container' -require 'java_buildpack/container/tomcat/tomcat_utils' -require 'java_buildpack/logging/logger_factory' +require 'java_buildpack/framework' +require 'java_buildpack/util/spring_boot_utils' module JavaBuildpack - module Container + module Framework - # Encapsulates the detect, compile, and release functionality for Tomcat Redis support. - class GemFireModules < JavaBuildpack::Component::VersionedDependencyComponent - include JavaBuildpack::Container + # Encapsulates the functionality for customizing a Spring Boot container. + class ContainerCustomizer < JavaBuildpack::Component::VersionedDependencyComponent # (see JavaBuildpack::Component::BaseComponent#compile) def compile - download_jar(jar_name, tomcat_lib) + download_jar + @droplet.additional_libraries << (@droplet.sandbox + jar_name) end # (see JavaBuildpack::Component::BaseComponent#release) def release + @droplet.additional_libraries << (@droplet.sandbox + jar_name) end protected # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) def supports? - true + spring_boot? && war? end private - def jar_name - "gemfire-modules-#{@version}.jar" + def spring_boot? + JavaBuildpack::Util::SpringBootUtils.new.is?(@application) + end + + def war? + (@droplet.root + 'WEB-INF/lib').exist? end + end end diff --git a/lib/java_buildpack/framework/container_security_provider.rb b/lib/java_buildpack/framework/container_security_provider.rb new file mode 100644 index 0000000000..fd98af2cdd --- /dev/null +++ b/lib/java_buildpack/framework/container_security_provider.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for contributing a container-based security provider to an application. + class ContainerSecurityProvider < JavaBuildpack::Component::VersionedDependencyComponent + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_jar + @droplet.security_providers.insert 1, 'org.cloudfoundry.security.CloudFoundryContainerProvider' + @droplet.root_libraries << (@droplet.sandbox + jar_name) if @droplet.java_home.java_9_or_later? + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + if @droplet.java_home.java_9_or_later? + @droplet.root_libraries << (@droplet.sandbox + jar_name) + else + @droplet.extension_directories << @droplet.sandbox + end + + unless key_manager_enabled.nil? + @droplet.java_opts.add_system_property 'org.cloudfoundry.security.keymanager.enabled', key_manager_enabled + end + + return if trust_manager_enabled.nil? + + @droplet.java_opts.add_system_property 'org.cloudfoundry.security.trustmanager.enabled', trust_manager_enabled + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + true + end + + private + + def key_manager_enabled + @configuration['key_manager_enabled'] + end + + def trust_manager_enabled + @configuration['trust_manager_enabled'] + end + + end + + end +end diff --git a/lib/java_buildpack/framework/contrast_security_agent.rb b/lib/java_buildpack/framework/contrast_security_agent.rb new file mode 100644 index 0000000000..9d5d3fb63c --- /dev/null +++ b/lib/java_buildpack/framework/contrast_security_agent.rb @@ -0,0 +1,137 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' +require 'java_buildpack/util/qualify_path' +require 'rexml/document' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for running the Contrast Security Agent support. + class ContrastSecurityAgent < JavaBuildpack::Component::VersionedDependencyComponent + include JavaBuildpack::Util + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_jar + @droplet.copy_resources + + write_configuration @application.services.find_service(FILTER, API_KEY, SERVICE_KEY, TEAMSERVER_URL, + USERNAME)['credentials'] + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + @droplet.java_opts.add_system_property('contrast.override.appname', application_name) unless appname_exist? + + @droplet.java_opts + .add_system_property('contrast.dir', '$TMPDIR') + .add_preformatted_options("-javaagent:#{qualify_path(@droplet.sandbox + jar_name, @droplet.root)}=" \ + "#{qualify_path(contrast_config, @droplet.root)}") + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#jar_name) + def jar_name + @version < INFLECTION_VERSION ? "contrast-engine-#{short_version}.jar" : "java-agent-#{short_version}.jar" + end + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.one_service? FILTER, API_KEY, SERVICE_KEY, TEAMSERVER_URL, USERNAME + end + + private + + API_KEY = 'api_key' + + FILTER = 'contrast-security' + + INFLECTION_VERSION = JavaBuildpack::Util::TokenizedVersion.new('3.4.3').freeze + + PLUGIN_PACKAGE = 'com.aspectsecurity.contrast.runtime.agent.plugins' + + SERVICE_KEY = 'service_key' + + TEAMSERVER_URL = 'teamserver_url' + + USERNAME = 'username' + + private_constant :API_KEY, :FILTER, :INFLECTION_VERSION, :PLUGIN_PACKAGE, :SERVICE_KEY, :TEAMSERVER_URL, + :USERNAME + + def add_contrast(doc, credentials) + contrast = doc.add_element('contrast') + (contrast.add_element 'id').add_text('default') + (contrast.add_element 'global-key').add_text(credentials[API_KEY]) + (contrast.add_element 'url').add_text("#{credentials[TEAMSERVER_URL]}/Contrast/s/") + (contrast.add_element 'results-mode').add_text('never') + + add_user contrast, credentials + add_plugins contrast + end + + def add_plugins(contrast) + plugin_group = contrast.add_element('plugins') + + (plugin_group.add_element 'plugin').add_text("#{PLUGIN_PACKAGE}.security.SecurityPlugin") + (plugin_group.add_element 'plugin').add_text("#{PLUGIN_PACKAGE}.architecture.ArchitecturePlugin") + (plugin_group.add_element 'plugin').add_text("#{PLUGIN_PACKAGE}.appupdater.ApplicationUpdatePlugin") + (plugin_group.add_element 'plugin').add_text("#{PLUGIN_PACKAGE}.sitemap.SitemapPlugin") + (plugin_group.add_element 'plugin').add_text("#{PLUGIN_PACKAGE}.frameworks.FrameworkSupportPlugin") + (plugin_group.add_element 'plugin').add_text("#{PLUGIN_PACKAGE}.http.HttpPlugin") + end + + def add_user(contrast, credentials) + user = contrast.add_element('user') + (user.add_element 'id').add_text(credentials[USERNAME]) + (user.add_element 'key').add_text(credentials[SERVICE_KEY]) + end + + def application_name + @application.details['application_name'] || 'ROOT' + end + + def appname_exist? + @droplet.java_opts.any? { |java_opt| java_opt =~ /contrast.override.appname/ } + end + + def contrast_config + @droplet.sandbox + 'contrast.config' + end + + def short_version + "#{@version[0]}.#{@version[1]}.#{@version[2]}" + end + + def write_configuration(credentials) + doc = REXML::Document.new + + add_contrast doc, credentials + + contrast_config.open(File::CREAT | File::WRONLY) { |f| f.write(doc) } + end + + end + + end + +end diff --git a/lib/java_buildpack/framework/datadog_javaagent.rb b/lib/java_buildpack/framework/datadog_javaagent.rb new file mode 100644 index 0000000000..a2e2d11f33 --- /dev/null +++ b/lib/java_buildpack/framework/datadog_javaagent.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2021 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for enabling zero-touch Elastic APM support. + class DatadogJavaagent < JavaBuildpack::Component::VersionedDependencyComponent + include JavaBuildpack::Util + + def initialize(context) + super(context) + @logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger DatadogJavaagent + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + @logger.error 'Datadog Buildpack is required, but not found' unless datadog_buildpack? + + return unless datadog_buildpack? + + download_jar + fix_class_count + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + return unless datadog_buildpack? + + java_opts = @droplet.java_opts + java_opts.add_javaagent(@droplet.sandbox + jar_name) + + unless @application.environment.key?('DD_SERVICE') + app_name = @configuration['default_application_name'] || @application.details['application_name'] + java_opts.add_system_property('dd.service', "\\\"#{app_name}\\\"") + end + + version = @application.environment['DD_VERSION'] || @configuration['default_application_version'] || + @application.details['application_version'] + java_opts.add_system_property('dd.version', version) + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + api_key_defined = @application.environment.key?('DD_API_KEY') && !@application.environment['DD_API_KEY'].empty? + apm_disabled = @application.environment['DD_APM_ENABLED'] == 'false' + (api_key_defined && !apm_disabled) + end + + # determins if the datadog buildpack is present + def datadog_buildpack? + File.exist?(File.join(@droplet.root, '.datadog')) || File.exist?(File.join(@droplet.root, 'datadog')) + end + + # fixes issue where some classes are not counted by adding shadow class files + def fix_class_count + cnt = classdata_count(@droplet.sandbox + jar_name) + zipdir = "#{@droplet.sandbox}/datadog_fakeclasses" + zipfile = "#{@droplet.sandbox}/datadog_fakeclasses.jar" + + File.delete(zipfile) if File.exist? zipfile + FileUtils.rm_rf(zipdir) + FileUtils.mkdir_p(zipdir) + + 1.upto(cnt) do |i| + File.open("#{zipdir}/#{i}.class", 'w') do |f| + File.write(f, i.to_s) + end + end + + `cd #{zipdir} && zip -r #{zipfile} .` + FileUtils.rm_rf(zipdir) + end + + # count hidden class files in the agent JAR + def classdata_count(archive) + `unzip -l #{archive} | grep '\\(\\.classdata\\)$' | wc -l`.to_i + end + end + end +end diff --git a/lib/java_buildpack/framework/debug.rb b/lib/java_buildpack/framework/debug.rb new file mode 100644 index 0000000000..fe13734b2a --- /dev/null +++ b/lib/java_buildpack/framework/debug.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component/base_component' +require 'java_buildpack/framework' +require 'java_buildpack/util/dash_case' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for contributing Java debug options to an application. + class Debug < JavaBuildpack::Component::BaseComponent + + # (see JavaBuildpack::Component::BaseComponent#detect) + def detect + enabled? ? "#{self.class.to_s.dash_case}=#{port}" : nil + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + puts "#{'----->'.red.bold} #{'Debugging'.blue.bold} enabled on port #{port}#{suspend_message}" + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + @droplet.java_opts.add_preformatted_options debug + end + + private + + def debug + "-agentlib:jdwp=transport=dt_socket,server=y,address=#{port},suspend=#{suspend}" + end + + def enabled? + @configuration['enabled'] + end + + def port + @configuration['port'] || 8000 + end + + def suspend + @configuration['suspend'] ? 'y' : 'n' + end + + def suspend_message + ', suspended on start' if @configuration['suspend'] + end + + end + + end +end diff --git a/lib/java_buildpack/framework/dynatrace_one_agent.rb b/lib/java_buildpack/framework/dynatrace_one_agent.rb new file mode 100644 index 0000000000..b4a9070575 --- /dev/null +++ b/lib/java_buildpack/framework/dynatrace_one_agent.rb @@ -0,0 +1,232 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' +require 'java_buildpack/util/cache/internet_availability' +require 'java_buildpack/util/to_b' +require 'json' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for enabling zero-touch Dynatrace SaaS/Managed support. + class DynatraceOneAgent < JavaBuildpack::Component::VersionedDependencyComponent + + # Creates an instance + # + # @param [Hash] context a collection of utilities used the component + def initialize(context) + @application = context[:application] + @component_name = self.class.to_s.space_case + @configuration = context[:configuration] + @droplet = context[:droplet] + + @version, @uri = agent_download_url if supports? + @logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger DynatraceOneAgent + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + JavaBuildpack::Util::Cache::InternetAvailability.instance.available( + true, 'The Dynatrace One Agent download location is always accessible' + ) do + download(@version, @uri) { |file| expand file } + end + + @droplet.copy_resources + rescue StandardError => e + raise unless skip_errors? + + @logger.error { "Dynatrace OneAgent download failed: #{e}" } + @logger.warn { "Agent injection disabled because of #{SKIP_ERRORS} credential is set to true!" } + FileUtils.mkdir_p(error_file.parent) + File.write(error_file, e.to_s) + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + if error_file.exist? + @logger.warn { "Dynatrace OneAgent injection disabled due to download error: #{File.read(error_file)}" } + return + end + + manifest = agent_manifest + + environment_variables = @droplet.environment_variables + environment_variables.add_environment_variable(LD_PRELOAD, agent_path(manifest)) + + File.delete(@droplet.sandbox + 'agent/dt_fips_disabled.flag') if enable_fips? + + dynatrace_environment_variables(manifest) + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.one_service? FILTER, APITOKEN, ENVIRONMENTID + end + + private + + ADDTECHNOLOGIES = 'addtechnologies' + + APIURL = 'apiurl' + + APITOKEN = 'apitoken' + + ENABLE_FIPS = 'enablefips' + + DT_APPLICATION_ID = 'DT_APPLICATIONID' + + DT_CONNECTION_POINT = 'DT_CONNECTION_POINT' + + DT_TENANT = 'DT_TENANT' + + DT_TENANTTOKEN = 'DT_TENANTTOKEN' + + DT_LOGSTREAM = 'DT_LOGSTREAM' + + DT_NETWORK_ZONE = 'DT_NETWORK_ZONE' + + LD_PRELOAD = 'LD_PRELOAD' + + ENVIRONMENTID = 'environmentid' + + FILTER = /dynatrace/.freeze + + NETWORKZONE = 'networkzone' + + SKIP_ERRORS = 'skiperrors' + + private_constant :ADDTECHNOLOGIES, :APIURL, :APITOKEN, :ENABLE_FIPS, :DT_APPLICATION_ID, :DT_CONNECTION_POINT, + :DT_NETWORK_ZONE, :DT_LOGSTREAM, :DT_TENANT, :DT_TENANTTOKEN, :LD_PRELOAD, :ENVIRONMENTID, + :FILTER, :NETWORKZONE, :SKIP_ERRORS + + def agent_download_url + download_uri = "#{api_base_url(credentials)}" \ + "/v1/deployment/installer/agent/unix/paas/latest?#{technologies(credentials)}" \ + '&bitness=64' \ + "&Api-Token=#{credentials[APITOKEN]}" + + download_uri += "&networkZone=#{networkzone}" if networkzone? + + ['latest', download_uri] + end + + def technologies(credentials) + code_modules = 'include=java' + if credentials.key?(ADDTECHNOLOGIES) + credentials[ADDTECHNOLOGIES].split(',').each do |tech| + code_modules += "&include=#{tech}" + end + end + code_modules + end + + def agent_manifest + JSON.parse(File.read(@droplet.sandbox + 'manifest.json')) + end + + def agent_path(manifest) + technologies = manifest['technologies'] + java_binaries = technologies['process']['linux-x86-64'] + loader = java_binaries.find { |bin| bin['binarytype'] == 'primary' } + @droplet.sandbox + loader['path'] + end + + def api_base_url(credentials) + credentials[APIURL] || "https://#{credentials[ENVIRONMENTID]}.live.dynatrace.com/api" + end + + def application_id + @application.details['application_name'] + end + + def application_id? + @application.environment.key?(DT_APPLICATION_ID) + end + + def credentials + @application.services.find_service(FILTER, APITOKEN, ENVIRONMENTID)['credentials'] + end + + def dynatrace_environment_variables(manifest) + environment_variables = @droplet.environment_variables + + environment_variables + .add_environment_variable(DT_TENANT, credentials[ENVIRONMENTID]) + .add_environment_variable(DT_TENANTTOKEN, tenanttoken(manifest)) + .add_environment_variable(DT_CONNECTION_POINT, endpoints(manifest)) + + environment_variables.add_environment_variable(DT_APPLICATION_ID, application_id) unless application_id? + environment_variables.add_environment_variable(DT_NETWORK_ZONE, credentials[NETWORKZONE]) if networkzone? + environment_variables.add_environment_variable(DT_LOGSTREAM, 'stdout') unless logstream? + end + + def endpoints(manifest) + "\"#{manifest['communicationEndpoints'].join(';')}\"" + end + + def error_file + @droplet.sandbox + 'dynatrace_download_error' + end + + def expand(file) + with_timing "Expanding Dynatrace OneAgent to #{@droplet.sandbox.relative_path_from(@droplet.root)}" do + Dir.mktmpdir do |root| + root_path = Pathname.new(root) + shell "unzip -qq #{file.path} -d #{root_path} 2>&1" + unpack_agent root_path + end + end + end + + def networkzone + credentials[NETWORKZONE] + end + + def networkzone? + credentials.key?(NETWORKZONE) + end + + def logstream? + @application.environment.key?(DT_LOGSTREAM) + end + + def skip_errors? + credentials[SKIP_ERRORS] == 'true' + end + + def enable_fips? + credentials[ENABLE_FIPS] == 'true' + end + + def tenanttoken(manifest) + manifest['tenantToken'] + end + + def unpack_agent(root) + FileUtils.mkdir_p(@droplet.sandbox) + FileUtils.mv(root + 'agent', @droplet.sandbox) + FileUtils.mv(root + 'manifest.json', @droplet.sandbox) + end + end + end +end diff --git a/lib/java_buildpack/framework/elastic_apm_agent.rb b/lib/java_buildpack/framework/elastic_apm_agent.rb new file mode 100644 index 0000000000..5ff72b8a0f --- /dev/null +++ b/lib/java_buildpack/framework/elastic_apm_agent.rb @@ -0,0 +1,104 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'shellwords' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for enabling zero-touch Elastic APM support. + class ElasticApmAgent < JavaBuildpack::Component::VersionedDependencyComponent + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_jar + end + + # Modifies the application's runtime configuration. The component is expected to transform members of the + # +context+ # (e.g. +@java_home+, +@java_opts+, etc.) in whatever way is necessary to support the function of the + # component. + # + # Container components are also expected to create the command required to run the application. These components + # are expected to read the +context+ values and take them into account when creating the command. + # + # @return [void, String] components other than containers and JREs are not expected to return any value. + # Container and JRE components are expected to return a command required to run the + # application. + # (see JavaBuildpack::Component::BaseComponent#release) + def release + credentials = @application.services.find_service(FILTER, [SERVER_URLS, SECRET_TOKEN])['credentials'] + java_opts = @droplet.java_opts + configuration = {} + + apply_configuration(credentials, configuration) + apply_user_configuration(credentials, configuration) + write_java_opts(java_opts, configuration) + + java_opts.add_javaagent(@droplet.sandbox + jar_name) + .add_system_property('elastic.apm.home', @droplet.sandbox) + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.one_service? FILTER, [SERVER_URLS, SECRET_TOKEN] + end + + private + + FILTER = /elastic-apm/.freeze + + BASE_KEY = 'elastic.apm.' + + SERVER_URLS = 'server_urls' + + SECRET_TOKEN = 'secret_token' + + SERVICE_NAME = 'service_name' + + private_constant :FILTER, :SERVER_URLS, :BASE_KEY, :SECRET_TOKEN + + def apply_configuration(credentials, configuration) + configuration['log_file_name'] = 'STDOUT' + configuration[SERVER_URLS] = credentials[SERVER_URLS] + configuration[SECRET_TOKEN] = credentials[SECRET_TOKEN] + configuration[SERVICE_NAME] = @application.details['application_name'] + end + + def apply_user_configuration(credentials, configuration) + credentials.each do |key, value| + configuration[key] = value + end + end + + def write_java_opts(java_opts, configuration) + configuration.each do |key, value| + if /\$[({][^)}]+[)}]/ =~ value.to_s + # we need \" because this is a system property which ends up inside `JAVA_OPTS` which is already quoted + java_opts.add_system_property("elastic.apm.#{key}", "\\\"#{value}\\\"") + else + java_opts.add_system_property("elastic.apm.#{key}", Shellwords.escape(value)) + end + end + end + + end + end +end diff --git a/lib/java_buildpack/framework/google_stackdriver_debugger.rb b/lib/java_buildpack/framework/google_stackdriver_debugger.rb new file mode 100644 index 0000000000..e8e519cbd8 --- /dev/null +++ b/lib/java_buildpack/framework/google_stackdriver_debugger.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'base64' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for enabling zero-touch Google Cloud Debugger support. + class GoogleStackdriverDebugger < JavaBuildpack::Component::VersionedDependencyComponent + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_tar false + + credentials = @application.services.find_service(FILTER, PRIVATE_KEY_DATA)['credentials'] + write_json_file credentials[PRIVATE_KEY_DATA] + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + java_opts = @droplet.java_opts + + java_opts + .add_agentpath_with_props(@droplet.sandbox + 'cdbg_java_agent.so', '--logtostderr' => 1) + .add_system_property('com.google.cdbg.auth.serviceaccount.enable', true) + .add_system_property('com.google.cdbg.auth.serviceaccount.jsonfile', json_file) + .add_system_property('com.google.cdbg.module', application_name) + .add_system_property('com.google.cdbg.version', application_version) + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.one_service? FILTER, PRIVATE_KEY_DATA + end + + FILTER = /google-stackdriver-debugger/.freeze + + PRIVATE_KEY_DATA = 'PrivateKeyData' + + private_constant :FILTER, :PRIVATE_KEY_DATA + + private + + def application_name + @configuration['application_name'] || @application.details['application_name'] + end + + def application_version + @configuration['application_version'] || @application.details['application_version'] + end + + def json_file + @droplet.sandbox + 'svc.json' + end + + def write_json_file(json_file_data) + FileUtils.mkdir_p json_file.parent + json_file.open(File::CREAT | File::WRONLY) do |f| + f.write "#{Base64.decode64 json_file_data}\n" + f.sync + f + end + end + + end + + end +end diff --git a/lib/java_buildpack/framework/google_stackdriver_profiler.rb b/lib/java_buildpack/framework/google_stackdriver_profiler.rb new file mode 100644 index 0000000000..8be7fa6f7f --- /dev/null +++ b/lib/java_buildpack/framework/google_stackdriver_profiler.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'base64' +require 'json' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for enabling zero-touch Google Cloud Profiler support. + class GoogleStackdriverProfiler < JavaBuildpack::Component::VersionedDependencyComponent + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_tar false + + write_json_file private_key_data + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + java_opts = @droplet.java_opts + + java_opts + .add_agentpath_with_props(@droplet.sandbox + 'profiler_java_agent.so', + '--logtostderr' => 1, + '-cprof_project_id' => project_id, + '-cprof_service' => application_name, + '-cprof_service_version' => application_version) + + @droplet.environment_variables.add_environment_variable 'GOOGLE_APPLICATION_CREDENTIALS', json_file + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.one_service? FILTER, PRIVATE_KEY_DATA + end + + FILTER = /google-stackdriver-profiler/.freeze + + PRIVATE_KEY_DATA = 'PrivateKeyData' + + private_constant :FILTER, :PRIVATE_KEY_DATA + + private + + def application_name + @configuration['application_name'] || @application.details['application_name'] + end + + def application_version + @configuration['application_version'] || @application.details['application_version'] + end + + def credentials + @application.services.find_service(FILTER, PRIVATE_KEY_DATA)['credentials'] + end + + def private_key_data + Base64.decode64 credentials[PRIVATE_KEY_DATA] + end + + def project_id + JSON.parse(private_key_data)['project_id'] + end + + def json_file + @droplet.sandbox + 'svc.json' + end + + def write_json_file(private_key_data) + FileUtils.mkdir_p json_file.parent + json_file.open(File::CREAT | File::WRONLY) do |f| + f.write "#{private_key_data}\n" + f.sync + f + end + end + + end + + end +end diff --git a/lib/java_buildpack/framework/introscope_agent.rb b/lib/java_buildpack/framework/introscope_agent.rb new file mode 100644 index 0000000000..abc5ecef2b --- /dev/null +++ b/lib/java_buildpack/framework/introscope_agent.rb @@ -0,0 +1,142 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' +require 'java_buildpack/util/to_b' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for enabling zero-touch Introscope support. + class IntroscopeAgent < JavaBuildpack::Component::VersionedDependencyComponent + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_tar + @droplet.copy_resources + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + credentials = @application.services.find_service(FILTER)['credentials'] + java_opts = @droplet.java_opts + + java_opts + .add_javaagent(agent_jar) + .add_system_property('com.wily.introscope.agentProfile', agent_profile) + .add_system_property('introscope.agent.hostName', agent_host_name) + .add_system_property('com.wily.introscope.agent.agentName', agent_name(credentials)) + .add_system_property('introscope.agent.defaultProcessName', default_process_name(credentials)) + .add_system_property('com.wily.introscope.agent.startup.mode', 'neo') + + export_all_properties(credentials, java_opts) + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.one_service? FILTER, %w[agentManager_url_1 agent_manager_url] + end + + private + + FILTER = /introscope/.freeze + + private_constant :FILTER + + def agent_host_name + @application.details['application_uris'][0] + end + + def agent_jar + @droplet.sandbox + 'Agent.jar' + end + + def add_url(credentials, java_opts) + agent_manager = agent_manager_url(credentials) + + host, port, socket_factory = parse_url(agent_manager) + java_opts.add_system_property('agentManager.url.1', agent_manager) + java_opts.add_system_property('introscope.agent.enterprisemanager.transport.tcp.host.DEFAULT', host) + java_opts.add_system_property('introscope.agent.enterprisemanager.transport.tcp.port.DEFAULT', port) + java_opts.add_system_property('introscope.agent.enterprisemanager.transport.tcp.socketfactory.DEFAULT', + socket_factory) + end + + # Parse the agent manager url, split first by '://', and then with ':' + # components is of the format [host, port, socket_factory] + def parse_url(url) + components = url.split('://') + components.unshift('') if components.length == 1 + components[1] = components[1].split(':') + components.flatten! + components.push(protocol_mapping(components[0])) + components.shift + components + end + + def agent_name(credentials) + credentials['agent_name'] || @configuration['default_agent_name'] + end + + def agent_profile + @droplet.sandbox + 'core/config/IntroscopeAgent.profile' + end + + def default_process_name(credentials) + credentials['agent_default_process_name'] || @application.details['application_name'] + end + + def protocol_mapping(protocol) + socket_factory_base = 'com.wily.isengard.postofficehub.link.net.' + + protocol_socket_factory = { + '' => socket_factory_base + 'DefaultSocketFactory', + 'ssl' => socket_factory_base + 'SSLSocketFactory', + 'http' => socket_factory_base + 'HttpTunnelingSocketFactory', + 'https' => socket_factory_base + 'HttpsTunnelingSocketFactory' + } + + protocol_socket_factory[protocol] || protocol + end + + def agent_manager_url(credentials) + credentials['agentManager_url_1'] || credentials['agent_manager_url'] + end + + def agent_manager_credential(credentials) + credentials['agent_manager_credential'] || credentials['credential'] + end + + def export_all_properties(credentials, java_opts) + credentials.each_key do |key| + correct_key = key.tr('_', '.') + if %w[agentManager.url.1 agent.manager.url].include?(correct_key) + add_url(credentials, java_opts) + elsif %w[agent.manager.credential credential].include?(correct_key) + java_opts.add_system_property('agentManager.credential', agent_manager_credential(credentials)) + else + java_opts.add_system_property(correct_key, credentials[key]) + end + end + end + end + end +end diff --git a/lib/java_buildpack/framework/jacoco_agent.rb b/lib/java_buildpack/framework/jacoco_agent.rb new file mode 100644 index 0000000000..e585776b85 --- /dev/null +++ b/lib/java_buildpack/framework/jacoco_agent.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for enabling zero-touch JacCoCo support. + class JacocoAgent < JavaBuildpack::Component::VersionedDependencyComponent + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_zip false + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + credentials = @application.services.find_service(FILTER, ADDRESS)['credentials'] + properties = { + 'address' => credentials[ADDRESS], + 'output' => 'tcpclient', + 'sessionid' => '$CF_INSTANCE_GUID' + } + + properties['excludes'] = credentials['excludes'] if credentials.key? 'excludes' + properties['includes'] = credentials['includes'] if credentials.key? 'includes' + properties['port'] = credentials['port'] if credentials.key? 'port' + properties['output'] = credentials['output'] if credentials.key? 'output' + + @droplet.java_opts.add_javaagent_with_props(@droplet.sandbox + 'jacocoagent.jar', properties) + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.one_service? FILTER, ADDRESS + end + + ADDRESS = 'address' + + FILTER = /jacoco/.freeze + + private_constant :ADDRESS, :FILTER + + end + + end +end diff --git a/lib/java_buildpack/framework/play_framework_jpa_plugin.rb b/lib/java_buildpack/framework/java_cf_env.rb similarity index 59% rename from lib/java_buildpack/framework/play_framework_jpa_plugin.rb rename to lib/java_buildpack/framework/java_cf_env.rb index 272d444dc5..51a7a211a5 100644 --- a/lib/java_buildpack/framework/play_framework_jpa_plugin.rb +++ b/lib/java_buildpack/framework/java_cf_env.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,16 +17,20 @@ require 'java_buildpack/component/versioned_dependency_component' require 'java_buildpack/framework' -require 'java_buildpack/util/play/factory' +require 'java_buildpack/util/spring_boot_utils' module JavaBuildpack module Framework - # Encapsulates the detect, compile, and release functionality for enabling cloud auto-reconfiguration in Play - # applications that use JPA. Note that Spring auto-reconfiguration is covered by the SpringAutoReconfiguration - # framework. The reconfiguration performed here is to override Play application configuration to bind a Play - # application to cloud resources. - class PlayFrameworkJPAPlugin < JavaBuildpack::Component::VersionedDependencyComponent + # Encapsulates the detect, compile, and release functionality for enabling cloud auto-reconfiguration in Spring + # applications. + class JavaCfEnv < JavaBuildpack::Component::VersionedDependencyComponent + include JavaBuildpack::Util + + def initialize(context) + @spring_boot_utils = JavaBuildpack::Util::SpringBootUtils.new + super(context) + end # (see JavaBuildpack::Component::BaseComponent#compile) def compile @@ -42,25 +47,20 @@ def release # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) def supports? - candidate = false - - play_app = JavaBuildpack::Util::Play::Factory.create @droplet - candidate = uses_jpa?(play_app) || play20?(play_app.version) if play_app - - @configuration['enabled'] && candidate + @configuration['enabled'] && spring_boot_3? && !java_cfenv? end private - def play20?(version) - version.start_with? '2.0' + def spring_boot_3? + @spring_boot_utils.is?(@application) && + Gem::Version.new((@spring_boot_utils.version_strict @application)).release >= Gem::Version.new('3.0.0') end - def uses_jpa?(play_app) - play_app.jar?(/.*play-java-jpa.*\.jar/) + def java_cfenv? + (@droplet.root + '**/*java-cfenv*.jar').glob.any? end end - end end diff --git a/lib/java_buildpack/framework/java_memory_assistant.rb b/lib/java_buildpack/framework/java_memory_assistant.rb new file mode 100644 index 0000000000..a9bddea2b9 --- /dev/null +++ b/lib/java_buildpack/framework/java_memory_assistant.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component/modular_component' +require 'java_buildpack/framework' +require 'java_buildpack/framework/java_memory_assistant/agent' +require 'java_buildpack/framework/java_memory_assistant/clean_up' +require 'java_buildpack/framework/java_memory_assistant/heap_dump_folder' + +module JavaBuildpack + module Framework + + # Encapsulates the integraton of the JavaMemoryAssistant. + class JavaMemoryAssistant < JavaBuildpack::Component::ModularComponent + + protected + + # (see JavaBuildpack::Component::ModularComponent#command) + def command; end + + # (see JavaBuildpack::Component::ModularComponent#sub_components) + def sub_components(context) + [ + JavaMemoryAssistantAgent.new(sub_configuration_context(context, 'agent')), + JavaMemoryAssistantHeapDumpFolder.new(sub_configuration_context(context, 'agent')), + JavaMemoryAssistantCleanUp.new(sub_configuration_context(context, 'clean_up')) + ] + end + + # (see JavaBuildpack::Component::ModularComponent#supports?) + def supports? + @configuration['enabled'] + end + + end + end +end diff --git a/lib/java_buildpack/framework/java_memory_assistant/agent.rb b/lib/java_buildpack/framework/java_memory_assistant/agent.rb new file mode 100644 index 0000000000..50edfc800c --- /dev/null +++ b/lib/java_buildpack/framework/java_memory_assistant/agent.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/component' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/component/droplet' +require 'java_buildpack/component/environment_variables' +require 'java_buildpack/framework' + +module JavaBuildpack + module Framework + + # Encapsulates the integraton of the JavaMemoryAssistant to inject the agent in the JVM. + class JavaMemoryAssistantAgent < JavaBuildpack::Component::VersionedDependencyComponent + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_jar + @droplet.copy_resources + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + @droplet.java_opts + .add_javaagent(@droplet.sandbox + jar_name) + .add_system_property('jma.enabled', 'true') + .add_system_property('jma.heap_dump_name', %("#{name_pattern}")) + .add_system_property 'jma.log_level', normalized_log_level + + if @droplet.java_home.java_9_or_later? + # Enable access to com.sun.management.HotSpotDiagnosticMXBean to circumvent + # Java modules limitations in Java 9+ + # See https://github.com/SAP/java-memory-assistant#running-the-java-memory-assistant-on-java-11 + @droplet.java_opts + .add_preformatted_options('--add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED') + end + + add_system_prop_if_config_present 'check_interval', 'jma.check_interval' + + if @configuration.key?('max_frequency') + @droplet.java_opts.add_preformatted_options "'-Djma.max_frequency=#{@configuration['max_frequency']}'" + end + + return unless @configuration.key?('thresholds') + + @configuration['thresholds'].each do |key, value| + @droplet.java_opts.add_preformatted_options "'-Djma.thresholds.#{key}=#{value}'" + end + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#jar_name) + def jar_name + "java-memory-assistant-#{@version}.jar" + end + + def supports? + true + end + + private + + LOG_LEVEL_MAPPING = { + 'DEBUG' => 'DEBUG', + 'WARN' => 'WARNING', + 'INFO' => 'INFO', + 'ERROR' => 'ERROR', + 'FATAL' => 'ERROR' + }.freeze + + private_constant :LOG_LEVEL_MAPPING + + def add_system_prop_if_config_present(config_entry, system_property_name) + return unless @configuration.key?(config_entry) + + @droplet.java_opts.add_system_property(system_property_name, @configuration[config_entry]) + end + + def log_level + @configuration['log_level'] || ENV.fetch('JBP_LOG_LEVEL', nil) || 'ERROR' + end + + def normalized_log_level + normalized_log_level = LOG_LEVEL_MAPPING[log_level.upcase] + raise "Invalid value of the 'log_level' property: '#{log_level}'" unless normalized_log_level + + normalized_log_level + end + + def name_pattern + # Double escaping quotes of doom. Nothing less would work. + %q(%env:CF_INSTANCE_INDEX%-%ts:yyyy-MM-dd'"'"'T'"'"'mm'"'"':'"'"'ss'"'"':'"'"'SSSZ%-) \ + '%env:CF_INSTANCE_GUID[,8]%.hprof' + end + + end + end +end diff --git a/lib/java_buildpack/framework/java_memory_assistant/clean_up.rb b/lib/java_buildpack/framework/java_memory_assistant/clean_up.rb new file mode 100644 index 0000000000..aa5c6acff1 --- /dev/null +++ b/lib/java_buildpack/framework/java_memory_assistant/clean_up.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/component/droplet' +require 'java_buildpack/framework' + +module JavaBuildpack + module Framework + + # Encapsulates the integraton of the JavaMemoryAssistant to set up clean up of dumps. + class JavaMemoryAssistantCleanUp < JavaBuildpack::Component::VersionedDependencyComponent + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + return unless supports? + + download_zip false + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + return unless supports? + + @droplet.environment_variables + .add_environment_variable 'JMA_MAX_DUMP_COUNT', @configuration['max_dump_count'].to_s + + @droplet.java_opts + .add_system_property('jma.command.interpreter', '') + .add_system_property('jma.execute.before', @droplet.sandbox + 'cleanup') + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @configuration['max_dump_count'].to_i.positive? + end + + end + end +end diff --git a/lib/java_buildpack/framework/java_memory_assistant/heap_dump_folder.rb b/lib/java_buildpack/framework/java_memory_assistant/heap_dump_folder.rb new file mode 100644 index 0000000000..dcdcb0dd2a --- /dev/null +++ b/lib/java_buildpack/framework/java_memory_assistant/heap_dump_folder.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component/base_component' +require 'java_buildpack/component/droplet' +require 'java_buildpack/framework' +require 'java_buildpack/framework/java_memory_assistant' + +module JavaBuildpack + module Framework + + # Encapsulates the integraton of the JavaMemoryAssistant to store generated heap dumps. + class JavaMemoryAssistantHeapDumpFolder < JavaBuildpack::Component::BaseComponent + + # Creates an instance + # + # @param [Hash] context a collection of utilities used by the component + def initialize(context) + @logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger JavaMemoryAssistantHeapDumpFolder + super(context) + end + + # (see JavaBuildpack::Component::BaseComponent#detect) + def detect + true + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile; end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + environment_variables = @droplet.environment_variables + + heap_dump_folder = @configuration['heap_dump_folder'] || default_heap_dump_folder + + # If there is a bound volume service, use the heap_dump_folder under the volume's path + service = find_heap_dump_volume_service + if service + volume_mount = service['volume_mounts'][0] + container_dir = volume_mount['container_dir'] + mode = volume_mount['mode'] + + raise "Volume mounted under '#{container_dir}' not in write mode" unless mode.to_s.include? 'w' + + heap_dump_folder = "#{container_dir}/#{heap_dump_folder}" + @logger.info { "Using volume service mounted under '#{container_dir}' to store heap dumps" } + end + + # This is needed by the clean_up module + environment_variables.add_environment_variable 'JMA_HEAP_DUMP_FOLDER', heap_dump_folder.to_s + @droplet.java_opts.add_system_property 'jma.heap_dump_folder', "\"#{heap_dump_folder}\"" + @logger.info { "Heap dumps will be stored under '#{heap_dump_folder}'" } + end + + private + + # Matcher for service names or tags associated with the Java Memory Assistant + FILTER = 'heap-dump' + + def find_heap_dump_volume_service + @application.services.find_volume_service FILTER + end + + def default_heap_dump_folder + "#{@application.details['space_name']}-#{@application.details['space_id'][0...8]}/" \ + "#{@application.details['application_name']}-#{@application.details['application_id'][0...8]}" + end + + end + end +end diff --git a/lib/java_buildpack/framework/java_opts.rb b/lib/java_buildpack/framework/java_opts.rb index f9ba928e80..195c98772a 100644 --- a/lib/java_buildpack/framework/java_opts.rb +++ b/lib/java_buildpack/framework/java_opts.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -27,53 +28,46 @@ class JavaOpts < JavaBuildpack::Component::BaseComponent # (see JavaBuildpack::Component::BaseComponent#detect) def detect - (supports_configuration? || supports_environment?) ? JavaOpts.to_s.dash_case : nil + JavaOpts.to_s.dash_case end # (see JavaBuildpack::Component::BaseComponent#compile) - def compile - parsed_java_opts.each do |option| - if memory_option? option - fail "Java option '#{option}' configures a memory region. Use JRE configuration for this instead." - end - end - end + def compile; end # (see JavaBuildpack::Component::BaseComponent#release) def release - @droplet.java_opts.concat parsed_java_opts + configured + .shellsplit + .map { |java_opt| /(?.+?)=(?.+)/ =~ java_opt ? "#{key}=#{escape_value(value)}" : java_opt } + .each { |java_opt| @droplet.java_opts << java_opt } + + @droplet.java_opts << '$JAVA_OPTS' if from_environment? + + @droplet.java_opts.as_env_var end private - CONFIGURATION_PROPERTY = 'java_opts'.freeze + CONFIGURATION_PROPERTY = 'java_opts' - ENVIRONMENT_PROPERTY = 'from_environment'.freeze + ENVIRONMENT_PROPERTY = 'from_environment' - ENVIRONMENT_VARIABLE = 'JAVA_OPTS'.freeze + private_constant :CONFIGURATION_PROPERTY, :ENVIRONMENT_PROPERTY - private_constant :CONFIGURATION_PROPERTY, :ENVIRONMENT_PROPERTY, :ENVIRONMENT_VARIABLE - - def memory_option?(option) - option =~ /-Xms/ || option =~ /-Xmx/ || option =~ /-XX:MaxMetaspaceSize/ || option =~ /-XX:MaxPermSize/ || - option =~ /-Xss/ || option =~ /-XX:MetaspaceSize/ || option =~ /-XX:PermSize/ + def configured + @configuration[CONFIGURATION_PROPERTY] || '' end - def parsed_java_opts - parsed_java_opts = [] - - parsed_java_opts.concat @configuration[CONFIGURATION_PROPERTY].shellsplit if supports_configuration? - parsed_java_opts.concat ENV[ENVIRONMENT_VARIABLE].shellsplit if supports_environment? - - parsed_java_opts.map { |java_opt| java_opt.gsub(/([\s])/, '\\\\\1') } - end + def escape_value(str) + return "''" if str.empty? - def supports_configuration? - @configuration.key? CONFIGURATION_PROPERTY + str + .gsub(%r{([^A-Za-z0-9_\-.,:/@\n$\\])}, '\\\\\\1') + .gsub(/\n/, "'\n'") end - def supports_environment? - @configuration[ENVIRONMENT_PROPERTY] && ENV.key?(ENVIRONMENT_VARIABLE) + def from_environment? + @configuration[ENVIRONMENT_PROPERTY] end end diff --git a/lib/java_buildpack/framework/java_security.rb b/lib/java_buildpack/framework/java_security.rb new file mode 100644 index 0000000000..3f58245a32 --- /dev/null +++ b/lib/java_buildpack/framework/java_security.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component/base_component' +require 'java_buildpack/framework' +require 'java_buildpack/util/dash_case' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for contributing custom Security Providers to an application. + class JavaSecurity < JavaBuildpack::Component::BaseComponent + + # (see JavaBuildpack::Component::BaseComponent#detect) + def detect + JavaSecurity.to_s.dash_case + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + @droplet.networking.write_to java_security + @droplet.security_providers.write_to java_security + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + @droplet.java_opts + .add_system_property('java.ext.dirs', @droplet.extension_directories.as_paths) + .add_system_property('java.security.properties', java_security) + end + + private + + def java_security + @droplet.sandbox + 'java.security' + end + + end + + end +end diff --git a/lib/java_buildpack/framework/jmx.rb b/lib/java_buildpack/framework/jmx.rb new file mode 100644 index 0000000000..47171d88f1 --- /dev/null +++ b/lib/java_buildpack/framework/jmx.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component/base_component' +require 'java_buildpack/framework' +require 'java_buildpack/util/dash_case' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for contributing Java JMX options to an application. + class Jmx < JavaBuildpack::Component::BaseComponent + + # (see JavaBuildpack::Component::BaseComponent#detect) + def detect + enabled? ? "#{self.class.to_s.dash_case}=#{port}" : nil + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + puts "#{'----->'.red.bold} #{'JMX'.blue.bold} enabled on port #{port}" + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + @droplet + .java_opts + .add_system_property('java.rmi.server.hostname', '127.0.0.1') + .add_system_property('com.sun.management.jmxremote.authenticate', false) + .add_system_property('com.sun.management.jmxremote.ssl', false) + .add_system_property('com.sun.management.jmxremote.port', port) + .add_system_property('com.sun.management.jmxremote.rmi.port', port) + end + + private + + def enabled? + @configuration['enabled'] + end + + def port + @configuration['port'] || 5000 + end + + end + + end +end diff --git a/lib/java_buildpack/framework/jprofiler_profiler.rb b/lib/java_buildpack/framework/jprofiler_profiler.rb new file mode 100644 index 0000000000..71e942beb0 --- /dev/null +++ b/lib/java_buildpack/framework/jprofiler_profiler.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' +require 'java_buildpack/util/qualify_path' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for enabling zero-touch JProfiler profiler support. + class JprofilerProfiler < JavaBuildpack::Component::VersionedDependencyComponent + include JavaBuildpack::Util + + def initialize(context, &version_validator) + super(context, &version_validator) + @component_name = 'JProfiler Profiler' + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_tar + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + properties = { 'port' => port } + properties['nowait'] = nil if nowait + + @droplet + .java_opts + .add_agentpath_with_props(file_name, properties) + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @configuration['enabled'] + end + + private + + def file_name + @droplet.sandbox + 'bin/linux-x64/libjprofilerti.so' + end + + def nowait + v = @configuration['nowait'] + v.nil? ? true : v + end + + def port + @configuration['port'] || 8_849 + end + + end + + end +end diff --git a/lib/java_buildpack/framework/jrebel_agent.rb b/lib/java_buildpack/framework/jrebel_agent.rb new file mode 100644 index 0000000000..1b09fa3ed0 --- /dev/null +++ b/lib/java_buildpack/framework/jrebel_agent.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for enabling zero-touch JRebel support. + class JrebelAgent < JavaBuildpack::Component::VersionedDependencyComponent + + def initialize(context, &version_validator) + super(context, &version_validator) + @component_name = 'JRebel Agent' + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_zip + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + @droplet + .java_opts + .add_agentpath(@droplet.sandbox + ('lib/' + lib_name)) + .add_system_property('rebel.remoting_plugin', true) + .add_system_property('rebel.cloud.platform', 'cloudfoundry/java-buildpack') + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + enabled? && ( + jrebel_configured?(@application.root) || + jrebel_configured?(@application.root + 'WEB-INF/classes') || + jrebel_configured?(@application.root + 'BOOT-INF/classes') || + jars_with_jrebel_configured?(@application.root)) + end + + private + + def jrebel_configured?(root_path) + (root_path + 'rebel-remote.xml').exist? + end + + def jars_with_jrebel_configured?(root_path) + (root_path + '**/*.jar') + .glob.reject(&:directory?) + .any? { |jar| !`unzip -l "#{jar}" | grep "rebel-remote\\.xml$"`.strip.empty? } + end + + def lib_name + architecture == 'x86_64' || architecture == 'i686' ? 'libjrebel64.so' : 'libjrebel32.so' + end + + def architecture + `uname -m`.strip + end + + def enabled? + @configuration['enabled'].nil? || @configuration['enabled'] + end + + end + + end +end diff --git a/lib/java_buildpack/framework/luna_security_provider.rb b/lib/java_buildpack/framework/luna_security_provider.rb new file mode 100644 index 0000000000..3a41d30a5d --- /dev/null +++ b/lib/java_buildpack/framework/luna_security_provider.rb @@ -0,0 +1,302 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' +require 'java_buildpack/util/external_config' +require 'java_buildpack/util/qualify_path' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for enabling zero-touch Safenet Luna HSM Java Security Provider support. + class LunaSecurityProvider < JavaBuildpack::Component::VersionedDependencyComponent + include JavaBuildpack::Util + include JavaBuildpack::Util::ExternalConfig + + # Full list of configuration files that can be downloaded remotely + CONFIG_FILES = %w[Chrystoki.conf server-certificates.pem].freeze + + # Prefix to be used with external configuration environment variable + CONFIG_PREFIX = 'LUNA' + + def initialize(context) + super(context) + @logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger LunaSecurityProvider + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_tar + setup_ext_dir + + @droplet.copy_resources + @droplet.security_providers << 'com.safenetinc.luna.provider.LunaProvider' + @droplet.root_libraries << luna_provider_jar if @droplet.java_home.java_9_or_later? + + write_credentials + + override_default_config_remote do |file, conf_file| + FileUtils.cp_r file, @droplet.sandbox + conf_file + end + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + @droplet.environment_variables.add_environment_variable 'ChrystokiConfigurationPath', @droplet.sandbox + + if @droplet.java_home.java_9_or_later? + @droplet.root_libraries << luna_provider_jar + + @droplet.environment_variables.add_environment_variable( + 'LD_LIBRARY_PATH', "$LD_LIBRARY_PATH:#{ld_lib_path}" + ) + else + @droplet.extension_directories << ext_dir + end + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.one_service?(FILTER, 'client', 'servers', 'groups') || + @application.services.one_service?(FILTER, 'client', 'servers') || + @application.services.one_service?(FILTER, 'client') + end + + private + + def write_credentials + service = @application.services.find_service(FILTER, 'client', 'servers', 'groups') || + @application.services.find_service(FILTER, 'client', 'servers') || + @application.services.find_service(FILTER, 'client') + credentials = service['credentials'] + + write_client credentials['client'] if credentials.key? 'client' + write_servers credentials['servers'] if credentials.key? 'servers' + + return unless credentials.key?('servers') && credentials.key?('groups') + + write_configuration credentials['servers'], credentials['groups'] + end + + FILTER = /luna/.freeze + + private_constant :FILTER + + def chrystoki + @droplet.sandbox + 'Chrystoki.conf' + end + + def client_certificate + @droplet.sandbox + 'client-certificate.pem' + end + + def client_private_key + @droplet.sandbox + 'client-private-key.pem' + end + + def ext_dir + @droplet.sandbox + 'ext' + end + + def luna_provider_jar + @droplet.sandbox + 'jsp/LunaProvider.jar' + end + + def luna_api_so + @droplet.sandbox + 'jsp/64/libLunaAPI.so' + end + + def lib_cryptoki + @droplet.sandbox + 'libs/64/libCryptoki2.so' + end + + def lib_cklog + @droplet.sandbox + 'libs/64/libcklog2.so' + end + + def ld_lib_path + qualify_path(@droplet.sandbox, @droplet.root) + '/jsp/64/' + end + + def setup_ext_dir + FileUtils.mkdir ext_dir + [luna_provider_jar, luna_api_so].each do |file| + FileUtils.ln_s file.relative_path_from(ext_dir), ext_dir, force: true + end + end + + def logging? + @configuration['logging_enabled'] + end + + def ha_logging? + @configuration['ha_logging_enabled'] + end + + def tcp_keep_alive + @configuration['tcp_keep_alive_enabled'] ? 1 : 0 + end + + def padded_index(index) + index.to_s.rjust(2, '0') + end + + def relative(path) + path.relative_path_from(@droplet.root) + end + + def server_certificates + @droplet.sandbox + 'server-certificates.pem' + end + + def write_client(client) + FileUtils.mkdir_p client_certificate.parent + client_certificate.open(File::CREAT | File::WRONLY) do |f| + f.write "#{client['certificate']}\n" + end + + FileUtils.mkdir_p client_private_key.parent + client_private_key.open(File::CREAT | File::WRONLY) do |f| + f.write "#{client['private-key']}\n" + end + end + + def write_configuration(servers, groups) + chrystoki.open(File::APPEND | File::WRONLY) do |f| + write_prologue f + servers.each_with_index { |server, index| write_server f, index, server } + f.write <<~TOKEN + } + + VirtualToken = { + TOKEN + groups.each_with_index { |group, index| write_group f, index, group } + write_epilogue f, groups + end + end + + def write_epilogue(f, groups) + f.write <<~HA + } + + HAConfiguration = { + AutoReconnectInterval = 60; + HAOnly = 1; + reconnAtt = -1; + HA + write_ha_logging(f) if ha_logging? + f.write <<~HA + } + + HASynchronize = { + HA + groups.each { |group| f.write " #{group['label']} = 1;\n" } + f.write "}\n" + end + + def write_group(f, index, group) + padded_index = padded_index index + + f.write " VirtualToken#{padded_index}Label = #{group['label']};\n" + f.write " VirtualToken#{padded_index}SN = 1#{group['members'][0]};\n" + f.write " VirtualToken#{padded_index}Members = #{group['members'].join(',')};\n" + f.write "\n" + end + + def write_lib(f) + f.write <<~CONFIG + + Chrystoki2 = { + CONFIG + + if logging? + write_logging(f) + else + f.write <<~LIB + LibUNIX64 = #{relative(lib_cryptoki)}; + } + LIB + end + end + + def write_logging(f) + f.write <<~LOGGING + LibUNIX64 = #{relative(lib_cklog)}; + } + + CkLog2 = { + Enabled = 1; + LibUNIX64 = #{relative(lib_cryptoki)}; + LoggingMask = ALL_FUNC; + LogToStreams = 1; + NewFormat = 1; + } + LOGGING + end + + def write_ha_logging(f) + f.write <<~HA + haLogStatus = enabled; + haLogToStdout = enabled; + HA + end + + def write_prologue(f) + write_lib(f) + + f.write <<~CLIENT + + LunaSA Client = { + TCPKeepAlive = #{tcp_keep_alive}; + NetClient = 1; + + ClientCertFile = #{relative(client_certificate)}; + ClientPrivKeyFile = #{relative(client_private_key)}; + HtlDir = #{relative(@droplet.sandbox + 'htl')}; + ServerCAFile = #{relative(server_certificates)}; + + CLIENT + end + + def write_server(f, index, server) + padded_index = padded_index index + + f.write " ServerName#{padded_index} = #{server['name']};\n" + f.write " ServerPort#{padded_index} = 1792;\n" + f.write " ServerHtl#{padded_index} = 0;\n" + f.write "\n" + end + + def write_servers(servers) + FileUtils.mkdir_p server_certificates.parent + server_certificates.open(File::CREAT | File::WRONLY) do |f| + servers.each { |server| f.write "#{server['certificate']}\n" } + end + end + + # Overrides method from ExternalConfig module & provides root URL for where external configuration will be located + def external_config_root + @application.environment["#{CONFIG_PREFIX}_CONF_HTTP_URL"].chomp('/') + '/' + end + + end + end +end diff --git a/lib/java_buildpack/framework/maria_db_jdbc.rb b/lib/java_buildpack/framework/maria_db_jdbc.rb index 4e336b0117..7a16997ce8 100644 --- a/lib/java_buildpack/framework/maria_db_jdbc.rb +++ b/lib/java_buildpack/framework/maria_db_jdbc.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -45,7 +46,7 @@ def supports? private def driver? - %w(mariadb-java-client*.jar mysql-connector-java*.jar).any? do |candidate| + %w[mariadb-java-client*.jar mysql-connector-j*.jar aws-mysql-jdbc*.jar].any? do |candidate| (@application.root + '**' + candidate).glob.any? end end diff --git a/lib/java_buildpack/container/tomcat/gemfire/gemfire.rb b/lib/java_buildpack/framework/metric_writer.rb similarity index 63% rename from lib/java_buildpack/container/tomcat/gemfire/gemfire.rb rename to lib/java_buildpack/framework/metric_writer.rb index c4780176e3..5eb68a5162 100644 --- a/lib/java_buildpack/container/tomcat/gemfire/gemfire.rb +++ b/lib/java_buildpack/framework/metric_writer.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,37 +16,30 @@ # limitations under the License. require 'java_buildpack/component/versioned_dependency_component' -require 'java_buildpack/container' -require 'java_buildpack/container/tomcat/tomcat_utils' -require 'java_buildpack/logging/logger_factory' +require 'java_buildpack/framework' module JavaBuildpack - module Container + module Framework - # Encapsulates the detect, compile, and release functionality for Tomcat Redis support. - class GemFire < JavaBuildpack::Component::VersionedDependencyComponent - include JavaBuildpack::Container + # Encapsulates the functionality for contributing a container-based security provider to an application. + class MetricWriter < JavaBuildpack::Component::VersionedDependencyComponent # (see JavaBuildpack::Component::BaseComponent#compile) def compile - download_jar(jar_name, tomcat_lib) + download_jar + @droplet.additional_libraries << (@droplet.sandbox + jar_name) end # (see JavaBuildpack::Component::BaseComponent#release) def release + @droplet.additional_libraries << (@droplet.sandbox + jar_name) end protected # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) def supports? - true - end - - private - - def jar_name - "gemfire-#{@version}.jar" + @configuration['enabled'] && (@droplet.root + '**/*micrometer-core*.jar').glob.any? end end diff --git a/lib/java_buildpack/framework/multi_buildpack.rb b/lib/java_buildpack/framework/multi_buildpack.rb new file mode 100644 index 0000000000..a1c97f7617 --- /dev/null +++ b/lib/java_buildpack/framework/multi_buildpack.rb @@ -0,0 +1,318 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'pathname' +require 'java_buildpack/component/base_component' +require 'java_buildpack/framework' +require 'java_buildpack/logging/logger_factory' +require 'java_buildpack/util/filtering_pathname' +require 'yaml' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for multi buildpack support. + class MultiBuildpack < JavaBuildpack::Component::BaseComponent + include JavaBuildpack::Util + + # (see JavaBuildpack::Component::BaseComponent#initialize) + def initialize(context) + super(context) + + @logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger MultiBuildpack + @logger.debug { "Dependencies Directory: #{ARGV[3]}" } + end + + # (see JavaBuildpack::Component::BaseComponent#detect) + def detect + dep_directories.empty? ? nil : "multi-buildpack=#{names(dep_directories).join(',')}" + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + puts "#{'----->'.red.bold} #{'Multiple Buildpacks'.blue.bold} detected" + + dep_directories.each do |dep_directory| + config = config(config_file(dep_directory)) + name = name(config) + + log_configuration config + log_dep_contents dep_directory + + contributions = [ + add_bin(dep_directory), + add_lib(dep_directory), + add_additional_libraries(config), + add_environment_variables(config), + add_extension_directories(config), + add_java_opts(config), + add_security_providers(config) + ] + + puts " #{name}#{contributions_message(contributions)}" + end + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + dep_directories.each do |dep_directory| + config = config(config_file(dep_directory)) + + add_bin(dep_directory) + add_lib(dep_directory) + add_additional_libraries(config) + add_environment_variables(config) + add_extension_directories(config) + add_java_opts(config) + add_security_providers(config) + end + end + + private + + def add_additional_libraries(config) + additional_libraries = config['config']['additional_libraries'] + return unless additional_libraries + + additional_libraries.each do |additional_library| + @droplet.additional_libraries << filtering_pathname(additional_library) + end + + 'Additional Libraries' + end + + def add_agentpaths(java_opts) + agentpaths = java_opts['agentpaths'] + return unless agentpaths + + agentpaths.each do |agentpath| + @droplet.java_opts.add_agentpath filtering_pathname(agentpath) + end + + 'Agents' + end + + def add_agentpaths_with_props(java_opts) + agentpaths = java_opts['agentpaths_with_props'] + return unless agentpaths + + agentpaths.each do |agentpath, props| + @droplet.java_opts.add_agentpath_with_props filtering_pathname(agentpath), props + end + + 'Agent with Properties' + end + + def add_bin(dep_directory) + bin_directory = dep_directory + 'bin' + return unless bin_directory.exist? + + @droplet.environment_variables + .add_environment_variable('PATH', "$PATH:#{qualify_dep(bin_directory)}") + + '$PATH' + end + + def filtering_pathname(path) + JavaBuildpack::Util::FilteringPathname.new(Pathname.new(path), ->(_) { true }, false) + end + + def qualify_dep(dep_dir) + ret = dep_dir.to_s.gsub(%r{.+(/deps/[0-9]+/\w+)$}, '\1') + "$PWD/..#{ret}" + end + + def add_bootclasspath_ps(java_opts) + bootclasspath_ps = java_opts['bootclasspath_ps'] + return unless bootclasspath_ps + + bootclasspath_ps.each do |bootclasspath_p| + @droplet.java_opts.add_bootclasspath_p filtering_pathname(bootclasspath_p) + end + + 'Boot Classpaths' + end + + def add_environment_variables(config) + environment_variables = config['config']['environment_variables'] + return unless environment_variables + + environment_variables.each do |key, value| + path = Pathname.new(value) + + if path.exist? + @droplet.environment_variables.add_environment_variable key, filtering_pathname(value) + else + @droplet.environment_variables.add_environment_variable key, value + end + end + + 'Environment Variables' + end + + def add_extension_directories(config) + extension_directories = config['config']['extension_directories'] + return unless extension_directories + + extension_directories.each do |extension_directory| + @droplet.extension_directories << filtering_pathname(extension_directory) + end + + 'Extension Directories' + end + + def add_javaagent(java_opts) + javaagents = java_opts['javaagents'] + return unless javaagents + + javaagents.each do |javaagent| + @droplet.java_opts.add_javaagent filtering_pathname(javaagent) + end + + 'Java Agents' + end + + def add_java_opts(config) + java_opts = config['config']['java_opts'] + return unless java_opts + + [ + add_agentpaths(java_opts), + add_agentpaths_with_props(java_opts), + add_bootclasspath_ps(java_opts), + add_javaagent(java_opts), + add_options(java_opts), + add_preformatted_options(java_opts), + add_system_properties(java_opts) + ] + end + + def add_lib(dep_directory) + lib_directory = dep_directory + 'lib' + return unless lib_directory.exist? + + @droplet.environment_variables + .add_environment_variable('LD_LIBRARY_PATH', "$LD_LIBRARY_PATH:#{qualify_dep(lib_directory)}") + + '$LD_LIBRARY_PATH' + end + + def add_options(java_opts) + options = java_opts['options'] + return unless options + + options.each do |key, value| + path = Pathname.new(value) + + if path.exist? + @droplet.java_opts.add_option key, filtering_pathname(value) + else + @droplet.java_opts.add_option key, value + end + end + + 'Options' + end + + def add_preformatted_options(java_opts) + preformatted_options = java_opts['preformatted_options'] + return unless preformatted_options + + preformatted_options.each do |preformatted_option| + @droplet.java_opts.add_preformatted_options preformatted_option + end + + 'Preformatted Options' + end + + def add_security_providers(config) + security_providers = config['config']['security_providers'] + return unless security_providers + + security_providers.each do |security_provider| + @droplet.security_providers << security_provider + end + + 'Security Providers' + end + + def add_system_properties(java_opts) + system_properties = java_opts['system_properties'] + return unless system_properties + + system_properties.each do |key, value| + path = Pathname.new(value) + + if path.exist? + @droplet.java_opts.add_system_property key, filtering_pathname(value) + else + @droplet.java_opts.add_system_property key, value + end + end + + 'System Properties' + end + + def config(config_file) + YAML.load_file(config_file, permitted_classes: [Symbol], aliases: true) + end + + def config_file(dep_directory) + dep_directory + 'config.yml' + end + + def contributions_message(contributions) + return if contributions.compact.empty? + + " contributed to: #{contributions.flatten.compact.sort.join(', ')}" + end + + def dep_directories + deps = Pathname.glob('/tmp/*/deps').map(&:children).flatten + return [] unless deps + + deps + .select { |dep_directory| config_file(dep_directory).exist? } + .sort_by(&:basename) + end + + def log_configuration(config) + @logger.debug { "Configuration: #{config}" } + end + + def log_dep_contents(dep_directory) + @logger.debug do + paths = [] + dep_directory.find { |f| paths << f.relative_path_from(dep_directory).to_s } + + "Application Contents (#{dep_directory}): #{paths}" + end + end + + def name(config) + config['name'] + end + + def names(dep_directories) + dep_directories.map { |dep_directory| name(config(config_file(dep_directory))) } + end + + end + + end +end diff --git a/lib/java_buildpack/framework/new_relic_agent.rb b/lib/java_buildpack/framework/new_relic_agent.rb index 6f4a7a8e95..8aa764276d 100644 --- a/lib/java_buildpack/framework/new_relic_agent.rb +++ b/lib/java_buildpack/framework/new_relic_agent.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,7 +15,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'fileutils' require 'java_buildpack/component/versioned_dependency_component' require 'java_buildpack/framework' @@ -24,53 +24,108 @@ module Framework # Encapsulates the functionality for enabling zero-touch New Relic support. class NewRelicAgent < JavaBuildpack::Component::VersionedDependencyComponent + def initialize(context, &version_validator) + super(context, &version_validator) + + extensions_context = context.clone + extensions_context[:configuration] = context[:configuration]['extensions'] || {} + + return unless supports_extensions?(extensions_context[:configuration]) + + @extensions = NewRelicAgentExtensions.new(extensions_context) + end + # (see JavaBuildpack::Component::BaseComponent#compile) def compile - FileUtils.mkdir_p logs_dir download_jar + @extensions&.compile @droplet.copy_resources end # (see JavaBuildpack::Component::BaseComponent#release) def release - @droplet.java_opts - .add_javaagent(@droplet.sandbox + jar_name) - .add_system_property('newrelic.home', @droplet.sandbox) - .add_system_property('newrelic.config.license_key', license_key) - .add_system_property('newrelic.config.app_name', "'#{application_name}'") - .add_system_property('newrelic.config.log_file_path', logs_dir) - @droplet.java_opts.add_system_property('newrelic.enable.java.8', 'true') if java_8? + credentials = @application.services.find_service(FILTER, [LICENSE_KEY, LICENSE_KEY_USER])['credentials'] + java_opts = @droplet.java_opts + configuration = {} + + apply_configuration(credentials, configuration) + apply_user_configuration(credentials, configuration) + write_java_opts(java_opts, configuration) + + java_opts.add_javaagent(@droplet.sandbox + jar_name) + .add_system_property('newrelic.home', @droplet.sandbox) + java_opts.add_system_property('newrelic.enable.java.8', 'true') if @droplet.java_home.java_8_or_later? end protected # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) def supports? - @application.services.one_service? FILTER, 'licenseKey' + @application.services.one_service? FILTER, [LICENSE_KEY, LICENSE_KEY_USER] end private FILTER = /newrelic/.freeze - private_constant :FILTER + LICENSE_KEY = 'licenseKey' + + LICENSE_KEY_USER = 'license_key' + + private_constant :FILTER, :LICENSE_KEY, :LICENSE_KEY_USER + + def apply_configuration(credentials, configuration) + configuration['log_file_name'] = 'STDOUT' + configuration[LICENSE_KEY_USER] = credentials[LICENSE_KEY] + configuration['app_name'] = @application.details['application_name'] + end + + def apply_user_configuration(credentials, configuration) + credentials.each do |key, value| + configuration[key] = value + end + end - def java_8? - @droplet.java_home.version[1] == '8' + def supports_extensions?(configuration) + !(configuration['repository_root'] || '').empty? end - def application_name - @application.details['application_name'] + def write_java_opts(java_opts, configuration) + configuration.each do |key, value| + java_opts.add_system_property("newrelic.config.#{key}", value) + end end - def license_key - @application.services.find_service(FILTER)['credentials']['licenseKey'] + end + + # Used by the main NewRelicAgent class to download the extensions tarball(if configured) + class NewRelicAgentExtensions < JavaBuildpack::Component::VersionedDependencyComponent + + # (see JavaBuildpack::Component::VersionedDependencyComponent#initialize) + def initialize(context, &version_validator) + JavaBuildpack::Util::Cache::InternetAvailability.instance.available( + true, 'The New Relic Extensions download location is always accessible' + ) do + super(context, &version_validator) + end end - def logs_dir - @droplet.sandbox + 'logs' + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + JavaBuildpack::Util::Cache::InternetAvailability.instance.available( + true, 'The New Relic Extensions download location is always accessible' + ) do + download_tar(true, @droplet.sandbox + 'extensions') + end end + # (see JavaBuildpack::Component::BaseComponent#release) + def release; end + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + !(@configuration['repository_root'] || '').empty? + end end end diff --git a/lib/java_buildpack/framework/open_telemetry_javaagent.rb b/lib/java_buildpack/framework/open_telemetry_javaagent.rb new file mode 100644 index 0000000000..b78049bbf2 --- /dev/null +++ b/lib/java_buildpack/framework/open_telemetry_javaagent.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2023 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' + +module JavaBuildpack + module Framework + + # Main class for adding the OpenTelemetry Javaagent instrumentation + class OpenTelemetryJavaagent < JavaBuildpack::Component::VersionedDependencyComponent + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_jar + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + java_opts = @droplet.java_opts + java_opts.add_javaagent(@droplet.sandbox + jar_name) + + credentials = @application.services.find_service(REQUIRED_SERVICE_NAME_FILTER)['credentials'] + # Add all otel.* credentials from the service bind as jvm system properties + credentials&.each do |key, value| + java_opts.add_system_property(key, value) if key.start_with?('otel.') + end + + # Set the otel.service.name to the application_name if not specified in credentials + return if credentials.key? 'otel.service.name' + + # Set the otel.service.name to the application_name + app_name = @application.details['application_name'] + java_opts.add_system_property('otel.service.name', app_name) + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.one_service? REQUIRED_SERVICE_NAME_FILTER + end + + # bound service must contain the string `otel-collector` + REQUIRED_SERVICE_NAME_FILTER = /otel-collector/.freeze + + end + end +end diff --git a/lib/java_buildpack/framework/postgresql_jdbc.rb b/lib/java_buildpack/framework/postgresql_jdbc.rb index d0bf03b59c..71c4846432 100644 --- a/lib/java_buildpack/framework/postgresql_jdbc.rb +++ b/lib/java_buildpack/framework/postgresql_jdbc.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/framework/protect_app_security_provider.rb b/lib/java_buildpack/framework/protect_app_security_provider.rb new file mode 100644 index 0000000000..b2a0c5eadb --- /dev/null +++ b/lib/java_buildpack/framework/protect_app_security_provider.rb @@ -0,0 +1,153 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'shellwords' +require 'tempfile' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' +require 'java_buildpack/util/qualify_path' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for enabling zero-touch Safenet ProtectApp Java Security Provider support. + class ProtectAppSecurityProvider < JavaBuildpack::Component::VersionedDependencyComponent + include JavaBuildpack::Util + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_zip false + + @droplet.copy_resources + @droplet.security_providers << 'com.ingrian.security.nae.IngrianProvider' + @droplet.additional_libraries << protect_app_jar if @droplet.java_home.java_9_or_later? + + credentials = @application.services.find_service(FILTER, 'client', 'trusted_certificates')['credentials'] + + pkcs12 = merge_client_credentials credentials['client'] + add_client_credentials pkcs12 + + add_trusted_certificates credentials['trusted_certificates'] + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + if @droplet.java_home.java_9_or_later? + @droplet.additional_libraries << protect_app_jar + else + @droplet.extension_directories << ext_dir + end + + credentials = @application.services.find_service(FILTER)['credentials'] + java_opts = @droplet.java_opts + + java_opts + .add_system_property('com.ingrian.security.nae.IngrianNAE_Properties_Conf_Filename', + @droplet.sandbox + 'IngrianNAE.properties') + .add_system_property('com.ingrian.security.nae.Key_Store_Location', keystore) + .add_system_property('com.ingrian.security.nae.Key_Store_Password', password) + + add_additional_properties(credentials, java_opts) + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.one_service? FILTER, 'client', 'trusted_certificates' + end + + private + + FILTER = /protectapp/.freeze + + private_constant :FILTER + + def add_additional_properties(credentials, java_opts) + credentials + .reject { |key, _| key =~ /^client$/ || key =~ /^trusted_certificates$/ } + .each { |key, value| java_opts.add_system_property("com.ingrian.security.nae.#{key}", value) } + end + + def add_client_credentials(pkcs12) + shell "#{keytool} -importkeystore -noprompt -destkeystore #{keystore} -deststorepass #{password} " \ + "-srckeystore #{pkcs12.path} -srcstorepass #{password} -srcstoretype pkcs12" \ + " -alias #{File.basename(pkcs12)}" + end + + def add_trusted_certificates(trusted_certificates) + trusted_certificates.each do |certificate| + pem = write_certificate certificate + + shell "#{keytool} -importcert -noprompt -keystore #{keystore} -storepass #{password} " \ + "-file #{pem.path} -alias #{File.basename(pem)}" + end + end + + def ext_dir + @droplet.sandbox + 'ext' + end + + def keystore + @droplet.sandbox + 'nae-keystore.jks' + end + + def keytool + @droplet.java_home.root + 'bin/keytool' + end + + def merge_client_credentials(credentials) + certificate = write_certificate credentials['certificate'] + private_key = write_private_key credentials['private_key'] + + pkcs12 = Tempfile.new('pkcs12-') + pkcs12.close + + shell "openssl pkcs12 -export -in #{certificate.path} -inkey #{private_key.path} " \ + "-name #{File.basename(pkcs12)} -out #{pkcs12.path} -passout pass:#{password}" + + pkcs12 + end + + def password + 'nae-keystore-password' + end + + def protect_app_jar + ext_dir + "IngrianNAE-#{@version}.000.jar" + end + + def write_certificate(certificate) + Tempfile.open('certificate-') do |f| + f.write "#{certificate}\n" + f.sync + f + end + end + + def write_private_key(private_key) + Tempfile.open('private-key-') do |f| + f.write "#{private_key}\n" + f.sync + f + end + end + + end + end +end diff --git a/lib/java_buildpack/framework/riverbed_appinternals_agent.rb b/lib/java_buildpack/framework/riverbed_appinternals_agent.rb new file mode 100644 index 0000000000..71e502af71 --- /dev/null +++ b/lib/java_buildpack/framework/riverbed_appinternals_agent.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for running the Riverbed Appinternals Agent support. + class RiverbedAppinternalsAgent < JavaBuildpack::Component::VersionedDependencyComponent + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_zip(false, @droplet.sandbox, @component_name) + @droplet.copy_resources + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + @droplet.environment_variables + .add_environment_variable('AIX_INSTRUMENT_ALL', 1) + .add_environment_variable('DSA_PORT', dsa_port(credentials)) + .add_environment_variable('RVBD_AGENT_FILES', 1) + .add_environment_variable('RVBD_AGENT_PORT', agent_port(credentials)) + .add_environment_variable('RVBD_JBP_VERSION', @version) + + @droplet.java_opts.add_agentpath(agent_path) + + return unless rvbd_moniker(credentials) + + @droplet.java_opts.add_system_property('riverbed.moniker', rvbd_moniker(credentials)) + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.one_service?(FILTER) + end + + private + + FILTER = /appinternals/.freeze + + private_constant :FILTER + + def agent_path + @droplet.sandbox + 'agent/lib' + lib_name + end + + def credentials + service['credentials'] unless service.nil? + end + + def service + @application.services.find_service(FILTER) + end + + def agent_port(credentials) + credentials['rvbd_agent_port'] || 7073 + end + + def architecture + `uname -m`.strip + end + + def dsa_port(credentials) + credentials['rvbd_dsa_port'] || 2111 + end + + def lib_name + %w[x86_64 i686].include?(architecture) ? 'librpilj64.so' : 'librpilj.so' + end + + def rvbd_moniker(credentials) + credentials['rvbd_moniker'] || @configuration['rvbd_moniker'] + end + + end + end +end diff --git a/lib/java_buildpack/framework/sealights_agent.rb b/lib/java_buildpack/framework/sealights_agent.rb new file mode 100644 index 0000000000..3e6331a7fe --- /dev/null +++ b/lib/java_buildpack/framework/sealights_agent.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/framework' +require 'java_buildpack/component/versioned_dependency_component' +require 'shellwords' +require 'fileutils' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for enabling zero-touch Sealights support. + class SealightsAgent < JavaBuildpack::Component::VersionedDependencyComponent + # include JavaBuildpack::Util + + def initialize(context) + super(context) + @logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger SealightsAgent + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_zip(false) + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + @droplet.java_opts.add_javaagent(agent) + credentials = @application.services.find_service(FILTER, TOKEN)['credentials'] + @droplet.java_opts.add_system_property('sl.token', Shellwords.escape(credentials[TOKEN])) + @droplet.java_opts.add_system_property('sl.tags', 'pivotal_cloud_foundry') + + # add sl.enableUpgrade system property + @droplet.java_opts.add_system_property('sl.enableUpgrade', @configuration[ENABLE_UPGRADE] ? 'true' : 'false') + + # add sl.proxy system property if defined (either in config or user provisioned service) + add_system_property_from_cfg_or_svc credentials, 'sl.proxy', PROXY + + # add sl.labId system property if defined (either in config or user provisioned service) + add_system_property_from_cfg_or_svc credentials, 'sl.labId', LAB_ID + + # add build session if defined in config + add_system_property 'sl.buildSessionId', BUILD_SESSION_ID + end + + # wrapper for setting system properties on the droplet from configuration keys + def add_system_property(system_property, config_key) + return unless @configuration.key?(config_key) + + @droplet.java_opts.add_system_property(system_property, Shellwords.escape(@configuration[config_key])) + end + + # add a system property based on either plugin configuration (which takes precedence) or user provisioned service + def add_system_property_from_cfg_or_svc(svc, system_property, config_key) + if @configuration.key?(config_key) + @droplet.java_opts.add_system_property(system_property, Shellwords.escape(@configuration[config_key])) + elsif svc.key?(config_key) + @droplet.java_opts.add_system_property(system_property, Shellwords.escape(svc[config_key])) + end + end + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.one_service? FILTER, TOKEN + end + + private + + def agent + @droplet.sandbox + "sl-test-listener-#{@version}.jar" + end + + # Configuration property names + TOKEN = 'token' + + ENABLE_UPGRADE = 'enable_upgrade' + + BUILD_SESSION_ID = 'build_session_id' + + LAB_ID = 'lab_id' + + PROXY = 'proxy' + + FILTER = /sealights/.freeze + + private_constant :TOKEN, :ENABLE_UPGRADE, :BUILD_SESSION_ID, :LAB_ID, :PROXY, :FILTER + + end + + end +end diff --git a/lib/java_buildpack/framework/seeker_security_provider.rb b/lib/java_buildpack/framework/seeker_security_provider.rb new file mode 100644 index 0000000000..face7bc672 --- /dev/null +++ b/lib/java_buildpack/framework/seeker_security_provider.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component/base_component' +require 'java_buildpack/framework' +require 'java_buildpack/util/dash_case' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for enabling zero-touch Seeker support. + class SeekerSecurityProvider < JavaBuildpack::Component::BaseComponent + + # Creates an instance + # + # @param [Hash] context a collection of utilities used the component + def initialize(context) + super(context) + + @uri = download_url(credentials) if supports? + end + + # (see JavaBuildpack::Component::BaseComponent#detect) + def detect + @uri ? self.class.to_s.dash_case : nil + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + JavaBuildpack::Util::Cache::InternetAvailability.instance.available( + true, 'Downloading from Synopsys Seeker Server' + ) do + download_zip('', @uri, false, @droplet.sandbox, @component_name) + end + @droplet.copy_resources + rescue StandardError => e + raise "Synopsys Seeker download failed: #{e}" + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + c = credentials + + @droplet.java_opts.add_javaagent(@droplet.sandbox + 'seeker-agent.jar') + @droplet.environment_variables + .add_environment_variable('SEEKER_SERVER_URL', c[SEEKER_SERVER_URL_CONFIG_KEY]) + end + + private + + # Relative path of the agent zip + AGENT_PATH = '/rest/api/latest/installers/agents/binaries/JAVA' + + # seeker service name identifier + FILTER = /seeker/i.freeze + + # JSON key for the address of seeker sensor + SEEKER_SERVER_URL_CONFIG_KEY = 'seeker_server_url' + + private_constant :AGENT_PATH, :FILTER, :SEEKER_SERVER_URL_CONFIG_KEY + + def credentials + @application.services.find_service(FILTER, SEEKER_SERVER_URL_CONFIG_KEY)['credentials'] + end + + def download_url(credentials) + "#{credentials[SEEKER_SERVER_URL_CONFIG_KEY]}#{AGENT_PATH}" + end + + def supports? + @application.services.one_service?(FILTER, SEEKER_SERVER_URL_CONFIG_KEY) + end + + end + + end + +end diff --git a/lib/java_buildpack/framework/sky_walking_agent.rb b/lib/java_buildpack/framework/sky_walking_agent.rb new file mode 100644 index 0000000000..c914f091f5 --- /dev/null +++ b/lib/java_buildpack/framework/sky_walking_agent.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for enabling Sky walking APM support. + class SkyWalkingAgent < JavaBuildpack::Component::VersionedDependencyComponent + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_tar(true, @droplet.sandbox, 'sky_walking_agent') + @droplet.copy_resources + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + credentials = @application.services.find_service(FILTER, 'servers')['credentials'] + java_opts = @droplet.java_opts + java_opts.add_javaagent(@droplet.sandbox + 'skywalking-agent.jar') + + application_name java_opts, credentials + sample_n_per_3_secs java_opts, credentials + span_limit_per_segment java_opts, credentials + ignore_suffix java_opts, credentials + open_debugging_class java_opts, credentials + servers java_opts, credentials + logging_level java_opts, credentials + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.one_service? FILTER, 'servers' + end + + private + + FILTER = /sky-?walking/.freeze + + private_constant :FILTER + + def servers(java_opts, credentials) + servers = credentials['servers'] + raise "'servers' credential must be set" unless servers + + java_opts.add_system_property 'skywalking.collector.servers', servers + end + + def application_name(java_opts, credentials) + name = credentials['application-name'] || @configuration['default_application_name'] || + @application.details['application_name'] + java_opts.add_system_property('skywalking.agent.application_code', name.to_s) + end + + def sample_n_per_3_secs(java_opts, credentials) + sample_n_per_3_secs = credentials['sample-n-per-3-secs'] + java_opts.add_system_property 'skywalking.agent.sample_n_per_3_secs', sample_n_per_3_secs if sample_n_per_3_secs + end + + def span_limit_per_segment(java_opts, credentials) + span_lmt_per_seg = credentials['span-limit-per-segment'] + java_opts.add_system_property 'skywalking.agent.span_limit_per_segment', span_lmt_per_seg if span_lmt_per_seg + end + + def ignore_suffix(java_opts, credentials) + ignore_suffix = credentials['ignore-suffix'] + java_opts.add_system_property 'skywalking.agent.ignore_suffix', ignore_suffix if ignore_suffix + end + + def open_debugging_class(java_opts, credentials) + is_debug_class = credentials['is-open-debugging-class'] + java_opts.add_system_property 'skywalking.agent.is_open_debugging_class', is_debug_class if is_debug_class + end + + def logging_level(java_opts, credentials) + logging_level = credentials['logging-level'] + java_opts.add_system_property 'skywalking.logging.level', logging_level if logging_level + end + + end + + end +end diff --git a/lib/java_buildpack/framework/splunk_otel_java_agent.rb b/lib/java_buildpack/framework/splunk_otel_java_agent.rb new file mode 100644 index 0000000000..f3361a869d --- /dev/null +++ b/lib/java_buildpack/framework/splunk_otel_java_agent.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' + +module JavaBuildpack + module Framework + + # Main class for adding the Splunk OpenTelemetry instrumentation agent + class SplunkOtelJavaAgent < JavaBuildpack::Component::VersionedDependencyComponent + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_jar + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + java_opts = @droplet.java_opts + java_opts.add_javaagent(@droplet.sandbox + jar_name) + + credentials = @application.services.find_service(REQUIRED_SERVICE_NAME_FILTER)['credentials'] + # Add all otel.* and splunk.* credentials from the service bind as jvm system properties + credentials&.each do |key, value| + java_opts.add_system_property(key, value) if key.start_with?('splunk.') || key.start_with?('otel.') + end + + # Set the otel.service.name to the application_name if not specified in credentials + return if credentials.key? 'otel.service.name' + + app_name = @application.details['application_name'] + java_opts.add_system_property('otel.service.name', app_name) + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.one_service? REQUIRED_SERVICE_NAME_FILTER + end + + # bound service must contain the string `splunk-o11y` + REQUIRED_SERVICE_NAME_FILTER = /splunk-o11y/.freeze + + end + end +end diff --git a/lib/java_buildpack/framework/spring_auto_reconfiguration.rb b/lib/java_buildpack/framework/spring_auto_reconfiguration.rb index 68e9a4c823..f53646e240 100644 --- a/lib/java_buildpack/framework/spring_auto_reconfiguration.rb +++ b/lib/java_buildpack/framework/spring_auto_reconfiguration.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,9 +16,7 @@ # limitations under the License. require 'java_buildpack/component/versioned_dependency_component' -require 'java_buildpack/logging/logger_factory' require 'java_buildpack/framework' -require 'java_buildpack/framework/spring_auto_reconfiguration/web_xml_modifier' module JavaBuildpack module Framework @@ -26,9 +25,6 @@ module Framework # applications. class SpringAutoReconfiguration < JavaBuildpack::Component::VersionedDependencyComponent - # Creates an instance - # - # @param [Hash] context a collection of utilities used the component def initialize(context) super(context) @logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger SpringAutoReconfiguration @@ -36,14 +32,19 @@ def initialize(context) # (see JavaBuildpack::Component::BaseComponent#compile) def compile + log_warning_scc_manual if spring_cloud_connectors? + return if java_cf_env_framework? + download_jar @droplet.additional_libraries << (@droplet.sandbox + jar_name) - modify_web_xml + log_warning_sar_scc_auto end # (see JavaBuildpack::Component::BaseComponent#release) def release + return if java_cf_env_framework? + @droplet.additional_libraries << (@droplet.sandbox + jar_name) end @@ -51,32 +52,58 @@ def release # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) def supports? - @configuration['enabled'] && (@droplet.root + '**/*spring-core*.jar').glob.any? + @configuration['enabled'] && spring? && !java_cfenv? end private - def modify_web_xml - web_xml = @droplet.root + 'WEB-INF/web.xml' + def spring? + (@droplet.root + '**/*spring-core*.jar').glob.any? + end - return unless web_xml.exist? + def java_cfenv? + (@droplet.root + '**/*java-cfenv*.jar').glob.any? || java_cf_env_framework? + end - puts ' Modifying /WEB-INF/web.xml for Auto Reconfiguration' - @logger.debug { " Original web.xml: #{web_xml.read}" } + def java_cf_env_framework? + @droplet.additional_libraries.any? do |additional_library| + additional_library.instance_variable_get(:@pathname).fnmatch?('*java_cf_env*.jar') + end + end - modifier = web_xml.open { |file| WebXmlModifier.new(file) } - modifier.augment_root_context - modifier.augment_servlet_contexts + def spring_cloud_connectors? + (@droplet.root + '**/spring-cloud-cloudfoundry-connector*.jar').glob.any? || + (@droplet.root + '**/spring-cloud-spring-service-connector*.jar').glob.any? + end - web_xml.open('w') do |file| - file.write(modifier.to_s) - file.fsync + def log_warning_scc_manual + @logger.warn do + 'ATTENTION: The Spring Cloud Connectors library is present in your application. This library ' \ + 'has been in maintenance mode since July 2019 and will stop receiving all updates after ' \ + 'Mar 2024.' + end + @logger.warn do + 'Please migrate to java-cfenv immediately. See https://via.vmw.com/EiBW for migration instructions.' \ end - - @logger.debug { " Modified web.xml: #{web_xml.read}" } end + def log_warning_sar_scc_auto + @logger.warn do + 'ATTENTION: The Spring Auto Reconfiguration and shaded Spring Cloud Connectors libraries are ' \ + 'being installed. These projects have been deprecated, are no longer receiving updates and should ' \ + 'not be used going forward.' + end + @logger.warn do + 'If you are not using these libraries, set `JBP_CONFIG_SPRING_AUTO_RECONFIGURATION=\'{enabled: false}\'` ' \ + 'to disable their installation and clear this warning message. The buildpack will switch its default ' \ + 'to disable by default after March 2023. Spring Auto Reconfiguration and its shaded Spring Cloud ' \ + 'Connectors will be removed from the buildpack after March 2024.' + end + @logger.warn do + 'If you are using these libraries, please migrate to java-cfenv immediately. ' \ + 'See https://via.vmw.com/EiBW for migration instructions. Once you upgrade this message will go away.' + end + end end - end end diff --git a/lib/java_buildpack/framework/spring_auto_reconfiguration/web_xml_modifier.rb b/lib/java_buildpack/framework/spring_auto_reconfiguration/web_xml_modifier.rb deleted file mode 100644 index 4fbf029924..0000000000 --- a/lib/java_buildpack/framework/spring_auto_reconfiguration/web_xml_modifier.rb +++ /dev/null @@ -1,128 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'java_buildpack/framework' -require 'rexml/document' -require 'rexml/formatters/pretty' - -module JavaBuildpack - module Framework - - # A class that encapsulates the modification of a +web.xml+ Servlet configuration file for the Auto-reconfiguration - # framework. The modifications of +web.xml+ consist of augmenting +contextInitializerClasses+. The function starts - # by enumerating the current +contextInitializerClasses+. If none exist, a default configuration is created with no - # value as the default. The +org.cloudfoundry.reconfiguration.spring.CloudProfileApplicationContextInitializer+, - # +org.cloudfoundry.reconfiguration.spring.CloudPropertySourceApplicationContextInitializer+, and - # +org.cloudfoundry.reconfiguration.spring.CloudAutoReconfigurationApplicationContextInitializer+ classes are then - # added to the collection of classes. - class WebXmlModifier - - # Creates a new instance of the modifier. - # - # @param [REXML::Document, String, IO] source the content of the +web.xml+ file to modify - def initialize(source) - @document = REXML::Document.new(source) - end - - # Make modifications to the root context - # - # @return [Void] - def augment_root_context - augment web_app(@document), 'context-param' if context_loader_listener? - end - - # Make modifications to the the servlet contexts - # - # @return [Void] - def augment_servlet_contexts - servlets.each do |servlet| - augment servlet, 'init-param' - end - end - - # Returns a +String+ representation of the modified +web.xml+. - # - # @return [String] a +String+ representation of the modified +web.xml+. - def to_s - output = '' - formatter.write(@document, output) - output << "\n" - - output - end - - private - - CONTEXT_INITIALIZER_ADDITIONAL = %w( - org.cloudfoundry.reconfiguration.spring.CloudProfileApplicationContextInitializer - org.cloudfoundry.reconfiguration.spring.CloudPropertySourceApplicationContextInitializer - org.cloudfoundry.reconfiguration.spring.CloudAutoReconfigurationApplicationContextInitializer).freeze - - CONTEXT_INITIALIZER_CLASSES = 'contextInitializerClasses'.freeze - - CONTEXT_LOADER_LISTENER = 'ContextLoaderListener'.freeze - - DISPATCHER_SERVLET = 'DispatcherServlet'.freeze - - private_constant :CONTEXT_INITIALIZER_CLASSES, :CONTEXT_LOADER_LISTENER, :DISPATCHER_SERVLET - - def augment(root, param_type) - classes_string = xpath(root, "#{param_type}[param-name[contains(text(), - '#{CONTEXT_INITIALIZER_CLASSES}')]]/param-value/text()").first - classes_string = create_param(root, param_type, CONTEXT_INITIALIZER_CLASSES, '') unless classes_string - - classes = classes_string.value.strip.split(/[,;\s]+/) - classes = classes.concat CONTEXT_INITIALIZER_ADDITIONAL - - classes_string.value = classes.join(',') # rubocop:disable Lint/UselessSetterCall - end - - def context_loader_listener? - xpath(@document, "/web-app/listener/listener-class[contains(text(), '#{CONTEXT_LOADER_LISTENER}')]").any? - end - - def create_param(root, param_type, name, value) - param = REXML::Element.new param_type, root - - param_name = REXML::Element.new 'param-name', param - REXML::Text.new name, true, param_name - - param_value = REXML::Element.new 'param-value', param - REXML::Text.new value, true, param_value - end - - def formatter - formatter = REXML::Formatters::Pretty.new(4) - formatter.compact = true - formatter - end - - def servlets - xpath(@document, "/web-app/servlet[servlet-class[contains(text(), '#{DISPATCHER_SERVLET}')]]") - end - - def web_app(root) - xpath(root, '/web-app').first - end - - def xpath(root, path) - REXML::XPath.match(root, path) - end - - end - - end -end diff --git a/lib/java_buildpack/framework/spring_insight.rb b/lib/java_buildpack/framework/spring_insight.rb index f8377cdad4..9adcf32548 100644 --- a/lib/java_buildpack/framework/spring_insight.rb +++ b/lib/java_buildpack/framework/spring_insight.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -33,7 +34,7 @@ class SpringInsight < JavaBuildpack::Component::BaseComponent # @param [Hash] context a collection of utilities used the component def initialize(context) super(context) - @version, @uri, @agent_id, @agent_pass = supports? ? find_insight_agent : [nil, nil, nil, nil] + @version, @uri, @agent_transport = find_insight_agent if supports? end # (see JavaBuildpack::Component::BaseComponent#detect) @@ -44,29 +45,26 @@ def detect # (see JavaBuildpack::Component::BaseComponent#compile) def compile JavaBuildpack::Util::Cache::InternetAvailability.instance.available( - true, 'The Spring Insight download location is always accessible') do - # TODO: AGENT_DOWNLOAD_URI_SUFFIX To be removed once the full path is included in VCAP_SERVICES see #58873498 - download(@version, @uri.chomp('/') + AGENT_DOWNLOAD_URI_SUFFIX) { |file| expand file } + true, 'The Spring Insight download location is always accessible' + ) do + download(@version, @uri) { |file| expand file } end end # (see JavaBuildpack::Component::BaseComponent#release) def release - @droplet.java_opts + @droplet + .java_opts .add_javaagent(weaver_jar) .add_system_property('insight.base', insight_directory) .add_system_property('insight.logs', logs_directory) .add_system_property('aspectj.overweaving', true) .add_system_property('org.aspectj.tracing.factory', 'default') - .add_system_property('insight.transport.type', 'HTTP') - - add_agent_configuration end protected - # The unique identifier of the component, incorporating the version of the dependency (e.g. - # +spring-insight=1.9.3+) + # The unique identifier of the component, incorporating the version of the dependency) # # @param [String] version the version of the dependency # @return [String] the unique identifier of the component @@ -76,24 +74,9 @@ def id(version) private - # TODO: To be removed once the full path is included in VCAP_SERVICES see issue 58873498 - AGENT_DOWNLOAD_URI_SUFFIX = '/services/config/agent-download'.freeze + FILTER = /p-insight/.freeze - FILTER = /insight/.freeze - - private_constant :AGENT_DOWNLOAD_URI_SUFFIX, :FILTER - - def add_agent_configuration - @droplet.java_opts - .add_system_property('agent.http.protocol', 'http') - .add_system_property('agent.http.host', URI(@uri).host) - .add_system_property('agent.http.port', 80) - .add_system_property('agent.http.context.path', 'insight') - .add_system_property('agent.http.username', @agent_id) - .add_system_property('agent.http.password', @agent_pass) - .add_system_property('agent.http.send.json', false) - .add_system_property('agent.http.use.proxy', false) - end + private_constant :FILTER def expand(file) with_timing "Expanding Spring Insight to #{@droplet.sandbox.relative_path_from(@droplet.root)}" do @@ -112,6 +95,9 @@ def unpack_agent_installer(root, file) FileUtils.mkdir_p(agent_dir) shell "unzip -qq #{file.path} -d #{installer_dir} 2>&1" shell "unzip -qq #{uber_agent_zip(installer_dir)} -d #{agent_dir} 2>&1" + move agent_dir, + installer_dir + 'answers.properties', + installer_dir + 'agent.override.properties' agent_dir end @@ -119,35 +105,34 @@ def unpack_agent_installer(root, file) def install_insight(agent_dir) root = Pathname.glob(agent_dir + 'springsource-insight-uber-agent-*')[0] - init_container_libs root - init_insight_cloudfoundry_agent_plugin root init_insight root + init_insight_properties agent_dir init_insight_agent_plugins root init_weaver root end - def init_container_libs(root) - move container_libs_directory, - root + 'agents/common/insight-bootstrap-generic-*.jar', - root + 'agents/tomcat/7/lib/insight-bootstrap-tomcat-common-*.jar', - root + 'agents/tomcat/7/lib/insight-agent-*.jar' - end - def init_insight(root) move insight_directory, root + 'insight/collection-plugins', - root + 'insight/conf' + root + 'insight/conf', + root + 'insight/bootstrap', + root + 'insight/extras' end - def init_insight_agent_plugins(root) - move insight_directory + 'agent-plugins', - root + 'transport/http/insight-agent-http-*.jar', - root + 'cloudfoundry/insight-agent-cloudfoundry-*.jar' + def init_insight_properties(root) + move insight_directory, + root + 'agent.override.properties' + + answers_properties = root + 'answers.properties' + insight_properties = insight_directory + 'conf/insight.properties' + system "cat #{answers_properties} >> #{insight_properties}" end - def init_insight_cloudfoundry_agent_plugin(root) - move container_libs_directory, - root + 'cloudfoundry/cloudfoundry-runtime-*.jar' + def init_insight_agent_plugins(root) + move insight_directory + 'agent-plugins', + root + 'agents/tomcat/7/lib/insight-agent-*.jar' + transport_jar = transport_plugin root + move insight_directory + 'agent-plugins', transport_jar end def init_weaver(root) @@ -155,18 +140,13 @@ def init_weaver(root) root + 'cloudfoundry/insight-weaver-*.jar' end - def container_libs_directory - @droplet.root + '.spring-insight/container-libs' - end - def find_insight_agent - service = @application.services.find_service FILTER - version = service['label'].match(/(.*)-(.*)/)[2] + service = @application.services.find_service FILTER, 'agent_download_url', 'service_instance_id' credentials = service['credentials'] - uri = credentials['dashboard_url'] - id = credentials['agent_username'] - pass = credentials['agent_password'] - [version, uri, id, pass] + version = credentials['version'] || '1.0.0' + uri = credentials['agent_download_url'] + transport = credentials['agent_transport'] || 'rabbitmq' + [version, uri, transport] end def insight_directory @@ -186,12 +166,13 @@ def move(destination, *globs) end def supports? - @application.services.one_service? FILTER, 'dashboard_url', 'agent_username', 'agent_password' + @application.services.one_service? FILTER, 'agent_download_url', 'service_instance_id' end def uber_agent_zip(location) candidates = Pathname.glob(location + 'springsource-insight-uber-agent-*.zip') - fail 'There was not exactly one Uber Agent zip' if candidates.size != 1 + raise 'There was not exactly one Uber Agent zip' if candidates.size != 1 + candidates[0] end @@ -203,6 +184,26 @@ def weaver_jar (weaver_directory + 'insight-weaver-*.jar').glob[0] end + def transport_plugin(root) + return root + 'transport/http/insight-agent-http-*.jar' if http_transport? + return root + 'transport/rabbitmq/insight-agent-rabbitmq-*.jar' if rabbit_transport? + + (root + 'transport/activemq/insight-agent-activemq-*.jar') if active_transport? + end + + def http_transport? + @agent_transport.eql? 'http' + end + + def rabbit_transport? + @agent_transport.eql? 'rabbitmq' + end + + def active_transport? + @agent_transport.eql? 'activemq' + end + end + end end diff --git a/lib/java_buildpack/framework/takipi_agent.rb b/lib/java_buildpack/framework/takipi_agent.rb new file mode 100644 index 0000000000..ceeae84b97 --- /dev/null +++ b/lib/java_buildpack/framework/takipi_agent.rb @@ -0,0 +1,116 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' +require 'java_buildpack/util/qualify_path' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for enabling zero-touch OverOps (fka Takipi) support. + class TakipiAgent < JavaBuildpack::Component::VersionedDependencyComponent + include JavaBuildpack::Util + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_tar + @droplet.copy_resources + FileUtils.mkdir_p @droplet.sandbox + 'log/agents/' + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + @droplet.java_opts + .add_agentpath(agent) + .add_system_property('takipi.name', application_name) + + update_java9 + + @droplet.environment_variables + .add_environment_variable('LD_LIBRARY_PATH', + "$LD_LIBRARY_PATH:#{qualify_path(lib, @droplet.root)}") + .add_environment_variable('JVM_LIB_FILE', libjvm) + .add_environment_variable('TAKIPI_HOME', @droplet.sandbox) + .add_environment_variable('TAKIPI_MACHINE_NAME', node_name) + + config_env_vars @application.services.find_service(FILTER, [SECRET_KEY, COLLECTOR_HOST])['credentials'] + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.one_service? FILTER, [SECRET_KEY, COLLECTOR_HOST] + end + + private + + COLLECTOR_HOST = 'collector_host' + + FILTER = /takipi/.freeze + + SECRET_KEY = 'secret_key' + + private_constant :COLLECTOR_HOST, :FILTER, :SECRET_KEY + + def agent + @droplet.sandbox + 'lib/libTakipiAgent.so' + end + + def application_name + @configuration['application_name'] || @application.details['application_name'] + end + + def config_env_vars(credentials) + env = @droplet.environment_variables + + collector_host = credentials['collector_host'] + env.add_environment_variable 'TAKIPI_COLLECTOR_HOST', collector_host if collector_host + + collector_port = credentials['collector_port'] + env.add_environment_variable 'TAKIPI_COLLECTOR_PORT', collector_port if collector_port + + secret_key = credentials['secret_key'] + env.add_environment_variable 'TAKIPI_SECRET_KEY', secret_key if secret_key + end + + def lib + @droplet.sandbox + 'lib' + end + + def libjvm + @droplet.java_home.root + 'lib/amd64/server/libjvm.so' + end + + def node_name + "#{@configuration['node_name_prefix']}-$CF_INSTANCE_INDEX" + end + + def update_java9 + return unless @droplet.java_home.java_9_or_later? + + @droplet.java_opts + .add_preformatted_options('-Xshare:off') + .add_preformatted_options('-XX:-UseTypeSpeculation') + end + + end + + end +end diff --git a/lib/java_buildpack/framework/your_kit_profiler.rb b/lib/java_buildpack/framework/your_kit_profiler.rb new file mode 100644 index 0000000000..1d648f0ba3 --- /dev/null +++ b/lib/java_buildpack/framework/your_kit_profiler.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' +require 'java_buildpack/util/qualify_path' + +module JavaBuildpack + module Framework + + # Encapsulates the functionality for enabling zero-touch YourKit profiler support. + class YourKitProfiler < JavaBuildpack::Component::VersionedDependencyComponent + include JavaBuildpack::Util + + def initialize(context, &version_validator) + super(context, &version_validator) + @component_name = 'YourKit Profiler' + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download(@version, @uri, @component_name) do |file| + FileUtils.mkdir_p @droplet.sandbox + FileUtils.cp_r(file.path, file_name) + end + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + @droplet + .java_opts + .add_agentpath_with_props(file_name, + 'dir' => snapshots, 'logdir' => logs, + 'port' => port, 'sessionname' => session_name) + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @configuration['enabled'] + end + + private + + def file_name + @droplet.sandbox + "#{@droplet.component_id}-#{@version}" + end + + def logs + qualify_path(@droplet.sandbox + 'logs', @droplet.root) + end + + def port + @configuration['port'] || 10_001 + end + + def session_name + @configuration['default_session_name'] || @application.details['application_name'] + end + + def snapshots + qualify_path(@droplet.sandbox + 'snapshots', @droplet.root) + end + + end + + end +end diff --git a/lib/java_buildpack/jre.rb b/lib/java_buildpack/jre.rb index 189f0b6929..44ee90454b 100644 --- a/lib/java_buildpack/jre.rb +++ b/lib/java_buildpack/jre.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/jre/graal_vm_jre.rb b/lib/java_buildpack/jre/graal_vm_jre.rb new file mode 100644 index 0000000000..3623238579 --- /dev/null +++ b/lib/java_buildpack/jre/graal_vm_jre.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/jre' +require 'java_buildpack/jre/open_jdk_like' + +module JavaBuildpack + module Jre + + # Encapsulates the detect, compile, and release functionality for selecting an GraalVM JRE. + class GraalVmJRE < OpenJDKLike + end + + end +end diff --git a/lib/java_buildpack/jre/ibm_jre.rb b/lib/java_buildpack/jre/ibm_jre.rb new file mode 100644 index 0000000000..cc64e2dac0 --- /dev/null +++ b/lib/java_buildpack/jre/ibm_jre.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component/modular_component' +require 'java_buildpack/jre' +require 'java_buildpack/jre/ibm_jre_initializer' +require 'java_buildpack/jre/jvmkill_agent' +require 'java_buildpack/jre/open_jdk_like_security_providers' + +module JavaBuildpack + module Jre + + # Encapsulates the detect, compile, and release functionality for selecting a JRE. + class IbmJRE < JavaBuildpack::Component::ModularComponent + + protected + + # (see JavaBuildpack::Component::ModularComponent#command) + def command + # no command need to be executed + end + + # (see JavaBuildpack::Component::ModularComponent#sub_components) + def sub_components(context) + [ + IbmJreInitializer.new(sub_configuration_context(context, 'jre') + .merge(component_name: self.class.to_s.space_case)), + JvmkillAgent.new(sub_configuration_context(context, 'jvmkill_agent')), + OpenJDKLikeSecurityProviders.new(context) + ] + end + + # (see JavaBuildpack::Component::ModularComponent#supports?) + def supports? + true + end + + end + + end +end diff --git a/lib/java_buildpack/jre/ibm_jre_initializer.rb b/lib/java_buildpack/jre/ibm_jre_initializer.rb new file mode 100644 index 0000000000..8b53ac9452 --- /dev/null +++ b/lib/java_buildpack/jre/ibm_jre_initializer.rb @@ -0,0 +1,188 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'tempfile' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/jre' +require 'java_buildpack/util/tokenized_version' + +module JavaBuildpack + module Jre + + # Encapsulates the detect, compile, and release functionality for selecting a JRE. + class IbmJreInitializer < JavaBuildpack::Component::VersionedDependencyComponent + + # Creates an instance + # + # @param [Hash] context a collection of utilities used the component + def initialize(context) + @application = context[:application] + @component_name = context[:component_name] + @configuration = context[:configuration] + @droplet = context[:droplet] + + @droplet.java_home.root = @droplet.sandbox + 'jre/' + end + + # (see JavaBuildpack::Component::BaseComponent#detect) + def detect + @version, @uri = JavaBuildpack::Repository::ConfiguredItem.find_item(@component_name, + @configuration) + @droplet.java_home.version = @version + super + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download(@version, @uri, @component_name) do |file| + with_timing "Installing #{@component_name} to #{@droplet.sandbox.relative_path_from(@droplet.root)}" do + install_bin(@droplet.sandbox, file) + end + end + @droplet.copy_resources + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + @droplet + .java_opts + .add_system_property('java.io.tmpdir', '$TMPDIR') + .add_preformatted_options('-Xtune:virtualized') + .add_preformatted_options('-Xshareclasses:none') + + @droplet.java_opts.concat mem_opts + end + + private + + # constant HEAP_RATIO is the ratio of memory assigned to the heap + # as against the container total and is set using -Xmx. + HEAP_RATIO = 0.75 + + KILO = 1024 + + private_constant :HEAP_RATIO, :KILO + + def install_bin(target_directory, file) + FileUtils.mkdir_p target_directory + response_file = Tempfile.new('response.properties') + response_file.puts('INSTALLER_UI=silent') + response_file.puts('LICENSE_ACCEPTED=TRUE') + response_file.puts("USER_INSTALL_DIR=#{target_directory}") + response_file.close + + File.chmod(0o755, file.path) unless File.executable?(file.path) + shell "#{file.path} -i silent -f #{response_file.path} 2>&1" + end + + def mem_opts + mopts = [] + total_memory = memory_limit_finder + if total_memory.nil? + # if no memory option has been set by cloudfoundry, we just assume defaults + else + calculated_heap_ratio = heap_ratio_verification(heap_ratio) + heap_size = heap_size_calculator(total_memory, calculated_heap_ratio) + mopts.push "-Xmx#{heap_size}" + end + mopts + end + + def heap_ratio + @configuration['heap_ratio'] || HEAP_RATIO + end + + def memory_limit_finder + memory_limit = ENV.fetch('MEMORY_LIMIT', nil) + return nil unless memory_limit + + memory_limit_size = memory_size_bytes(memory_limit) + raise "Invalid negative $MEMORY_LIMIT #{memory_limit}" if memory_limit_size.negative? + + memory_limit_size + end + + def memory_size_bytes(size) + if size == '0' + bytes = 0 + else + raise "Invalid memory size '#{size}'" if !size || size.length < 2 + + unit = size[-1] + value = size[0..-2] + raise "Invalid memory size '#{size}'" unless check_is_integer? value + + value = size.to_i + # store the bytes + bytes = calculate_bytes(unit, value) + end + bytes + end + + def calculate_bytes(unit, value) + if %w[b B].include?(unit) + bytes = value + elsif %w[k K].include?(unit) + bytes = KILO * value + elsif %w[m M].include?(unit) + bytes = KILO * KILO * value + elsif %w[g G].include?(unit) + bytes = KILO * KILO * KILO * value + else + raise "Invalid unit '#{unit}' in memory size" + end + bytes + end + + def check_is_integer?(v) + v = Float(v) + v && v.floor == v + end + + def heap_size_calculator(membytes, heapratio) + memory_size_minified(membytes * heapratio) + end + + def memory_size_minified(membytes) + giga = membytes / (2**(10 * 3)) + mega = membytes / (2**(10 * 2)) + kilo = (membytes / (2**(10 * 1))).round + if check_is_integer?(giga) + minified_size_calculator(giga, 'G') + elsif check_is_integer?(mega) + minified_size_calculator(mega, 'M') + elsif check_is_integer?(kilo) + minified_size_calculator(kilo, 'K') + end + end + + def minified_size_calculator(order, char) + order.to_i.to_s + char + end + + def heap_ratio_verification(ratio) + raise 'Invalid heap ratio' unless ratio.is_a? Numeric + raise 'heap ratio cannot be greater than 100%' unless ratio <= 1 + + ratio + end + + end + + end +end diff --git a/lib/java_buildpack/jre/jvmkill_agent.rb b/lib/java_buildpack/jre/jvmkill_agent.rb new file mode 100644 index 0000000000..e4897bd972 --- /dev/null +++ b/lib/java_buildpack/jre/jvmkill_agent.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/jre' + +module JavaBuildpack + module Jre + + # Encapsulates the detect, compile, and release functionality for the jvmkill agent + class JvmkillAgent < JavaBuildpack::Component::VersionedDependencyComponent + include JavaBuildpack::Util + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download(@version, @uri) do |file| + FileUtils.mkdir_p jvmkill_agent.parent + FileUtils.cp(file.path, jvmkill_agent) + jvmkill_agent.chmod 0o755 + end + + puts " Write terminal heap dumps to #{heap_dump_path}" if @application.services.one_volume_service? FILTER + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + properties = { 'printHeapHistogram' => '1' } + properties['heapDumpPath'] = heap_dump_path if @application.services.one_volume_service? FILTER + + @droplet.java_opts.add_agentpath_with_props(jvmkill_agent, properties) + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + true + end + + private + + FILTER = /heap-dump/.freeze + + private_constant :FILTER + + def application_identifier + "#{@application.details['application_name']}-#{@application.details['application_id'][0...8]}" + end + + def container_dir + @application.services.find_volume_service(FILTER)['volume_mounts'].first['container_dir'] + end + + def heap_dump_path + "#{container_dir}/#{space_identifier}/#{application_identifier}/#{instance_identifier}.hprof" + end + + def instance_identifier + '$CF_INSTANCE_INDEX-%FT%T%z-${CF_INSTANCE_GUID:0:8}' + end + + def jvmkill_agent + @droplet.sandbox + "bin/jvmkill-#{@version}" + end + + def space_identifier + "#{@application.details['space_name']}-#{@application.details['space_id'][0...8]}" + end + + end + + end +end diff --git a/lib/java_buildpack/jre/memory/memory_bucket.rb b/lib/java_buildpack/jre/memory/memory_bucket.rb deleted file mode 100644 index db909efbda..0000000000 --- a/lib/java_buildpack/jre/memory/memory_bucket.rb +++ /dev/null @@ -1,93 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'java_buildpack/logging/logger_factory' -require 'java_buildpack/jre' -require 'java_buildpack/jre/memory/memory_range' - -module JavaBuildpack - module Jre - - # A MemoryBucket is used to calculate default sizes for various type of memory - class MemoryBucket - - # @!attribute [r] size - # @return [Numeric, nil] the size of the memory bucket in KB or nil if this has not been specified by the user or - # defaulted - attr_reader :size - - # @!attribute [r] range - # @return [MemoryRange] the permissible range of the memory bucket - attr_accessor :range - - # @!attribute [r] weighting - # @return [Numeric] the weighting of the memory bucket - attr_reader :weighting - - # Constructs a memory bucket. - # - # @param [String] name a non-empty, human-readable name for this memory bucket, used only in diagnostics - # @param [Numeric] weighting a number between 0 and 1 corresponding to the proportion of total memory which this - # memory bucket should consume by default - # @param [MemoryRange, nil] range a user-specified range for the memory bucket or nil if the user did not specify - # a range - def initialize(name, weighting, range) - @name = validate_name name - @weighting = validate_weighting weighting - @range = range ? validate_memory_range(range) : nil - logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger MemoryBucket - logger.debug { inspect } - end - - attr_writer :size - - private - - def validate_name(name) - fail "Invalid MemoryBucket name '#{name}'" if name.nil? || name.to_s.size == 0 - name - end - - def validate_weighting(weighting) - fail diagnose_weighting(weighting, 'not numeric') unless numeric? weighting - fail diagnose_weighting(weighting, 'negative') if weighting < 0 - weighting - end - - def diagnose_weighting(weighting, reason) - "Invalid weighting '#{weighting}' for #{identify} : #{reason}" - end - - def numeric?(w) - Float(w) rescue false - end - - def identify - "MemoryBucket #{@name}" - end - - def validate_memory_range(range) - unless range.is_a? MemoryRange - fail "Invalid 'range' parameter of class '#{range.class}' for #{identify} : not a MemoryRange" - end - - range - end - - end - - end -end diff --git a/lib/java_buildpack/jre/memory/memory_limit.rb b/lib/java_buildpack/jre/memory/memory_limit.rb deleted file mode 100644 index afc26c75f2..0000000000 --- a/lib/java_buildpack/jre/memory/memory_limit.rb +++ /dev/null @@ -1,46 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'java_buildpack/jre' -require 'java_buildpack/jre/memory/memory_size' - -module JavaBuildpack - module Jre - - # A utility for handling Java memory settings. - class MemoryLimit - - private_class_method :new - - class << self - - # Returns the application's memory limit. - # - # @return [MemorySize, nil] the application's memory limit or nil if no memory limit has been provided - def memory_limit - memory_limit = ENV['MEMORY_LIMIT'] - return nil unless memory_limit - memory_limit_size = MemorySize.new(memory_limit) - fail "Invalid negative $MEMORY_LIMIT #{memory_limit}" if memory_limit_size < 0 - memory_limit_size - end - - end - - end - - end -end diff --git a/lib/java_buildpack/jre/memory/memory_range.rb b/lib/java_buildpack/jre/memory/memory_range.rb deleted file mode 100644 index cf034e1efc..0000000000 --- a/lib/java_buildpack/jre/memory/memory_range.rb +++ /dev/null @@ -1,138 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'java_buildpack/jre' - -module JavaBuildpack - module Jre - - # A class representing a permissible range of memory sizes. - class MemoryRange - - # @!attribute [r] floor - # @return [MemorySize] the lower bound of this memory range - attr_reader :floor - - # @!attribute [r] ceiling - # @return [MemorySize, nil] the upper bound of this memory range or +nil+ if there is no upper bound - attr_reader :ceiling - - # Creates a memory range based on either a memory range string or lower and upper bounds expressed as MemorySizes. - # - # @param [MemorySize, String] value the lower bound of the range or a string range - # @param [MemorySize, nil] ceiling the upper bound of the range - def initialize(value, ceiling = nil) - if value.is_a? String - fail "Invalid combination of parameter types #{value.class} and #{ceiling.class}" if ceiling - lower_bound, upper_bound = get_bounds(value) - @floor = create_memory_size lower_bound - @ceiling = upper_bound ? create_memory_size(upper_bound) : nil - else - validate_memory_size value - validate_memory_size ceiling if ceiling - @floor = value - @ceiling = ceiling - end - fail "Invalid range: floor #{@floor} is higher than ceiling #{@ceiling}" if @ceiling && @floor > @ceiling - end - - # Determines whether or not this range is bounded. Reads better than testing for a +nil+ ceiling. - # - # @return [Boolean] +true+ if and only if this range is bounded - alias_method :bounded?, :ceiling - - # Determines whether a given memory size falls in this range. - # - # @param [MemorySize] size the memory size to be checked - # @return [Boolean] +true+ if and only if the given memory size falls in this range - def contains?(size) - @floor <= size && (@ceiling.nil? || size <= @ceiling) - end - - # Constrains a given memory size to this range. If the size falls within the range, returns the size. - # If the size is below the range, return the floor of the range. If the size is above the range, - # return the ceiling of the range. - # - # @param [MemorySize] size the memory size to be constrained - # @return [MemorySize] the constrained memory size - def constrain(size) - if size < @floor - @floor - else - @ceiling && size > @ceiling ? @ceiling : size - end - end - - # Returns true if and only if this range consists of a single value. - # - # @return [Boolean] whether or not this range consists of a single value - def degenerate? - @floor == @ceiling - end - - # Multiply this memory range by a numeric factor. - # - # @param [Numeric] other the factor to multiply by - # @return [MemoryRange] the result - def *(other) - fail "Cannot multiply a MemoryRange by an instance of #{other.class}" unless other.is_a? Numeric - fail 'Cannot multiply an unbounded MemoryRange by 0' if !bounded? && other == 0 - MemoryRange.new(@floor * other, bounded? ? @ceiling * other : nil) - end - - # Compare this memory range for equality with another memory range - # - # @param [MemoryRange] other - # @return [Boolean] the result - def ==(other) - @floor == other.floor && @ceiling == other.ceiling - end - - # Returns a string representation of this range. - # - # @return [String] the string representation of this range - def to_s - "#{@floor}..#{@ceiling}" - end - - private - - RANGE_SEPARATOR = '..'.freeze - - private_constant :RANGE_SEPARATOR - - def get_bounds(range) - if range.index(RANGE_SEPARATOR) - lower_bound, upper_bound = range.split(RANGE_SEPARATOR) - lower_bound = '0' if lower_bound.nil? || lower_bound == '' - return lower_bound, upper_bound - else - return range, range - end - end - - def create_memory_size(size) - MemorySize.new(size) - end - - def validate_memory_size(size) - fail "Invalid MemorySize parameter of type #{size.class}" unless size.is_a? MemorySize - end - - end - - end -end diff --git a/lib/java_buildpack/jre/memory/memory_size.rb b/lib/java_buildpack/jre/memory/memory_size.rb deleted file mode 100644 index 0571a0ce74..0000000000 --- a/lib/java_buildpack/jre/memory/memory_size.rb +++ /dev/null @@ -1,166 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'java_buildpack/jre' - -module JavaBuildpack - module Jre - - # A class representing the size of a category of memory. - class MemorySize - include Comparable - - # Creates a memory size based on a memory size string including a unit of 'K', 'M', or 'G'. - # - # @param [String] size a memory size including a unit - def initialize(size) - if size == '0' - @bytes = 0 - else - fail "Invalid memory size '#{size}'" if !size || size.length < 2 - unit = size[-1] - v = size[0..-2] - fail "Invalid memory size '#{size}'" unless integer? v - v = size.to_i - - store_bytes unit, v, size - end - end - - # Returns a memory size as a string including a unit. If the memory size is not a whole number, it is rounded - # down. The returned unit is always kilobytes, megabytes, or gigabytes which are commonly used units. - # - # @return [String] the memory size as a string, e.g. "10K" - def to_s - kilobytes = (@bytes / KILO).round - if kilobytes == 0 - '0' - elsif kilobytes % KILO == 0 - megabytes = kilobytes / KILO - if megabytes % KILO == 0 - gigabytes = megabytes / KILO - gigabytes.to_s + 'G' - else - megabytes.to_s + 'M' - end - else - kilobytes.to_s + 'K' - end - end - - # Compare this memory size with another memory size - # - # @param [MemorySize, 0] other - # @return [Numeric] the result - def <=>(other) - if other == 0 - @bytes <=> 0 - else - fail "Cannot compare a MemorySize to an instance of #{other.class}" unless other.is_a? MemorySize - @bytes <=> other.bytes - end - end - - # Add a memory size to this memory size. - # - # @param [MemorySize] other the memory size to add - # @return [MemorySize] the result - def +(other) - memory_size_operation(other) do |self_bytes, other_bytes| - self_bytes + other_bytes - end - end - - # Multiply this memory size by a numeric factor. - # - # @param [Numeric] other the factor to multiply by - # @return [MemorySize] the result - def *(other) - fail "Cannot multiply a Memory size by an instance of #{other.class}" unless other.is_a? Numeric - from_numeric((@bytes * other).round) - end - - # Subtract a memory size from this memory size. - # - # @param [MemorySize] other the memory size to subtract - # @return [MemorySize] the result - def -(other) - memory_size_operation(other) do |self_bytes, other_bytes| - self_bytes - other_bytes - end - end - - # Divide a memory size by a memory size or a numeric value. The units are respected, so the result of diving by a - # memory size is a numeric whereas the result of dividing by a numeric value is a memory size. - # - # @param [MemorySize, Numeric] other the memory size or numeric value to divide by - # @return [MemorySize, Numeric] the result - def /(other) - return @bytes / other.bytes.to_f if other.is_a? MemorySize - return from_numeric((@bytes / other.to_f).round) if other.is_a? Numeric - fail "Cannot divide a MemorySize by an instance of #{other.class}" - end - - # @!attribute [r] bytes - # @return [Numeric] the size in bytes of this memory size - attr_reader :bytes - - protected :bytes - - private - - KILO = 1024.freeze - - private_constant :KILO - - def store_bytes(unit, v, size) - # Store the number of bytes. - case unit - when 'b', 'B' - @bytes = v - when 'k', 'K' - @bytes = v * KILO - when 'm', 'M' - @bytes = KILO * KILO * v - when 'g', 'G' - @bytes = KILO * KILO * KILO * v - else - fail "Invalid unit '#{unit}' in memory size '#{size}'" - end - end - - def memory_size_operation(other) - fail "Invalid parameter: instance of #{other.class} is not a MemorySize" unless other.is_a? MemorySize - from_numeric(yield @bytes, other.bytes) - end - - def integer?(v) - f = Float(v) - f && f.floor == f - rescue - false - end - - def from_numeric(n) - MemorySize.new("#{n}B") - end - - # Zero byte memory size - ZERO = MemorySize.new('0B').freeze - end - - end -end diff --git a/lib/java_buildpack/jre/memory/openjdk_memory_heuristic_factory.rb b/lib/java_buildpack/jre/memory/openjdk_memory_heuristic_factory.rb deleted file mode 100644 index 69e6271802..0000000000 --- a/lib/java_buildpack/jre/memory/openjdk_memory_heuristic_factory.rb +++ /dev/null @@ -1,64 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'java_buildpack/jre' -require 'java_buildpack/jre/memory/weight_balancing_memory_heuristic' -require 'java_buildpack/util/tokenized_version' - -module JavaBuildpack - module Jre - - # A MemoryBucket is used to calculate default sizes for various type of memory - class OpenJDKMemoryHeuristicFactory - - private_class_method :new - - class << self - - # Returns a memory heuristics instance for the given version of OpenJDK. - # - # @param [Hash] sizes any sizes specified by the user - # @param [Hash] heuristics the memory heuristics specified by the user - # @param [JavaBuildpack::Util::TokenizedVersion] version the version of OpenJDK - # @return [WeightBalancingMemoryHeuristic] the memory heuristics instance - def create_memory_heuristic(sizes, heuristics, version) - extra = permgen_or_metaspace(version) - WeightBalancingMemoryHeuristic.new(sizes, heuristics, VALID_TYPES.dup << extra, JAVA_OPTS) - end - - private - - VALID_TYPES = %w(heap stack native).freeze - - private_constant :VALID_TYPES - - JAVA_OPTS = { - 'heap' => ->(v) { %W(-Xmx#{v} -Xms#{v}) }, - 'metaspace' => ->(v) { %W(-XX:MaxMetaspaceSize=#{v} -XX:MetaspaceSize=#{v}) }, - 'permgen' => ->(v) { %W(-XX:MaxPermSize=#{v} -XX:PermSize=#{v}) }, - 'stack' => ->(v) { ["-Xss#{v}"] } - }.freeze - - def permgen_or_metaspace(version) - version < JavaBuildpack::Util::TokenizedVersion.new('1.8.0') ? 'permgen' : 'metaspace' - end - - end - - end - - end -end diff --git a/lib/java_buildpack/jre/memory/stack_memory_bucket.rb b/lib/java_buildpack/jre/memory/stack_memory_bucket.rb deleted file mode 100644 index f3935cbaa6..0000000000 --- a/lib/java_buildpack/jre/memory/stack_memory_bucket.rb +++ /dev/null @@ -1,53 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'java_buildpack/jre' -require 'java_buildpack/jre/memory/memory_bucket' -require 'java_buildpack/jre/memory/memory_size' - -module JavaBuildpack - module Jre - - # This class represents a memory bucket for stack memory. This is treated differently to other memory buckets - # which have absolute sizes since stack memory is specified in terms of the size of an individual stack with no - # definition of how many stacks may exist. - class StackMemoryBucket < MemoryBucket - - # Constructs a stack memory bucket. - # - # @param [Numeric] weighting a number between 0 and 1 corresponding to the proportion of total memory which this - # memory bucket should consume by default - # @param [MemoryRange, nil] range a user-specified range for the memory bucket or nil if the user did not specify - # a range - def initialize(weighting, range) - super('stack', weighting, range) - end - - # Returns the default stack size. - # - # @return [MemorySize] the default stack size - def default_size - range.floor == 0 ? JVM_DEFAULT_STACK_SIZE : range.floor - end - - JVM_DEFAULT_STACK_SIZE = MemorySize.new('1M').freeze - - private_constant :JVM_DEFAULT_STACK_SIZE - - end - - end -end diff --git a/lib/java_buildpack/jre/memory/weight_balancing_memory_heuristic.rb b/lib/java_buildpack/jre/memory/weight_balancing_memory_heuristic.rb deleted file mode 100644 index f769131b31..0000000000 --- a/lib/java_buildpack/jre/memory/weight_balancing_memory_heuristic.rb +++ /dev/null @@ -1,245 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'java_buildpack/logging/logger_factory' -require 'java_buildpack/jre' -require 'java_buildpack/jre/memory/memory_limit' -require 'java_buildpack/jre/memory/memory_range' -require 'java_buildpack/jre/memory/memory_size' -require 'java_buildpack/jre/memory/memory_bucket' -require 'java_buildpack/jre/memory/stack_memory_bucket' - -module JavaBuildpack - module Jre - - # A utility for defaulting Java memory settings. - class WeightBalancingMemoryHeuristic - - # Creates an instance based on a hash containing memory settings, and the application's memory size in - # $MEMORY_LIMIT. - # - # @param [Hash] sizes any sizes specified by the user - # @param [Hash] heuristics the memory heuristics specified by the user - # @param [Array] valid_types the valid types of memory - # @param [Hash] java_opts a mapping from a memory type to a +JAVA_OPTS+ option - def initialize(sizes, heuristics, valid_types, java_opts) - @logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger WeightBalancingMemoryHeuristic - validate 'size', valid_types, sizes.keys - validate 'heuristic', valid_types, heuristics.keys - @memory_limit = MemoryLimit.memory_limit - @sizes = sizes - @heuristics = heuristics - @java_opts = java_opts - end - - # Computes the JRE memory switch values based on the current state. - # - # @return [Array] an array of JRE memory switches with values - def resolve - buckets = create_memory_buckets(@sizes, @heuristics) - - if @memory_limit - allocate_by_balancing(buckets) - else - allocate_lower_bounds(buckets) - end - - switches(buckets) - end - - private - - NATIVE_MEMORY_WARNING_FACTOR = 3.freeze - - TOTAL_MEMORY_WARNING_FACTOR = 0.8.freeze - - CLOSE_TO_DEFAULT_FACTOR = 0.1.freeze - - private_constant :NATIVE_MEMORY_WARNING_FACTOR, :TOTAL_MEMORY_WARNING_FACTOR, :CLOSE_TO_DEFAULT_FACTOR - - def allocate_lower_bounds(buckets) - buckets.each_value do |bucket| - bucket.size = bucket.range.floor - end - end - - def weighted_proportion(bucket, buckets) - apply_weighting_to_memory_limit(bucket, calculate_total_weighting(buckets)) - end - - def apply_weighting_to_memory_limit(bucket, total_weighting) - apply_weighting(@memory_limit, bucket, total_weighting) - end - - def apply_weighting(memory, bucket, total_weighting) - (memory * bucket.weighting) / total_weighting - end - - def allocate_by_balancing(buckets) - stack_bucket = buckets['stack'] - if stack_bucket - # Convert stack range from range of stack sizes to range of total stack memory - buckets['stack'], num_threads = normalise_stack_bucket(stack_bucket, buckets) - end - - balance_buckets(buckets) - - issue_memory_wastage_warning(buckets) - issue_close_to_default_warnings(buckets) - - return unless stack_bucket - - # Convert stack size from total stack memory to stack size - stack_bucket.size = buckets['stack'].size / num_threads - buckets['stack'] = stack_bucket - end - - def normalise_stack_bucket(stack_bucket, buckets) - stack_memory = weighted_proportion(stack_bucket, buckets) - num_threads = [stack_memory / stack_bucket.default_size, 1].max - normalised_bucket = MemoryBucket.new('normalised stack', stack_bucket.weighting, - stack_bucket.range * num_threads) - [normalised_bucket, num_threads] - end - - def balance_buckets(buckets) - remaining_buckets = buckets.clone - remaining_memory = @memory_limit - deleted = true - while !remaining_buckets.empty? && deleted - remaining_memory, deleted = balance_remainder(remaining_buckets, remaining_memory) - end - end - - def balance_remainder(remaining_buckets, remaining_memory) - deleted = false - total_weighting = calculate_total_weighting remaining_buckets - - allocated_memory = MemorySize::ZERO - remaining_buckets.each do |type, bucket| - size = apply_weighting(remaining_memory, bucket, total_weighting) - if bucket.range.contains? size - bucket.size = size - else - allocated_memory = constrain_bucket_size(allocated_memory, bucket, size) - remaining_buckets.delete type - deleted = true - end - end - remaining_memory -= allocated_memory - fail "Total memory #{@memory_limit} exceeded by configured memory #{@sizes}" if remaining_memory < 0 - [remaining_memory, deleted] - end - - def constrain_bucket_size(allocated_memory, bucket, size) - constrained_size = bucket.range.constrain(size) - bucket.size = constrained_size - allocated_memory + constrained_size - end - - def calculate_total_weighting(buckets) - total_weighting = 0 - buckets.each_value do |bucket| - total_weighting += bucket.weighting - end - total_weighting - end - - def create_memory_bucket(type, weighting, range) - if type == 'stack' - StackMemoryBucket.new(weighting, range) - else - MemoryBucket.new(type, weighting, range) - end - end - - def create_memory_buckets(sizes, heuristics) - buckets = {} - - heuristics.each_pair do |type, weighting| - range = nil_safe_range sizes[type] - buckets[type] = create_memory_bucket(type, weighting, range) - end - - buckets - end - - def issue_memory_wastage_warning(buckets) - native_bucket = buckets['native'] - if native_bucket && native_bucket.range.floor == 0 - if native_bucket.size > weighted_proportion(native_bucket, buckets) * NATIVE_MEMORY_WARNING_FACTOR - @logger.warn do - "There is more than #{NATIVE_MEMORY_WARNING_FACTOR} times more spare native memory than the default, " \ - 'so configured Java memory may be too small or available memory may be too large' - end - end - end - - total_size = MemorySize::ZERO - buckets.each_value { |bucket| total_size += bucket.size } - return unless @memory_limit * TOTAL_MEMORY_WARNING_FACTOR > total_size - - @logger.warn do - "The allocated Java memory sizes total #{total_size} which is less than #{TOTAL_MEMORY_WARNING_FACTOR} of " \ - 'the available memory, so configured Java memory sizes may be too small or available memory may be too large' - end - end - - def nil_safe_range(size) - size ? MemoryRange.new(size) : MemoryRange.new('..') - end - - def validate(type, expected, actual) - actual.each do |key| - fail "'#{key}' is not a valid memory #{type}" unless expected.include? key - end - end - - def issue_close_to_default_warnings(buckets) - total_weighting = calculate_total_weighting buckets - buckets.each do |type, bucket| - check_close_to_default(type, bucket, total_weighting) if type != 'stack' && @sizes[type] - end - end - - def check_close_to_default(type, bucket, total_weighting) - return unless bucket.range.degenerate? - - default_size = apply_weighting_to_memory_limit(bucket, total_weighting) - actual_size = bucket.size - if default_size > 0 - factor = ((actual_size - default_size) / default_size).abs - @logger.debug { "factor for memory size #{type} is #{factor}" } - end - - return unless (default_size == 0 && actual_size == 0) || (factor && (factor < CLOSE_TO_DEFAULT_FACTOR)) - - @logger.warn do - "The computed value #{actual_size} of memory size #{type} is close to the default value #{default_size}. " \ - 'Consider taking the default.' - end - end - - def switches(buckets) - buckets.map do |type, bucket| - @java_opts[type][bucket.size] if bucket.size && bucket.size > 0 && @java_opts.key?(type) - end.flatten(1).compact - end - - end - - end -end diff --git a/lib/java_buildpack/jre/open_jdk_jre.rb b/lib/java_buildpack/jre/open_jdk_jre.rb index 34e8b82c21..7f962d119c 100644 --- a/lib/java_buildpack/jre/open_jdk_jre.rb +++ b/lib/java_buildpack/jre/open_jdk_jre.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/jre/open_jdk_like.rb b/lib/java_buildpack/jre/open_jdk_like.rb index 4b665c02b6..9cdd7aa68f 100644 --- a/lib/java_buildpack/jre/open_jdk_like.rb +++ b/lib/java_buildpack/jre/open_jdk_like.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,67 +15,40 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'fileutils' -require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/component/modular_component' require 'java_buildpack/jre' -require 'java_buildpack/jre/memory/openjdk_memory_heuristic_factory' +require 'java_buildpack/jre/jvmkill_agent' +require 'java_buildpack/jre/open_jdk_like_jre' +require 'java_buildpack/jre/open_jdk_like_memory_calculator' +require 'java_buildpack/jre/open_jdk_like_security_providers' module JavaBuildpack module Jre # Encapsulates the detect, compile, and release functionality for selecting an OpenJDK-like JRE. - class OpenJDKLike < JavaBuildpack::Component::VersionedDependencyComponent - - # Creates an instance - # - # @param [Hash] context a collection of utilities used the component - def initialize(context) - @application = context[:application] - @component_name = self.class.to_s.space_case - @configuration = context[:configuration] - @droplet = context[:droplet] - - @droplet.java_home.root = @droplet.sandbox - end + class OpenJDKLike < JavaBuildpack::Component::ModularComponent - # (see JavaBuildpack::Component::BaseComponent#detect) - def detect - @version, @uri = JavaBuildpack::Repository::ConfiguredItem.find_item(@component_name, - @configuration) - @droplet.java_home.version = @version - super - end + protected - # (see JavaBuildpack::Component::BaseComponent#compile) - def compile - download_tar - @droplet.copy_resources + # (see JavaBuildpack::Component::ModularComponent#command) + def command + @sub_components.find { |candidate| candidate.is_a? OpenJDKLikeMemoryCalculator }.memory_calculation_command end - # (see JavaBuildpack::Component::BaseComponent#release) - def release - @droplet.java_opts - .add_system_property('java.io.tmpdir', '$TMPDIR') - .add_option('-XX:OnOutOfMemoryError', killjava) - .concat memory - end - - private - - KEY_MEMORY_HEURISTICS = 'memory_heuristics'.freeze - - KEY_MEMORY_SIZES = 'memory_sizes'.freeze - - private_constant :KEY_MEMORY_HEURISTICS, :KEY_MEMORY_SIZES - - def killjava - @droplet.sandbox + 'bin/killjava.sh' + # (see JavaBuildpack::Component::ModularComponent#sub_components) + def sub_components(context) + [ + JvmkillAgent.new(sub_configuration_context(context, 'jvmkill_agent')), + OpenJDKLikeJre.new(sub_configuration_context(context, 'jre') + .merge(component_name: self.class.to_s.space_case)), + OpenJDKLikeMemoryCalculator.new(sub_configuration_context(context, 'memory_calculator')), + OpenJDKLikeSecurityProviders.new(context) + ] end - def memory - sizes = @configuration[KEY_MEMORY_SIZES] || {} - heuristics = @configuration[KEY_MEMORY_HEURISTICS] || {} - OpenJDKMemoryHeuristicFactory.create_memory_heuristic(sizes, heuristics, @version).resolve + # (see JavaBuildpack::Component::ModularComponent#supports?) + def supports? + true end end diff --git a/lib/java_buildpack/jre/open_jdk_like_jre.rb b/lib/java_buildpack/jre/open_jdk_like_jre.rb new file mode 100644 index 0000000000..a0a81a6334 --- /dev/null +++ b/lib/java_buildpack/jre/open_jdk_like_jre.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'ipaddr' +require 'fileutils' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/jre' +require 'java_buildpack/util/tokenized_version' +require 'resolv' + +module JavaBuildpack + module Jre + + # rubocop: disable Naming/VariableNumber + # Encapsulates the detect, compile, and release functionality for selecting an OpenJDK-like JRE. + class OpenJDKLikeJre < JavaBuildpack::Component::VersionedDependencyComponent + + # Creates an instance + # + # @param [Hash] context a collection of utilities used the component + def initialize(context) + @application = context[:application] + @component_name = context[:component_name] + @configuration = context[:configuration] + @droplet = context[:droplet] + + @droplet.java_home.root = @droplet.sandbox + end + + # (see JavaBuildpack::Component::BaseComponent#detect) + def detect + @version, @uri = JavaBuildpack::Repository::ConfiguredItem.find_item(@component_name, + @configuration) + @droplet.java_home.version = @version + super + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_tar + @droplet.copy_resources + disable_dns_caching if link_local_dns? + + return if @droplet.java_home.java_8_or_later? + + warn "\n WARNING: You are using #{@droplet.java_home.version}. Oracle has ended public updates of Java " \ + "1.7 as of April 2015, possibly rendering your application vulnerable.\n\n" + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + @droplet.java_opts.add_system_property('java.io.tmpdir', '$TMPDIR') + + return if @droplet.java_home.version < JAVA_8_191 + + @droplet.java_opts.add_option('-XX:ActiveProcessorCount', '$(nproc)') + end + + private + + JAVA_8_191 = JavaBuildpack::Util::TokenizedVersion.new('1.8.0_191').freeze + + LINK_LOCAL = IPAddr.new('169.254.0.0/16').freeze + + private_constant :JAVA_8_191, :LINK_LOCAL + + def disable_dns_caching + puts ' JVM DNS caching disabled in lieu of BOSH DNS caching' + + @droplet.networking.networkaddress_cache_ttl = 0 + @droplet.networking.networkaddress_cache_negative_ttl = 0 + end + + def link_local_dns? + Resolv::DNS::Config.new.lazy_initialize.nameserver_port.any? do |nameserver_port| + LINK_LOCAL.include? IPAddr.new(nameserver_port[0]) + end + end + + end + # rubocop: enable Naming/VariableNumber + end +end diff --git a/lib/java_buildpack/jre/open_jdk_like_memory_calculator.rb b/lib/java_buildpack/jre/open_jdk_like_memory_calculator.rb new file mode 100644 index 0000000000..7a2fd20d4f --- /dev/null +++ b/lib/java_buildpack/jre/open_jdk_like_memory_calculator.rb @@ -0,0 +1,140 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/jre' +require 'java_buildpack/util/filtering_pathname' +require 'java_buildpack/util/shell' +require 'java_buildpack/util/qualify_path' +require 'open3' +require 'tmpdir' + +module JavaBuildpack + module Jre + + # Encapsulates the detect, compile, and release functionality for the OpenJDK-like memory calculator + class OpenJDKLikeMemoryCalculator < JavaBuildpack::Component::VersionedDependencyComponent + include JavaBuildpack::Util + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download(@version, @uri) do |file| + FileUtils.mkdir_p memory_calculator.parent + + if @version[0] < '2' + unpack_calculator file + else + unpack_compressed_calculator file + end + + memory_calculator.chmod 0o755 + + puts " Loaded Classes: #{class_count @configuration}, " \ + "Threads: #{stack_threads @configuration}" + end + end + + # Returns a fully qualified memory calculation command to be prepended to the buildpack's command sequence + # + # @return [String] the memory calculation command + def memory_calculation_command + "CALCULATED_MEMORY=$(#{memory_calculation_string(@droplet.root)}) && " \ + 'echo JVM Memory Configuration: $CALCULATED_MEMORY && ' \ + 'JAVA_OPTS="$JAVA_OPTS $CALCULATED_MEMORY"' + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + @droplet.environment_variables.add_environment_variable 'MALLOC_ARENA_MAX', 2 + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + true + end + + private + + def actual_class_count(root) + (root + '**/*.class').glob.count + + (root + '**/*.groovy').glob.count + + (root + '**/*.jar').glob(File::FNM_DOTMATCH).reject(&:directory?) + .inject(0) { |a, e| a + archive_class_count(e) } + + (@droplet.java_home.java_9_or_later? ? 42_215 : 0) + end + + def archive_class_count(archive) + `unzip -l #{archive} | grep '\\(\\.class\\|\\.groovy\\)$' | wc -l`.to_i + end + + def class_count(configuration) + root = JavaBuildpack::Util::FilteringPathname.new(@droplet.root, ->(_) { true }, true) + configuration['class_count'] || (0.35 * actual_class_count(root)).ceil + end + + def headroom(configuration) + configuration['headroom'] + end + + def memory_calculator + @droplet.sandbox + "bin/java-buildpack-memory-calculator-#{@version}" + end + + def memory_calculator_tar + platform = `uname -s` =~ /Darwin/ ? 'darwin' : 'linux' + @droplet.sandbox + "bin/java-buildpack-memory-calculator-#{platform}" + end + + def memory_calculation_string(relative_path) + memory_calculation_string = [qualify_path(memory_calculator, relative_path)] + memory_calculation_string << '-totMemory=$MEMORY_LIMIT' + + headroom = headroom(@configuration) + memory_calculation_string << "-headRoom=#{headroom}" if headroom + + memory_calculation_string << "-loadedClasses=#{class_count @configuration}" + memory_calculation_string << "-poolType=#{pool_type}" + memory_calculation_string << "-stackThreads=#{stack_threads @configuration}" + memory_calculation_string << '-vmOptions="$JAVA_OPTS"' + + memory_calculation_string.join(' ') + end + + def pool_type + @droplet.java_home.java_8_or_later? ? 'metaspace' : 'permgen' + end + + def stack_threads(configuration) + configuration['stack_threads'] + end + + def unpack_calculator(file) + FileUtils.cp_r(file.path, memory_calculator) + end + + def unpack_compressed_calculator(file) + shell "tar xzf #{file.path} -C #{memory_calculator.parent} 2>&1" + FileUtils.mv(memory_calculator_tar, memory_calculator) + end + + end + + end +end diff --git a/lib/java_buildpack/jre/open_jdk_like_security_providers.rb b/lib/java_buildpack/jre/open_jdk_like_security_providers.rb new file mode 100644 index 0000000000..0439f23e28 --- /dev/null +++ b/lib/java_buildpack/jre/open_jdk_like_security_providers.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/component/base_component' +require 'java_buildpack/jre' +require 'java_buildpack/util/properties' + +module JavaBuildpack + module Jre + + # Encapsulates the detect, compile, and release functionality for the OpenJDK-like security provider configuration + class OpenJDKLikeSecurityProviders < JavaBuildpack::Component::BaseComponent + + # (see JavaBuildpack::Component::BaseComponent#detect) + def detect + OpenJDKLikeSecurityProviders.to_s.dash_case + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + @droplet.security_providers.concat existing_security_providers(java_security) unless java_security.nil? + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + return if @droplet.java_home.java_9_or_later? + + @droplet.extension_directories << (java_security.parent.parent + 'ext') unless java_security.nil? + end + + private + + JAVA_9_SECURITY = 'conf/security/java.security' + + JRE_SECURITY = 'lib/security/java.security' + + SERVER_JRE_SECURITY = 'jre/lib/security/java.security' + + private_constant :JAVA_9_SECURITY, :JRE_SECURITY, :SERVER_JRE_SECURITY + + def existing_security_providers(existing_security) + JavaBuildpack::Util::Properties.new(existing_security) + .keep_if { |key, _| key =~ /security.provider/ } + .sort_by { |entry| index(entry) } + .map(&:last) + end + + def index(entry) + entry.first.match(/^security\.provider\.(\d+)/).captures.first.to_i + end + + def java_security + return java_9_security if @droplet.java_home.java_9_or_later? && java_9_security.exist? + return jre_security if jre_security.exist? + return server_jre_security if server_jre_security.exist? + + nil + end + + def java_9_security + @droplet.java_home.root + JAVA_9_SECURITY + end + + def jre_security + @droplet.java_home.root + JRE_SECURITY + end + + def server_jre_security + @droplet.java_home.root + SERVER_JRE_SECURITY + end + + end + + end +end diff --git a/lib/java_buildpack/jre/oracle_jre.rb b/lib/java_buildpack/jre/oracle_jre.rb index 849030062f..b229a6a3bf 100644 --- a/lib/java_buildpack/jre/oracle_jre.rb +++ b/lib/java_buildpack/jre/oracle_jre.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/jre/sap_machine_jre.rb b/lib/java_buildpack/jre/sap_machine_jre.rb new file mode 100644 index 0000000000..73243d0564 --- /dev/null +++ b/lib/java_buildpack/jre/sap_machine_jre.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/jre' +require 'java_buildpack/jre/open_jdk_like' + +module JavaBuildpack + module Jre + + # Encapsulates the detect, compile, and release functionality for selecting an SapMachine JRE. + class SapMachineJRE < OpenJDKLike + end + + end +end diff --git a/lib/java_buildpack/jre/zing_jre.rb b/lib/java_buildpack/jre/zing_jre.rb new file mode 100755 index 0000000000..4022ee7ac0 --- /dev/null +++ b/lib/java_buildpack/jre/zing_jre.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/jre' +require 'java_buildpack/jre/open_jdk_like' + +module JavaBuildpack + module Jre + + # Encapsulates the detect, compile, and release functionality for selecting an Azul Platform Prime JRE. + class ZingJRE < OpenJDKLike + # (see JavaBuildpack::Component::ModularComponent#command) + def command + # no command + end + + # (see JavaBuildpack::Component::ModularComponent#sub_components) + def sub_components(context) + [ + OpenJDKLikeJre.new(sub_configuration_context(context, 'jre') + .merge(component_name: self.class.to_s.space_case)), + OpenJDKLikeSecurityProviders.new(context) + ] + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + @droplet.java_opts.add_preformatted_options '-XX:+ExitOnOutOfMemoryError' + super + end + end + end +end diff --git a/lib/java_buildpack/jre/zulu_jre.rb b/lib/java_buildpack/jre/zulu_jre.rb new file mode 100755 index 0000000000..ab5067162b --- /dev/null +++ b/lib/java_buildpack/jre/zulu_jre.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' +require 'java_buildpack/jre' +require 'java_buildpack/jre/open_jdk_like' + +module JavaBuildpack + module Jre + + # Encapsulates the detect, compile, and release functionality for selecting an Zulu JRE. + class ZuluJRE < OpenJDKLike + end + + end +end diff --git a/lib/java_buildpack/logging.rb b/lib/java_buildpack/logging.rb index 512914f693..cf4f1f41c5 100644 --- a/lib/java_buildpack/logging.rb +++ b/lib/java_buildpack/logging.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/logging/delegating_logger.rb b/lib/java_buildpack/logging/delegating_logger.rb index 6d87f1aa79..070e1db734 100644 --- a/lib/java_buildpack/logging/delegating_logger.rb +++ b/lib/java_buildpack/logging/delegating_logger.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/logging/logger_factory.rb b/lib/java_buildpack/logging/logger_factory.rb index 405b0add76..ce1d435b5b 100644 --- a/lib/java_buildpack/logging/logger_factory.rb +++ b/lib/java_buildpack/logging/logger_factory.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -46,8 +47,13 @@ def initialize # @return [Void] def setup(app_dir) @monitor.synchronize do - @log_file = app_dir + '.java-buildpack.log' - @delegates = [file_logger, console_logger] + configuration = JavaBuildpack::Util::ConfigurationUtils.load('logging', true, false) + + @log_file = app_dir + '.java-buildpack.log' + + @delegates = [console_logger(configuration)] + @delegates << file_logger if configuration['enable_log_file'] + @initialized = true end end @@ -59,7 +65,8 @@ def setup(app_dir) # @return [Logger] the logger that was requested def get_logger(klass) @monitor.synchronize do - fail "Attempted to get Logger for #{short_class(klass)} before initialization" unless @initialized + raise "Attempted to get Logger for #{short_class(klass)} before initialization" unless @initialized + DelegatingLogger.new wrapped_short_class(klass), @delegates end end @@ -70,7 +77,8 @@ def get_logger(klass) # @return [Pathname] the location of the log file def log_file @monitor.synchronize do - fail 'Attempted to get log file before initialization' unless @initialized + raise 'Attempted to get log file before initialization' unless @initialized + @log_file end end @@ -100,9 +108,9 @@ def get_logger(klass) private - def console_logger + def console_logger(configuration) logger = Logger.new($stderr) - logger.level = severity + logger.level = severity(configuration) logger.formatter = lambda do |severity, _datetime, klass, message| "#{klass.ljust(32)} #{severity.ljust(5)} #{message}\n" end @@ -126,11 +134,11 @@ def ruby_mode $VERBOSE || $DEBUG ? 'DEBUG' : nil end - def severity - severity = ENV['JBP_LOG_LEVEL'] - severity = ruby_mode unless severity - severity = JavaBuildpack::Util::ConfigurationUtils.load('logging', false)['default_log_level'] unless severity - severity = 'INFO' unless severity + def severity(configuration) + severity = ENV.fetch('JBP_LOG_LEVEL', nil) + severity ||= ruby_mode + severity ||= configuration['default_log_level'] + severity ||= 'INFO' "::Logger::Severity::#{severity.upcase}".constantize end diff --git a/lib/java_buildpack/repository.rb b/lib/java_buildpack/repository.rb index dd5cef100b..94d7b586bc 100644 --- a/lib/java_buildpack/repository.rb +++ b/lib/java_buildpack/repository.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/repository/configured_item.rb b/lib/java_buildpack/repository/configured_item.rb index 9bd6b413b6..7655d5eb04 100644 --- a/lib/java_buildpack/repository/configured_item.rb +++ b/lib/java_buildpack/repository/configured_item.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -35,7 +36,7 @@ class << self # @param [Hash] configuration the configuration # @option configuration [String] :repository_root the root directory of the repository # @option configuration [String] :version the version of the file to resolve - # @param [Block, nil] version_validator an optional version validation block + # @yield [Block] optional version_validator to yield to # @return [String] the URI of the chosen version of the file # @return [JavaBuildpack::Util::TokenizedVersion] the chosen version of the file def find_item(component_name, configuration) @@ -46,15 +47,15 @@ def find_item(component_name, configuration) index = index(repository_root) index.find_item version - rescue => e + rescue StandardError => e raise RuntimeError, "#{component_name} error: #{e.message}", e.backtrace end private - KEY_REPOSITORY_ROOT = 'repository_root'.freeze + KEY_REPOSITORY_ROOT = 'repository_root' - KEY_VERSION = 'version'.freeze + KEY_VERSION = 'version' private_constant :KEY_REPOSITORY_ROOT, :KEY_VERSION @@ -64,8 +65,8 @@ def index(repository_root) def repository_root(configuration) unless configuration.key? KEY_REPOSITORY_ROOT - fail "A repository root must be specified as a key-value pair of '#{KEY_REPOSITORY_ROOT}'' to the URI of " \ - 'the repository.' + raise "A repository root must be specified as a key-value pair of '#{KEY_REPOSITORY_ROOT}' to the URI " \ + 'of the repository.' end configuration[KEY_REPOSITORY_ROOT] diff --git a/lib/java_buildpack/repository/repository_index.rb b/lib/java_buildpack/repository/repository_index.rb index 62e0118446..728ac7d480 100644 --- a/lib/java_buildpack/repository/repository_index.rb +++ b/lib/java_buildpack/repository/repository_index.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,8 +18,7 @@ require 'java_buildpack/logging/logger_factory' require 'java_buildpack/repository' require 'java_buildpack/repository/version_resolver' -require 'java_buildpack/util/cache' -require 'java_buildpack/util/cache/download_cache' +require 'java_buildpack/util/cache/cache_factory' require 'java_buildpack/util/configuration_utils' require 'rbconfig' require 'yaml' @@ -36,10 +36,10 @@ def initialize(repository_root) @logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger RepositoryIndex @default_repository_root = JavaBuildpack::Util::ConfigurationUtils.load('repository')['default_repository_root'] - .chomp('/') + .chomp('/') cache.get("#{canonical repository_root}#{INDEX_PATH}") do |file| - @index = YAML.load_file(file) + @index = YAML.load_file(file, permitted_classes: [Symbol], aliases: true) @logger.debug { @index } end end @@ -51,14 +51,15 @@ def initialize(repository_root) # @return [String] the URI of the file found def find_item(version) found_version = VersionResolver.resolve(version, @index.keys) - fail "No version resolvable for '#{version}' in #{@index.keys.join(', ')}" if found_version.nil? + raise "No version resolvable for '#{version}' in #{@index.keys.join(', ')}" if found_version.nil? + uri = @index[found_version.to_s] [found_version, uri] end private - INDEX_PATH = '/index.yml'.freeze + INDEX_PATH = '/index.yml' private_constant :INDEX_PATH @@ -67,16 +68,15 @@ def architecture end def cache - JavaBuildpack::Util::Cache::DownloadCache.new(Pathname.new(Dir.tmpdir), - JavaBuildpack::Util::Cache::CACHED_RESOURCES_DIRECTORY) + JavaBuildpack::Util::Cache::CacheFactory.create end def canonical(raw) cooked = raw - .gsub(/\{default.repository.root\}/, @default_repository_root) - .gsub(/\{platform\}/, platform) - .gsub(/\{architecture\}/, architecture) - .chomp('/') + .gsub(/\{default.repository.root\}/, @default_repository_root) + .gsub(/\{platform\}/, platform) + .gsub(/\{architecture\}/, architecture) + .chomp('/') @logger.debug { "#{raw} expanded to #{cooked}" } cooked end @@ -85,13 +85,14 @@ def platform redhat_release = Pathname.new('/etc/redhat-release') if redhat_release.exist? - "centos#{redhat_release.read.match(/CentOS release (\d)/)[1]}" + tokens = redhat_release.read.match(/(\w+) (?:Linux )?release (\d+)/) + "#{tokens[1].downcase}#{tokens[2]}" elsif `uname -s` =~ /Darwin/ 'mountainlion' - elsif !`which lsb_release 2> /dev/null`.empty? - `lsb_release -cs`.strip + elsif `cat /etc/os-release | grep '^ID=' | cut -d'=' -f 2` =~ /ubuntu/ + `cat /etc/os-release | grep '^VERSION_CODENAME=' | cut -d'=' -f 2`.strip else - fail 'Unable to determine platform' + raise 'Unable to determine platform' end end diff --git a/lib/java_buildpack/repository/version_resolver.rb b/lib/java_buildpack/repository/version_resolver.rb index 61d4872673..107c22eb1b 100644 --- a/lib/java_buildpack/repository/version_resolver.rb +++ b/lib/java_buildpack/repository/version_resolver.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,6 +17,7 @@ require 'java_buildpack/repository' require 'java_buildpack/util/tokenized_version' +require 'java_buildpack/logging/logger_factory' module JavaBuildpack module Repository @@ -38,15 +40,11 @@ class << self # @return [TokenizedVersion] the resolved version or nil if no matching version is found def resolve(candidate_version, versions) tokenized_candidate_version = safe_candidate_version candidate_version - tokenized_versions = versions.map do |version| - JavaBuildpack::Util::TokenizedVersion.new(version, false) - end + tokenized_versions = versions.map { |version| create_token(version) }.compact - version = tokenized_versions - .select { |tokenized_version| matches? tokenized_candidate_version, tokenized_version } - .max { |a, b| a <=> b } - - version + tokenized_versions + .select { |tokenized_version| matches? tokenized_candidate_version, tokenized_version } + .max { |a, b| a <=> b } end private @@ -55,12 +53,20 @@ def resolve(candidate_version, versions) private_constant :TOKENIZED_WILDCARD + def create_token(version) + JavaBuildpack::Util::TokenizedVersion.new(version, false) + rescue StandardError => e + logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger VersionResolver + logger.warn { "Discarding illegal version #{version}: #{e.message}" } + nil + end + def safe_candidate_version(candidate_version) if candidate_version.nil? TOKENIZED_WILDCARD else unless candidate_version.is_a?(JavaBuildpack::Util::TokenizedVersion) - fail "Invalid TokenizedVersion '#{candidate_version}'" + raise "Invalid TokenizedVersion '#{candidate_version}'" end candidate_version @@ -68,16 +74,25 @@ def safe_candidate_version(candidate_version) end def matches?(tokenized_candidate_version, tokenized_version) + wildcard_matched = false (0..3).all? do |i| - tokenized_candidate_version[i].nil? || - tokenized_candidate_version[i] == JavaBuildpack::Util::TokenizedVersion::WILDCARD || - tokenized_candidate_version[i] == tokenized_version[i] + next true if wildcard_matched || (tokenized_candidate_version[i].nil? && tokenized_version[i].nil?) + + next false if tokenized_candidate_version[i].nil? && !tokenized_version[i].nil? + + if tokenized_candidate_version[i] == JavaBuildpack::Util::TokenizedVersion::WILDCARD + wildcard_matched = true + next true + end + + if tokenized_candidate_version[i].end_with?(JavaBuildpack::Util::TokenizedVersion::WILDCARD) + next !tokenized_version[i].nil? && tokenized_version[i].start_with?(tokenized_candidate_version[i][0..-2]) + end + + tokenized_candidate_version[i] == tokenized_version[i] end end - end - end - end end diff --git a/lib/java_buildpack/util.rb b/lib/java_buildpack/util.rb index 3439a66f9d..8815609f9f 100644 --- a/lib/java_buildpack/util.rb +++ b/lib/java_buildpack/util.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/util/cache.rb b/lib/java_buildpack/util/cache.rb index 700d23830e..8258724e28 100644 --- a/lib/java_buildpack/util/cache.rb +++ b/lib/java_buildpack/util/cache.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,7 +25,7 @@ module Util module Cache # The location to find cached resources in the buildpack - CACHED_RESOURCES_DIRECTORY = Pathname.new(File.expand_path('../../../../resources/cache', __FILE__)) + CACHED_RESOURCES_DIRECTORY = Pathname.new(File.expand_path('../../../resources/cache', __dir__)) end diff --git a/lib/java_buildpack/util/cache/application_cache.rb b/lib/java_buildpack/util/cache/application_cache.rb index aa9c4aa541..2a38962724 100644 --- a/lib/java_buildpack/util/cache/application_cache.rb +++ b/lib/java_buildpack/util/cache/application_cache.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,6 +15,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +require 'java_buildpack/logging/logger_factory' require 'java_buildpack/util/cache' require 'java_buildpack/util/cache/download_cache' @@ -27,11 +29,33 @@ module Cache # WARNING: This cache should only by used by code run by the +compile+ script class ApplicationCache < DownloadCache + class << self + + # Whether an +ApplicationCache+ can be created + # + # @return [Boolean] whether an +ApplicationCache+ can be created + def available? + !application_cache_directory.nil? + end + + # The path to the application cache directory if it exists + # + # @return [void, String] the path to the application cache directory if it exists + def application_cache_directory + ARGV[1] + end + + end + # Creates an instance of the cache that is backed by the the application cache def initialize - application_cache_directory = ARGV[1] - fail 'Application cache directory is undefined' if application_cache_directory.nil? - super(Pathname.new(application_cache_directory), CACHED_RESOURCES_DIRECTORY) + logger = Logging::LoggerFactory.instance.get_logger ApplicationCache + + raise 'Application cache directory is undefined' unless self.class.available? + + logger.debug { "Application Cache Directory: #{self.class.application_cache_directory}" } + + super(Pathname.new(self.class.application_cache_directory), CACHED_RESOURCES_DIRECTORY) end end diff --git a/lib/java_buildpack/util/cache/cache_factory.rb b/lib/java_buildpack/util/cache/cache_factory.rb new file mode 100644 index 0000000000..628dbc5211 --- /dev/null +++ b/lib/java_buildpack/util/cache/cache_factory.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/util/cache' +require 'java_buildpack/util/cache/application_cache' +require 'java_buildpack/util/cache/download_cache' + +module JavaBuildpack + module Util + module Cache + + # A factory for creating {DownloadCache}s. Will create an {ApplicationCache} if it can, otherwise a + # {DownloadCache}. + class CacheFactory + + class << self + + # Creates a new instance of an {ApplicationCache} if it can, otherwise a {DownloadCache} + # + # @return [ApplicationCache, DownloadCache] a new instance of an {ApplicationCache} if it can, otherwise a + # {DownloadCache} + def create + if ApplicationCache.available? + ApplicationCache.new + else + DownloadCache.new(Pathname.new(Dir.tmpdir), JavaBuildpack::Util::Cache::CACHED_RESOURCES_DIRECTORY) + end + end + + end + + end + + end + end +end diff --git a/lib/java_buildpack/util/cache/cached_file.rb b/lib/java_buildpack/util/cache/cached_file.rb index 5b3356589c..dab3030f47 100644 --- a/lib/java_buildpack/util/cache/cached_file.rb +++ b/lib/java_buildpack/util/cache/cached_file.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,8 +15,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +require 'digest' require 'fileutils' require 'java_buildpack/util/cache' +require 'java_buildpack/util/sanitizer' module JavaBuildpack module Util @@ -25,6 +28,7 @@ module Cache # # Note: this class is thread-safe, however access to the cached files is not class CachedFile + include JavaBuildpack::Util # Creates an instance of the cached file. Files created and expected by this class will all be rooted at # +cache_root+. @@ -33,7 +37,7 @@ class CachedFile # @param [String] uri a uri which uniquely identifies the file in the cache # @param [Boolean] mutable whether the cached file should be mutable def initialize(cache_root, uri, mutable) - key = URI.escape(uri.sanitize_uri, ':/') + key = Digest::SHA256.hexdigest uri.sanitize_uri @cached = cache_root + "#{key}.cached" @etag = cache_root + "#{key}.etag" @last_modified = cache_root + "#{key}.last_modified" @@ -49,8 +53,8 @@ def initialize(cache_root, uri, mutable) # @param [Array] additional_args any additional arguments to be passed to the block # @yield [file, additional_args] the cached file and any additional arguments passed in # @return [Void] - def cached(mode_enc, *additional_args, &block) - @cached.open(mode_enc) { |f| block.call f, *additional_args } + def cached(mode_enc, *additional_args, &_) + @cached.open(mode_enc) { |f| yield f, *additional_args } end # Returns whether or not data is cached. @@ -72,8 +76,8 @@ def destroy # @param [Array] additional_args any additional arguments to be passed to the block # @yield [file] the etag file # @return [Void] - def etag(mode_enc, *additional_args, &block) - @etag.open(mode_enc) { |f| block.call f, *additional_args } + def etag(mode_enc, *additional_args, &_) + @etag.open(mode_enc) { |f| yield f, *additional_args } end # Returns whether or not an etag is stored. @@ -90,8 +94,8 @@ def etag? # @param [Array] additional_args any additional arguments to be passed to the block # @yield [file] the last modified file # @return [Void] - def last_modified(mode_enc, *additional_args, &block) - @last_modified.open(mode_enc) { |f| block.call f, *additional_args } + def last_modified(mode_enc, *additional_args, &_) + @last_modified.open(mode_enc) { |f| yield f, *additional_args } end # Returns whether or not a last modified time stamp is stored. diff --git a/lib/java_buildpack/util/cache/download_cache.rb b/lib/java_buildpack/util/cache/download_cache.rb index 1bda3625b9..9da9f33e72 100644 --- a/lib/java_buildpack/util/cache/download_cache.rb +++ b/lib/java_buildpack/util/cache/download_cache.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,9 +20,11 @@ require 'java_buildpack/util/cache/cached_file' require 'java_buildpack/util/cache/inferred_network_failure' require 'java_buildpack/util/cache/internet_availability' +require 'java_buildpack/util/configuration_utils' require 'java_buildpack/util/sanitizer' require 'monitor' require 'net/http' +require 'openssl' require 'pathname' require 'tmpdir' require 'uri' @@ -39,6 +42,8 @@ module Cache # * {http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html HTTP/1.1 Header Field Definitions} class DownloadCache + attr_writer :retry_max + # Creates an instance of the cache that is backed by a number of filesystem locations. The first argument # (+mutable_cache_root+) is the only location that downloaded files will be stored in. # @@ -50,6 +55,7 @@ def initialize(mutable_cache_root = Pathname.new(Dir.tmpdir), *immutable_cache_r @logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger DownloadCache @mutable_cache_root = mutable_cache_root @immutable_cache_roots = immutable_cache_roots.unshift mutable_cache_root + @retry_max = RETRY_MAX end # Retrieves an item from the cache. Yields an open file containing the item's content or raises an exception if @@ -60,11 +66,18 @@ def initialize(mutable_cache_root = Pathname.new(Dir.tmpdir), *immutable_cache_r # already in the cache # @return [Void] def get(uri, &block) - cached_file, downloaded = nil, nil + cached_file = nil + downloaded = nil + cached_file, downloaded = from_mutable_cache uri if InternetAvailability.instance.available? - cached_file, downloaded = from_immutable_caches(uri), false unless cached_file - fail "Unable to find cached file for #{uri.sanitize_uri}" unless cached_file + unless cached_file + cached_file = from_immutable_caches(uri) + downloaded = false + end + + raise "Unable to find cached file for #{uri.sanitize_uri}" unless cached_file + cached_file.cached(File::RDONLY | File::BINARY, downloaded, &block) end @@ -80,7 +93,7 @@ def evict(uri) CA_FILE = (Pathname.new(__FILE__).dirname + '../../../../resources/ca_certs.pem').freeze - FAILURE_LIMIT = 5.freeze + FAILURE_LIMIT = 5 HTTP_ERRORS = [ EOFError, @@ -111,13 +124,18 @@ def evict(uri) Net::HTTPTemporaryRedirect ].freeze - private_constant :CA_FILE, :FAILURE_LIMIT, :HTTP_ERRORS, :REDIRECT_TYPES + RETRY_MAX = 60 + + RETRY_MIN = 5 + + private_constant :CA_FILE, :FAILURE_LIMIT, :HTTP_ERRORS, :REDIRECT_TYPES, :RETRY_MAX, :RETRY_MIN def attempt(http, request, cached_file) downloaded = false http.request request do |response| - @logger.debug { "Status: #{response.code}" } + @logger.debug { "Response headers: #{response.to_hash}" } + @logger.debug { "Response status: #{response.code}" } if response.is_a? Net::HTTPOK cache_etag response, cached_file @@ -129,14 +147,43 @@ def attempt(http, request, cached_file) elsif redirect?(response) downloaded = update URI(response['Location']), cached_file else - fail InferredNetworkFailure, "Bad response: #{response}" + raise InferredNetworkFailure, "#{response.code} #{response.message}\n#{response.body}" end end downloaded end + def attempt_update(cached_file, http, uri) + request = request uri, cached_file + request.basic_auth uri.user, uri.password if uri.user && uri.password + + failures = 0 + begin + attempt http, request, cached_file + rescue InferredNetworkFailure, *HTTP_ERRORS => e + if (failures += 1) > FAILURE_LIMIT + InternetAvailability.instance.available false, "Request failed: #{e.message}" + raise e + else + delay = calculate_delay failures + @logger.warn { "Request failure #{failures}, retrying after #{delay}s. Failure: #{e.message}" } + sleep delay + retry + end + end + end + + def ca_file(http_options) + return unless CA_FILE.exist? + + http_options[:ca_file] = CA_FILE.to_s + @logger.debug { "Adding additional CA certificates from #{CA_FILE}" } + end + def cache_content(response, cached_file) + compressed = compressed?(response) + cached_file.cached(File::CREAT | File::WRONLY | File::BINARY) do |f| @logger.debug { "Persisting content to #{f.path}" } @@ -145,7 +192,7 @@ def cache_content(response, cached_file) f.fsync end - validate_size response['Content-Length'], cached_file + validate_size response['Content-Length'], cached_file unless compressed end def cache_etag(response, cached_file) @@ -153,7 +200,7 @@ def cache_etag(response, cached_file) return unless etag - @logger.debug { "Persisting etag: #{etag}" } + @logger.debug { "Persisting Etag: #{etag}" } cached_file.etag(File::CREAT | File::WRONLY | File::BINARY) do |f| f.truncate(0) @@ -167,7 +214,7 @@ def cache_last_modified(response, cached_file) return unless last_modified - @logger.debug { "Persisting last-modified: #{last_modified}" } + @logger.debug { "Persisting Last-Modified: #{last_modified}" } cached_file.last_modified(File::CREAT | File::WRONLY | File::BINARY) do |f| f.truncate(0) @@ -176,6 +223,35 @@ def cache_last_modified(response, cached_file) end end + def calculate_delay(failures) + [@retry_max, RETRY_MIN * (2**(failures - 1))].min + end + + def client_authentication(http_options) + client_authentication = JavaBuildpack::Util::ConfigurationUtils.load('cache')['client_authentication'] + + certificate_location = client_authentication['certificate_location'] + if certificate_location + File.open(certificate_location) do |f| + http_options[:cert] = OpenSSL::X509::Certificate.new f.read + @logger.debug { "Adding client certificate from #{certificate_location}" } + end + end + + private_key_location = client_authentication['private_key_location'] + + return unless private_key_location + + File.open(private_key_location) do |f| + http_options[:key] = OpenSSL::PKey.read f.read, client_authentication['private_key_password'] + @logger.debug { "Adding private key from #{private_key_location}" } + end + end + + def compressed?(response) + %w[br compress deflate gzip x-gzip].include?(response['Content-Encoding']) + end + def debug_ssl(http) socket = http.instance_variable_get('@socket') return unless socket @@ -191,7 +267,7 @@ def from_mutable_cache(uri) cached_file = CachedFile.new @mutable_cache_root, uri, true cached = update URI(uri), cached_file [cached_file, cached] - rescue => e + rescue StandardError => e @logger.warn { "Unable to download #{uri.sanitize_uri} into cache #{@mutable_cache_root}: #{e.message}" } nil end @@ -217,24 +293,32 @@ def http_options(rich_uri) http_options[:use_ssl] = true @logger.debug { 'Adding HTTP options for secure connection' } - if CA_FILE.exist? - http_options[:ca_file] = CA_FILE.to_s - @logger.debug { "Adding additional certs from #{CA_FILE}" } - end + ca_file http_options + client_authentication http_options end http_options end + def no_proxy?(uri) + hosts = (ENV.fetch('no_proxy', nil) || ENV.fetch('NO_PROXY', nil) || '').split ',' + hosts.any? { |host| uri.host.end_with? host } + end + def proxy(uri) - proxy_uri = if secure?(uri) - URI.parse(ENV['https_proxy'] || ENV['HTTPS_PROXY'] || '') + proxy_uri = if no_proxy?(uri) + URI.parse('') + elsif secure?(uri) + URI.parse(ENV.fetch('https_proxy', nil) || ENV.fetch('HTTPS_PROXY', nil) || '') else - URI.parse(ENV['http_proxy'] || ENV['HTTP_PROXY'] || '') + URI.parse(ENV.fetch('http_proxy', nil) || ENV.fetch('HTTP_PROXY', nil) || '') end - @logger.debug { "Proxy: #{proxy_uri.host}, #{proxy_uri.port}, #{proxy_uri.user}, #{proxy_uri.password}" } - Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password) + proxy_user = proxy_uri.user ? URI.decode_www_form_component(proxy_uri.user) : nil + proxy_pass = proxy_uri.password ? URI.decode_www_form_component(proxy_uri.password) : nil + + @logger.debug { "Proxy: #{proxy_uri.host}, #{proxy_uri.port}, #{proxy_user}, #{proxy_pass}" } + Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port, proxy_user, proxy_pass) end def redirect?(response) @@ -247,10 +331,12 @@ def request(uri, cached_file) if cached_file.etag? cached_file.etag(File::RDONLY | File::BINARY) { |f| request['If-None-Match'] = File.read(f) } end + @logger.debug { "Adding If-None-Match: #{request['If-None-Match']}" } if cached_file.last_modified? cached_file.last_modified(File::RDONLY | File::BINARY) { |f| request['If-Modified-Since'] = File.read(f) } end + @logger.debug { "Adding If-Modified-Since: #{request['If-Modified-Since']}" } @logger.debug { "Request: #{request.path}, #{request.to_hash}" } request @@ -261,42 +347,26 @@ def secure?(uri) end def update(uri, cached_file) - proxy(uri).start(uri.host, uri.port, http_options(uri)) do |http| - @logger.debug { "HTTP: #{http.address}, #{http.port}, #{http_options(uri)}" } + http_options = http_options(uri) + + proxy(uri).start(uri.host, uri.port, **http_options) do |http| + @logger.debug { "HTTP: #{http.address}, #{http.port}, #{http_options}" } debug_ssl(http) if secure?(uri) attempt_update(cached_file, http, uri) end end - def attempt_update(cached_file, http, uri) - request = request uri, cached_file - request.basic_auth uri.user, uri.password if uri.user && uri.password - - failures = 0 - begin - attempt http, request, cached_file - rescue InferredNetworkFailure, *HTTP_ERRORS => e - if (failures += 1) > FAILURE_LIMIT - InternetAvailability.instance.available false, "Request failed: #{e.message}" - raise e - else - @logger.warn { "Request failure #{failures}, retrying: #{e.message}" } - retry - end - end - end - def validate_size(expected_size, cached_file) - return unless expected_size + return unless expected_size - actual_size = cached_file.cached(File::RDONLY) { |f| f.size } + actual_size = cached_file.cached(File::RDONLY, &:size) @logger.debug { "Validated content size #{actual_size} is #{expected_size}" } return if expected_size.to_i == actual_size cached_file.destroy - fail InferredNetworkFailure, "Content has invalid size. Was #{actual_size}, should be #{expected_size}." + raise InferredNetworkFailure, "Content has invalid size. Was #{actual_size}, should be #{expected_size}." end end diff --git a/lib/java_buildpack/util/cache/inferred_network_failure.rb b/lib/java_buildpack/util/cache/inferred_network_failure.rb index 72af10eb81..ae67e8be66 100644 --- a/lib/java_buildpack/util/cache/inferred_network_failure.rb +++ b/lib/java_buildpack/util/cache/inferred_network_failure.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/util/cache/internet_availability.rb b/lib/java_buildpack/util/cache/internet_availability.rb index a0608c2c58..b6d1b8fe1f 100644 --- a/lib/java_buildpack/util/cache/internet_availability.rb +++ b/lib/java_buildpack/util/cache/internet_availability.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -47,7 +48,7 @@ def available? # # @param [Boolean] available whether the internet is available # @param [String, nil] message an optional message to be printed when the availability is set - # @yields an environment with internet availability temporarily overridden if block given + # @yield an environment with internet availability temporarily overridden if block given def available(available, message = nil) @monitor.synchronize do if block_given? diff --git a/lib/java_buildpack/util/class_file_utils.rb b/lib/java_buildpack/util/class_file_utils.rb index aa17c8c9d1..5b7e6abf6b 100644 --- a/lib/java_buildpack/util/class_file_utils.rb +++ b/lib/java_buildpack/util/class_file_utils.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -35,7 +36,7 @@ def class_files(application) (application.root + CLASS_FILE_PATTERN).glob.reject(&:directory?).sort end - CLASS_FILE_PATTERN = '**/*.class'.freeze + CLASS_FILE_PATTERN = '**/*.class' private_constant :CLASS_FILE_PATTERN diff --git a/lib/java_buildpack/util/colorize.rb b/lib/java_buildpack/util/colorize.rb new file mode 100644 index 0000000000..96115b1cd0 --- /dev/null +++ b/lib/java_buildpack/util/colorize.rb @@ -0,0 +1,173 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +class String + + @color_enabled = true + + class << self + attr_accessor :color_enabled + end + + # Sets the string to bold + def bold + return self unless self.class.color_enabled + + "\e[1m#{self}\e[22m" + end + + # Sets the string to italic + def italic + return self unless self.class.color_enabled + + "\e[3m#{self}\e[23m" + end + + # Sets the string to underlined + def underline + return self unless self.class.color_enabled + + "\e[4m#{self}\e[24m" + end + + # Sets the string to blink + def blink + return self unless self.class.color_enabled + + "\e[5m#{self}\e[25m" + end + + # Sets the string reverse the current colors + def reverse_color + return self unless self.class.color_enabled + + "\e[7m#{self}\e[27m" + end + + # Sets the string to black + def black + return self unless self.class.color_enabled + + "\e[30m#{self}\e[0m" + end + + # Sets the string to red + def red + return self unless self.class.color_enabled + + "\e[31m#{self}\e[0m" + end + + # Sets the string to green + def green + return self unless self.class.color_enabled + + "\e[32m#{self}\e[0m" + end + + # Sets the string to yellow + def yellow + return self unless self.class.color_enabled + + "\e[33m#{self}\e[0m" + end + + # Sets the string to blue + def blue + return self unless self.class.color_enabled + + "\e[34m#{self}\e[0m" + end + + # Sets the string to magenta + def magenta + return self unless self.class.color_enabled + + "\e[35m#{self}\e[0m" + end + + # Sets the string to cyan + def cyan + return self unless self.class.color_enabled + + "\e[36m#{self}\e[0m" + end + + # Sets the string to white + def white + return self unless self.class.color_enabled + + "\e[37m#{self}\e[0m" + end + + # Sets the string background to black + def bg_black + return self unless self.class.color_enabled + + "\e[40m#{self}\e[0m" + end + + # Sets the string background to red + def bg_red + return self unless self.class.color_enabled + + "\e[41m#{self}\e[0m" + end + + # Sets the string background to green + def bg_green + return self unless self.class.color_enabled + + "\e[42m#{self}\e[0m" + end + + # Sets the string background to yellow + def bg_yellow + return self unless self.class.color_enabled + + "\e[43m#{self}\e[0m" + end + + # Sets the string background to blue + def bg_blue + return self unless self.class.color_enabled + + "\e[44m#{self}\e[0m" + end + + # Sets the string background to magenta + def bg_magenta + return self unless self.class.color_enabled + + "\e[45m#{self}\e[0m" + end + + # Sets the string background to cyan + def bg_cyan + return self unless self.class.color_enabled + + "\e[46m#{self}\e[0m" + end + + # Sets the string background to white + def bg_white + return self unless self.class.color_enabled + + "\e[47m#{self}\e[0m" + end + +end diff --git a/lib/java_buildpack/util/configuration_utils.rb b/lib/java_buildpack/util/configuration_utils.rb index 83ddaf54d0..9bad2b07fb 100644 --- a/lib/java_buildpack/util/configuration_utils.rb +++ b/lib/java_buildpack/util/configuration_utils.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,12 +18,13 @@ require 'pathname' require 'java_buildpack/util' require 'java_buildpack/logging/logger_factory' +require 'shellwords' require 'yaml' module JavaBuildpack module Util - # Utilities for dealing with Groovy applications + # Utility for loading configuration class ConfigurationUtils private_class_method :new @@ -30,30 +32,157 @@ class ConfigurationUtils class << self # Loads a configuration file from the buildpack configuration directory. If the configuration file does not - # exist, returns an empty hash. + # exist, returns an empty hash. Overlays configuration in a matching environment variable, on top of the loaded + # configuration, if present. Will not add a new configuration key where an existing one does not exist. # - # @param [String] identifier the identifier of the configuration + # @param [String] identifier the identifier of the configuration to load + # @param [Boolean] clean_nil_values whether empty/nil values should be removed along with their keys from the + # returned configuration. # @param [Boolean] should_log whether the contents of the configuration file should be logged. This value # should be left to its default and exists to allow the logger to use the utility. # @return [Hash] the configuration or an empty hash if the configuration file does not exist - def load(identifier, should_log = true) - file = CONFIG_DIRECTORY + "#{identifier}.yml" + def load(identifier, clean_nil_values = true, should_log = true) + file = file_name(identifier) if file.exist? - configuration = YAML.load_file(file) - logger.debug { "Configuration from #{file}: #{configuration}" } if should_log - else - logger.debug { "No configuration file #{file} found" } if should_log + operator_var_name = default_variable_name(identifier) + operator_provided = ENV.fetch(operator_var_name, nil) + + user_var_name = environment_variable_name(identifier) + user_provided = ENV.fetch(user_var_name, nil) + configuration = load_configuration(file, operator_provided, operator_var_name, user_provided, + user_var_name, clean_nil_values, should_log) + elsif should_log + logger.debug { "No configuration file #{file} found" } end configuration || {} end + # Write a new configuration file to the buildpack configuration directory. Any existing file will be replaced. + # + # @param [String] identifier the identifier of the configuration to write + # @param [Boolean] should_log whether the contents of the configuration file should be logged. This value + # should be left to its default and exists to allow the logger to use the utility. + def write(identifier, new_content, should_log = true) + file = file_name(identifier) + + if file.exist? + logger.debug { "Writing configuration file #{file}" } if should_log + header = header(file) + + File.open(file, 'w') do |f| + header.each { |line| f.write line } + YAML.dump(new_content, f) + end + elsif should_log + logger.debug { "No configuration file #{file} found" } + end + end + private CONFIG_DIRECTORY = Pathname.new(File.expand_path('../../../config', File.dirname(__FILE__))).freeze - private_constant :CONFIG_DIRECTORY + DEFAULT_VARIABLE_PATTERN = 'JBP_DEFAULT_' + ENVIRONMENT_VARIABLE_PATTERN = 'JBP_CONFIG_' + + private_constant :CONFIG_DIRECTORY, :ENVIRONMENT_VARIABLE_PATTERN + + def clean_nil_values(configuration) + configuration.each do |key, value| + if value.is_a?(Hash) + configuration[key] = clean_nil_values value + elsif value.nil? + configuration.delete key + end + end + configuration + end + + def file_name(identifier) + CONFIG_DIRECTORY + "#{identifier}.yml" + end + + def header(file) + header = [] + File.open(file, 'r') do |f| + f.each do |line| + break if line =~ /^---/ + raise unless line =~ /^#/ || line =~ /^$/ + + header << line + end + end + header + end + + def load_configuration(file, operator_provided, operator_var_name, user_provided, user_var_name, + clean_nil_values, should_log) + configuration = YAML.load_file(file, permitted_classes: [Symbol], aliases: true) + logger.debug { "Configuration from #{file}: #{configuration}" } if should_log + + if operator_provided + begin + operator_provided_value = YAML.safe_load(operator_provided) + configuration = merge_configuration(configuration, operator_provided_value, operator_var_name, should_log) + rescue Psych::SyntaxError, Psych::DisallowedClass => e + raise "Default configuration value in environment variable #{operator_var_name} has invalid syntax: #{e}" + end + end + + if user_provided + begin + user_provided_value = YAML.safe_load(user_provided) + configuration = merge_configuration(configuration, user_provided_value, user_var_name, should_log) + rescue Psych::SyntaxError, Psych::DisallowedClass => e + raise "User configuration value in environment variable #{user_var_name} has invalid syntax: #{e}" + end + logger.debug { "Configuration from #{file} modified with: #{user_provided}" } if should_log + end + + clean_nil_values configuration if clean_nil_values + configuration + end + + def merge_configuration(configuration, user_provided_value, var_name, should_log) + case user_provided_value + when Hash + configuration = do_merge(configuration, user_provided_value, should_log) + when Array + user_provided_value.each { |new_prop| configuration = do_merge(configuration, new_prop, should_log) } + else + raise "User configuration value in environment variable #{var_name} is not valid: #{user_provided_value}" + end + configuration + end + + def do_merge(hash_v1, hash_v2, should_log) + hash_v2.each do |key, value| + if hash_v1.key? key + hash_v1[key] = do_resolve_value(key, hash_v1[key], value, should_log) + elsif should_log + logger.warn { "User config value for '#{key}' is not valid, existing property not present" } + end + end + hash_v1 + end + + def do_resolve_value(key, v1, v2, should_log) + return do_merge(v1, v2, should_log) if v1.is_a?(Hash) && v2.is_a?(Hash) + return v2 if !v1.is_a?(Hash) && !v2.is_a?(Hash) + + logger.warn { "User config value for '#{key}' is not valid, must be of a similar type" } if should_log + v1 + end + + def default_variable_name(config_name) + DEFAULT_VARIABLE_PATTERN + config_name.upcase + end + + def environment_variable_name(config_name) + ENVIRONMENT_VARIABLE_PATTERN + config_name.upcase + end def logger JavaBuildpack::Logging::LoggerFactory.instance.get_logger ConfigurationUtils diff --git a/lib/java_buildpack/util/constantize.rb b/lib/java_buildpack/util/constantize.rb index b0c2aa292f..535c9ac407 100644 --- a/lib/java_buildpack/util/constantize.rb +++ b/lib/java_buildpack/util/constantize.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/util/dash_case.rb b/lib/java_buildpack/util/dash_case.rb index 0c662fd475..943429ebe8 100644 --- a/lib/java_buildpack/util/dash_case.rb +++ b/lib/java_buildpack/util/dash_case.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,7 +22,8 @@ class String # # @return [String] The dash case rendering of this +String+ def dash_case - split('::').last + split('::') + .last .gsub(/([A-Z]+)([A-Z][a-z])/, '\1-\2') .gsub(/([a-z\d])([A-Z])/, '\1-\2') .downcase diff --git a/lib/java_buildpack/util/external_config.rb b/lib/java_buildpack/util/external_config.rb new file mode 100644 index 0000000000..2d2dfbe170 --- /dev/null +++ b/lib/java_buildpack/util/external_config.rb @@ -0,0 +1,94 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/util' +require 'java_buildpack/util/sanitizer' +require 'pathname' + +module JavaBuildpack + module Util + + # A module encapsulating all of the utility components for external configuration + module ExternalConfig + + # Root URL for where external configuration will be located + def external_config_root + @application.environment["#{self.class::CONFIG_PREFIX}_CONF_HTTP_URL"].chomp('/') + '/java/' + end + + # Check for configuration files on a remote server. If found, copy to conf dir under each ver* dir + # @return [Void] + def override_default_config_remote + return unless @application.environment["#{self.class::CONFIG_PREFIX}_CONF_HTTP_URL"] + + JavaBuildpack::Util::Cache::InternetAvailability.instance.available( + true, "The #{self.class.name} remote configuration download location is always accessible" + ) do + @logger.info { "Downloading override configuration files from #{external_config_root.sanitize_uri}" } + self.class::CONFIG_FILES.each do |conf_file| + uri = URI(external_config_root + conf_file) + + # `download()` uses retries with exponential backoff which is expensive + # for situations like 404 File not Found. Also, `download()` doesn't expose + # an api to disable retries, which makes this check necessary to prevent + # long install times. + next unless check_if_resource_exists(uri, conf_file) + + download('N/A', uri.to_s) do |file| + yield file, conf_file + end + end + end + end + + # Check if configuration file exists on the server before download + # @param [ResourceURI] resource_uri URI of the remote configuration server + # @param [ConfigFileName] conf_file Name of the configuration file + # @return [Boolean] returns true if files exists on path specified by resource_uri, false otherwise + def check_if_resource_exists(resource_uri, conf_file) + # check if resource exists on remote server + begin + opts = { use_ssl: true } if resource_uri.scheme == 'https' + response = Net::HTTP.start(resource_uri.host, resource_uri.port, **opts) do |http| + req = Net::HTTP::Head.new(resource_uri) + if resource_uri.user != '' || resource_uri.password != '' + req.basic_auth(resource_uri.user, resource_uri.password) + end + http.request(req) + end + rescue StandardError => e + @logger.error { "Request failure: #{e.message}" } + return false + end + + case response + when Net::HTTPSuccess + true + when Net::HTTPRedirection + location = response['location'] + @logger.info { "redirected to #{location.sanitize_uri}" } + check_if_resource_exists(location, conf_file) + else + clean_url = resource_uri.to_s.sanitize_uri + @logger.info { "Could not fetch #{clean_url}. Code: #{response.code} - #{response.message}" } + false + end + end + end + + end +end diff --git a/lib/java_buildpack/util/file_enumerable.rb b/lib/java_buildpack/util/file_enumerable.rb index 0a4b02f7b2..db54bd974b 100644 --- a/lib/java_buildpack/util/file_enumerable.rb +++ b/lib/java_buildpack/util/file_enumerable.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -63,7 +64,7 @@ def select(candidates, &block) def open(default, candidate, &block) candidate.open('r', external_encoding: 'UTF-8', &block) - rescue => e + rescue StandardError => e @logger.warn e.message default end diff --git a/lib/java_buildpack/util/filtering_pathname.rb b/lib/java_buildpack/util/filtering_pathname.rb index fcc7203bcd..dd04d77a7e 100644 --- a/lib/java_buildpack/util/filtering_pathname.rb +++ b/lib/java_buildpack/util/filtering_pathname.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -55,7 +56,7 @@ class FilteringPathname # +false+ (to filter out the pathname). Defaults to keeping everything # @param [Boolean] mutable +true+ if and only if the +FilteringPathname+ may be used to mutate the file system def initialize(pathname, filter, mutable) - fail 'Non-absolute pathname' unless pathname.absolute? + raise 'Non-absolute pathname' unless pathname.absolute? @pathname = pathname @filter = filter @@ -82,10 +83,6 @@ def ===(other) @pathname === comparison_target(other) # rubocop:disable Style/CaseEquality end - # Dispatch superclass methods via method_missing. - undef_method :taint - undef_method :untaint - # @see Pathname. def +(other) filtered_pathname(@pathname + other) @@ -102,9 +99,9 @@ def entries end # @see Pathname. - def open(mode = nil, *args, &block) - check_mutable if mode =~ /[wa]/ - delegate.open(mode, *args, &block) + def open(mode = nil, *args, **kwargs, &block) + check_mutable if /[wa]/ =~ mode.to_s + delegate.open(mode, *args, **kwargs, &block) end # @see Pathname. @@ -142,23 +139,23 @@ def glob(flags = 0) end end - attr_reader :pathname + protected - protected :pathname + attr_reader :pathname private - MUTATORS = [:chmod, :chown, :delete, :lchmod, :lchown, :make_link, :make_symlink, :mkdir, :mkpath, :rename, - :rmdir, :rmtree, :taint, :unlink, :untaint].to_set.freeze + MUTATORS = %i[chmod chown delete lchmod lchown make_link make_symlink mkdir mkpath rename rmdir rmtree taint + unlink].to_set.freeze private_constant :MUTATORS def check_file_does_not_exist(file) - fail "#{file} should not exist" if file.exist? + raise "#{file} should not exist" if file.exist? end def check_mutable - fail 'FilteringPathname is immutable' unless @mutable + raise 'FilteringPathname is immutable' unless @mutable end def comparison_target(other) @@ -198,14 +195,14 @@ def filtered_pathname(pathname) def method_missing(method, *args) check_mutable if MUTATORS.member? method - if block_given? - result = delegate.send(method, *args) do |*values| - converted_values = values.map { |value| convert_if_necessary(value) }.compact - yield(*converted_values) unless converted_values.empty? - end - else - result = delegate.send(method, *args) - end + result = if block_given? + delegate.send(method, *args) do |*values| + converted_values = values.map { |value| convert_if_necessary(value) }.compact + yield(*converted_values) unless converted_values.empty? + end + else + delegate.send(method, *args) + end convert_result_if_necessary(result) end @@ -222,7 +219,8 @@ def visible(entry) end def filter(pathname) - fail 'Non-absolute pathname' unless pathname.absolute? + raise 'Non-absolute pathname' unless pathname.absolute? + @filter.call(pathname.cleanpath) end diff --git a/lib/java_buildpack/util/find_single_directory.rb b/lib/java_buildpack/util/find_single_directory.rb index 795d772741..fe15adbd0d 100644 --- a/lib/java_buildpack/util/find_single_directory.rb +++ b/lib/java_buildpack/util/find_single_directory.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,6 +20,8 @@ module JavaBuildpack module Util + module_function + # Find the single directory in the root of the droplet # # @return [Pathname, nil] the single directory in the root of the droplet, otherwise +nil+ @@ -27,7 +30,5 @@ def find_single_directory roots.size == 1 ? roots.first : nil end - module_function :find_single_directory - end end diff --git a/lib/java_buildpack/util/format_duration.rb b/lib/java_buildpack/util/format_duration.rb index e2b8ee7ce4..2a8d2f56c2 100644 --- a/lib/java_buildpack/util/format_duration.rb +++ b/lib/java_buildpack/util/format_duration.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -26,32 +27,32 @@ class Numeric def duration remainder = self - hours = (remainder / HOUR).to_int + hours = (remainder / HOUR).to_int remainder -= HOUR * hours - minutes = (remainder / MINUTE).to_int + minutes = (remainder / MINUTE).to_int remainder -= MINUTE * minutes - return "#{hours}h #{minutes}m" if hours > 0 + return "#{hours}h #{minutes}m" if hours.positive? - seconds = (remainder / SECOND).to_int + seconds = (remainder / SECOND).to_int remainder -= SECOND * seconds - return "#{minutes}m #{seconds}s" if minutes > 0 + return "#{minutes}m #{seconds}s" if minutes.positive? tenths = (remainder / TENTH).to_int "#{seconds}.#{tenths}s" end - MILLISECOND = 0.001.freeze + MILLISECOND = 0.001 - TENTH = (100 * MILLISECOND).freeze + TENTH = 100 * MILLISECOND - SECOND = (10 * TENTH).freeze + SECOND = 10 * TENTH - MINUTE = (60 * SECOND).freeze + MINUTE = 60 * SECOND - HOUR = (60 * MINUTE).freeze + HOUR = 60 * MINUTE private_constant :MILLISECOND, :TENTH, :SECOND, :MINUTE, :HOUR diff --git a/lib/java_buildpack/util/groovy_utils.rb b/lib/java_buildpack/util/groovy_utils.rb index 6d04b899a0..39e798312e 100644 --- a/lib/java_buildpack/util/groovy_utils.rb +++ b/lib/java_buildpack/util/groovy_utils.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -32,7 +33,7 @@ class << self # @param [File] file the file to scan # @return [Boolean] +true+ if the file is a +beans+style configuration, +false+ otherwise. def beans?(file) - safe_read(file) { Pathname.new(file).read =~ /beans[\s]*\{/ } + safe_read(file) { Pathname.new(file).read =~ /beans\s*\{/ } end # Indicates whether a file has a +main()+ method in it @@ -48,7 +49,7 @@ def main_method?(file) # @param [File] file the file to scan # @return [Boolean] +true+ if the file is a POGO, +false+ otherwise. def pogo?(file) - safe_read(file) { Pathname.new(file).read =~ /class [\w]+[\s\w]*\{/ } + safe_read(file) { Pathname.new(file).read =~ /class \w+[\s\w]*\{/ } end # Indicates whether a file has a shebang @@ -69,13 +70,13 @@ def groovy_files(application) private - GROOVY_FILE_PATTERN = '**/*.groovy'.freeze + GROOVY_FILE_PATTERN = '**/*.groovy' private_constant :GROOVY_FILE_PATTERN def safe_read(file) yield - rescue => e + rescue StandardError => e raise "Unable to read file #{file.path}: #{e.message}" end diff --git a/lib/java_buildpack/util/jar_finder.rb b/lib/java_buildpack/util/jar_finder.rb index 7e2ea914d9..779985999a 100644 --- a/lib/java_buildpack/util/jar_finder.rb +++ b/lib/java_buildpack/util/jar_finder.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -38,7 +39,7 @@ def is?(application) jar application end - # The version of of the JAR file used by the application + # The version of the JAR file used by the application # # @param [Application] application the application to search # @return [String] the version of the JAR file used by the application @@ -49,7 +50,7 @@ def version(application) private def jar(application) - (application.root + '**/lib/*.jar').glob.find { |jar| jar.to_s =~ @pattern } + (application.root + '**/*.jar').glob.find { |jar| jar.to_s =~ @pattern } end end diff --git a/lib/java_buildpack/util/java_main_utils.rb b/lib/java_buildpack/util/java_main_utils.rb index f723e3074b..0ecbfe21f5 100644 --- a/lib/java_buildpack/util/java_main_utils.rb +++ b/lib/java_buildpack/util/java_main_utils.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -45,15 +46,13 @@ def main_class(application, configuration = nil) # @return [Properties] the properties from the application's manifest (if any) def manifest(application) manifest_file = application.root + 'META-INF/MANIFEST.MF' - manifest_file = manifest_file.exist? ? manifest_file : nil + manifest_file = nil unless manifest_file.exist? JavaBuildpack::Util::Properties.new(manifest_file) end - private - - MAIN_CLASS_PROPERTY = 'java_main_class'.freeze + MAIN_CLASS_PROPERTY = 'java_main_class' - MANIFEST_PROPERTY = 'Main-Class'.freeze + MANIFEST_PROPERTY = 'Main-Class' private_constant :MAIN_CLASS_PROPERTY, :MANIFEST_PROPERTY diff --git a/lib/java_buildpack/util/play.rb b/lib/java_buildpack/util/play.rb index ce53ade7e9..9e9f0a5dbb 100644 --- a/lib/java_buildpack/util/play.rb +++ b/lib/java_buildpack/util/play.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/util/play/base.rb b/lib/java_buildpack/util/play/base.rb index 3b97c9ebc5..c66a75b5b8 100644 --- a/lib/java_buildpack/util/play/base.rb +++ b/lib/java_buildpack/util/play/base.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -36,7 +37,7 @@ def initialize(droplet) # (see JavaBuildpack::Component::BaseComponent#compile) def compile update_file start_script, ORIGINAL_BOOTSTRAP, REPLACEMENT_BOOTSTRAP - start_script.chmod 0755 + start_script.chmod 0o755 augment_classpath end @@ -51,10 +52,13 @@ def jar?(pattern) # (see JavaBuildpack::Component::BaseComponent#release) def release @droplet.java_opts.add_system_property 'http.port', '$PORT' + @droplet.environment_variables + .add_environment_variable 'PATH', "#{qualify_path(@droplet.java_home.root, @droplet.root)}/bin:$PATH" [ - "PATH=#{@droplet.java_home.root}/bin:$PATH", + @droplet.environment_variables.as_env_vars, @droplet.java_home.as_env_var, + 'exec', qualify_path(start_script, @droplet.root), java_opts ].flatten.compact.join(' ') @@ -62,7 +66,7 @@ def release # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) def supports? - start_script && start_script.exist? && play_jar + start_script&.exist? && play_jar end # Returns the version of the play application @@ -78,28 +82,28 @@ def version # # @return [Void] def augment_classpath - fail "Method 'augment_classpath' must be defined" + raise "Method 'augment_classpath' must be defined" end # Returns the +JAVA_OPTS+ in the form that they need to be added to the command line # # @return [Array] the +JAVA_OPTS+ in the form that they need to be added to the command line def java_opts - fail "Method 'java_opts' must be defined" + raise "Method 'java_opts' must be defined" end # Returns the path to the play application library dir. May return +nil+ if no library dir exists. # # @return [Pathname] the path to the play application library dir. May return +nil+ if no library dir exists. def lib_dir - fail "Method 'lib_dir' must be defined" + raise "Method 'lib_dir' must be defined" end # Returns the path to the play application start script. May return +nil+ if no script exists. # # @return [Pathname] the path to the play application start script. May return +nil+ if no script exists. def start_script - fail "Method 'start_script' must be defined" + raise "Method 'start_script' must be defined" end # Updates the contents of a file @@ -119,9 +123,9 @@ def update_file(path, pattern, replacement) private - ORIGINAL_BOOTSTRAP = 'play.core.server.NettyServer'.freeze + ORIGINAL_BOOTSTRAP = 'play.core.server.NettyServer' - REPLACEMENT_BOOTSTRAP = 'org.cloudfoundry.reconfiguration.play.Bootstrap'.freeze + REPLACEMENT_BOOTSTRAP = 'org.cloudfoundry.reconfiguration.play.Bootstrap' private_constant :ORIGINAL_BOOTSTRAP, :REPLACEMENT_BOOTSTRAP diff --git a/lib/java_buildpack/util/play/factory.rb b/lib/java_buildpack/util/play/factory.rb index f688663f6f..10f2b35348 100644 --- a/lib/java_buildpack/util/play/factory.rb +++ b/lib/java_buildpack/util/play/factory.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -43,7 +44,8 @@ def create(droplet) Pre22Staged.new(droplet) ].select(&:supports?) - fail "Play Framework application version cannot be determined: #{candidates}" if candidates.size > 1 + raise "Play Framework application version cannot be determined: #{candidates}" if candidates.size > 1 + candidates.empty? ? nil : candidates.first end diff --git a/lib/java_buildpack/util/play/post22.rb b/lib/java_buildpack/util/play/post22.rb index 5c8cc102ab..7bb19629bf 100644 --- a/lib/java_buildpack/util/play/post22.rb +++ b/lib/java_buildpack/util/play/post22.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -34,21 +35,13 @@ def augment_classpath "$app_home/#{additional_library.relative_path_from(start_script.dirname)}" end - update_file start_script, /^declare -r app_classpath=\"(.*)\"$/, + update_file start_script, /^declare -r app_classpath="(.*)"$/, "declare -r app_classpath=\"#{additional_classpath.join(':')}:\\1\"" end # (see JavaBuildpack::Util::Play::Base#java_opts) def java_opts - java_opts = @droplet.java_opts - - java_opts.each do |option| - next unless option.shellsplit.length > 1 && !bash_expression?(option) - - fail "Invalid Java option contains more than one option: '#{option}'" - end - - java_opts.map { |java_opt| "-J#{java_opt}" } + '$(for I in $JAVA_OPTS ; do echo "-J$I" ; done)' end # (see JavaBuildpack::Util::Play::Base#lib_dir) @@ -65,7 +58,7 @@ def start_script # # @return [Pathname] the root of the play application def root - fail "Method 'root' must be defined" + raise "Method 'root' must be defined" end private diff --git a/lib/java_buildpack/util/play/post22_dist.rb b/lib/java_buildpack/util/play/post22_dist.rb index 0777944f01..8e5748980f 100644 --- a/lib/java_buildpack/util/play/post22_dist.rb +++ b/lib/java_buildpack/util/play/post22_dist.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -23,9 +24,7 @@ module Play # Encapsulate inspection and modification of Play dist applications from Play 2.2.0 onwards. class Post22Dist < Post22 - alias_method :root, :find_single_directory - - protected :root + alias root find_single_directory end diff --git a/lib/java_buildpack/util/play/post22_staged.rb b/lib/java_buildpack/util/play/post22_staged.rb index 331311b802..2c49728a61 100644 --- a/lib/java_buildpack/util/play/post22_staged.rb +++ b/lib/java_buildpack/util/play/post22_staged.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/util/play/pre22.rb b/lib/java_buildpack/util/play/pre22.rb index 3b6f63660c..9bdd3f7e0f 100644 --- a/lib/java_buildpack/util/play/pre22.rb +++ b/lib/java_buildpack/util/play/pre22.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -27,7 +28,7 @@ class Pre22 < Base # (see JavaBuildpack::Util::Play::Base#java_opts) def java_opts - @droplet.java_opts + '$JAVA_OPTS' end # (see JavaBuildpack::Util::Play::Base#start_script) @@ -39,7 +40,7 @@ def start_script # # @return [Pathname] the root of the play application def root - fail "Method 'root' must be defined" + raise "Method 'root' must be defined" end end diff --git a/lib/java_buildpack/util/play/pre22_dist.rb b/lib/java_buildpack/util/play/pre22_dist.rb index e6c808b44c..9503100b4f 100644 --- a/lib/java_buildpack/util/play/pre22_dist.rb +++ b/lib/java_buildpack/util/play/pre22_dist.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -34,7 +35,7 @@ def augment_classpath "$scriptdir/#{additional_library.relative_path_from(root)}" end - update_file start_script, /^classpath=\"(.*)\"$/, "classpath=\"#{additional_classpath.join(':')}:\\1\"" + update_file start_script, /^classpath="(.*)"$/, "classpath=\"#{additional_classpath.join(':')}:\\1\"" end end @@ -43,7 +44,7 @@ def lib_dir root + 'lib' end - alias_method :root, :find_single_directory + alias root find_single_directory end diff --git a/lib/java_buildpack/util/play/pre22_staged.rb b/lib/java_buildpack/util/play/pre22_staged.rb index 13796c4fe6..583a988409 100644 --- a/lib/java_buildpack/util/play/pre22_staged.rb +++ b/lib/java_buildpack/util/play/pre22_staged.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/util/properties.rb b/lib/java_buildpack/util/properties.rb index b78ba6de8b..8efc512d19 100644 --- a/lib/java_buildpack/util/properties.rb +++ b/lib/java_buildpack/util/properties.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -27,7 +28,7 @@ class Properties < Hash # @param [Pathname, nil] file_name the file to use for initialization. If no file is passed in, the instance is # empty. def initialize(file_name) - return self if file_name.nil? + return if file_name.nil? contents = file_name.open(&:read) contents.gsub!(/[\r\n\f]+ /, '') @@ -35,7 +36,7 @@ def initialize(file_name) contents.each_line do |line| next if blank_line?(line) || comment_line?(line) - match_data = /^[\s]*([^:=\s]+)[\s]*[=:]?[\s]*(.*?)\s*$/.match(line) + match_data = /^\s*([^:=\s]+)\s*[=:]?\s*(.*?)\s*$/.match(line) self[match_data[1]] = match_data[2] if match_data end end @@ -43,11 +44,11 @@ def initialize(file_name) private def blank_line?(line) - line =~ /^[\s]*$/ + line =~ /^\s*$/ end def comment_line?(line) - line =~ /^[\s]*[#!].*$/ + line =~ /^\s*[#!].*$/ end end diff --git a/lib/java_buildpack/util/qualify_path.rb b/lib/java_buildpack/util/qualify_path.rb index 06bed2d946..5d15028892 100644 --- a/lib/java_buildpack/util/qualify_path.rb +++ b/lib/java_buildpack/util/qualify_path.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/util/ratpack_utils.rb b/lib/java_buildpack/util/ratpack_utils.rb index acb88286b3..064da366bd 100644 --- a/lib/java_buildpack/util/ratpack_utils.rb +++ b/lib/java_buildpack/util/ratpack_utils.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/lib/java_buildpack/util/sanitizer.rb b/lib/java_buildpack/util/sanitizer.rb index 97a3386f22..50a2947cd2 100644 --- a/lib/java_buildpack/util/sanitizer.rb +++ b/lib/java_buildpack/util/sanitizer.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2014 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,6 +18,32 @@ # A mixin that adds the ability to turn a +String+ into sanitized uri class String + # Takes the uri query params and strips out credentials + # + # @return [String] the sanitized query params + def handle_params(params) + keywords = /key + |password + |username + |cred(ential)*(s)* + |password + |token + |api[-_]token + |api + |auth(entication)* + |access[-_]token + |secret[-_]token/ix + + query_params = '' + + params.split('&').each do |single_param| + k, v = single_param.split('=') + v = '***' if k.match(keywords) + query_params += k + '=' + v + '&' + end + query_params + end + # Takes a uri and strips out any credentials it may contain. # # @return [String] the sanitized uri @@ -24,7 +51,12 @@ def sanitize_uri rich_uri = URI(self) rich_uri.user = nil rich_uri.password = nil + + if rich_uri.query + query_params = handle_params(rich_uri.query) + rich_uri.query = query_params.chop + end + rich_uri.to_s end - end diff --git a/lib/java_buildpack/util/shell.rb b/lib/java_buildpack/util/shell.rb index 9be88eb00f..d8a0a1035b 100644 --- a/lib/java_buildpack/util/shell.rb +++ b/lib/java_buildpack/util/shell.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -25,16 +26,19 @@ module Shell # A +system()+-like command that ensure that the execution fails if the command returns a non-zero exit code # - # @param [String] command the command to run + # @param [Object] args The command to run # @return [Void] - def shell(command) - Open3.popen3(command) do |_stdin, stdout, stderr, wait_thr| - if wait_thr.value != 0 - puts "\nCommand '#{command}' has failed" - puts "STDOUT: #{stdout.gets}" - puts "STDERR: #{stderr.gets}" - - fail + def shell(*args) + Open3.popen3(*args) do |_stdin, stdout, stderr, wait_thr| + out = stdout.gets nil + err = stderr.gets nil + + unless wait_thr.value.success? + puts "\nCommand '#{args.join ' '}' has failed" + puts "STDOUT: #{out}" + puts "STDERR: #{err}" + + raise end end end diff --git a/lib/java_buildpack/util/snake_case.rb b/lib/java_buildpack/util/snake_case.rb index eb185318c1..115ea5f56c 100644 --- a/lib/java_buildpack/util/snake_case.rb +++ b/lib/java_buildpack/util/snake_case.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,7 +18,7 @@ # A mixin that adds the ability to turn a +String+ into snake case class String - # Converts a string to snake case. For example, the Spring +SnakeCase+ would become +snake_case+. + # Converts a string to snake case. For example, the String +SnakeCase+ would become +snake_case+. # # @return [String] The snake case rendering of this +String+ def snake_case diff --git a/lib/java_buildpack/util/space_case.rb b/lib/java_buildpack/util/space_case.rb index d6a60838c3..3183807802 100644 --- a/lib/java_buildpack/util/space_case.rb +++ b/lib/java_buildpack/util/space_case.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,11 +18,12 @@ # A mixin that adds the ability to turn a +String+ into space case class String - # Converts a string to space case. For example, the Spring +SpaceCase+ would become +Space Case+. + # Converts a string to space case. For example, the String +SpaceCase+ would become +Space Case+. # # @return [String] The space case rendering of this +String+ def space_case - split('::').last + split('::') + .last .gsub(/([A-Z]+)([A-Z][a-z])/, '\1 \2') .gsub(/([a-z\d])([A-Z])/, '\1 \2') .tr('-', ' ') diff --git a/lib/java_buildpack/util/spring_boot_utils.rb b/lib/java_buildpack/util/spring_boot_utils.rb index a793b7f01b..28bde3c063 100644 --- a/lib/java_buildpack/util/spring_boot_utils.rb +++ b/lib/java_buildpack/util/spring_boot_utils.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,15 +18,108 @@ require 'pathname' require 'java_buildpack/util' require 'java_buildpack/util/jar_finder' +require 'java_buildpack/util/java_main_utils' +require 'java_buildpack/util/shell' module JavaBuildpack module Util # Utilities for dealing with Spring Boot applications - class SpringBootUtils < JarFinder + class SpringBootUtils + include JavaBuildpack::Util::Shell def initialize - super(/.*spring-boot-([\d].*)\.jar/) + @jar_finder = JavaBuildpack::Util::JarFinder.new(/.*spring-boot-(\d.*)\.jar/) + end + + # Caches the dependencies of a Thin Launcher application by execute the application with +dryRun+ + # + # @param [Pathname] java_home the Java home to find +java+ in + # @param [Pathname] application_root the root of the application to run + # @param [Pathname] thin_root the root to cache cache dependencies at + def cache_thin_dependencies(java_home, application_root, thin_root) + shell "#{java_home + 'bin/java'} -Dthin.dryrun -Dthin.root=#{thin_root} -cp #{application_root} #{THIN_WRAPPER}" + end + + # Indicates whether an application is a Spring Boot application + # + # @param [Application] application the application to search + # @return [Boolean] +true+ if the application is a Spring Boot application, +false+ otherwise + def is?(application) + JavaBuildpack::Util::JavaMainUtils.manifest(application).key?(SPRING_BOOT_VERSION) || + @jar_finder.is?(application) + end + + # Indicates whether an application is a Spring Boot Thin Launcher application + # + # @param [Application] application the application to search + # @return [Boolean] +true+ if the application is a Spring Boot Thin Launcher application, +false+ otherwise + def thin?(application) + THIN_WRAPPER == JavaBuildpack::Util::JavaMainUtils.main_class(application) + end + + # The lib directory of Spring Boot used by the application + # + # @param [Droplet] droplet the droplet to search + # @return [String] the lib directory of Spring Boot used by the application + def lib(droplet) + candidate = manifest_lib_dir(droplet) + return candidate if candidate&.exist? + + candidate = boot_inf_lib_dir(droplet) + return candidate if candidate&.exist? + + candidate = web_inf_lib_dir(droplet) + return candidate if candidate&.exist? + + candidate = lib_dir(droplet) + return candidate if candidate&.exist? + + raise 'No lib directory found' + end + + # The version of Spring Boot used by the application + # + # @param [Application] application the application to search + # @return [String] the version of Spring Boot used by the application + def version(application) + JavaBuildpack::Util::JavaMainUtils.manifest(application)[SPRING_BOOT_VERSION] || + @jar_finder.version(application) + end + + # The version of Spring Boot used by the application - only considers the MANIFEST entry + # + # @param [Application] application the application to search + # @return [String] the version of Spring Boot used by the application + def version_strict(application) + JavaBuildpack::Util::JavaMainUtils.manifest(application)[SPRING_BOOT_VERSION] + end + + private + + SPRING_BOOT_LIB = 'Spring-Boot-Lib' + + SPRING_BOOT_VERSION = 'Spring-Boot-Version' + + THIN_WRAPPER = 'org.springframework.boot.loader.wrapper.ThinJarWrapper' + + private_constant :SPRING_BOOT_LIB, :SPRING_BOOT_VERSION + + def boot_inf_lib_dir(droplet) + droplet.root + 'BOOT-INF/lib' + end + + def manifest_lib_dir(droplet) + value = JavaBuildpack::Util::JavaMainUtils.manifest(droplet)[SPRING_BOOT_LIB] + value ? droplet.root + value : nil + end + + def lib_dir(droplet) + droplet.root + 'lib' + end + + def web_inf_lib_dir(droplet) + droplet.root + 'WEB-INF/lib' end end diff --git a/lib/java_buildpack/util/start_script.rb b/lib/java_buildpack/util/start_script.rb index 354b3ec73b..3c532ef9a1 100644 --- a/lib/java_buildpack/util/start_script.rb +++ b/lib/java_buildpack/util/start_script.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,6 +20,8 @@ module JavaBuildpack module Util + module_function + # Find a start script relative to a root directory. A start script is defined as existing in the +bin/+ directory # and being either the only file, or the only file with a counterpart named +.bat+ # @@ -36,7 +39,5 @@ def start_script(root) end end - module_function :start_script - end end diff --git a/lib/java_buildpack/util/to_b.rb b/lib/java_buildpack/util/to_b.rb index ec51b5565a..76e36b259e 100644 --- a/lib/java_buildpack/util/to_b.rb +++ b/lib/java_buildpack/util/to_b.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2014 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,9 +20,9 @@ class String # Converts a +String+ to a boolean # - # @return [Boolean] +true+ if +.downcase == 'true'+. +false+ otherwise + # @return [Boolean] +true+ if +.casecmp 'true'+. +false+ otherwise def to_b - downcase == 'true' + casecmp 'true' end end diff --git a/lib/java_buildpack/util/tokenized_version.rb b/lib/java_buildpack/util/tokenized_version.rb index 9d6f076db9..12e0e82dc0 100644 --- a/lib/java_buildpack/util/tokenized_version.rb +++ b/lib/java_buildpack/util/tokenized_version.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -48,11 +49,11 @@ def initialize(version, allow_wildcards = true) def <=>(other) comparison = 0 i = 0 - while comparison == 0 && i < 3 + while comparison.zero? && i < 3 comparison = self[i].to_i <=> other[i].to_i - i += 1 + i += 1 end - comparison = qualifier_compare(non_nil_qualifier(self[3]), non_nil_qualifier(other[3])) if comparison == 0 + comparison = qualifier_compare(non_nil_qualifier(self[3]), non_nil_qualifier(other[3])) if comparison.zero? comparison end @@ -69,7 +70,7 @@ def to_s # @param [Integer] maximum_components the maximum number of components this version is allowed to have # @raise if this version has more than the given number of components def check_size(maximum_components) - fail "Malformed version #{self}: too many version components" if self[maximum_components] + raise "Malformed version #{self}: too many version components" if self[maximum_components] end private @@ -84,15 +85,17 @@ def char_compare(c1, c2) def major_or_minor_and_tail(s) if s.nil? || s.empty? - major_or_minor, tail = nil, nil + major_or_minor = nil + tail = nil else - fail "Invalid version '#{s}': must not end in '.'" if s[-1] == '.' - fail "Invalid version '#{s}': missing component" if s =~ /\.[\._]/ - tokens = s.match(/^([^\.]+)(?:\.(.*))?/) + raise "Invalid version '#{s}': must not end in '.'" if s[-1] == '.' + raise "Invalid version '#{s}': missing component" if s =~ /\.[._]/ + + tokens = s.match(/^([^.]+)(?:\.(.*))?/) - major_or_minor, tail = tokens[1..-1] + major_or_minor, tail = tokens[1..] - fail "Invalid major or minor version '#{major_or_minor}'" unless valid_major_minor_or_micro major_or_minor + raise "Invalid major or minor version '#{major_or_minor}'" unless valid_major_minor_or_micro major_or_minor end [major_or_minor, tail] @@ -100,15 +103,17 @@ def major_or_minor_and_tail(s) def micro_and_qualifier(s) if s.nil? || s.empty? - micro, qualifier = nil, nil + micro = nil + qualifier = nil else - fail "Invalid version '#{s}': must not end in '_'" if s[-1] == '_' - tokens = s.match(/^([^\_]+)(?:_(.*))?/) + raise "Invalid version '#{s}': must not end in '_'" if s[-1] == '_' - micro, qualifier = tokens[1..-1] + tokens = s.match(/^([^_]+)(?:_(.*))?/) - fail "Invalid micro version '#{micro}'" unless valid_major_minor_or_micro micro - fail "Invalid qualifier '#{qualifier}'" unless valid_qualifier qualifier + micro, qualifier = tokens[1..] + + raise "Invalid micro version '#{micro}'" unless valid_major_minor_or_micro micro + raise "Invalid qualifier '#{qualifier}'" unless valid_qualifier qualifier end [micro, qualifier] @@ -119,15 +124,15 @@ def minimum_qualifier_length(a, b) end def qualifier_compare(a, b) - comparison = 0 + comparison = a[/^\d+/].to_i <=> b[/^\d+/].to_i i = 0 - until comparison != 0 || i == minimum_qualifier_length(a, b) + until comparison.nonzero? || i == minimum_qualifier_length(a, b) comparison = char_compare(a[i], b[i]) - i += 1 + i += 1 end - comparison = a.length <=> b.length if comparison == 0 + comparison = a.length <=> b.length if comparison.zero? comparison end @@ -139,22 +144,27 @@ def non_nil_qualifier(qualifier) def validate(allow_wildcards) wildcarded = false each do |value| - if value == WILDCARD && !allow_wildcards - fail "Invalid version '#{@version}': wildcards are not allowed this context" + if ends_with_wildcard(value) && !allow_wildcards + raise "Invalid version '#{@version}': wildcards are not allowed this context" end - fail "Invalid version '#{@version}': no characters are allowed after a wildcard" if wildcarded && value - wildcarded = true if value == WILDCARD + raise "Invalid version '#{@version}': no characters are allowed after a wildcard" if wildcarded && value + + wildcarded = true if ends_with_wildcard(value) end - fail "Invalid version '#{@version}': missing component" if !wildcarded && compact.length < 3 + raise "Invalid version '#{@version}': missing component" if !wildcarded && compact.length < 3 + end + + def ends_with_wildcard(value) + !value.nil? && value.end_with?(WILDCARD) end def valid_major_minor_or_micro(major_minor_or_micro) - major_minor_or_micro =~ /^[\d]*$/ || major_minor_or_micro =~ /^\+$/ + major_minor_or_micro =~ /^\d*$/ || major_minor_or_micro =~ /^\+$/ end def valid_qualifier(qualifier) - qualifier.nil? || qualifier.empty? || qualifier =~ /^[-\.a-zA-Z\d]*$/ || qualifier =~ /^\+$/ + qualifier.nil? || qualifier.empty? || qualifier =~ /^[-.a-zA-Z\d]*\+?$/ end end diff --git a/rakelib/dependency_cache_task.rb b/rakelib/dependency_cache_task.rb index 7950a84a0e..421f0df323 100644 --- a/rakelib/dependency_cache_task.rb +++ b/rakelib/dependency_cache_task.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2014 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,13 +15,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) +$LOAD_PATH.unshift File.expand_path('../lib', __dir__) require 'java_buildpack/logging/logger_factory' require 'java_buildpack/repository/version_resolver' require 'java_buildpack/util/configuration_utils' require 'java_buildpack/util/cache/download_cache' require 'java_buildpack/util/snake_case' +require 'monitor' require 'rake/tasklib' require 'rakelib/package' require 'pathname' @@ -34,12 +36,15 @@ class DependencyCacheTask < Rake::TaskLib def initialize return unless BUILDPACK_VERSION.offline + @pkgcfg = nil + JavaBuildpack::Logging::LoggerFactory.instance.setup "#{BUILD_DIR}/" @default_repository_root = default_repository_root @cache = cache + @monitor = Monitor.new - configurations = component_ids.map { |component_id| configurations(configuration(component_id)) }.flatten + configurations = component_ids.map { |component_id| component_configuration(component_id) }.flatten uris(configurations).each { |uri| multitask PACKAGE_NAME => [cache_task(uri)] } end @@ -56,18 +61,16 @@ def initialize def augment(raw, key, pattern, candidates, &block) if raw.respond_to? :at raw.map(&block) - else - if raw[:uri] =~ pattern - candidates.map do |candidate| - dup = raw.clone - dup[key] = candidate - dup[:uri] = raw[:uri].gsub pattern, candidate - - dup - end - else - raw + elsif raw[:uri] =~ pattern + candidates.map do |candidate| + dup = raw.clone + dup[key] = candidate + dup[:uri] = raw[:uri].gsub pattern, candidate + + dup end + else + raw end end @@ -100,57 +103,39 @@ def cache def cache_task(uri) task uri do |t| - rake_output_message "Caching #{t.name}" + @monitor.synchronize { rake_output_message "Caching #{t.name}" } + # rubocop:disable Lint/EmptyBlock cache.get(t.name) {} + # rubocop:enable Lint/EmptyBlock end uri end def component_ids - configuration('components').values.flatten.map { |component| component.split('::').last.snake_case } - end - - def configuration(id) - JavaBuildpack::Util::ConfigurationUtils.load id, false - end - - def configurations(configuration) - configurations = [] - - if repository_configuration?(configuration) - configurations << configuration - else - configuration.values.each { |v| configurations << configurations(v) if v.is_a? Hash } + conf = configuration('components').values.flatten.map { |component| component.split('::').last.snake_case } + offline_cache = ENV.fetch('ADD_TO_CACHE', nil) + unless offline_cache.nil? + offline_cache = offline_cache.split(',') + (conf << offline_cache).flatten!.uniq! end + conf << 'ruby' + end - configurations + def component_configuration(component_id) + configurations(component_id, configuration(component_id)) end def default_repository_root configuration('repository')['default_repository_root'].chomp('/') end - def index_configuration(configuration) - [configuration['repository_root']] - .map { |r| { uri: r } } - .map { |r| augment_repository_root r } - .map { |r| augment_platform r } - .map { |r| augment_architecture r } - .map { |r| augment_path r }.flatten - end - - def repository_configuration?(configuration) - configuration['version'] && configuration['repository_root'] - end - def uris(configurations) uris = [] configurations.each do |configuration| index_configuration(configuration).each do |index_configuration| multitask PACKAGE_NAME => [cache_task(index_configuration[:uri])] - get_from_cache(configuration, index_configuration, uris) end end @@ -160,23 +145,70 @@ def uris(configurations) def get_from_cache(configuration, index_configuration, uris) @cache.get(index_configuration[:uri]) do |f| - index = YAML.load f - found_version = version(configuration, index) + index = YAML.safe_load f + found_version = Utils::VersionUtils.version(configuration, index) + pin_version(configuration, found_version.to_s) if ENV['PINNED'].to_b if found_version.nil? - rake_output_message "Unable to resolve version '#{configuration['version']}' for platform " \ - "'#{index_configuration[:platform]}'" + raise "Unable to resolve version '#{configuration['version']}' for platform " \ + "'#{index_configuration[:platform]}'" end uris << index[found_version.to_s] unless found_version.nil? end end - def version(configuration, index) - JavaBuildpack::Repository::VersionResolver.resolve( - JavaBuildpack::Util::TokenizedVersion.new(configuration['version']), index.keys) + def pin_version(old_configuration, version) + component_id = old_configuration['component_id'] + sub_component_id = old_configuration['sub_component_id'] + if Utils::VersionUtils.openjdk_jre? old_configuration + rake_output_message "Pinning JRE #{sub_component_id || component_id} version to #{version}" + pin_jre(component_id, sub_component_id, version) + elsif Utils::VersionUtils.tomcat? old_configuration + rake_output_message "Pinning Tomcat #{sub_component_id || component_id} version to #{version}" + pin_tomcat(component_id, sub_component_id, version) + else + rake_output_message "Pinning #{sub_component_id || component_id} version to #{version}" + pin_component(component_id, sub_component_id, version) + end + end + + def pin_component(component_id, sub_component_id, version) + configuration_to_update = JavaBuildpack::Util::ConfigurationUtils.load(component_id, false, true) + update_configuration(configuration_to_update, version, sub_component_id) + JavaBuildpack::Util::ConfigurationUtils.write(component_id, configuration_to_update) end - end + def pin_jre(component_id, sub_component_id, version) + # update configuration file, pin version & version lines + configuration_to_update = JavaBuildpack::Util::ConfigurationUtils.load(component_id, false, true) + update_configuration(configuration_to_update, version, sub_component_id) + configuration_to_update['jre']['version_lines'].each_with_index do |version_pattern, index| + configuration_to_update['jre']['version_lines'][index] = version \ + if Utils::VersionUtils.version_matches?(version_pattern, [version]) + end + JavaBuildpack::Util::ConfigurationUtils.write(component_id, configuration_to_update) + end + + def pin_tomcat(component_id, sub_component_id, version) + # update configuration file, pin version & version lines + configuration_to_update = JavaBuildpack::Util::ConfigurationUtils.load(component_id, false, true) + update_configuration(configuration_to_update, version, sub_component_id) + configuration_to_update['tomcat']['version_lines'].each_with_index do |version_pattern, index| + configuration_to_update['tomcat']['version_lines'][index] = version \ + if Utils::VersionUtils.version_matches?(version_pattern, [version]) + end + JavaBuildpack::Util::ConfigurationUtils.write(component_id, configuration_to_update) + end + def update_configuration(config, version, sub_component) + if sub_component.nil? + config['version'] = version + elsif config.key?(sub_component) + config[sub_component]['version'] = version + else + config.each_value { |v| update_configuration(v, version, sub_component) if v.is_a? Hash } + end + end + end end diff --git a/rakelib/package.rb b/rakelib/package.rb index 4e5bf15744..3635f27d55 100644 --- a/rakelib/package.rb +++ b/rakelib/package.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2014 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,6 +19,49 @@ module Package + def packaging + @pkgcfg = configuration('packaging') if @pkgcfg.nil? + @pkgcfg + end + + def configuration(id) + JavaBuildpack::Util::ConfigurationUtils.load(id, false, false) + end + + def configurations(component_id, configuration, sub_component_id = nil) + configurations = [] + + if repository_configuration?(configuration) + configuration['component_id'] = component_id + configuration['sub_component_id'] = sub_component_id if sub_component_id + + Utils::VersionUtils.java_version_lines(configuration, configurations) \ + if Utils::VersionUtils.openjdk_jre? configuration + + Utils::VersionUtils.tomcat_version_lines(configuration, configurations) \ + if Utils::VersionUtils.tomcat? configuration + + configurations << configuration + else + configuration.each { |k, v| configurations << configurations(component_id, v, k) if v.is_a? Hash } + end + + configurations + end + + def index_configuration(configuration) + [configuration['repository_root']] + .map { |r| { uri: r } } + .map { |r| augment_repository_root r } + .map { |r| augment_platform r } + .map { |r| augment_architecture r } + .map { |r| augment_path r }.flatten + end + + def repository_configuration?(configuration) + configuration['version'] && configuration['repository_root'] + end + def self.offline '-offline' if BUILDPACK_VERSION.offline end @@ -26,13 +70,13 @@ def self.version BUILDPACK_VERSION.version || 'unknown' end - ARCHITECTURES = %w(x86_64).freeze + ARCHITECTURES = %w[x86_64].freeze - BUILD_DIR = 'build'.freeze + BUILD_DIR = 'build' BUILDPACK_VERSION = JavaBuildpack::BuildpackVersion.new(false).freeze - PLATFORMS = %w(centos6 lucid mountainlion precise trusty).freeze + PLATFORMS = %w[jammy noble].freeze STAGING_DIR = "#{BUILD_DIR}/staging".freeze diff --git a/rakelib/package_task.rb b/rakelib/package_task.rb index e043e0ab19..82c31a059d 100644 --- a/rakelib/package_task.rb +++ b/rakelib/package_task.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2014 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/rakelib/stage_buildpack_task.rb b/rakelib/stage_buildpack_task.rb index 9b70b59bfd..b3259f6a62 100644 --- a/rakelib/stage_buildpack_task.rb +++ b/rakelib/stage_buildpack_task.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2014 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -37,6 +38,12 @@ def copy_task(source, target) directory parent file(target => [source, parent]) do |t| cp t.source, t.name + + if t.source.include? 'bin' + chmod 0o755, t.name + else + chmod 0o644, t.name + end end target @@ -45,7 +52,7 @@ def copy_task(source, target) def disable_remote_downloads_task file "#{STAGING_DIR}/config/cache.yml" do |t| content = File.open(t.source, 'r') { |f| f.read.gsub(/enabled/, 'disabled') } - File.open(t.name, 'w') { |f| f.write content } + File.open(t.name, 'w') { |f| File.write(f, content) } end end @@ -60,7 +67,7 @@ def version_task directory parent file target => [parent] do |t| File.open(t.name, 'w') do |f| - f.write(BUILDPACK_VERSION.to_hash.to_yaml) + File.write(f, BUILDPACK_VERSION.to_hash.to_yaml) end end diff --git a/rakelib/utils.rb b/rakelib/utils.rb new file mode 100644 index 0000000000..0202066f25 --- /dev/null +++ b/rakelib/utils.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2022 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module Utils + class VersionUtils + class << self + def version_wildcard?(version_pattern) + version_pattern.include? '+' + end + + def version(configuration, index) + matched_version(configuration['version'], index.keys) + end + + def matched_version(version_pattern, versions) + JavaBuildpack::Repository::VersionResolver + .resolve(JavaBuildpack::Util::TokenizedVersion.new(version_pattern), versions) + end + + def version_matches?(version_pattern, versions) + !matched_version(version_pattern, versions).nil? + end + + def openjdk_jre?(configuration) + configuration['component_id'].end_with?('_jre') && configuration['sub_component_id'].start_with?('jre') + end + + def tomcat?(configuration) + configuration['component_id'].end_with?('tomcat') && configuration['sub_component_id'].start_with?('tomcat') + end + + def java_version_lines(configuration, configurations) + configuration['version_lines'].each do |v| + next if version_line_matches?(configuration, v) + + c1 = configuration.clone + c1['sub_component_id'] = "jre-#{v.split('.')[0]}" + c1['version'] = v + configurations << c1 + end + end + + def tomcat_version_lines(configuration, configurations) + configuration['version_lines'].each do |v| + next if version_line_matches?(configuration, v) + + c1 = configuration.clone + c1['sub_component_id'] = "tomcat-#{v.split('.')[0]}" + c1['version'] = v + configurations << c1 + end + end + + def version_line_matches?(configuration, v) + return true if v == configuration['version'] + return false if version_wildcard? v + + version_matches?(configuration['version'], [v]) + end + end + end +end diff --git a/rakelib/versions_task.rb b/rakelib/versions_task.rb new file mode 100644 index 0000000000..8869c181b1 --- /dev/null +++ b/rakelib/versions_task.rb @@ -0,0 +1,216 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +$LOAD_PATH.unshift File.expand_path('../lib', __dir__) + +require 'java_buildpack/logging/logger_factory' +require 'java_buildpack/repository/version_resolver' +require 'java_buildpack/util/configuration_utils' +require 'java_buildpack/util/cache/download_cache' +require 'json' +require 'rake/tasklib' +require 'rakelib/package' +require 'rakelib/utils' +require 'terminal-table' +require 'yaml' + +module Package + + # rubocop:disable Metrics/ClassLength + class VersionsTask < Rake::TaskLib + include Package + + def initialize + JavaBuildpack::Logging::LoggerFactory.instance.setup "#{BUILD_DIR}/" + + @pkgcfg = nil + + version_task + + namespace 'versions' do + version_json_task + version_markdown_task + version_yaml_task + end + end + + private + + ARCHITECTURE_PATTERN = /\{architecture\}/.freeze + + DEFAULT_REPOSITORY_ROOT_PATTERN = /\{default.repository.root\}/.freeze + + PLATFORM_PATTERN = /\{platform\}/.freeze + + private_constant :ARCHITECTURE_PATTERN, :DEFAULT_REPOSITORY_ROOT_PATTERN, :PLATFORM_PATTERN + + def augment(raw, key, pattern, candidates, &block) + if raw.respond_to? :at + raw.map(&block) + elsif raw[:uri] =~ pattern + candidates.map do |candidate| + dup = raw.clone + dup[key] = candidate + dup[:uri] = raw[:uri].gsub pattern, candidate + + dup + end + else + raw + end + end + + def augment_architecture(raw) + augment(raw, :architecture, ARCHITECTURE_PATTERN, ARCHITECTURES) { |r| augment_architecture r } + end + + def augment_path(raw) + if raw.respond_to? :at + raw.map { |r| augment_path r } + else + raw[:uri] = "#{raw[:uri].chomp('/')}/index.yml" + raw + end + end + + def augment_platform(raw) + augment(raw, :platform, PLATFORM_PATTERN, PLATFORMS) { |r| augment_platform r } + end + + def augment_repository_root(raw) + augment(raw, :repository_root, DEFAULT_REPOSITORY_ROOT_PATTERN, [default_repository_root]) do |r| + augment_repository_root r + end + end + + def component_configuration(component_id) + configurations(component_id, configuration(component_id)) + end + + def component_ids + configuration('components').values.flatten.map { |component| component.split('::').last.snake_case } + end + + def default_repository_root + configuration('repository')['default_repository_root'].chomp('/') + end + + def get_from_cache(cache, configuration, index_configuration) + cache.get(index_configuration[:uri]) do |f| + index = YAML.safe_load f + found_version = Utils::VersionUtils.version(configuration, index) + + if found_version.nil? + raise "Unable to resolve version '#{configuration['version']}' for platform " \ + "'#{index_configuration[:platform]}'" + end + + return found_version.to_s, index[found_version.to_s] + end + end + + def dependency_versions + dependency_versions = [] + + cache = JavaBuildpack::Util::Cache::DownloadCache.new + configurations = component_ids.map { |component_id| component_configuration(component_id) }.flatten + + configurations.each do |configuration| + map_config_to_dependency(cache, configuration, dependency_versions) + end + + dependency_versions + .uniq { |dependency| dependency['id'] } + .sort_by { |dependency| dependency['id'] } + end + + def map_config_to_dependency(cache, configuration, dependency_versions) + id = configuration['sub_component_id'] || configuration['component_id'] + + index_configuration(configuration).each do |index_configuration| + version, uri = get_from_cache(cache, configuration, index_configuration) + + name = packaging[id]['name'] + raise "Unable to resolve name for '#{id}'" unless name + + dependency_versions << { + 'id' => id, + 'name' => name, + 'uri' => uri, + 'version' => version, + 'cve_link' => packaging[id]['cve_notes'] || '', + 'release_notes_link' => packaging[id]['release_notes'] || '' + } + end + end + + def version_task + desc 'Display the versions of buildpack dependencies in human readable form' + task versions: [] do + v = versions + + rows = v['dependencies'] + .sort_by { |dependency| dependency['name'].downcase } + .map do |dependency| + [dependency['name'], dependency['version'], dependency['cve_link'], dependency['release_notes_link']] + end + + puts Terminal::Table.new title: "Java Buildpack #{v['buildpack']}", rows: rows + end + end + + def version_json_task + desc 'Display the versions of buildpack dependencies in JSON form' + task json: [] do + puts JSON.pretty_generate(versions['dependencies'] + .sort_by { |dependency| dependency['name'].downcase }) + end + end + + def version_markdown_task + desc 'Display the versions of buildpack dependencies in Markdown form' + task markdown: [] do + puts '| Dependency | Version | CVEs | Release Notes |' + puts '| ---------- | ------- | ---- | ------------- |' + + versions['dependencies'] + .sort_by { |dependency| dependency['name'].downcase } + .each do |dependency| + puts "| #{dependency['name']} | `#{dependency['version']}` |" \ + "#{dependency['cve_link']} | #{dependency['release_notes_link']} |" + end + end + end + + def version_yaml_task + desc 'Display the versions of buildpack dependencies in YAML form' + task yaml: [] do + puts YAML.dump(versions) + end + end + + def versions + { + 'buildpack' => Package.version, + 'dependencies' => dependency_versions + } + end + + end + # rubocop:enable Metrics/ClassLength + +end diff --git a/resources/app_dynamics_agent/defaults/conf/app-agent-config.xml b/resources/app_dynamics_agent/defaults/conf/app-agent-config.xml new file mode 100644 index 0000000000..b09a260565 --- /dev/null +++ b/resources/app_dynamics_agent/defaults/conf/app-agent-config.xml @@ -0,0 +1,729 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + com.singularity.ee.agent.appagent.kernel.DynamicServiceManager + + + + + + + + + + BCIEngine,TransactionMonitoringService,SnapshotService + + + + + + + + BCIEngine + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BCIEngine + + + + + + + + + + + + + + + + + + + + + + + + + + + + BCIEngine,SnapshotService + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JMXService + + + BCIEngine + + + + + + + + + + + + + + + + + + + + + + + + + + BCIEngine + + + + + + + + + + + + + + + + + + + + + + + BCIEngine + + + + + TransactionMonitoringService + + + + + + + + + + + + + + diff --git a/resources/azure_application_insights_agent/AI-Agent.xml b/resources/azure_application_insights_agent/AI-Agent.xml new file mode 100644 index 0000000000..7e1ff431c0 --- /dev/null +++ b/resources/azure_application_insights_agent/AI-Agent.xml @@ -0,0 +1,26 @@ + + + + + + + + 1000 + + + diff --git a/resources/luna_security_provider/Chrystoki.conf b/resources/luna_security_provider/Chrystoki.conf new file mode 100644 index 0000000000..19439d9956 --- /dev/null +++ b/resources/luna_security_provider/Chrystoki.conf @@ -0,0 +1,13 @@ +Luna = { + CloningCommandTimeOut = 300000; + CommandTimeOutPedSet = 720000; + DefaultTimeOut = 500000; + KeypairGenTimeOut = 2700000; + PEDTimeout1 = 100000; + PEDTimeout2 = 200000; + PEDTimeout3 = 10000; +} + +Misc = { + PE1746Enabled = 0; +} diff --git a/resources/new_relic_agent/newrelic.yml b/resources/new_relic_agent/newrelic.yml index 5e9507b4b9..43ff1893ec 100644 --- a/resources/new_relic_agent/newrelic.yml +++ b/resources/new_relic_agent/newrelic.yml @@ -1,24 +1,40 @@ -# # This file configures the New Relic Agent. New Relic monitors -# Java applications with deep visibility and low overhead. For more -# information, visit www.newrelic.com. +# Java applications with deep visibility and low overhead. For more details and additional +# configuration options visit https://docs.newrelic.com/docs/java/java-agent-configuration. # +# <%= generated_for_user %> # # This section is for settings common to all environments. # Do not add anything above this next line. common: &default_settings - # - # ============================== LICENSE KEY =============================== + # ============================== LICENSE KEY =============================== # You must specify the license key associated with your New Relic - # account. This key binds your Agent's data to your account in the - # New Relic service. - license_key: '' + # account. For example, if your license key is 12345 use this: + # license_key: '12345' + # The key binds your Agent's data to your account in the New Relic service. + license_key: '<%= license_key %>' # Agent Enabled - # Use this setting to force the agent to run or not run. + # Use this setting to disable the agent instead of removing it from the startup command. # Default is true. - # agent_enabled: true + agent_enabled: true + + # Set the name of your application as you'd like it show up in New Relic. + # If enable_auto_app_naming is false, the agent reports all data to this application. + # Otherwise, the agent reports only background tasks (transactions for non-web applications) + # to this application. To report data to more than one application + # (useful for rollup reporting), separate the application names with ";". + # For example, to report data to "My Application" and "My Application 2" use this: + # app_name: My Application;My Application 2 + # This setting is required. Up to 3 different application names can be specified. + # The first application name must be unique. + app_name: My Application + + # To enable high security, set this property to true. When in high + # security mode, the agent will use SSL and obfuscated SQL. Additionally, + # request parameters and message parameters will not be sent to New Relic. + high_security: false # Set to true to enable support for auto app naming. # The name of each web app is detected automatically @@ -33,78 +49,72 @@ common: &default_settings # Default is true. enable_auto_transaction_naming: true - # Set the name of your application as you'd like it show up in New Relic. - # if enable_auto_app_naming is false, the agent reports all data to this application. - # Otherwise, the agent reports only background tasks (transactions for non-web applications) to this application. - # To report data to more than one application, separate the application names with ";". - # For example, to report data to"My Application" and "My Application 2" use this: - # app_name: My Application;My Application 2 - # This setting is required. - app_name: My Application - # The agent uses its own log file to keep its logging # separate from that of your application. Specify the log level here. # This setting is dynamic, so changes do not require restarting your application. - # The levels in increasing order of verboseness are: off, severe, warning, info, fine, finer, finest + # The levels in increasing order of verboseness are: + # off, severe, warning, info, fine, finer, finest # Default is info. log_level: info - # Log all data to and from New Relic in plain text. + # Log all data sent to and from New Relic in plain text. # This setting is dynamic, so changes do not require restarting your application. # Default is false. - #audit_mode: true + audit_mode: false - # The number of log files to use. + # The number of backup log files to save. # Default is 1. - #log_file_count: 1 + log_file_count: 1 - # The maximum number of bytes to write to any one log file. + # The maximum number of kbytes to write to any one log file. # The log_file_count must be set greater than 1. # Default is 0 (no limit). - #log_limit_in_kbytes: 0 + log_limit_in_kbytes: 0 # Override other log rolling configuration and roll the logs daily. # Default is false. - #log_daily: false + log_daily: false # The name of the log file. # Default is newrelic_agent.log. - #log_file_name: newrelic_agent.log + log_file_name: newrelic_agent.log # The log file directory. # Default is the logs directory in the newrelic.jar parent directory. #log_file_path: - # The agent communicates with New Relic via https by - # default. If you want to communicate with newrelic via http, - # then turn off SSL by setting this value to false. - # This work is done asynchronously to the threads that process your - # application code, so response times will not be directly affected - # by this change. - # Default is true. - ssl: true - - # Proxy settings for connecting to the New Relic server. - # + # Proxy settings for connecting to the New Relic server: # If a proxy is used, the host setting is required. Other settings # are optional. Default port is 8080. The username and password # settings will be used to authenticate to Basic Auth challenges - # from a proxy server. - # - # proxy_host: hostname - # proxy_port: 8080 - # proxy_user: username - # proxy_password: password - - # Tells transaction tracer and error collector (when enabled) - # whether or not to capture HTTP params. When true, frameworks can - # exclude HTTP parameters from being captured. - # Default is false. - capture_params: false + # from a proxy server. Proxy scheme will allow the agent to + # connect through proxies using the HTTPS scheme. + #proxy_host: hostname + #proxy_port: 8080 + #proxy_user: username + #proxy_password: password + #proxy_scheme: https + + # Limits the number of lines to capture for each stack trace. + # Default is 30 + max_stack_trace_lines: 30 + + # Provides the ability to configure the attributes sent to New Relic. These + # attributes can be found in transaction traces, traced errors, Insight's + # transaction events, and Insight's page views. + attributes: + + # When true, attributes will be sent to New Relic. The default is true. + enabled: true + + #A comma separated list of attribute keys whose values should + # be sent to New Relic. + #include: + + # A comma separated list of attribute keys whose values should + # not be sent to New Relic. + #exclude: - # Tells transaction tracer and error collector to not to collect - # specific http request parameters. - # ignored_params: credit_card, ssn, password # Transaction tracer captures deep information about slow # transactions and sends this to the New Relic service once a @@ -112,8 +122,8 @@ common: &default_settings # the transactions including any SQL statements issued. transaction_tracer: - # Transaction tracer is enabled by default. Set this to false to - # turn it off. This feature is only available at the higher product levels. + # Transaction tracer is enabled by default. Set this to false to turn it off. + # This feature is not available to Lite accounts and is automatically disabled. # Default is true. enabled: true @@ -133,10 +143,6 @@ common: &default_settings # Default is obfuscated. record_sql: obfuscated - # Obfuscate only occurrences of specific SQL fields names. - # This setting only applies if "record_sql" is set to "raw". - #obfuscated_sql_fields: credit_card, ssn, password - # Set this to true to log SQL statements instead of recording them. # SQL is logged using the record_sql mode. # Default is false. @@ -165,64 +171,113 @@ common: &default_settings # Default is 20. top_n: 20 - # Error collector captures information about uncaught exceptions and - # sends them to New Relic for viewing + # sends them to New Relic for viewing. error_collector: - # Error collector is enabled by default. Set this to false to turn - # it off. This feature is only available at the higher product levels. + # This property enables the collection of errors. If the property is not + # set or the property is set to false, then errors will not be collected. # Default is true. enabled: true - # To stop specific exceptions from reporting to New Relic, set this property - # to a comma separated list of full class names. - # - # ignore_errors: + # Use this property to exclude specific exceptions from being reported as errors + # by providing a comma separated list of full class names. + # The default is to exclude akka.actor.ActorKilledException. If you want to override + # this, you must provide any new value as an empty list is ignored. + ignore_errors: akka.actor.ActorKilledException + + # Use this property to exclude specific http status codes from being reported as errors + # by providing a comma separated list of status codes. + # The default is to exclude 404s. If you want to override + # this, you must provide any new value as an empty list is ignored. + ignore_status_codes: 404 + + # Transaction Events are used for Histograms and Percentiles. Unaggregated data is collected + # for each web transaction and sent to the server on harvest. + transaction_events: - # To stop specific http status codes from being reporting to New Relic as errors, - # set this property to a comma separated list of status codes to ignore. - # When this property is commented out it defaults to ignoring 404s. - # - # ignore_status_codes: 404 + # Set to false to disable transaction events. + # Default is true. + enabled: true + + # Events are collected up to the configured amount. Afterwards, events are sampled to + # maintain an even distribution across the harvest cycle. + # Default is 2000. Setting to 0 will disable. + max_samples_stored: 2000 + + # Distributed tracing lets you see the path that a request takes through your distributed system. + # Enabling distributed tracing changes the behavior of some New Relic features, so carefully consult the transition + # guide before you enable this feature: https://docs.newrelic.com/docs/transition-guide-distributed-tracing + # Default is false. + distributed_tracing: + enabled: false # Cross Application Tracing adds request and response headers to - # external calls using the Apache HttpClient libraries to provided better + # external calls using supported HTTP libraries to provide better # performance data when calling applications monitored by other New Relic Agents. - # cross_application_tracer: - # Set to true to enable cross application tracing. + + # Set to false to disable cross application tracing. # Default is true. enabled: true # Thread profiler measures wall clock time, CPU time, and method call counts # in your application's threads as they run. + # This feature is not available to Lite accounts and is automatically disabled. thread_profiler: # Set to false to disable the thread profiler. # Default is true. enabled: true - #============================== Browser Monitoring =============================== # New Relic Real User Monitoring gives you insight into the performance real users are # experiencing with your website. This is accomplished by measuring the time it takes for # your users' browsers to download and render your web pages by injecting a small amount # of JavaScript code into the header and footer of each page. browser_monitoring: + # By default the agent automatically inserts API calls in compiled JSPs to - # inject the monitoring JavaScript into web pages. + # inject the monitoring JavaScript into web pages. Not all rendering engines are supported. + # See https://docs.newrelic.com/docs/java/real-user-monitoring-in-java#manual_instrumentation + # for instructions to add these manually to your pages. # Set this attribute to false to turn off this behavior. auto_instrument: true - # Set this attribute to false to prevent injection of the monitoring JavaScript. - # Default is true. - enabled: true + + class_transformer: + # This instrumentation reports the name of the user principal returned from + # HttpServletRequest.getUserPrincipal() when servlets and filters are invoked. + com.newrelic.instrumentation.servlet-user: + enabled: false + + com.newrelic.instrumentation.spring-aop-2: + enabled: false + + # Classes loaded by classloaders in this list will not be instrumented. + # This is a useful optimization for runtimes which use classloaders to + # load dynamic classes which the agent would not instrument. + classloader_excludes: + groovy.lang.GroovyClassLoader$InnerLoader, + org.codehaus.groovy.runtime.callsite.CallSiteClassLoader, + com.collaxa.cube.engine.deployment.BPELClassLoader, + org.springframework.data.convert.ClassGeneratingEntityInstantiator$ObjectInstantiatorClassGenerator, + org.mvel2.optimizers.impl.asm.ASMAccessorOptimizer$ContextClassLoader, + gw.internal.gosu.compiler.SingleServingGosuClassLoader, + + # User-configurable custom labels for this agent. Labels are name-value pairs. + # There is a maximum of 64 labels per agent. Names and values are limited to 255 characters. + # Names and values may not contain colons (:) or semicolons (;). + labels: + + # An example label + #label_name: label_value + # Application Environments # ------------------------------------------ # Environment specific settings are in this section. # You can use the environment to override the default settings. # For example, to change the app_name setting. -# Use -Dnewrelic.environment= on the Java command line +# Use -Dnewrelic.environment= on the Java startup command line # to set the environment. # The default environment is production. diff --git a/resources/protect_app_security_provider/IngrianNAE.properties b/resources/protect_app_security_provider/IngrianNAE.properties new file mode 100644 index 0000000000..be9e361c04 --- /dev/null +++ b/resources/protect_app_security_provider/IngrianNAE.properties @@ -0,0 +1,43 @@ +Version=2.4 +NAE_IP.1= +NAE_Port=9000 +KMIP_Port=5696 +Protocol=ssl +Verify_SSL_Certificate=no +SSL_Handshake_Timeout= +Use_Persistent_Connections=yes +Size_of_Connection_Pool=300 +Load_Balancing_Algorithm=round-robin +Connection_Idle_Timeout=600000 +Unreachable_Server_Retry_Period=60000 +Maximum_Server_Retry_Period=0 +Connection_Timeout=30000 +Connection_Read_Timeout=7000 +Connection_Retry_Interval=600000 +Client_Cert_Alias= +Client_Cert_Passphrase= +Key_Store_Location= +Key_Store_Password= +Cluster_Synchronization_Delay=100 +Symmetric_Key_Cache_Enabled=no +Asymmetric_Key_Cache_Enabled=no +Symmetric_Key_Cache_Expiry=43200 +Local_Cipher_Cache_Expiry=-1 +Local_Crypto_Provider= +Persistent_Cache_Enabled=no +Persistent_Cache_Expiry_Keys=43200 +Persistent_Cache_Directory= +Persistent_Cache_Max_Size=100 +FIPS_Mode=off +Credentials_Encrypted=no +Passphrase_Encrypted=no +Log_Level=NONE +Log_File= +Log_Rotation=Daily +Log_GMT=no +Log_Size_Limit=100k +SysLog_IP= +SysLog_Port= +Log_Config_Advanced= +Key_non_exportable_policy=no +Log_MaxBackupIndex=-1 diff --git a/resources/tomcat/conf/context.xml b/resources/tomcat/conf/context.xml index 7e3cf282f6..3fd6a275d8 100644 --- a/resources/tomcat/conf/context.xml +++ b/resources/tomcat/conf/context.xml @@ -1,6 +1,7 @@ + ~ Cloud Foundry Java Buildpack + ~ Copyright 2013-2020 the original author or authors. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> - + + + - - + - - + enabled='${access.logging.enabled}'/> + + + diff --git a/spec/application_helper.rb b/spec/application_helper.rb index 8ad361a5ed..e34c35ab8c 100644 --- a/spec/application_helper.rb +++ b/spec/application_helper.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,17 +18,17 @@ require 'spec_helper' require 'java_buildpack/component/application' require 'java_buildpack/component/services' -require 'yaml' +require 'json' -shared_context 'application_helper' do +shared_context 'with application help' do let(:app_dir) { Pathname.new Dir.mktmpdir } previous_environment = ENV.to_hash let(:environment) do - { 'test-key' => 'test-value', 'VCAP_APPLICATION' => vcap_application.to_yaml, - 'VCAP_SERVICES' => vcap_services.to_yaml } + { 'test-key' => 'test-value', 'VCAP_APPLICATION' => vcap_application.to_json, + 'VCAP_SERVICES' => vcap_services.to_json } end before do @@ -46,11 +47,17 @@ let(:services) { application.services } - let(:vcap_application) { { 'application_name' => 'test-application-name' } } + let(:vcap_application) do + { 'application_id' => 'test-application-id', + 'application_name' => 'test-application-name', + 'application_version' => 'test-application-version', + 'space_id' => 'test-space-id', + 'space_name' => 'test-space-name' } + end let(:vcap_services) do - { 'test-service-n/a' => [{ 'name' => 'test-service-name', 'label' => 'test-service-n/a', - 'tags' => ['test-service-tag'], 'plan' => 'test-plan', + { 'test-service-n/a' => [{ 'name' => 'test-service-name', 'label' => 'test-service-n/a', + 'tags' => ['test-service-tag'], 'plan' => 'test-plan', 'credentials' => { 'uri' => 'test-uri' } }] } end @@ -65,8 +72,12 @@ application end - after do - FileUtils.rm_rf app_dir + after do |example| + if example.metadata[:no_cleanup] + puts "Application Directory: #{app_dir}" + else + FileUtils.rm_rf app_dir + end end end diff --git a/spec/bin/compile_spec.rb b/spec/bin/compile_spec.rb index 74e099ab8c..2557c34032 100644 --- a/spec/bin/compile_spec.rb +++ b/spec/bin/compile_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,12 +17,15 @@ require 'spec_helper' require 'integration_helper' +require 'memory_limit_helper' describe 'compile script', :integration do # rubocop:disable RSpec/DescribeClass - include_context 'integration_helper' + include_context 'with integration help' + include_context 'with memory limit help' it 'returns zero if success', - app_fixture: 'integration_valid' do + app_fixture: 'integration_valid', + memory_limit: '1024m' do run("bin/compile #{app_dir} #{app_dir + '.cache'}") { |status| expect(status).to be_success } end diff --git a/spec/bin/detect_spec.rb b/spec/bin/detect_spec.rb index b5144ac74b..a3a434a6f3 100644 --- a/spec/bin/detect_spec.rb +++ b/spec/bin/detect_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,14 +19,14 @@ require 'integration_helper' describe 'detect script', :integration do # rubocop:disable RSpec/DescribeClass - include_context 'integration_helper' + include_context 'with integration help' it 'returns zero if success', app_fixture: 'integration_valid' do run("bin/detect #{app_dir}") do |status| expect(status).to be_success - expect(stdout.string.rstrip.length).to be < 255 + expect(stdout.string.rstrip.length).to be <= 255 end end @@ -37,7 +38,7 @@ end it 'truncates long detect strings', - app_fixture: 'integration_valid', + app_fixture: 'integration_valid', buildpack_fixture: 'integration_long_detect_tag' do run("bin/detect #{app_dir}") do |status| diff --git a/spec/bin/release_spec.rb b/spec/bin/release_spec.rb index 0e2dd6aa3c..d45f6bd3bc 100644 --- a/spec/bin/release_spec.rb +++ b/spec/bin/release_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,7 +19,7 @@ require 'integration_helper' describe 'release script', :integration do # rubocop:disable RSpec/DescribeClass - include_context 'integration_helper' + include_context 'with integration help' it 'returns zero if success', app_fixture: 'integration_valid' do diff --git a/spec/component_helper.rb b/spec/component_helper.rb index 3c4379e38b..8a8076c8cd 100644 --- a/spec/component_helper.rb +++ b/spec/component_helper.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -26,23 +27,23 @@ require 'java_buildpack/util/tokenized_version' require 'pathname' -shared_context 'component_helper' do - include_context 'application_helper' - include_context 'console_helper' - include_context 'droplet_helper' - include_context 'internet_availability_helper' - include_context 'logging_helper' +shared_context 'with component help' do + include_context 'with application help' + include_context 'with console help' + include_context 'with droplet help' + include_context 'with internet availability help' + include_context 'with logging help' - let(:application_cache) { double('ApplicationCache') } + let(:application_cache) { instance_double('ApplicationCache') } let(:component) { described_class.new context } let(:configuration) { {} } let(:context) do - { application: application, + { application: application, component_name: described_class.to_s.split('::').last.space_case, - configuration: configuration, - droplet: droplet } + configuration: configuration, + droplet: droplet } end let(:uri) { 'test-uri' } @@ -55,7 +56,7 @@ cache_fixture = example.metadata[:cache_fixture] if cache_fixture allow(application_cache).to receive(:get).with(uri) - .and_yield(Pathname.new("spec/fixtures/#{cache_fixture}").open, false) + .and_yield(Pathname.new("spec/fixtures/#{cache_fixture}").open, false) end end @@ -64,7 +65,7 @@ tokenized_version = JavaBuildpack::Util::TokenizedVersion.new(version) allow(JavaBuildpack::Repository::ConfiguredItem).to receive(:find_item) do |&block| - block.call(tokenized_version) if block + block&.call(tokenized_version) end.and_return([tokenized_version, uri]) end diff --git a/spec/console_helper.rb b/spec/console_helper.rb index 22965baeb6..26efb84df5 100644 --- a/spec/console_helper.rb +++ b/spec/console_helper.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,8 +17,10 @@ require 'spec_helper' require 'tee' +require 'java_buildpack/util/colorize' -shared_context 'console_helper' do +# rubocop:disable Style/GlobalStdStream +shared_context 'with console help' do STDOUT.sync STDERR.sync @@ -33,6 +36,8 @@ $stdout.add STDOUT $stderr.add STDERR end + + String.color_enabled = false end after do @@ -56,3 +61,4 @@ def copy_stream(source, destination) end end +# rubocop:enable Style/GlobalStdStream diff --git a/spec/droplet_helper.rb b/spec/droplet_helper.rb index c63cf94fb6..1e63d9a406 100644 --- a/spec/droplet_helper.rb +++ b/spec/droplet_helper.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,14 +20,22 @@ require 'logging_helper' require 'java_buildpack/component/additional_libraries' require 'java_buildpack/component/droplet' -require 'java_buildpack/component/java_opts' +require 'java_buildpack/component/environment_variables' +require 'java_buildpack/component/extension_directories' require 'java_buildpack/component/immutable_java_home' +require 'java_buildpack/component/java_opts' +require 'java_buildpack/component/mutable_java_home' +require 'java_buildpack/component/networking' +require 'java_buildpack/component/root_libraries' +require 'java_buildpack/component/security_providers' require 'java_buildpack/util/snake_case' +require 'java_buildpack/util/tokenized_version' require 'pathname' -shared_context 'droplet_helper' do - include_context 'application_helper' - include_context 'logging_helper' +# rubocop: disable RSpec/MultipleMemoizedHelpers +shared_context 'with droplet help' do + include_context 'with application help' + include_context 'with logging help' let(:additional_libraries) { JavaBuildpack::Component::AdditionalLibraries.new app_dir } @@ -35,25 +44,59 @@ let(:component_id) { described_class.to_s.split('::').last.snake_case } let(:droplet) do - JavaBuildpack::Component::Droplet.new(additional_libraries, component_id, java_home, java_opts, app_dir) + JavaBuildpack::Component::Droplet.new(additional_libraries, component_id, environment_variables, + extension_directories, java_home, java_opts, networking, app_dir, + root_libraries, security_providers) end + let(:extension_directories) { JavaBuildpack::Component::ExtensionDirectories.new app_dir } + + let(:root_libraries) { JavaBuildpack::Component::RootLibraries.new app_dir } + + let(:root_libs_directory) { droplet.root + '.root_libs' } + let(:sandbox) { droplet.sandbox } let(:java_home) do - delegate = double('MutableJavaHome', root: app_dir + '.test-java-home', version: %w(1 7 55 u60)) - JavaBuildpack::Component::ImmutableJavaHome.new delegate, app_dir + JavaBuildpack::Component::ImmutableJavaHome.new java_home_delegate, app_dir + end + + let(:java_home_delegate) do + delegate = JavaBuildpack::Component::MutableJavaHome.new + delegate.root = app_dir + '.test-java-home' + delegate.version = JavaBuildpack::Util::TokenizedVersion.new('1.7.0_55') + + delegate + end + + let(:environment_variables) do + java_opts = JavaBuildpack::Component::EnvironmentVariables.new app_dir + java_opts.concat %w[test-var-2 test-var-1] + java_opts end let(:java_opts) do java_opts = JavaBuildpack::Component::JavaOpts.new app_dir - java_opts.concat %w(test-opt-2 test-opt-1) + java_opts.concat %w[test-opt-2 test-opt-1] java_opts end + let(:networking) { JavaBuildpack::Component::Networking.new } + + let(:security_providers) { JavaBuildpack::Component::SecurityProviders.new } + before do FileUtils.cp_r 'spec/fixtures/additional_libs/.', additional_libs_directory additional_libs_directory.children.each { |child| additional_libraries << child } + + extension_directories << (sandbox + 'test-extension-directory-1') + extension_directories << (sandbox + 'test-extension-directory-2') + + FileUtils.cp_r 'spec/fixtures/root_libs/.', root_libs_directory + root_libs_directory.children.each { |child| root_libraries << child } + + security_providers.concat %w[test-security-provider-1 test-security-provider-2] end end +# rubocop: enable RSpec/MultipleMemoizedHelpers diff --git a/spec/fixtures/additional_libs/test-jar-1.jar b/spec/fixtures/additional_libs/test-jar-1.jar index e69de29bb2..8b0d26227e 100644 Binary files a/spec/fixtures/additional_libs/test-jar-1.jar and b/spec/fixtures/additional_libs/test-jar-1.jar differ diff --git a/spec/fixtures/additional_libs/test-jar-2.jar b/spec/fixtures/additional_libs/test-jar-2.jar index e69de29bb2..8b0d26227e 100644 Binary files a/spec/fixtures/additional_libs/test-jar-2.jar and b/spec/fixtures/additional_libs/test-jar-2.jar differ diff --git a/spec/fixtures/container_groovy_logback/Alpha.java b/spec/fixtures/container_groovy_logback/Alpha.java new file mode 100644 index 0000000000..cc0f9f5af5 --- /dev/null +++ b/spec/fixtures/container_groovy_logback/Alpha.java @@ -0,0 +1,19 @@ +/* + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Alpha { +} diff --git a/spec/fixtures/container_groovy_logback/ch/qos/logback/pogo.groovy b/spec/fixtures/container_groovy_logback/ch/qos/logback/pogo.groovy new file mode 100644 index 0000000000..e923969a98 --- /dev/null +++ b/spec/fixtures/container_groovy_logback/ch/qos/logback/pogo.groovy @@ -0,0 +1,22 @@ +/* + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fixtures.container_groovy_logback.ch.qos.logback + +class Directory { + +} diff --git a/spec/fixtures/container_groovy_main_method/Alpha.groovy b/spec/fixtures/container_groovy_main_method/Alpha.groovy index 15a37df0bf..cc0f9f5af5 100644 --- a/spec/fixtures/container_groovy_main_method/Alpha.groovy +++ b/spec/fixtures/container_groovy_main_method/Alpha.groovy @@ -1,5 +1,6 @@ /* - * Copyright 2013 the original author or authors. + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spec/fixtures/container_groovy_main_method/Application.groovy b/spec/fixtures/container_groovy_main_method/Application.groovy index 87f20e9cee..2fd2c1e928 100644 --- a/spec/fixtures/container_groovy_main_method/Application.groovy +++ b/spec/fixtures/container_groovy_main_method/Application.groovy @@ -1,5 +1,6 @@ /* - * Copyright 2013 the original author or authors. + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spec/fixtures/container_groovy_main_method/directory/Beta.groovy b/spec/fixtures/container_groovy_main_method/directory/Beta.groovy index 0714d4a479..5db3b95c0b 100644 --- a/spec/fixtures/container_groovy_main_method/directory/Beta.groovy +++ b/spec/fixtures/container_groovy_main_method/directory/Beta.groovy @@ -1,5 +1,6 @@ /* - * Copyright 2013 the original author or authors. + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spec/fixtures/container_groovy_non_pogo/Alpha.groovy b/spec/fixtures/container_groovy_non_pogo/Alpha.groovy index 15a37df0bf..cc0f9f5af5 100644 --- a/spec/fixtures/container_groovy_non_pogo/Alpha.groovy +++ b/spec/fixtures/container_groovy_non_pogo/Alpha.groovy @@ -1,5 +1,6 @@ /* - * Copyright 2013 the original author or authors. + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spec/fixtures/container_groovy_non_pogo/Application.groovy b/spec/fixtures/container_groovy_non_pogo/Application.groovy index 700494f792..bb0e9f845f 100644 --- a/spec/fixtures/container_groovy_non_pogo/Application.groovy +++ b/spec/fixtures/container_groovy_non_pogo/Application.groovy @@ -1,5 +1,6 @@ /* - * Copyright 2013 the original author or authors. + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spec/fixtures/container_groovy_non_pogo_with_class_file/logback.groovy b/spec/fixtures/container_groovy_non_pogo_with_class_file/logback.groovy index 60afd6f609..8a0b33cb9c 100644 --- a/spec/fixtures/container_groovy_non_pogo_with_class_file/logback.groovy +++ b/spec/fixtures/container_groovy_non_pogo_with_class_file/logback.groovy @@ -1,5 +1,6 @@ /* - * Copyright 2013 the original author or authors. + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spec/fixtures/container_groovy_shebang/Alpha.groovy b/spec/fixtures/container_groovy_shebang/Alpha.groovy index 15a37df0bf..cc0f9f5af5 100644 --- a/spec/fixtures/container_groovy_shebang/Alpha.groovy +++ b/spec/fixtures/container_groovy_shebang/Alpha.groovy @@ -1,5 +1,6 @@ /* - * Copyright 2013 the original author or authors. + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spec/fixtures/container_groovy_shebang/Application.groovy b/spec/fixtures/container_groovy_shebang/Application.groovy index 46f4979966..6174765a9c 100755 --- a/spec/fixtures/container_groovy_shebang/Application.groovy +++ b/spec/fixtures/container_groovy_shebang/Application.groovy @@ -1,6 +1,7 @@ #!/usr/bin/env groovy /* - * Copyright 2013 the original author or authors. + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spec/fixtures/container_groovy_shebang_containing_class/Application.groovy b/spec/fixtures/container_groovy_shebang_containing_class/Application.groovy index 75cf24b0ce..f102da537a 100755 --- a/spec/fixtures/container_groovy_shebang_containing_class/Application.groovy +++ b/spec/fixtures/container_groovy_shebang_containing_class/Application.groovy @@ -1,6 +1,7 @@ #!/usr/bin/env groovy /* - * Copyright 2013 the original author or authors. + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spec/fixtures/container_groovy_with_jars/Alpha.jar b/spec/fixtures/container_groovy_with_jars/Alpha.jar index 15a37df0bf..bbf9ca2412 100644 --- a/spec/fixtures/container_groovy_with_jars/Alpha.jar +++ b/spec/fixtures/container_groovy_with_jars/Alpha.jar @@ -1,5 +1,5 @@ /* - * Copyright 2013 the original author or authors. + * Copyright 2013-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spec/fixtures/container_groovy_with_jars/Application.groovy b/spec/fixtures/container_groovy_with_jars/Application.groovy index 87f20e9cee..2fd2c1e928 100644 --- a/spec/fixtures/container_groovy_with_jars/Application.groovy +++ b/spec/fixtures/container_groovy_with_jars/Application.groovy @@ -1,5 +1,6 @@ /* - * Copyright 2013 the original author or authors. + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spec/fixtures/container_groovy_with_jars/directory/Beta.jar b/spec/fixtures/container_groovy_with_jars/directory/Beta.jar index 0714d4a479..eb614ac776 100644 --- a/spec/fixtures/container_groovy_with_jars/directory/Beta.jar +++ b/spec/fixtures/container_groovy_with_jars/directory/Beta.jar @@ -1,5 +1,5 @@ /* - * Copyright 2013 the original author or authors. + * Copyright 2013-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spec/fixtures/container_main_spring_boot_jar_launcher/META-INF/MANIFEST.MF b/spec/fixtures/container_main_spring_boot_jar_launcher/META-INF/MANIFEST.MF index 09e4a608aa..0a359b2f04 100644 --- a/spec/fixtures/container_main_spring_boot_jar_launcher/META-INF/MANIFEST.MF +++ b/spec/fixtures/container_main_spring_boot_jar_launcher/META-INF/MANIFEST.MF @@ -1 +1,4 @@ -Main-Class: org.springframework.boot.loader.JarLauncher \ No newline at end of file +Spring-Boot-Lib: manifest-lib-value/ +Spring-Boot-Version: 1.2.5.RELEASE +Main-Class: org.springframework.boot.loader.JarLauncher + diff --git a/spec/fixtures/integration_valid/.lib/.gitignore b/spec/fixtures/container_main_spring_boot_jar_launcher/lib/.gitignore similarity index 100% rename from spec/fixtures/integration_valid/.lib/.gitignore rename to spec/fixtures/container_main_spring_boot_jar_launcher/lib/.gitignore diff --git a/spec/fixtures/container_main_spring_boot_properties_launcher/META-INF/MANIFEST.MF b/spec/fixtures/container_main_spring_boot_properties_launcher/META-INF/MANIFEST.MF index 3085e2c40f..2a9ae82898 100644 --- a/spec/fixtures/container_main_spring_boot_properties_launcher/META-INF/MANIFEST.MF +++ b/spec/fixtures/container_main_spring_boot_properties_launcher/META-INF/MANIFEST.MF @@ -1 +1,2 @@ +Spring-Boot-Version: 1.2.5.RELEASE Main-Class: org.springframework.boot.loader.PropertiesLauncher diff --git a/spec/fixtures/framework_auto_reconfiguration_servlet_2/WEB-INF/lib/spring-core-3.2.3.RELEASE.jar b/spec/fixtures/container_main_spring_boot_properties_launcher/lib/.gitignore similarity index 100% rename from spec/fixtures/framework_auto_reconfiguration_servlet_2/WEB-INF/lib/spring-core-3.2.3.RELEASE.jar rename to spec/fixtures/container_main_spring_boot_properties_launcher/lib/.gitignore diff --git a/spec/fixtures/container_main_spring_boot_thin_launcher/META-INF/MANIFEST.MF b/spec/fixtures/container_main_spring_boot_thin_launcher/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..33dde1d32b --- /dev/null +++ b/spec/fixtures/container_main_spring_boot_thin_launcher/META-INF/MANIFEST.MF @@ -0,0 +1,4 @@ +Spring-Boot-Lib: manifest-lib-value/ +Spring-Boot-Version: 1.2.5.RELEASE +Main-Class: org.springframework.boot.loader.wrapper.ThinJarWrapper + diff --git a/spec/fixtures/framework_auto_reconfiguration_servlet_2/WEB-INF/web.xml b/spec/fixtures/container_main_spring_boot_thin_launcher/lib/.gitignore similarity index 100% rename from spec/fixtures/framework_auto_reconfiguration_servlet_2/WEB-INF/web.xml rename to spec/fixtures/container_main_spring_boot_thin_launcher/lib/.gitignore diff --git a/spec/fixtures/container_main_spring_boot_war_launcher/META-INF/MANIFEST.MF b/spec/fixtures/container_main_spring_boot_war_launcher/META-INF/MANIFEST.MF index 74c2d4f39a..aaf7184069 100644 --- a/spec/fixtures/container_main_spring_boot_war_launcher/META-INF/MANIFEST.MF +++ b/spec/fixtures/container_main_spring_boot_war_launcher/META-INF/MANIFEST.MF @@ -1 +1,2 @@ +Spring-Boot-Version: 1.2.5.RELEASE Main-Class: org.springframework.boot.loader.WarLauncher diff --git a/spec/fixtures/container_main_spring_boot_war_launcher/lib/.gitignore b/spec/fixtures/container_main_spring_boot_war_launcher/lib/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/container_tomcat_gemfire_store_context_after.xml b/spec/fixtures/container_no_tomcat_version_geode_store_context_after.xml similarity index 63% rename from spec/fixtures/container_tomcat_gemfire_store_context_after.xml rename to spec/fixtures/container_no_tomcat_version_geode_store_context_after.xml index 60f6988b65..6eec7d93fe 100644 --- a/spec/fixtures/container_tomcat_gemfire_store_context_after.xml +++ b/spec/fixtures/container_no_tomcat_version_geode_store_context_after.xml @@ -1,6 +1,7 @@ + --> - + diff --git a/spec/fixtures/container_ratpack_dist/application-root/app/Ratpack.groovy b/spec/fixtures/container_ratpack_dist/application-root/app/Ratpack.groovy index e69de29bb2..0d3fb37ae3 100644 --- a/spec/fixtures/container_ratpack_dist/application-root/app/Ratpack.groovy +++ b/spec/fixtures/container_ratpack_dist/application-root/app/Ratpack.groovy @@ -0,0 +1,17 @@ +/* + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + diff --git a/spec/fixtures/container_ratpack_staged/app/ratpack.groovy b/spec/fixtures/container_ratpack_staged/app/ratpack.groovy index e69de29bb2..0d3fb37ae3 100644 --- a/spec/fixtures/container_ratpack_staged/app/ratpack.groovy +++ b/spec/fixtures/container_ratpack_staged/app/ratpack.groovy @@ -0,0 +1,17 @@ +/* + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + diff --git a/spec/fixtures/container_spring_boot_cli_beans_configuration/configuration.groovy b/spec/fixtures/container_spring_boot_cli_beans_configuration/configuration.groovy index b074157865..f476f1a137 100644 --- a/spec/fixtures/container_spring_boot_cli_beans_configuration/configuration.groovy +++ b/spec/fixtures/container_spring_boot_cli_beans_configuration/configuration.groovy @@ -1,3 +1,20 @@ +/* + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + beans { } diff --git a/spec/fixtures/container_spring_boot_cli_beans_configuration/pogo_1.groovy b/spec/fixtures/container_spring_boot_cli_beans_configuration/pogo_1.groovy index 7002753afa..b905ca339d 100644 --- a/spec/fixtures/container_spring_boot_cli_beans_configuration/pogo_1.groovy +++ b/spec/fixtures/container_spring_boot_cli_beans_configuration/pogo_1.groovy @@ -1 +1,18 @@ -class X { \ No newline at end of file +/* + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class X { diff --git a/spec/fixtures/container_spring_boot_cli_groovy_with_web_inf/pogo.groovy b/spec/fixtures/container_spring_boot_cli_groovy_with_web_inf/pogo.groovy index 7002753afa..b905ca339d 100644 --- a/spec/fixtures/container_spring_boot_cli_groovy_with_web_inf/pogo.groovy +++ b/spec/fixtures/container_spring_boot_cli_groovy_with_web_inf/pogo.groovy @@ -1 +1,18 @@ -class X { \ No newline at end of file +/* + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class X { diff --git a/spec/fixtures/container_spring_boot_cli_main_method/main.groovy b/spec/fixtures/container_spring_boot_cli_main_method/main.groovy index a0803117f9..2d5ca87bf1 100644 --- a/spec/fixtures/container_spring_boot_cli_main_method/main.groovy +++ b/spec/fixtures/container_spring_boot_cli_main_method/main.groovy @@ -1 +1,18 @@ -static void main( \ No newline at end of file +/* + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +static void main( diff --git a/spec/fixtures/container_spring_boot_cli_main_method/pogo.groovy b/spec/fixtures/container_spring_boot_cli_main_method/pogo.groovy index 7002753afa..b905ca339d 100644 --- a/spec/fixtures/container_spring_boot_cli_main_method/pogo.groovy +++ b/spec/fixtures/container_spring_boot_cli_main_method/pogo.groovy @@ -1 +1,18 @@ -class X { \ No newline at end of file +/* + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class X { diff --git a/spec/fixtures/container_spring_boot_cli_non_pogo/non_pogo.groovy b/spec/fixtures/container_spring_boot_cli_non_pogo/non_pogo.groovy index c1b0730e01..34aee0fc01 100644 --- a/spec/fixtures/container_spring_boot_cli_non_pogo/non_pogo.groovy +++ b/spec/fixtures/container_spring_boot_cli_non_pogo/non_pogo.groovy @@ -1 +1,18 @@ -x \ No newline at end of file +/* + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +x diff --git a/spec/fixtures/container_spring_boot_cli_non_pogo/pogo.groovy b/spec/fixtures/container_spring_boot_cli_non_pogo/pogo.groovy index 7002753afa..b905ca339d 100644 --- a/spec/fixtures/container_spring_boot_cli_non_pogo/pogo.groovy +++ b/spec/fixtures/container_spring_boot_cli_non_pogo/pogo.groovy @@ -1 +1,18 @@ -class X { \ No newline at end of file +/* + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class X { diff --git a/spec/fixtures/container_spring_boot_cli_valid_app/directory/pogo_4.groovy b/spec/fixtures/container_spring_boot_cli_valid_app/directory/pogo_4.groovy index 6377b7ec89..2a87cca0c0 100644 --- a/spec/fixtures/container_spring_boot_cli_valid_app/directory/pogo_4.groovy +++ b/spec/fixtures/container_spring_boot_cli_valid_app/directory/pogo_4.groovy @@ -1 +1,18 @@ +/* + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + class Directory { diff --git a/spec/fixtures/container_spring_boot_cli_valid_app/pogo_1.groovy b/spec/fixtures/container_spring_boot_cli_valid_app/pogo_1.groovy index 7002753afa..b905ca339d 100644 --- a/spec/fixtures/container_spring_boot_cli_valid_app/pogo_1.groovy +++ b/spec/fixtures/container_spring_boot_cli_valid_app/pogo_1.groovy @@ -1 +1,18 @@ -class X { \ No newline at end of file +/* + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class X { diff --git a/spec/fixtures/container_spring_boot_cli_valid_app/pogo_2.groovy b/spec/fixtures/container_spring_boot_cli_valid_app/pogo_2.groovy index b94ec93fee..a9ed51bc28 100644 --- a/spec/fixtures/container_spring_boot_cli_valid_app/pogo_2.groovy +++ b/spec/fixtures/container_spring_boot_cli_valid_app/pogo_2.groovy @@ -1 +1,18 @@ -class Yy_y { \ No newline at end of file +/* + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Yy_y { diff --git a/spec/fixtures/container_spring_boot_cli_valid_app/pogo_3.groovy b/spec/fixtures/container_spring_boot_cli_valid_app/pogo_3.groovy index 0bd14452b4..8ce2e126af 100644 --- a/spec/fixtures/container_spring_boot_cli_valid_app/pogo_3.groovy +++ b/spec/fixtures/container_spring_boot_cli_valid_app/pogo_3.groovy @@ -1 +1,18 @@ +/* + * Cloud Foundry Java Buildpack + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + class Runner implements CommandLineRunner { diff --git a/spec/fixtures/container_tomcat_gemfire_store/.java-buildpack/tomcat/conf/server.xml b/spec/fixtures/container_tomcat_gemfire_store/.java-buildpack/tomcat/conf/server.xml deleted file mode 100644 index 08bf2f1b1a..0000000000 --- a/spec/fixtures/container_tomcat_gemfire_store/.java-buildpack/tomcat/conf/server.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/spec/fixtures/container_tomcat_gemfire_store_cache_client_after.xml b/spec/fixtures/container_tomcat_gemfire_store_cache_client_after.xml deleted file mode 100644 index 729788cece..0000000000 --- a/spec/fixtures/container_tomcat_gemfire_store_cache_client_after.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/spec/fixtures/container_tomcat_gemfire_store_server_after.xml b/spec/fixtures/container_tomcat_gemfire_store_server_after.xml deleted file mode 100644 index 6599b8b711..0000000000 --- a/spec/fixtures/container_tomcat_gemfire_store_server_after.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/spec/fixtures/container_tomcat_gemfire_store/.java-buildpack/tomcat/conf/context.xml b/spec/fixtures/container_tomcat_geode_store/.java-buildpack/tomcat/conf/context.xml similarity index 88% rename from spec/fixtures/container_tomcat_gemfire_store/.java-buildpack/tomcat/conf/context.xml rename to spec/fixtures/container_tomcat_geode_store/.java-buildpack/tomcat/conf/context.xml index 6c04c4f8fe..df55748ecd 100644 --- a/spec/fixtures/container_tomcat_gemfire_store/.java-buildpack/tomcat/conf/context.xml +++ b/spec/fixtures/container_tomcat_geode_store/.java-buildpack/tomcat/conf/context.xml @@ -1,6 +1,7 @@ + + + + + + + + + + + + + + + diff --git a/spec/fixtures/container_tomcat_geode_store/WEB-INF/.gitkeep b/spec/fixtures/container_tomcat_geode_store/WEB-INF/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/container_tomcat_geode_store_cache_client_after.xml b/spec/fixtures/container_tomcat_geode_store_cache_client_after.xml new file mode 100644 index 0000000000..41e41f1612 --- /dev/null +++ b/spec/fixtures/container_tomcat_geode_store_cache_client_after.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/spec/fixtures/container_tomcat_geode_store_context_after.xml b/spec/fixtures/container_tomcat_geode_store_context_after.xml new file mode 100644 index 0000000000..5eb5387deb --- /dev/null +++ b/spec/fixtures/container_tomcat_geode_store_context_after.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/spec/fixtures/container_tomcat_geode_store_server_after.xml b/spec/fixtures/container_tomcat_geode_store_server_after.xml new file mode 100644 index 0000000000..df95030a49 --- /dev/null +++ b/spec/fixtures/container_tomcat_geode_store_server_after.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + diff --git a/spec/fixtures/container_tomcat_redis_store/.java-buildpack/tomcat/conf/context.xml b/spec/fixtures/container_tomcat_redis_store/.java-buildpack/tomcat/conf/context.xml index 6c04c4f8fe..df55748ecd 100644 --- a/spec/fixtures/container_tomcat_redis_store/.java-buildpack/tomcat/conf/context.xml +++ b/spec/fixtures/container_tomcat_redis_store/.java-buildpack/tomcat/conf/context.xml @@ -1,6 +1,7 @@ + --> diff --git a/spec/fixtures/container_tomcat_with_index/index.html b/spec/fixtures/container_tomcat_with_index/index.html index e69de29bb2..6ac21946c3 100644 --- a/spec/fixtures/container_tomcat_with_index/index.html +++ b/spec/fixtures/container_tomcat_with_index/index.html @@ -0,0 +1,17 @@ + + diff --git a/spec/fixtures/framework_app_dynamics_agent/BOOT-INF/classes/appdynamics/conf/app-agent-config.xml b/spec/fixtures/framework_app_dynamics_agent/BOOT-INF/classes/appdynamics/conf/app-agent-config.xml new file mode 100644 index 0000000000..b934fa90f3 --- /dev/null +++ b/spec/fixtures/framework_app_dynamics_agent/BOOT-INF/classes/appdynamics/conf/app-agent-config.xml @@ -0,0 +1,730 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + com.singularity.ee.agent.appagent.kernel.DynamicServiceManager + + + + + + + + + + BCIEngine,TransactionMonitoringService,SnapshotService + + + + + + + + BCIEngine + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BCIEngine + + + + + + + + + + + + + + + + + + + + + + + + + + + + BCIEngine,SnapshotService + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JMXService + + + BCIEngine + + + + + + + + + + + + + + + + + + + + + + + + + + BCIEngine + + + + + + + + + + + + + + + + + + + + + + + BCIEngine + + + + + TransactionMonitoringService + + + + + + + + + + + + + + diff --git a/spec/fixtures/framework_aspectj_weaver_aop_xml_only/BOOT-INF/classes/META-INF/aop.xml b/spec/fixtures/framework_aspectj_weaver_aop_xml_only/BOOT-INF/classes/META-INF/aop.xml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_aspectj_weaver_boot_inf_classes/BOOT-INF/classes/org/aspectj/aop.xml b/spec/fixtures/framework_aspectj_weaver_boot_inf_classes/BOOT-INF/classes/org/aspectj/aop.xml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_aspectj_weaver_boot_inf_classes/BOOT-INF/lib/aspectjweaver-1.8.10.jar b/spec/fixtures/framework_aspectj_weaver_boot_inf_classes/BOOT-INF/lib/aspectjweaver-1.8.10.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_aspectj_weaver_boot_inf_classes_meta_inf/BOOT-INF/classes/META-INF/aop.xml b/spec/fixtures/framework_aspectj_weaver_boot_inf_classes_meta_inf/BOOT-INF/classes/META-INF/aop.xml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_aspectj_weaver_boot_inf_classes_meta_inf/BOOT-INF/lib/aspectjweaver-1.8.10.jar b/spec/fixtures/framework_aspectj_weaver_boot_inf_classes_meta_inf/BOOT-INF/lib/aspectjweaver-1.8.10.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_aspectj_weaver_classes/org/aspectj/aop.xml b/spec/fixtures/framework_aspectj_weaver_classes/org/aspectj/aop.xml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_aspectj_weaver_classes/repository/org/aspectj/aspectjweaver/1.8.10/aspectjweaver-1.8.10.jar b/spec/fixtures/framework_aspectj_weaver_classes/repository/org/aspectj/aspectjweaver/1.8.10/aspectjweaver-1.8.10.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_aspectj_weaver_jar_only/BOOT-INF/lib/aspectjweaver-1.8.10.jar b/spec/fixtures/framework_aspectj_weaver_jar_only/BOOT-INF/lib/aspectjweaver-1.8.10.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_aspectj_weaver_meta_inf/BOOT-INF/lib/aspectjweaver-1.8.10.jar b/spec/fixtures/framework_aspectj_weaver_meta_inf/BOOT-INF/lib/aspectjweaver-1.8.10.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_aspectj_weaver_meta_inf/META-INF/aop.xml b/spec/fixtures/framework_aspectj_weaver_meta_inf/META-INF/aop.xml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_auto_reconfiguration_java_cfenv/WEB-INF/lib/java-cfenv-2.1.2.RELEASE.jar b/spec/fixtures/framework_auto_reconfiguration_java_cfenv/WEB-INF/lib/java-cfenv-2.1.2.RELEASE.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_auto_reconfiguration_java_cfenv/WEB-INF/lib/java-cfenv-boot-2.1.2.RELEASE.jar b/spec/fixtures/framework_auto_reconfiguration_java_cfenv/WEB-INF/lib/java-cfenv-boot-2.1.2.RELEASE.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_auto_reconfiguration_java_cfenv/WEB-INF/lib/java-cfenv-jdbc-2.1.2.RELEASE.jar b/spec/fixtures/framework_auto_reconfiguration_java_cfenv/WEB-INF/lib/java-cfenv-jdbc-2.1.2.RELEASE.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_auto_reconfiguration_java_cfenv/WEB-INF/lib/spring-core-3.2.3.RELEASE.jar b/spec/fixtures/framework_auto_reconfiguration_java_cfenv/WEB-INF/lib/spring-core-3.2.3.RELEASE.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_auto_reconfiguration_scc/WEB-INF/lib/spring-cloud-cloudfoundry-connector-1.2.3.jar b/spec/fixtures/framework_auto_reconfiguration_scc/WEB-INF/lib/spring-cloud-cloudfoundry-connector-1.2.3.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_auto_reconfiguration_scc/WEB-INF/lib/spring-core-3.2.3.RELEASE.jar b/spec/fixtures/framework_auto_reconfiguration_scc/WEB-INF/lib/spring-core-3.2.3.RELEASE.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_container_customizer/META-INF/MANIFEST.MF b/spec/fixtures/framework_container_customizer/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..18573ddd34 --- /dev/null +++ b/spec/fixtures/framework_container_customizer/META-INF/MANIFEST.MF @@ -0,0 +1,4 @@ +Spring-Boot-Lib: WEB-INF/lib/ +Spring-Boot-Version: 1.3.3.RELEASE +Main-Class: org.springframework.boot.loader.WarLauncher + diff --git a/spec/fixtures/framework_container_customizer/WEB-INF/lib/.gitignore b/spec/fixtures/framework_container_customizer/WEB-INF/lib/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_dynatrace_one_agent/.java-buildpack/dynatrace_one_agent/agent/lib64/liboneagentloader.so b/spec/fixtures/framework_dynatrace_one_agent/.java-buildpack/dynatrace_one_agent/agent/lib64/liboneagentloader.so new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_dynatrace_one_agent/.java-buildpack/dynatrace_one_agent/agent/lib64/libruxitagentloader.so b/spec/fixtures/framework_dynatrace_one_agent/.java-buildpack/dynatrace_one_agent/agent/lib64/libruxitagentloader.so new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_dynatrace_one_agent/.java-buildpack/dynatrace_one_agent/manifest.json b/spec/fixtures/framework_dynatrace_one_agent/.java-buildpack/dynatrace_one_agent/manifest.json new file mode 100644 index 0000000000..6af71ec43a --- /dev/null +++ b/spec/fixtures/framework_dynatrace_one_agent/.java-buildpack/dynatrace_one_agent/manifest.json @@ -0,0 +1,16 @@ +{ + "technologies" : { + "process" : { + "linux-x86-64" : [ + { + "path": "agent/lib64/liboneagentproc.so", + "binarytype" : "primary" + } + ] + } + }, + "version" : "1.105.147.20160930-153457", + "tenantUUID" : "tenant", + "tenantToken" : "token-from-file", + "communicationEndpoints" : [ "https://endpoint1/communication", "https://endpoint2/communication" ] +} diff --git a/spec/fixtures/framework_java_cf_boot_2/META-INF/MANIFEST.MF b/spec/fixtures/framework_java_cf_boot_2/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..ce0077f4ce --- /dev/null +++ b/spec/fixtures/framework_java_cf_boot_2/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Spring-Boot-Lib: manifest-lib-value/ +Main-Class: org.springframework.boot.loader.JarLauncher +Spring-Boot-Version: 2.1.0.RELEASE diff --git a/spec/fixtures/framework_java_cf_boot_2/WEB-INF/lib/spring-boot-1.0.0.RELEASE.jar b/spec/fixtures/framework_java_cf_boot_2/WEB-INF/lib/spring-boot-1.0.0.RELEASE.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_java_cf_boot_3/META-INF/MANIFEST.MF b/spec/fixtures/framework_java_cf_boot_3/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..a64827339d --- /dev/null +++ b/spec/fixtures/framework_java_cf_boot_3/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Spring-Boot-Lib: manifest-lib-value/ +Main-Class: org.springframework.boot.loader.JarLauncher +Spring-Boot-Version: 3.0.0.M1 diff --git a/spec/fixtures/framework_java_cf_boot_3/WEB-INF/lib/spring-boot-3.0.0.M1.jar b/spec/fixtures/framework_java_cf_boot_3/WEB-INF/lib/spring-boot-3.0.0.M1.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_java_cf_exists/META-INF/MANIFEST.MF b/spec/fixtures/framework_java_cf_exists/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..47eb0c27e7 --- /dev/null +++ b/spec/fixtures/framework_java_cf_exists/META-INF/MANIFEST.MF @@ -0,0 +1,4 @@ +Spring-Boot-Lib: manifest-lib-value/ +Spring-Boot-Version: 3.2.5.RELEASE +Main-Class: org.springframework.boot.loader.JarLauncher + diff --git a/spec/fixtures/framework_java_cf_exists/WEB-INF/lib/java-cfenv-2.1.2.RELEASE.jar b/spec/fixtures/framework_java_cf_exists/WEB-INF/lib/java-cfenv-2.1.2.RELEASE.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_java_cf_exists/WEB-INF/lib/java-cfenv-boot-2.1.2.RELEASE.jar b/spec/fixtures/framework_java_cf_exists/WEB-INF/lib/java-cfenv-boot-2.1.2.RELEASE.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_java_cf_exists/WEB-INF/lib/java-cfenv-jdbc-2.1.2.RELEASE.jar b/spec/fixtures/framework_java_cf_exists/WEB-INF/lib/java-cfenv-jdbc-2.1.2.RELEASE.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_java_cf_exists/WEB-INF/lib/spring-boot-3.2.3.RELEASE.jar b/spec/fixtures/framework_java_cf_exists/WEB-INF/lib/spring-boot-3.2.3.RELEASE.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_java_not_spring_boot3/META-INF/MANIFEST.MF b/spec/fixtures/framework_java_not_spring_boot3/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..a2cf1b406d --- /dev/null +++ b/spec/fixtures/framework_java_not_spring_boot3/META-INF/MANIFEST.MF @@ -0,0 +1,2 @@ +Main-Class: org.springframework.boot.loader.JarLauncher + diff --git a/spec/fixtures/framework_java_not_spring_boot3/WEB-INF/lib/spring-boot-3.2.3.RELEASE.jar b/spec/fixtures/framework_java_not_spring_boot3/WEB-INF/lib/spring-boot-3.2.3.RELEASE.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_java_security_networking b/spec/fixtures/framework_java_security_networking new file mode 100644 index 0000000000..60738b2246 --- /dev/null +++ b/spec/fixtures/framework_java_security_networking @@ -0,0 +1,4 @@ +networkaddress.cache.ttl=-1 +networkaddress.cache.negative.ttl=-2 +security.provider.1=test-security-provider-1 +security.provider.2=test-security-provider-2 diff --git a/spec/fixtures/framework_java_security_security_providers b/spec/fixtures/framework_java_security_security_providers new file mode 100644 index 0000000000..b4e7d7fc84 --- /dev/null +++ b/spec/fixtures/framework_java_security_security_providers @@ -0,0 +1,2 @@ +security.provider.1=test-security-provider-1 +security.provider.2=test-security-provider-2 diff --git a/spec/fixtures/framework_jrebel_app_simple/rebel-remote.xml b/spec/fixtures/framework_jrebel_app_simple/rebel-remote.xml new file mode 100644 index 0000000000..6ac21946c3 --- /dev/null +++ b/spec/fixtures/framework_jrebel_app_simple/rebel-remote.xml @@ -0,0 +1,17 @@ + + diff --git a/spec/fixtures/framework_jrebel_app_war/WEB-INF/classes/rebel-remote.xml b/spec/fixtures/framework_jrebel_app_war/WEB-INF/classes/rebel-remote.xml new file mode 100644 index 0000000000..6ac21946c3 --- /dev/null +++ b/spec/fixtures/framework_jrebel_app_war/WEB-INF/classes/rebel-remote.xml @@ -0,0 +1,17 @@ + + diff --git a/spec/fixtures/framework_jrebel_app_war_with_jar/WEB-INF/lib/dependency.jar b/spec/fixtures/framework_jrebel_app_war_with_jar/WEB-INF/lib/dependency.jar new file mode 100644 index 0000000000..89c8886b11 Binary files /dev/null and b/spec/fixtures/framework_jrebel_app_war_with_jar/WEB-INF/lib/dependency.jar differ diff --git a/spec/fixtures/framework_jrebel_jar_directory/dependency.jar/.gitkeep b/spec/fixtures/framework_jrebel_jar_directory/dependency.jar/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_luna_security_provider/Chrystoki.conf b/spec/fixtures/framework_luna_security_provider/Chrystoki.conf new file mode 100644 index 0000000000..9ee5e344ba --- /dev/null +++ b/spec/fixtures/framework_luna_security_provider/Chrystoki.conf @@ -0,0 +1,58 @@ +Luna = { + CloningCommandTimeOut = 300000; + CommandTimeOutPedSet = 720000; + DefaultTimeOut = 500000; + KeypairGenTimeOut = 2700000; + PEDTimeout1 = 100000; + PEDTimeout2 = 200000; + PEDTimeout3 = 10000; +} + +Misc = { + PE1746Enabled = 0; +} + +Chrystoki2 = { + LibUNIX64 = .java-buildpack/luna_security_provider/libs/64/libCryptoki2.so; +} + +LunaSA Client = { + TCPKeepAlive = 0; + NetClient = 1; + + ClientCertFile = .java-buildpack/luna_security_provider/client-certificate.pem; + ClientPrivKeyFile = .java-buildpack/luna_security_provider/client-private-key.pem; + HtlDir = .java-buildpack/luna_security_provider/htl; + ServerCAFile = .java-buildpack/luna_security_provider/server-certificates.pem; + + ServerName00 = test-server-1; + ServerPort00 = 1792; + ServerHtl00 = 0; + + ServerName01 = test-server-2; + ServerPort01 = 1792; + ServerHtl01 = 0; + +} + +VirtualToken = { + VirtualToken00Label = test-group-1; + VirtualToken00SN = 1test-group-1-member-1; + VirtualToken00Members = test-group-1-member-1,test-group-1-member-2; + + VirtualToken01Label = test-group-2; + VirtualToken01SN = 1test-group-2-member-1; + VirtualToken01Members = test-group-2-member-1,test-group-2-member-2; + +} + +HAConfiguration = { + AutoReconnectInterval = 60; + HAOnly = 1; + reconnAtt = -1; +} + +HASynchronize = { + test-group-1 = 1; + test-group-2 = 1; +} diff --git a/spec/fixtures/framework_luna_security_provider/client-certificate.pem b/spec/fixtures/framework_luna_security_provider/client-certificate.pem new file mode 100644 index 0000000000..40769aae71 --- /dev/null +++ b/spec/fixtures/framework_luna_security_provider/client-certificate.pem @@ -0,0 +1,3 @@ +-----BEGIN CERTIFICATE----- +test-client-cert +-----END CERTIFICATE----- diff --git a/spec/fixtures/framework_luna_security_provider/client-private-key.pem b/spec/fixtures/framework_luna_security_provider/client-private-key.pem new file mode 100644 index 0000000000..019359d6ba --- /dev/null +++ b/spec/fixtures/framework_luna_security_provider/client-private-key.pem @@ -0,0 +1,3 @@ +-----BEGIN RSA PRIVATE KEY----- +test-client-private-key +-----END RSA PRIVATE KEY----- diff --git a/spec/fixtures/framework_luna_security_provider/server-certificates.pem b/spec/fixtures/framework_luna_security_provider/server-certificates.pem new file mode 100644 index 0000000000..ea7e60aa91 --- /dev/null +++ b/spec/fixtures/framework_luna_security_provider/server-certificates.pem @@ -0,0 +1,6 @@ +-----BEGIN CERTIFICATE----- +test-server-1-cert +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +test-server-2-cert +-----END CERTIFICATE----- diff --git a/spec/fixtures/framework_luna_security_provider_logging/Chrystoki.conf b/spec/fixtures/framework_luna_security_provider_logging/Chrystoki.conf new file mode 100644 index 0000000000..df355a691b --- /dev/null +++ b/spec/fixtures/framework_luna_security_provider_logging/Chrystoki.conf @@ -0,0 +1,68 @@ +Luna = { + CloningCommandTimeOut = 300000; + CommandTimeOutPedSet = 720000; + DefaultTimeOut = 500000; + KeypairGenTimeOut = 2700000; + PEDTimeout1 = 100000; + PEDTimeout2 = 200000; + PEDTimeout3 = 10000; +} + +Misc = { + PE1746Enabled = 0; +} + +Chrystoki2 = { + LibUNIX64 = .java-buildpack/luna_security_provider/libs/64/libcklog2.so; +} + +CkLog2 = { + Enabled = 1; + LibUNIX64 = .java-buildpack/luna_security_provider/libs/64/libCryptoki2.so; + LoggingMask = ALL_FUNC; + LogToStreams = 1; + NewFormat = 1; +} + +LunaSA Client = { + TCPKeepAlive = 0; + NetClient = 1; + + ClientCertFile = .java-buildpack/luna_security_provider/client-certificate.pem; + ClientPrivKeyFile = .java-buildpack/luna_security_provider/client-private-key.pem; + HtlDir = .java-buildpack/luna_security_provider/htl; + ServerCAFile = .java-buildpack/luna_security_provider/server-certificates.pem; + + ServerName00 = test-server-1; + ServerPort00 = 1792; + ServerHtl00 = 0; + + ServerName01 = test-server-2; + ServerPort01 = 1792; + ServerHtl01 = 0; + +} + +VirtualToken = { + VirtualToken00Label = test-group-1; + VirtualToken00SN = 1test-group-1-member-1; + VirtualToken00Members = test-group-1-member-1,test-group-1-member-2; + + VirtualToken01Label = test-group-2; + VirtualToken01SN = 1test-group-2-member-1; + VirtualToken01Members = test-group-2-member-1,test-group-2-member-2; + +} + +HAConfiguration = { + AutoReconnectInterval = 60; + HAOnly = 1; + reconnAtt = -1; +haLogStatus = enabled; +haLogToStdout = enabled; +} + +HASynchronize = { + test-group-1 = 1; + test-group-2 = 1; +} diff --git a/spec/fixtures/framework_luna_security_provider_logging/client-certificate.pem b/spec/fixtures/framework_luna_security_provider_logging/client-certificate.pem new file mode 100644 index 0000000000..40769aae71 --- /dev/null +++ b/spec/fixtures/framework_luna_security_provider_logging/client-certificate.pem @@ -0,0 +1,3 @@ +-----BEGIN CERTIFICATE----- +test-client-cert +-----END CERTIFICATE----- diff --git a/spec/fixtures/framework_luna_security_provider_logging/client-private-key.pem b/spec/fixtures/framework_luna_security_provider_logging/client-private-key.pem new file mode 100644 index 0000000000..019359d6ba --- /dev/null +++ b/spec/fixtures/framework_luna_security_provider_logging/client-private-key.pem @@ -0,0 +1,3 @@ +-----BEGIN RSA PRIVATE KEY----- +test-client-private-key +-----END RSA PRIVATE KEY----- diff --git a/spec/fixtures/framework_luna_security_provider_logging/server-certificates.pem b/spec/fixtures/framework_luna_security_provider_logging/server-certificates.pem new file mode 100644 index 0000000000..ea7e60aa91 --- /dev/null +++ b/spec/fixtures/framework_luna_security_provider_logging/server-certificates.pem @@ -0,0 +1,6 @@ +-----BEGIN CERTIFICATE----- +test-server-1-cert +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +test-server-2-cert +-----END CERTIFICATE----- diff --git a/spec/fixtures/framework_luna_security_provider_tcp_keep_alive/Chrystoki.conf b/spec/fixtures/framework_luna_security_provider_tcp_keep_alive/Chrystoki.conf new file mode 100644 index 0000000000..96eeadd2cc --- /dev/null +++ b/spec/fixtures/framework_luna_security_provider_tcp_keep_alive/Chrystoki.conf @@ -0,0 +1,68 @@ +Luna = { + CloningCommandTimeOut = 300000; + CommandTimeOutPedSet = 720000; + DefaultTimeOut = 500000; + KeypairGenTimeOut = 2700000; + PEDTimeout1 = 100000; + PEDTimeout2 = 200000; + PEDTimeout3 = 10000; +} + +Misc = { + PE1746Enabled = 0; +} + +Chrystoki2 = { + LibUNIX64 = .java-buildpack/luna_security_provider/libs/64/libcklog2.so; +} + +CkLog2 = { + Enabled = 1; + LibUNIX64 = .java-buildpack/luna_security_provider/libs/64/libCryptoki2.so; + LoggingMask = ALL_FUNC; + LogToStreams = 1; + NewFormat = 1; +} + +LunaSA Client = { + TCPKeepAlive = 1; + NetClient = 1; + + ClientCertFile = .java-buildpack/luna_security_provider/client-certificate.pem; + ClientPrivKeyFile = .java-buildpack/luna_security_provider/client-private-key.pem; + HtlDir = .java-buildpack/luna_security_provider/htl; + ServerCAFile = .java-buildpack/luna_security_provider/server-certificates.pem; + + ServerName00 = test-server-1; + ServerPort00 = 1792; + ServerHtl00 = 0; + + ServerName01 = test-server-2; + ServerPort01 = 1792; + ServerHtl01 = 0; + +} + +VirtualToken = { + VirtualToken00Label = test-group-1; + VirtualToken00SN = 1test-group-1-member-1; + VirtualToken00Members = test-group-1-member-1,test-group-1-member-2; + + VirtualToken01Label = test-group-2; + VirtualToken01SN = 1test-group-2-member-1; + VirtualToken01Members = test-group-2-member-1,test-group-2-member-2; + +} + +HAConfiguration = { + AutoReconnectInterval = 60; + HAOnly = 1; + reconnAtt = -1; +haLogStatus = enabled; +haLogToStdout = enabled; +} + +HASynchronize = { + test-group-1 = 1; + test-group-2 = 1; +} diff --git a/spec/fixtures/framework_luna_security_provider_tcp_keep_alive/client-certificate.pem b/spec/fixtures/framework_luna_security_provider_tcp_keep_alive/client-certificate.pem new file mode 100644 index 0000000000..40769aae71 --- /dev/null +++ b/spec/fixtures/framework_luna_security_provider_tcp_keep_alive/client-certificate.pem @@ -0,0 +1,3 @@ +-----BEGIN CERTIFICATE----- +test-client-cert +-----END CERTIFICATE----- diff --git a/spec/fixtures/framework_luna_security_provider_tcp_keep_alive/client-private-key.pem b/spec/fixtures/framework_luna_security_provider_tcp_keep_alive/client-private-key.pem new file mode 100644 index 0000000000..019359d6ba --- /dev/null +++ b/spec/fixtures/framework_luna_security_provider_tcp_keep_alive/client-private-key.pem @@ -0,0 +1,3 @@ +-----BEGIN RSA PRIVATE KEY----- +test-client-private-key +-----END RSA PRIVATE KEY----- diff --git a/spec/fixtures/framework_luna_security_provider_tcp_keep_alive/server-certificates.pem b/spec/fixtures/framework_luna_security_provider_tcp_keep_alive/server-certificates.pem new file mode 100644 index 0000000000..ea7e60aa91 --- /dev/null +++ b/spec/fixtures/framework_luna_security_provider_tcp_keep_alive/server-certificates.pem @@ -0,0 +1,6 @@ +-----BEGIN CERTIFICATE----- +test-server-1-cert +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +test-server-2-cert +-----END CERTIFICATE----- diff --git a/spec/fixtures/framework_mariadb_jdbc_with_new_mysql_driver/WEB-INF/lib/mysql-connector-j-8.0.33.jar b/spec/fixtures/framework_mariadb_jdbc_with_new_mysql_driver/WEB-INF/lib/mysql-connector-j-8.0.33.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_metric_writer/micrometer-core-1.1.5.jar b/spec/fixtures/framework_metric_writer/micrometer-core-1.1.5.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_multi_buildpack_deps/0/0/bin/.gitkeep b/spec/fixtures/framework_multi_buildpack_deps/0/0/bin/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_multi_buildpack_deps/0/0/config.yml b/spec/fixtures/framework_multi_buildpack_deps/0/0/config.yml new file mode 100644 index 0000000000..91516bd0ae --- /dev/null +++ b/spec/fixtures/framework_multi_buildpack_deps/0/0/config.yml @@ -0,0 +1,3 @@ +--- +name: test-buildpack-0-0 +config: {} diff --git a/spec/fixtures/framework_multi_buildpack_deps/0/0/lib/.gitkeep b/spec/fixtures/framework_multi_buildpack_deps/0/0/lib/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_multi_buildpack_deps/0/1/.gitkeep b/spec/fixtures/framework_multi_buildpack_deps/0/1/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_multi_buildpack_deps/0/2/config.yml b/spec/fixtures/framework_multi_buildpack_deps/0/2/config.yml new file mode 100644 index 0000000000..0178fd53ee --- /dev/null +++ b/spec/fixtures/framework_multi_buildpack_deps/0/2/config.yml @@ -0,0 +1,41 @@ +--- +name: test-buildpack-0-2 +config: + additional_libraries: + - /multi-test-additional-library-1 + - /multi-test-additional-library-2 + environment_variables: + multi-test-key-1: multi-test-value-1 + multi-test-key-2: multi-test-value-2 + extension_directories: + - /multi-test-extension-directory-1 + - /multi-test-extension-directory-2 + java_opts: + agentpaths: + - /multi-test-agent-1 + - /multi-test-agent-2 + agentpaths_with_props: + /multi-test-agent-1: + test-key-1: test-value-1 + test-key-2: test-value-2 + /multi-test-agent-2: + test-key-1: test-value-1 + test-key-2: test-value-2 + bootclasspath_ps: + - /multi-test-bootclasspath-p-1 + - /multi-test-bootclasspath-p-2 + javaagents: + - /multi-test-java-agent-1 + - /multi-test-java-agent-2 + preformatted_options: + - multi-test-preformatted-option-1 + - multi-test-preformatted-option-2 + options: + multi-test-key-1: multi-test-value-1 + multi-test-key-2: multi-test-value-2 + system_properties: + multi-test-key-1: multi-test-value-1 + multi-test-key-2: multi-test-value-2 + security_providers: + - multi-test-security-provider-1 + - multi-test-security-provider-2 diff --git a/spec/fixtures/framework_multi_buildpack_deps/1/0/bin/.gitkeep b/spec/fixtures/framework_multi_buildpack_deps/1/0/bin/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_multi_buildpack_deps/1/0/config.yml b/spec/fixtures/framework_multi_buildpack_deps/1/0/config.yml new file mode 100644 index 0000000000..b7de0f797e --- /dev/null +++ b/spec/fixtures/framework_multi_buildpack_deps/1/0/config.yml @@ -0,0 +1,3 @@ +--- +name: test-buildpack-1-0 +config: {} diff --git a/spec/fixtures/framework_multi_buildpack_deps/1/0/lib/.gitkeep b/spec/fixtures/framework_multi_buildpack_deps/1/0/lib/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_multi_buildpack_deps/1/1/.gitkeep b/spec/fixtures/framework_multi_buildpack_deps/1/1/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_multi_buildpack_deps/1/2/config.yml b/spec/fixtures/framework_multi_buildpack_deps/1/2/config.yml new file mode 100644 index 0000000000..4dac13df8e --- /dev/null +++ b/spec/fixtures/framework_multi_buildpack_deps/1/2/config.yml @@ -0,0 +1,41 @@ +--- +name: test-buildpack-1-2 +config: + additional_libraries: + - /multi-test-additional-library-1 + - /multi-test-additional-library-2 + environment_variables: + multi-test-key-1: multi-test-value-1 + multi-test-key-2: multi-test-value-2 + extension_directories: + - /multi-test-extension-directory-1 + - /multi-test-extension-directory-2 + java_opts: + agentpaths: + - /multi-test-agent-1 + - /multi-test-agent-2 + agentpaths_with_props: + /multi-test-agent-1: + test-key-1: test-value-1 + test-key-2: test-value-2 + /multi-test-agent-2: + test-key-1: test-value-1 + test-key-2: test-value-2 + bootclasspath_ps: + - /multi-test-bootclasspath-p-1 + - /multi-test-bootclasspath-p-2 + javaagents: + - /multi-test-java-agent-1 + - /multi-test-java-agent-2 + preformatted_options: + - multi-test-preformatted-option-1 + - multi-test-preformatted-option-2 + options: + multi-test-key-1: multi-test-value-1 + multi-test-key-2: multi-test-value-2 + system_properties: + multi-test-key-1: multi-test-value-1 + multi-test-key-2: multi-test-value-2 + security_providers: + - multi-test-security-provider-1 + - multi-test-security-provider-2 diff --git a/spec/fixtures/framework_multi_buildpack_deps/2/0/bin/.gitkeep b/spec/fixtures/framework_multi_buildpack_deps/2/0/bin/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_multi_buildpack_deps/2/0/config.yml b/spec/fixtures/framework_multi_buildpack_deps/2/0/config.yml new file mode 100644 index 0000000000..8057f39eb1 --- /dev/null +++ b/spec/fixtures/framework_multi_buildpack_deps/2/0/config.yml @@ -0,0 +1,3 @@ +--- +name: test-buildpack-2-0 +config: {} diff --git a/spec/fixtures/framework_multi_buildpack_deps/2/0/lib/.gitkeep b/spec/fixtures/framework_multi_buildpack_deps/2/0/lib/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_multi_buildpack_deps/2/1/.gitkeep b/spec/fixtures/framework_multi_buildpack_deps/2/1/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/framework_multi_buildpack_deps/2/2/config.yml b/spec/fixtures/framework_multi_buildpack_deps/2/2/config.yml new file mode 100644 index 0000000000..89cc59bc43 --- /dev/null +++ b/spec/fixtures/framework_multi_buildpack_deps/2/2/config.yml @@ -0,0 +1,41 @@ +--- +name: test-buildpack-2-2 +config: + additional_libraries: + - /multi-test-additional-library-1 + - /multi-test-additional-library-2 + environment_variables: + multi-test-key-1: multi-test-value-1 + multi-test-key-2: multi-test-value-2 + extension_directories: + - /multi-test-extension-directory-1 + - /multi-test-extension-directory-2 + java_opts: + agentpaths: + - /multi-test-agent-1 + - /multi-test-agent-2 + agentpaths_with_props: + /multi-test-agent-1: + test-key-1: test-value-1 + test-key-2: test-value-2 + /multi-test-agent-2: + test-key-1: test-value-1 + test-key-2: test-value-2 + bootclasspath_ps: + - /multi-test-bootclasspath-p-1 + - /multi-test-bootclasspath-p-2 + javaagents: + - /multi-test-java-agent-1 + - /multi-test-java-agent-2 + preformatted_options: + - multi-test-preformatted-option-1 + - multi-test-preformatted-option-2 + options: + multi-test-key-1: multi-test-value-1 + multi-test-key-2: multi-test-value-2 + system_properties: + multi-test-key-1: multi-test-value-1 + multi-test-key-2: multi-test-value-2 + security_providers: + - multi-test-security-provider-1 + - multi-test-security-provider-2 diff --git a/spec/fixtures/integration_long_detect_tag/config/components.yml b/spec/fixtures/integration_long_detect_tag/config/components.yml index ddb5028726..e4a5422add 100644 --- a/spec/fixtures/integration_long_detect_tag/config/components.yml +++ b/spec/fixtures/integration_long_detect_tag/config/components.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,4 +24,4 @@ jres: - "JavaBuildpack::Jre::OpenJdkJRE" # - "JavaBuildpack::Jre::OracleJRE" -frameworks: [] \ No newline at end of file +frameworks: [] diff --git a/spec/fixtures/integration_long_detect_tag/lib/java_buildpack/container/long_detect_tags.rb b/spec/fixtures/integration_long_detect_tag/lib/java_buildpack/container/long_detect_tags.rb index d60439ea95..bc77ba69ff 100644 --- a/spec/fixtures/integration_long_detect_tag/lib/java_buildpack/container/long_detect_tags.rb +++ b/spec/fixtures/integration_long_detect_tag/lib/java_buildpack/container/long_detect_tags.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -29,12 +30,10 @@ def detect end # (see JavaBuildpack::Component::BaseComponent#compile) - def compile - end + def compile; end # (see JavaBuildpack::Component::BaseComponent#release) - def release - end + def release; end end diff --git a/spec/fixtures/integration_valid/BOOT-INF/lib/.gitignore b/spec/fixtures/integration_valid/BOOT-INF/lib/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/integration_valid/META-INF/MANIFEST.MF b/spec/fixtures/integration_valid/META-INF/MANIFEST.MF index 562a9b4e98..e5be5e0001 100644 --- a/spec/fixtures/integration_valid/META-INF/MANIFEST.MF +++ b/spec/fixtures/integration_valid/META-INF/MANIFEST.MF @@ -1,4 +1,3 @@ Manifest-Version: 1.0 -Class-Path: . -Main-Class: com.gopivotal.SimpleJava +Main-Class: io.pivotal.SimpleJava diff --git a/spec/fixtures/integration_valid/io/pivotal/SimpleJava.class b/spec/fixtures/integration_valid/io/pivotal/SimpleJava.class new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/integration_valid/system.properties b/spec/fixtures/integration_valid/system.properties index 3efecd69a7..5353d0d8b9 100644 --- a/spec/fixtures/integration_valid/system.properties +++ b/spec/fixtures/integration_valid/system.properties @@ -1 +1,18 @@ +# +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + java.runtime.version=1.7.0_+ diff --git a/spec/fixtures/java.security b/spec/fixtures/java.security new file mode 100644 index 0000000000..d82e0c58ea --- /dev/null +++ b/spec/fixtures/java.security @@ -0,0 +1,826 @@ +# +# This is the "master security properties file". +# +# An alternate java.security properties file may be specified +# from the command line via the system property +# +# -Djava.security.properties= +# +# This properties file appends to the master security properties file. +# If both properties files specify values for the same key, the value +# from the command-line properties file is selected, as it is the last +# one loaded. +# +# Also, if you specify +# +# -Djava.security.properties== (2 equals), +# +# then that properties file completely overrides the master security +# properties file. +# +# To disable the ability to specify an additional properties file from +# the command line, set the key security.overridePropertiesFile +# to false in the master security properties file. It is set to true +# by default. + +# In this file, various security properties are set for use by +# java.security classes. This is where users can statically register +# Cryptography Package Providers ("providers" for short). The term +# "provider" refers to a package or set of packages that supply a +# concrete implementation of a subset of the cryptography aspects of +# the Java Security API. A provider may, for example, implement one or +# more digital signature algorithms or message digest algorithms. +# +# Each provider must implement a subclass of the Provider class. +# To register a provider in this master security properties file, +# specify the Provider subclass name and priority in the format +# +# security.provider.= +# +# This declares a provider, and specifies its preference +# order n. The preference order is the order in which providers are +# searched for requested algorithms (when no specific provider is +# requested). The order is 1-based; 1 is the most preferred, followed +# by 2, and so on. +# +# must specify the subclass of the Provider class whose +# constructor sets the values of various properties that are required +# for the Java Security API to look up the algorithms or other +# facilities implemented by the provider. +# +# There must be at least one provider specification in java.security. +# There is a default provider that comes standard with the JDK. It +# is called the "SUN" provider, and its Provider subclass +# named Sun appears in the sun.security.provider package. Thus, the +# "SUN" provider is registered via the following: +# +# security.provider.1=sun.security.provider.Sun +# +# (The number 1 is used for the default provider.) +# +# Note: Providers can be dynamically registered instead by calls to +# either the addProvider or insertProviderAt method in the Security +# class. + +# +# List of providers and their preference orders (see above): +# +security.provider.10=apple.security.AppleProvider +security.provider.9=sun.security.smartcardio.SunPCSC +security.provider.8=org.jcp.xml.dsig.internal.dom.XMLDSigRI +security.provider.7=com.sun.security.sasl.Provider +security.provider.6=sun.security.jgss.SunProvider +security.provider.5=com.sun.crypto.provider.SunJCE +security.provider.4=com.sun.net.ssl.internal.ssl.Provider +security.provider.3=sun.security.ec.SunEC +security.provider.2=sun.security.rsa.SunRsaSign +security.provider.1=sun.security.provider.Sun + +# +# Sun Provider SecureRandom seed source. +# +# Select the primary source of seed data for the "SHA1PRNG" and +# "NativePRNG" SecureRandom implementations in the "Sun" provider. +# (Other SecureRandom implementations might also use this property.) +# +# On Unix-like systems (for example, Solaris/Linux/MacOS), the +# "NativePRNG" and "SHA1PRNG" implementations obtains seed data from +# special device files such as file:/dev/random. +# +# On Windows systems, specifying the URLs "file:/dev/random" or +# "file:/dev/urandom" will enable the native Microsoft CryptoAPI seeding +# mechanism for SHA1PRNG. +# +# By default, an attempt is made to use the entropy gathering device +# specified by the "securerandom.source" Security property. If an +# exception occurs while accessing the specified URL: +# +# SHA1PRNG: +# the traditional system/thread activity algorithm will be used. +# +# NativePRNG: +# a default value of /dev/random will be used. If neither +# are available, the implementation will be disabled. +# "file" is the only currently supported protocol type. +# +# The entropy gathering device can also be specified with the System +# property "java.security.egd". For example: +# +# % java -Djava.security.egd=file:/dev/random MainClass +# +# Specifying this System property will override the +# "securerandom.source" Security property. +# +# In addition, if "file:/dev/random" or "file:/dev/urandom" is +# specified, the "NativePRNG" implementation will be more preferred than +# SHA1PRNG in the Sun provider. +# +securerandom.source=file:/dev/random + +# +# A list of known strong SecureRandom implementations. +# +# To help guide applications in selecting a suitable strong +# java.security.SecureRandom implementation, Java distributions should +# indicate a list of known strong implementations using the property. +# +# This is a comma-separated list of algorithm and/or algorithm:provider +# entries. +# +securerandom.strongAlgorithms=NativePRNGBlocking:SUN + +# +# Class to instantiate as the javax.security.auth.login.Configuration +# provider. +# +login.configuration.provider=sun.security.provider.ConfigFile + +# +# Default login configuration file +# +#login.config.url.1=file:${user.home}/.java.login.config + +# +# Class to instantiate as the system Policy. This is the name of the class +# that will be used as the Policy object. +# +policy.provider=sun.security.provider.PolicyFile + +# The default is to have a single system-wide policy file, +# and a policy file in the user's home directory. +policy.url.1=file:${java.home}/lib/security/java.policy +policy.url.2=file:${user.home}/.java.policy + +# whether or not we expand properties in the policy file +# if this is set to false, properties (${...}) will not be expanded in policy +# files. +policy.expandProperties=true + +# whether or not we allow an extra policy to be passed on the command line +# with -Djava.security.policy=somefile. Comment out this line to disable +# this feature. +policy.allowSystemProperty=true + +# whether or not we look into the IdentityScope for trusted Identities +# when encountering a 1.1 signed JAR file. If the identity is found +# and is trusted, we grant it AllPermission. +policy.ignoreIdentityScope=false + +# +# Default keystore type. +# +keystore.type=jks + +# +# Controls compatibility mode for the JKS keystore type. +# +# When set to 'true', the JKS keystore type supports loading +# keystore files in either JKS or PKCS12 format. When set to 'false' +# it supports loading only JKS keystore files. +# +keystore.type.compat=true + +# +# List of comma-separated packages that start with or equal this string +# will cause a security exception to be thrown when +# passed to checkPackageAccess unless the +# corresponding RuntimePermission ("accessClassInPackage."+package) has +# been granted. +package.access=sun.,\ + com.sun.xml.internal.,\ + com.sun.imageio.,\ + com.sun.istack.internal.,\ + com.sun.jmx.,\ + com.sun.media.sound.,\ + com.sun.naming.internal.,\ + com.sun.proxy.,\ + com.sun.corba.se.,\ + com.sun.org.apache.bcel.internal.,\ + com.sun.org.apache.regexp.internal.,\ + com.sun.org.apache.xerces.internal.,\ + com.sun.org.apache.xpath.internal.,\ + com.sun.org.apache.xalan.internal.extensions.,\ + com.sun.org.apache.xalan.internal.lib.,\ + com.sun.org.apache.xalan.internal.res.,\ + com.sun.org.apache.xalan.internal.templates.,\ + com.sun.org.apache.xalan.internal.utils.,\ + com.sun.org.apache.xalan.internal.xslt.,\ + com.sun.org.apache.xalan.internal.xsltc.cmdline.,\ + com.sun.org.apache.xalan.internal.xsltc.compiler.,\ + com.sun.org.apache.xalan.internal.xsltc.trax.,\ + com.sun.org.apache.xalan.internal.xsltc.util.,\ + com.sun.org.apache.xml.internal.res.,\ + com.sun.org.apache.xml.internal.security.,\ + com.sun.org.apache.xml.internal.serializer.utils.,\ + com.sun.org.apache.xml.internal.utils.,\ + com.sun.org.glassfish.,\ + com.oracle.xmlns.internal.,\ + com.oracle.webservices.internal.,\ + oracle.jrockit.jfr.,\ + org.jcp.xml.dsig.internal.,\ + jdk.internal.,\ + jdk.nashorn.internal.,\ + jdk.nashorn.tools.,\ + com.sun.activation.registries.,\ + apple.,\ + com.sun.browser.,\ + com.sun.glass.,\ + com.sun.javafx.,\ + com.sun.media.,\ + com.sun.openpisces.,\ + com.sun.prism.,\ + com.sun.scenario.,\ + com.sun.t2k.,\ + com.sun.pisces.,\ + com.sun.webkit.,\ + jdk.management.resource.internal. + +# +# List of comma-separated packages that start with or equal this string +# will cause a security exception to be thrown when +# passed to checkPackageDefinition unless the +# corresponding RuntimePermission ("defineClassInPackage."+package) has +# been granted. +# +# by default, none of the class loaders supplied with the JDK call +# checkPackageDefinition. +# +package.definition=sun.,\ + com.sun.xml.internal.,\ + com.sun.imageio.,\ + com.sun.istack.internal.,\ + com.sun.jmx.,\ + com.sun.media.sound.,\ + com.sun.naming.internal.,\ + com.sun.proxy.,\ + com.sun.corba.se.,\ + com.sun.org.apache.bcel.internal.,\ + com.sun.org.apache.regexp.internal.,\ + com.sun.org.apache.xerces.internal.,\ + com.sun.org.apache.xpath.internal.,\ + com.sun.org.apache.xalan.internal.extensions.,\ + com.sun.org.apache.xalan.internal.lib.,\ + com.sun.org.apache.xalan.internal.res.,\ + com.sun.org.apache.xalan.internal.templates.,\ + com.sun.org.apache.xalan.internal.utils.,\ + com.sun.org.apache.xalan.internal.xslt.,\ + com.sun.org.apache.xalan.internal.xsltc.cmdline.,\ + com.sun.org.apache.xalan.internal.xsltc.compiler.,\ + com.sun.org.apache.xalan.internal.xsltc.trax.,\ + com.sun.org.apache.xalan.internal.xsltc.util.,\ + com.sun.org.apache.xml.internal.res.,\ + com.sun.org.apache.xml.internal.security.,\ + com.sun.org.apache.xml.internal.serializer.utils.,\ + com.sun.org.apache.xml.internal.utils.,\ + com.sun.org.glassfish.,\ + com.oracle.xmlns.internal.,\ + com.oracle.webservices.internal.,\ + oracle.jrockit.jfr.,\ + org.jcp.xml.dsig.internal.,\ + jdk.internal.,\ + jdk.nashorn.internal.,\ + jdk.nashorn.tools.,\ + com.sun.activation.registries.,\ + apple.,\ + com.sun.browser.,\ + com.sun.glass.,\ + com.sun.javafx.,\ + com.sun.media.,\ + com.sun.openpisces.,\ + com.sun.prism.,\ + com.sun.scenario.,\ + com.sun.t2k.,\ + com.sun.pisces.,\ + com.sun.webkit.,\ + jdk.management.resource.internal. + +# +# Determines whether this properties file can be appended to +# or overridden on the command line via -Djava.security.properties +# +security.overridePropertiesFile=true + +# +# Determines the default key and trust manager factory algorithms for +# the javax.net.ssl package. +# +ssl.KeyManagerFactory.algorithm=SunX509 +ssl.TrustManagerFactory.algorithm=PKIX + +# +# The Java-level namelookup cache policy for successful lookups: +# +# any negative value: caching forever +# any positive value: the number of seconds to cache an address for +# zero: do not cache +# +# default value is forever (FOREVER). For security reasons, this +# caching is made forever when a security manager is set. When a security +# manager is not set, the default behavior in this implementation +# is to cache for 30 seconds. +# +# NOTE: setting this to anything other than the default value can have +# serious security implications. Do not set it unless +# you are sure you are not exposed to DNS spoofing attack. +# +#networkaddress.cache.ttl=-1 + +# The Java-level namelookup cache policy for failed lookups: +# +# any negative value: cache forever +# any positive value: the number of seconds to cache negative lookup results +# zero: do not cache +# +# In some Microsoft Windows networking environments that employ +# the WINS name service in addition to DNS, name service lookups +# that fail may take a noticeably long time to return (approx. 5 seconds). +# For this reason the default caching policy is to maintain these +# results for 10 seconds. +# +# +networkaddress.cache.negative.ttl=10 + +# +# Properties to configure OCSP for certificate revocation checking +# + +# Enable OCSP +# +# By default, OCSP is not used for certificate revocation checking. +# This property enables the use of OCSP when set to the value "true". +# +# NOTE: SocketPermission is required to connect to an OCSP responder. +# +# Example, +# ocsp.enable=true + +# +# Location of the OCSP responder +# +# By default, the location of the OCSP responder is determined implicitly +# from the certificate being validated. This property explicitly specifies +# the location of the OCSP responder. The property is used when the +# Authority Information Access extension (defined in RFC 3280) is absent +# from the certificate or when it requires overriding. +# +# Example, +# ocsp.responderURL=http://ocsp.example.net:80 + +# +# Subject name of the OCSP responder's certificate +# +# By default, the certificate of the OCSP responder is that of the issuer +# of the certificate being validated. This property identifies the certificate +# of the OCSP responder when the default does not apply. Its value is a string +# distinguished name (defined in RFC 2253) which identifies a certificate in +# the set of certificates supplied during cert path validation. In cases where +# the subject name alone is not sufficient to uniquely identify the certificate +# then both the "ocsp.responderCertIssuerName" and +# "ocsp.responderCertSerialNumber" properties must be used instead. When this +# property is set then those two properties are ignored. +# +# Example, +# ocsp.responderCertSubjectName="CN=OCSP Responder, O=XYZ Corp" + +# +# Issuer name of the OCSP responder's certificate +# +# By default, the certificate of the OCSP responder is that of the issuer +# of the certificate being validated. This property identifies the certificate +# of the OCSP responder when the default does not apply. Its value is a string +# distinguished name (defined in RFC 2253) which identifies a certificate in +# the set of certificates supplied during cert path validation. When this +# property is set then the "ocsp.responderCertSerialNumber" property must also +# be set. When the "ocsp.responderCertSubjectName" property is set then this +# property is ignored. +# +# Example, +# ocsp.responderCertIssuerName="CN=Enterprise CA, O=XYZ Corp" + +# +# Serial number of the OCSP responder's certificate +# +# By default, the certificate of the OCSP responder is that of the issuer +# of the certificate being validated. This property identifies the certificate +# of the OCSP responder when the default does not apply. Its value is a string +# of hexadecimal digits (colon or space separators may be present) which +# identifies a certificate in the set of certificates supplied during cert path +# validation. When this property is set then the "ocsp.responderCertIssuerName" +# property must also be set. When the "ocsp.responderCertSubjectName" property +# is set then this property is ignored. +# +# Example, +# ocsp.responderCertSerialNumber=2A:FF:00 + +# +# Policy for failed Kerberos KDC lookups: +# +# When a KDC is unavailable (network error, service failure, etc), it is +# put inside a blacklist and accessed less often for future requests. The +# value (case-insensitive) for this policy can be: +# +# tryLast +# KDCs in the blacklist are always tried after those not on the list. +# +# tryLess[:max_retries,timeout] +# KDCs in the blacklist are still tried by their order in the configuration, +# but with smaller max_retries and timeout values. max_retries and timeout +# are optional numerical parameters (default 1 and 5000, which means once +# and 5 seconds). Please notes that if any of the values defined here is +# more than what is defined in krb5.conf, it will be ignored. +# +# Whenever a KDC is detected as available, it is removed from the blacklist. +# The blacklist is reset when krb5.conf is reloaded. You can add +# refreshKrb5Config=true to a JAAS configuration file so that krb5.conf is +# reloaded whenever a JAAS authentication is attempted. +# +# Example, +# krb5.kdc.bad.policy = tryLast +# krb5.kdc.bad.policy = tryLess:2,2000 +krb5.kdc.bad.policy = tryLast + +# Algorithm restrictions for certification path (CertPath) processing +# +# In some environments, certain algorithms or key lengths may be undesirable +# for certification path building and validation. For example, "MD2" is +# generally no longer considered to be a secure hash algorithm. This section +# describes the mechanism for disabling algorithms based on algorithm name +# and/or key length. This includes algorithms used in certificates, as well +# as revocation information such as CRLs and signed OCSP Responses. +# +# The syntax of the disabled algorithm string is described as this Java +# BNF-style: +# DisabledAlgorithms: +# " DisabledAlgorithm { , DisabledAlgorithm } " +# +# DisabledAlgorithm: +# AlgorithmName [Constraint] { '&' Constraint } +# +# AlgorithmName: +# (see below) +# +# Constraint: +# KeySizeConstraint, CertConstraint +# +# KeySizeConstraint: +# keySize Operator DecimalInteger +# +# Operator: +# <= | < | == | != | >= | > +# +# DecimalInteger: +# DecimalDigits +# +# DecimalDigits: +# DecimalDigit {DecimalDigit} +# +# DecimalDigit: one of +# 1 2 3 4 5 6 7 8 9 0 +# +# CertConstraint +# jdkCA +# +# The "AlgorithmName" is the standard algorithm name of the disabled +# algorithm. See "Java Cryptography Architecture Standard Algorithm Name +# Documentation" for information about Standard Algorithm Names. Matching +# is performed using a case-insensitive sub-element matching rule. (For +# example, in "SHA1withECDSA" the sub-elements are "SHA1" for hashing and +# "ECDSA" for signatures.) If the assertion "AlgorithmName" is a +# sub-element of the certificate algorithm name, the algorithm will be +# rejected during certification path building and validation. For example, +# the assertion algorithm name "DSA" will disable all certificate algorithms +# that rely on DSA, such as NONEwithDSA, SHA1withDSA. However, the assertion +# will not disable algorithms related to "ECDSA". +# +# A "Constraint" provides further guidance for the algorithm being specified. +# The "KeySizeConstraint" requires a key of a valid size range if the +# "AlgorithmName" is of a key algorithm. The "DecimalInteger" indicates the +# key size specified in number of bits. For example, "RSA keySize <= 1024" +# indicates that any RSA key with key size less than or equal to 1024 bits +# should be disabled, and "RSA keySize < 1024, RSA keySize > 2048" indicates +# that any RSA key with key size less than 1024 or greater than 2048 should +# be disabled. Note that the "KeySizeConstraint" only makes sense to key +# algorithms. +# +# "CertConstraint" specifies additional constraints for +# certificates that contain algorithms that are restricted: +# +# "jdkCA" prohibits the specified algorithm only if the algorithm is used +# in a certificate chain that terminates at a marked trust anchor in the +# lib/security/cacerts keystore. All other chains are not affected. +# If the jdkCA constraint is not set, then all chains using the +# specified algorithm are restricted. jdkCA may only be used once in +# a DisabledAlgorithm expression. +# Example: To apply this constraint to SHA-1 certificates, include +# the following: "SHA1 jdkCA" +# +# When an algorithm must satisfy more than one constraint, it must be +# delimited by an ampersand '&'. For example, to restrict certificates in a +# chain that terminate at a distribution provided trust anchor and contain +# RSA keys that are less than or equal to 1024 bits, add the following +# constraint: "RSA keySize <= 1024 & jdkCA". +# +# All DisabledAlgorithms expressions are processed in the order defined in the +# property. This requires lower keysize constraints to be specified +# before larger keysize constraints of the same algorithm. For example: +# "RSA keySize < 1024 & jdkCA, RSA keySize < 2048". +# +# Note: This property is currently used by Oracle's PKIX implementation. It +# is not guaranteed to be examined and used by other implementations. +# +# Example: +# jdk.certpath.disabledAlgorithms=MD2, DSA, RSA keySize < 2048 +# +# +jdk.certpath.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, \ + DSA keySize < 1024, EC keySize < 224 + +# Algorithm restrictions for signed JAR files +# +# In some environments, certain algorithms or key lengths may be undesirable +# for signed JAR validation. For example, "MD2" is generally no longer +# considered to be a secure hash algorithm. This section describes the +# mechanism for disabling algorithms based on algorithm name and/or key length. +# JARs signed with any of the disabled algorithms or key sizes will be treated +# as unsigned. +# +# The syntax of the disabled algorithm string is described as follows: +# DisabledAlgorithms: +# " DisabledAlgorithm { , DisabledAlgorithm } " +# +# DisabledAlgorithm: +# AlgorithmName [Constraint] +# +# AlgorithmName: +# (see below) +# +# Constraint: +# KeySizeConstraint +# +# KeySizeConstraint: +# keySize Operator KeyLength +# +# Operator: +# <= | < | == | != | >= | > +# +# KeyLength: +# Integer value of the algorithm's key length in bits +# +# Note: This property is currently used by the JDK Reference +# implementation. It is not guaranteed to be examined and used by other +# implementations. +# +jdk.jar.disabledAlgorithms=MD2, MD5, RSA keySize < 1024 + +# Algorithm restrictions for Secure Socket Layer/Transport Layer Security +# (SSL/TLS) processing +# +# In some environments, certain algorithms or key lengths may be undesirable +# when using SSL/TLS. This section describes the mechanism for disabling +# algorithms during SSL/TLS security parameters negotiation, including +# protocol version negotiation, cipher suites selection, peer authentication +# and key exchange mechanisms. +# +# Disabled algorithms will not be negotiated for SSL/TLS connections, even +# if they are enabled explicitly in an application. +# +# For PKI-based peer authentication and key exchange mechanisms, this list +# of disabled algorithms will also be checked during certification path +# building and validation, including algorithms used in certificates, as +# well as revocation information such as CRLs and signed OCSP Responses. +# This is in addition to the jdk.certpath.disabledAlgorithms property above. +# +# See the specification of "jdk.certpath.disabledAlgorithms" for the +# syntax of the disabled algorithm string. +# +# Note: This property is currently used by the JDK Reference implementation. +# It is not guaranteed to be examined and used by other implementations. +# +# Example: +# jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048 +jdk.tls.disabledAlgorithms=SSLv3, RC4, MD5withRSA, DH keySize < 768, \ + EC keySize < 224 + +# Legacy algorithms for Secure Socket Layer/Transport Layer Security (SSL/TLS) +# processing in JSSE implementation. +# +# In some environments, a certain algorithm may be undesirable but it +# cannot be disabled because of its use in legacy applications. Legacy +# algorithms may still be supported, but applications should not use them +# as the security strength of legacy algorithms are usually not strong enough +# in practice. +# +# During SSL/TLS security parameters negotiation, legacy algorithms will +# not be negotiated unless there are no other candidates. +# +# The syntax of the legacy algorithms string is described as this Java +# BNF-style: +# LegacyAlgorithms: +# " LegacyAlgorithm { , LegacyAlgorithm } " +# +# LegacyAlgorithm: +# AlgorithmName (standard JSSE algorithm name) +# +# See the specification of security property "jdk.certpath.disabledAlgorithms" +# for the syntax and description of the "AlgorithmName" notation. +# +# Per SSL/TLS specifications, cipher suites have the form: +# SSL_KeyExchangeAlg_WITH_CipherAlg_MacAlg +# or +# TLS_KeyExchangeAlg_WITH_CipherAlg_MacAlg +# +# For example, the cipher suite TLS_RSA_WITH_AES_128_CBC_SHA uses RSA as the +# key exchange algorithm, AES_128_CBC (128 bits AES cipher algorithm in CBC +# mode) as the cipher (encryption) algorithm, and SHA-1 as the message digest +# algorithm for HMAC. +# +# The LegacyAlgorithm can be one of the following standard algorithm names: +# 1. JSSE cipher suite name, e.g., TLS_RSA_WITH_AES_128_CBC_SHA +# 2. JSSE key exchange algorithm name, e.g., RSA +# 3. JSSE cipher (encryption) algorithm name, e.g., AES_128_CBC +# 4. JSSE message digest algorithm name, e.g., SHA +# +# See SSL/TLS specifications and "Java Cryptography Architecture Standard +# Algorithm Name Documentation" for information about the algorithm names. +# +# Note: This property is currently used by the JDK Reference implementation. +# It is not guaranteed to be examined and used by other implementations. +# There is no guarantee the property will continue to exist or be of the +# same syntax in future releases. +# +# Example: +# jdk.tls.legacyAlgorithms=DH_anon, DES_CBC, SSL_RSA_WITH_RC4_128_MD5 +# +jdk.tls.legacyAlgorithms= \ + K_NULL, C_NULL, M_NULL, \ + DHE_DSS_EXPORT, DHE_RSA_EXPORT, DH_anon_EXPORT, DH_DSS_EXPORT, \ + DH_RSA_EXPORT, RSA_EXPORT, \ + DH_anon, ECDH_anon, \ + RC4_128, RC4_40, DES_CBC, DES40_CBC, \ + 3DES_EDE_CBC + +# The pre-defined default finite field Diffie-Hellman ephemeral (DHE) +# parameters for Transport Layer Security (SSL/TLS/DTLS) processing. +# +# In traditional SSL/TLS/DTLS connections where finite field DHE parameters +# negotiation mechanism is not used, the server offers the client group +# parameters, base generator g and prime modulus p, for DHE key exchange. +# It is recommended to use dynamic group parameters. This property defines +# a mechanism that allows you to specify custom group parameters. +# +# The syntax of this property string is described as this Java BNF-style: +# DefaultDHEParameters: +# DefinedDHEParameters { , DefinedDHEParameters } +# +# DefinedDHEParameters: +# "{" DHEPrimeModulus , DHEBaseGenerator "}" +# +# DHEPrimeModulus: +# HexadecimalDigits +# +# DHEBaseGenerator: +# HexadecimalDigits +# +# HexadecimalDigits: +# HexadecimalDigit { HexadecimalDigit } +# +# HexadecimalDigit: one of +# 0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f +# +# Whitespace characters are ignored. +# +# The "DefinedDHEParameters" defines the custom group parameters, prime +# modulus p and base generator g, for a particular size of prime modulus p. +# The "DHEPrimeModulus" defines the hexadecimal prime modulus p, and the +# "DHEBaseGenerator" defines the hexadecimal base generator g of a group +# parameter. It is recommended to use safe primes for the custom group +# parameters. +# +# If this property is not defined or the value is empty, the underlying JSSE +# provider's default group parameter is used for each connection. +# +# If the property value does not follow the grammar, or a particular group +# parameter is not valid, the connection will fall back and use the +# underlying JSSE provider's default group parameter. +# +# Note: This property is currently used by OpenJDK's JSSE implementation. It +# is not guaranteed to be examined and used by other implementations. +# +# Example: +# jdk.tls.server.defaultDHEParameters= +# { \ +# FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 \ +# 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD \ +# EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 \ +# E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED \ +# EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381 \ +# FFFFFFFF FFFFFFFF, 2} + +# +# The policy for the XML Signature secure validation mode. The mode is +# enabled by setting the property "org.jcp.xml.dsig.secureValidation" to +# true with the javax.xml.crypto.XMLCryptoContext.setProperty() method, +# or by running the code with a SecurityManager. +# +# Policy: +# Constraint {"," Constraint } +# Constraint: +# AlgConstraint | MaxTransformsConstraint | MaxReferencesConstraint | +# ReferenceUriSchemeConstraint | KeySizeConstraint | OtherConstraint +# AlgConstraint +# "disallowAlg" Uri +# MaxTransformsConstraint: +# "maxTransforms" Integer +# MaxReferencesConstraint: +# "maxReferences" Integer +# ReferenceUriSchemeConstraint: +# "disallowReferenceUriSchemes" String { String } +# KeySizeConstraint: +# "minKeySize" KeyAlg Integer +# OtherConstraint: +# "noDuplicateIds" | "noRetrievalMethodLoops" +# +# For AlgConstraint, Uri is the algorithm URI String that is not allowed. +# See the XML Signature Recommendation for more information on algorithm +# URI Identifiers. For KeySizeConstraint, KeyAlg is the standard algorithm +# name of the key type (ex: "RSA"). If the MaxTransformsConstraint, +# MaxReferencesConstraint or KeySizeConstraint (for the same key type) is +# specified more than once, only the last entry is enforced. +# +# Note: This property is currently used by the JDK Reference implementation. It +# is not guaranteed to be examined and used by other implementations. +# +jdk.xml.dsig.secureValidationPolicy=\ + disallowAlg http://www.w3.org/TR/1999/REC-xslt-19991116,\ + disallowAlg http://www.w3.org/2001/04/xmldsig-more#rsa-md5,\ + disallowAlg http://www.w3.org/2001/04/xmldsig-more#hmac-md5,\ + disallowAlg http://www.w3.org/2001/04/xmldsig-more#md5,\ + maxTransforms 5,\ + maxReferences 30,\ + disallowReferenceUriSchemes file http https,\ + minKeySize RSA 1024,\ + minKeySize DSA 1024,\ + noDuplicateIds,\ + noRetrievalMethodLoops + +# +# Serialization process-wide filter +# +# A filter, if configured, is used by java.io.ObjectInputStream during +# deserialization to check the contents of the stream. +# A filter is configured as a sequence of patterns, each pattern is either +# matched against the name of a class in the stream or defines a limit. +# Patterns are separated by ";" (semicolon). +# Whitespace is significant and is considered part of the pattern. +# +# If a pattern includes a "=", it sets a limit. +# If a limit appears more than once the last value is used. +# Limits are checked before classes regardless of the order in the sequence of patterns. +# If any of the limits are exceeded, the filter status is REJECTED. +# +# maxdepth=value - the maximum depth of a graph +# maxrefs=value - the maximum number of internal references +# maxbytes=value - the maximum number of bytes in the input stream +# maxarray=value - the maximum array length allowed +# +# Other patterns, from left to right, match the class or package name as +# returned from Class.getName. +# If the class is an array type, the class or package to be matched is the element type. +# Arrays of any number of dimensions are treated the same as the element type. +# For example, a pattern of "!example.Foo", rejects creation of any instance or +# array of example.Foo. +# +# If the pattern starts with "!", the status is REJECTED if the remaining pattern +# is matched; otherwise the status is ALLOWED if the pattern matches. +# If the pattern ends with ".**" it matches any class in the package and all subpackages. +# If the pattern ends with ".*" it matches any class in the package. +# If the pattern ends with "*", it matches any class with the pattern as a prefix. +# If the pattern is equal to the class name, it matches. +# Otherwise, the status is UNDECIDED. +# +#jdk.serialFilter=pattern;pattern + +# +# RMI Registry Serial Filter +# +# The filter pattern uses the same format as jdk.serialFilter. +# This filter can override the builtin filter if additional types need to be +# allowed or rejected from the RMI Registry. +# +#sun.rmi.registry.registryFilter=pattern;pattern + +# +# RMI Distributed Garbage Collector (DGC) Serial Filter +# +# The filter pattern uses the same format as jdk.serialFilter. +# This filter can override the builtin filter if additional types need to be +# allowed or rejected from the RMI DGC. +# +# The builtin DGC filter can approximately be represented as the filter pattern: +# +#sun.rmi.transport.dgcFilter=\ +# java.rmi.server.ObjID;\ +# java.rmi.server.UID;\ +# java.rmi.dgc.VMID;\ +# java.rmi.dgc.Lease;\ +# maxdepth=5;maxarray=10000 diff --git a/spec/fixtures/jre_memory_calculator_application/bat.class b/spec/fixtures/jre_memory_calculator_application/bat.class new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/jre_memory_calculator_application/foo.class_with_suffix b/spec/fixtures/jre_memory_calculator_application/foo.class_with_suffix new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/jre_memory_calculator_application/stub-library.jar b/spec/fixtures/jre_memory_calculator_application/stub-library.jar new file mode 100644 index 0000000000..2ef99768fc Binary files /dev/null and b/spec/fixtures/jre_memory_calculator_application/stub-library.jar differ diff --git a/spec/fixtures/jre_memory_calculator_jar_directory/dependency.jar/.gitkeep b/spec/fixtures/jre_memory_calculator_jar_directory/dependency.jar/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/root_libs/test-jar-3.jar b/spec/fixtures/root_libs/test-jar-3.jar new file mode 100644 index 0000000000..8b0d26227e Binary files /dev/null and b/spec/fixtures/root_libs/test-jar-3.jar differ diff --git a/spec/fixtures/root_libs/test-jar-4.jar b/spec/fixtures/root_libs/test-jar-4.jar new file mode 100644 index 0000000000..8b0d26227e Binary files /dev/null and b/spec/fixtures/root_libs/test-jar-4.jar differ diff --git a/spec/fixtures/stub-app-dynamics-agent.zip b/spec/fixtures/stub-app-dynamics-agent.zip index a973e1e9dd..5e879a4c58 100644 Binary files a/spec/fixtures/stub-app-dynamics-agent.zip and b/spec/fixtures/stub-app-dynamics-agent.zip differ diff --git a/spec/fixtures/stub-azure-application-insights-agent.jar b/spec/fixtures/stub-azure-application-insights-agent.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/stub-checkmarx-agent.zip b/spec/fixtures/stub-checkmarx-agent.zip new file mode 100644 index 0000000000..853148a7d6 Binary files /dev/null and b/spec/fixtures/stub-checkmarx-agent.zip differ diff --git a/spec/fixtures/stub-client-certificate-mapper.jar b/spec/fixtures/stub-client-certificate-mapper.jar new file mode 100644 index 0000000000..0878c3ccb5 Binary files /dev/null and b/spec/fixtures/stub-client-certificate-mapper.jar differ diff --git a/spec/fixtures/stub-container-customizer.jar b/spec/fixtures/stub-container-customizer.jar new file mode 100644 index 0000000000..0878c3ccb5 Binary files /dev/null and b/spec/fixtures/stub-container-customizer.jar differ diff --git a/spec/fixtures/stub-container-security-provider.jar b/spec/fixtures/stub-container-security-provider.jar new file mode 100644 index 0000000000..0878c3ccb5 Binary files /dev/null and b/spec/fixtures/stub-container-security-provider.jar differ diff --git a/spec/fixtures/stub-contrast-security-agent.jar b/spec/fixtures/stub-contrast-security-agent.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/stub-datadog-javaagent.jar b/spec/fixtures/stub-datadog-javaagent.jar new file mode 100644 index 0000000000..1f33a1b6a1 Binary files /dev/null and b/spec/fixtures/stub-datadog-javaagent.jar differ diff --git a/spec/fixtures/stub-download.bin b/spec/fixtures/stub-download.bin new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/stub-dynatrace-appmon-agent.jar b/spec/fixtures/stub-dynatrace-appmon-agent.jar new file mode 100644 index 0000000000..5586734456 Binary files /dev/null and b/spec/fixtures/stub-dynatrace-appmon-agent.jar differ diff --git a/spec/fixtures/stub-dynatrace-one-agent.zip b/spec/fixtures/stub-dynatrace-one-agent.zip new file mode 100644 index 0000000000..b05d392798 Binary files /dev/null and b/spec/fixtures/stub-dynatrace-one-agent.zip differ diff --git a/spec/fixtures/stub-elastic-apm-agent.jar b/spec/fixtures/stub-elastic-apm-agent.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/stub-gemfire-modules-tomcat7.jar b/spec/fixtures/stub-gemfire-modules-tomcat7.jar deleted file mode 100644 index 55f9148b70..0000000000 Binary files a/spec/fixtures/stub-gemfire-modules-tomcat7.jar and /dev/null differ diff --git a/spec/fixtures/stub-gemfire-modules.jar b/spec/fixtures/stub-gemfire-modules.jar deleted file mode 100644 index 55f9148b70..0000000000 Binary files a/spec/fixtures/stub-gemfire-modules.jar and /dev/null differ diff --git a/spec/fixtures/stub-gemfire-security.jar b/spec/fixtures/stub-gemfire-security.jar deleted file mode 100644 index 55f9148b70..0000000000 Binary files a/spec/fixtures/stub-gemfire-security.jar and /dev/null differ diff --git a/spec/fixtures/stub-gemfire-slf4j-api.jar b/spec/fixtures/stub-gemfire-slf4j-api.jar deleted file mode 100644 index 55f9148b70..0000000000 Binary files a/spec/fixtures/stub-gemfire-slf4j-api.jar and /dev/null differ diff --git a/spec/fixtures/stub-gemfire-slf4j-jdk14.jar b/spec/fixtures/stub-gemfire-slf4j-jdk14.jar deleted file mode 100644 index 55f9148b70..0000000000 Binary files a/spec/fixtures/stub-gemfire-slf4j-jdk14.jar and /dev/null differ diff --git a/spec/fixtures/stub-gemfire.jar b/spec/fixtures/stub-gemfire.jar deleted file mode 100644 index 55f9148b70..0000000000 Binary files a/spec/fixtures/stub-gemfire.jar and /dev/null differ diff --git a/spec/fixtures/stub-geode-store-no-geode-tomcat.tar b/spec/fixtures/stub-geode-store-no-geode-tomcat.tar new file mode 100644 index 0000000000..3f7757e8a8 Binary files /dev/null and b/spec/fixtures/stub-geode-store-no-geode-tomcat.tar differ diff --git a/spec/fixtures/stub-geode-store-no-tomcat-version.tar b/spec/fixtures/stub-geode-store-no-tomcat-version.tar new file mode 100644 index 0000000000..89b9b75cd9 Binary files /dev/null and b/spec/fixtures/stub-geode-store-no-tomcat-version.tar differ diff --git a/spec/fixtures/stub-geode-store-tomcat-multi-version.tar b/spec/fixtures/stub-geode-store-tomcat-multi-version.tar new file mode 100644 index 0000000000..dc694f6332 Binary files /dev/null and b/spec/fixtures/stub-geode-store-tomcat-multi-version.tar differ diff --git a/spec/fixtures/stub-geode-store.tar b/spec/fixtures/stub-geode-store.tar new file mode 100644 index 0000000000..be8271f25b Binary files /dev/null and b/spec/fixtures/stub-geode-store.tar differ diff --git a/spec/fixtures/stub-google-stackdriver-debugger.tar.gz b/spec/fixtures/stub-google-stackdriver-debugger.tar.gz new file mode 100644 index 0000000000..8fb7fc3e92 Binary files /dev/null and b/spec/fixtures/stub-google-stackdriver-debugger.tar.gz differ diff --git a/spec/fixtures/stub-google-stackdriver-profiler.tar.gz b/spec/fixtures/stub-google-stackdriver-profiler.tar.gz new file mode 100644 index 0000000000..6f4deae0b1 Binary files /dev/null and b/spec/fixtures/stub-google-stackdriver-profiler.tar.gz differ diff --git a/spec/fixtures/stub-insight-agent.jar b/spec/fixtures/stub-insight-agent.jar index 6148c8743f..c5ecfa11d0 100644 Binary files a/spec/fixtures/stub-insight-agent.jar and b/spec/fixtures/stub-insight-agent.jar differ diff --git a/spec/fixtures/stub-introscope-agent.tar b/spec/fixtures/stub-introscope-agent.tar new file mode 100644 index 0000000000..4e95eecd73 Binary files /dev/null and b/spec/fixtures/stub-introscope-agent.tar differ diff --git a/spec/fixtures/stub-jacoco-agent.jar b/spec/fixtures/stub-jacoco-agent.jar new file mode 100644 index 0000000000..f7da890c62 Binary files /dev/null and b/spec/fixtures/stub-jacoco-agent.jar differ diff --git a/spec/fixtures/stub-java-cfenv.jar b/spec/fixtures/stub-java-cfenv.jar new file mode 100644 index 0000000000..0878c3ccb5 Binary files /dev/null and b/spec/fixtures/stub-java-cfenv.jar differ diff --git a/spec/fixtures/stub-java-memory-assistant-cleanup.zip b/spec/fixtures/stub-java-memory-assistant-cleanup.zip new file mode 100644 index 0000000000..b7a42c4d25 Binary files /dev/null and b/spec/fixtures/stub-java-memory-assistant-cleanup.zip differ diff --git a/spec/fixtures/stub-java.bin b/spec/fixtures/stub-java.bin new file mode 100755 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/stub-jprofiler-profiler.tar.gz b/spec/fixtures/stub-jprofiler-profiler.tar.gz new file mode 100644 index 0000000000..535bd3505e Binary files /dev/null and b/spec/fixtures/stub-jprofiler-profiler.tar.gz differ diff --git a/spec/fixtures/stub-jrebel-archive.zip b/spec/fixtures/stub-jrebel-archive.zip new file mode 100644 index 0000000000..f296cbd124 Binary files /dev/null and b/spec/fixtures/stub-jrebel-archive.zip differ diff --git a/spec/fixtures/stub-jvmkill-agent b/spec/fixtures/stub-jvmkill-agent new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/stub-luna-security-provider.tar b/spec/fixtures/stub-luna-security-provider.tar new file mode 100644 index 0000000000..440fd9064c Binary files /dev/null and b/spec/fixtures/stub-luna-security-provider.tar differ diff --git a/spec/fixtures/stub-memory-calculator.tar.gz b/spec/fixtures/stub-memory-calculator.tar.gz new file mode 100644 index 0000000000..8719119c57 Binary files /dev/null and b/spec/fixtures/stub-memory-calculator.tar.gz differ diff --git a/spec/fixtures/stub-metric-writer.jar b/spec/fixtures/stub-metric-writer.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/stub-new-relic-extensions.tar.gz b/spec/fixtures/stub-new-relic-extensions.tar.gz new file mode 100644 index 0000000000..91cfd187ea Binary files /dev/null and b/spec/fixtures/stub-new-relic-extensions.tar.gz differ diff --git a/spec/fixtures/stub-protect-app-security-provider.zip b/spec/fixtures/stub-protect-app-security-provider.zip new file mode 100644 index 0000000000..0ae4e7d3fb Binary files /dev/null and b/spec/fixtures/stub-protect-app-security-provider.zip differ diff --git a/spec/fixtures/stub-riverbed-appinternals-agent.zip b/spec/fixtures/stub-riverbed-appinternals-agent.zip new file mode 100644 index 0000000000..24e85d6504 Binary files /dev/null and b/spec/fixtures/stub-riverbed-appinternals-agent.zip differ diff --git a/spec/fixtures/stub-seeker-agent.zip b/spec/fixtures/stub-seeker-agent.zip new file mode 100644 index 0000000000..7020550543 Binary files /dev/null and b/spec/fixtures/stub-seeker-agent.zip differ diff --git a/spec/fixtures/stub-skywalking-agent.tar.gz b/spec/fixtures/stub-skywalking-agent.tar.gz new file mode 100644 index 0000000000..8f9eb201d7 Binary files /dev/null and b/spec/fixtures/stub-skywalking-agent.tar.gz differ diff --git a/spec/fixtures/stub-splunk-otel-javaagent.jar b/spec/fixtures/stub-splunk-otel-javaagent.jar new file mode 100644 index 0000000000..0878c3ccb5 Binary files /dev/null and b/spec/fixtures/stub-splunk-otel-javaagent.jar differ diff --git a/spec/fixtures/stub-takipi-agent.tar.gz b/spec/fixtures/stub-takipi-agent.tar.gz new file mode 100644 index 0000000000..707a6a4e43 Binary files /dev/null and b/spec/fixtures/stub-takipi-agent.tar.gz differ diff --git a/spec/fixtures/stub-tomcat-external-configuration.tar.gz b/spec/fixtures/stub-tomcat-external-configuration.tar.gz new file mode 100644 index 0000000000..770e1451a1 Binary files /dev/null and b/spec/fixtures/stub-tomcat-external-configuration.tar.gz differ diff --git a/spec/fixtures/stub-your-kit-profiler.so b/spec/fixtures/stub-your-kit-profiler.so new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/fixtures/test.properties b/spec/fixtures/test.properties index 061559c121..702246039c 100644 --- a/spec/fixtures/test.properties +++ b/spec/fixtures/test.properties @@ -1,4 +1,21 @@ +# +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + # Comment line ! Comment line 2 alpha=bravo diff --git a/spec/fixtures/web_root_existing_params_after.xml b/spec/fixtures/web_root_existing_params_after.xml deleted file mode 100644 index 1f25a169e4..0000000000 --- a/spec/fixtures/web_root_existing_params_after.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - org.springframework.web.context.ContextLoaderListener - - - contextInitializerClasses - - com.gopivotal.test,org.cloudfoundry.reconfiguration.spring.CloudProfileApplicationContextInitializer,org.cloudfoundry.reconfiguration.spring.CloudPropertySourceApplicationContextInitializer,org.cloudfoundry.reconfiguration.spring.CloudAutoReconfigurationApplicationContextInitializer - - - diff --git a/spec/fixtures/web_root_existing_params_before.xml b/spec/fixtures/web_root_existing_params_before.xml deleted file mode 100644 index 9bc681cad4..0000000000 --- a/spec/fixtures/web_root_existing_params_before.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - org.springframework.web.context.ContextLoaderListener - - - - - - contextInitializerClasses - - - com.gopivotal.test - - - - diff --git a/spec/fixtures/web_root_no_contextLoaderListener_after.xml b/spec/fixtures/web_root_no_contextLoaderListener_after.xml deleted file mode 100644 index d5a8f1d0b1..0000000000 --- a/spec/fixtures/web_root_no_contextLoaderListener_after.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/spec/fixtures/web_root_no_contextLoaderListener_before.xml b/spec/fixtures/web_root_no_contextLoaderListener_before.xml deleted file mode 100644 index a947783186..0000000000 --- a/spec/fixtures/web_root_no_contextLoaderListener_before.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/spec/fixtures/web_root_no_params_after.xml b/spec/fixtures/web_root_no_params_after.xml deleted file mode 100644 index 3d8e3b5d3e..0000000000 --- a/spec/fixtures/web_root_no_params_after.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - org.springframework.web.context.ContextLoaderListener - - - contextInitializerClasses - - org.cloudfoundry.reconfiguration.spring.CloudProfileApplicationContextInitializer,org.cloudfoundry.reconfiguration.spring.CloudPropertySourceApplicationContextInitializer,org.cloudfoundry.reconfiguration.spring.CloudAutoReconfigurationApplicationContextInitializer - - - diff --git a/spec/fixtures/web_root_no_params_before.xml b/spec/fixtures/web_root_no_params_before.xml deleted file mode 100644 index 69cc055522..0000000000 --- a/spec/fixtures/web_root_no_params_before.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - org.springframework.web.context.ContextLoaderListener - - - - diff --git a/spec/fixtures/web_servlet_existing_params_after.xml b/spec/fixtures/web_servlet_existing_params_after.xml deleted file mode 100644 index 51900f40ad..0000000000 --- a/spec/fixtures/web_servlet_existing_params_after.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - test - org.springframework.web.servlet.DispatcherServlet - - contextInitializerClasses - - com.gopivotal.test,org.cloudfoundry.reconfiguration.spring.CloudProfileApplicationContextInitializer,org.cloudfoundry.reconfiguration.spring.CloudPropertySourceApplicationContextInitializer,org.cloudfoundry.reconfiguration.spring.CloudAutoReconfigurationApplicationContextInitializer - - - - diff --git a/spec/fixtures/web_servlet_existing_params_before.xml b/spec/fixtures/web_servlet_existing_params_before.xml deleted file mode 100644 index b73ce8dccd..0000000000 --- a/spec/fixtures/web_servlet_existing_params_before.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - test - org.springframework.web.servlet.DispatcherServlet - - - contextInitializerClasses - - - com.gopivotal.test - - - - - diff --git a/spec/fixtures/web_servlet_no_DispatcherServlet_after.xml b/spec/fixtures/web_servlet_no_DispatcherServlet_after.xml deleted file mode 100644 index d2ccf44eac..0000000000 --- a/spec/fixtures/web_servlet_no_DispatcherServlet_after.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - test - com.gopivotal.test.Servlet - - diff --git a/spec/fixtures/web_servlet_no_DispatcherServlet_before.xml b/spec/fixtures/web_servlet_no_DispatcherServlet_before.xml deleted file mode 100644 index 9059b4dd6b..0000000000 --- a/spec/fixtures/web_servlet_no_DispatcherServlet_before.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - test - com.gopivotal.test.Servlet - - - diff --git a/spec/fixtures/web_servlet_no_params_after.xml b/spec/fixtures/web_servlet_no_params_after.xml deleted file mode 100644 index 10c83c1ecf..0000000000 --- a/spec/fixtures/web_servlet_no_params_after.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - test - org.springframework.web.servlet.DispatcherServlet - - contextInitializerClasses - - org.cloudfoundry.reconfiguration.spring.CloudProfileApplicationContextInitializer,org.cloudfoundry.reconfiguration.spring.CloudPropertySourceApplicationContextInitializer,org.cloudfoundry.reconfiguration.spring.CloudAutoReconfigurationApplicationContextInitializer - - - - diff --git a/spec/fixtures/web_servlet_no_params_before.xml b/spec/fixtures/web_servlet_no_params_before.xml deleted file mode 100644 index 763ba5d89a..0000000000 --- a/spec/fixtures/web_servlet_no_params_before.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - test - org.springframework.web.servlet.DispatcherServlet - - - diff --git a/spec/integration_helper.rb b/spec/integration_helper.rb index 8865e9621e..b772231887 100644 --- a/spec/integration_helper.rb +++ b/spec/integration_helper.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,10 +21,10 @@ require 'logging_helper' require 'open3' -shared_context 'integration_helper' do - include_context 'application_helper' - include_context 'console_helper' - include_context 'logging_helper' +shared_context 'with integration help' do + include_context 'with application help' + include_context 'with console help' + include_context 'with logging help' let(:buildpack_dir) { Pathname.new Dir.mktmpdir } @@ -32,14 +33,18 @@ end before do |example| - %w(bin config lib resources).each { |dir| FileUtils.cp_r dir, buildpack_dir } + %w[bin config lib resources].each { |dir| FileUtils.cp_r dir, buildpack_dir } buildpack_fixture = example.metadata[:buildpack_fixture] FileUtils.cp_r "spec/fixtures/#{buildpack_fixture.chomp}/.", buildpack_dir if buildpack_fixture end - after do - FileUtils.rm_rf buildpack_dir + after do |example| + if example.metadata[:no_cleanup] + puts "Buildpack Directory: #{buildpack_dir}" + else + FileUtils.rm_rf buildpack_dir + end end def run(command) diff --git a/spec/internet_availability_helper.rb b/spec/internet_availability_helper.rb index 43c73cddf5..30bd3e986c 100644 --- a/spec/internet_availability_helper.rb +++ b/spec/internet_availability_helper.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,8 +19,8 @@ require 'logging_helper' require 'java_buildpack/util/cache/internet_availability' -shared_context 'internet_availability_helper' do - include_context 'logging_helper' +shared_context 'with internet availability help' do + include_context 'with logging help' # Re-initialize internet availability before do |example| diff --git a/spec/java_buildpack/buildpack_spec.rb b/spec/java_buildpack/buildpack_spec.rb index b8a44a6017..738118b21d 100644 --- a/spec/java_buildpack/buildpack_spec.rb +++ b/spec/java_buildpack/buildpack_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,26 +19,31 @@ require 'application_helper' require 'logging_helper' require 'java_buildpack/buildpack' +require 'java_buildpack/component/base_component' describe JavaBuildpack::Buildpack do - include_context 'application_helper' - include_context 'logging_helper' + include_context 'with application help' + include_context 'with logging help' + + let(:stub_container1) { instance_double('StubContainer1', detect: nil, component_name: 'StubContainer1') } - let(:stub_container1) { double('StubContainer1', detect: nil, component_name: 'StubContainer1') } + let(:stub_container2) do + instance_double('StubContainer2', detect: nil, compile: nil, release: nil, component_name: 'StubContainer2') + end - let(:stub_container2) { double('StubContainer2', detect: nil, component_name: 'StubContainer2') } + let(:stub_framework1) { instance_double('StubFramework1', detect: nil) } - let(:stub_framework1) { double('StubFramework1', detect: nil) } + let(:stub_framework2) { instance_double('StubFramework2', detect: nil, compile: nil, release: nil) } - let(:stub_framework2) { double('StubFramework2', detect: nil) } + let(:stub_jre1) { instance_double('StubJre1', detect: nil, component_name: 'StubJre1') } - let(:stub_jre1) { double('StubJre1', detect: nil, component_name: 'StubJre1') } + let(:stub_jre2) { instance_double('StubJre2', detect: nil, compile: nil, release: nil, component_name: 'StubJre2') } - let(:stub_jre2) { double('StubJre2', detect: nil, component_name: 'StubJre2') } + let(:deps_dir) { Pathname.new Dir.mktmpdir } let(:buildpack) do buildpack = nil - described_class.with_buildpack(app_dir, 'Error %s') { |b| buildpack = b } + described_class.with_buildpack(app_dir, deps_dir, '0', 'Error %s') { |b| buildpack = b } buildpack end @@ -45,10 +51,10 @@ allow(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).and_call_original allow(JavaBuildpack::Util::ConfigurationUtils) .to receive(:load).with('components').and_return( - 'containers' => ['Test::StubContainer1', 'Test::StubContainer2'], - 'frameworks' => ['Test::StubFramework1', 'Test::StubFramework2'], - 'jres' => ['Test::StubJre1', 'Test::StubJre2'] - ) + 'containers' => %w[Test::StubContainer1 Test::StubContainer2], + 'frameworks' => %w[Test::StubFramework1 Test::StubFramework2], + 'jres' => %w[Test::StubJre1 Test::StubJre2] + ) allow(Test::StubContainer1).to receive(:new).and_return(stub_container1) allow(Test::StubContainer2).to receive(:new).and_return(stub_container2) @@ -60,12 +66,20 @@ allow(Test::StubJre2).to receive(:new).and_return(stub_jre2) end + after do |example| + if example.metadata[:no_cleanup] + puts "Deps Directory: #{deps_dir}" + else + FileUtils.rm_rf deps_dir + end + end + it 'raises an error if more than one container can run an application' do allow(stub_container1).to receive(:detect).and_return('stub-container-1') allow(stub_container2).to receive(:detect).and_return('stub-container-2') expect { buildpack.detect } - .to raise_error(/Application can be run by more than one container: Double, Double/) + .to raise_error(/Application can be run by more than one container/) end it 'raises an error if more than one JRE can run an application' do @@ -73,7 +87,7 @@ allow(stub_jre1).to receive(:detect).and_return('stub-jre-1') allow(stub_jre2).to receive(:detect).and_return('stub-jre-2') - expect { buildpack.detect }.to raise_error(/Application can be run by more than one JRE: Double, Double/) + expect { buildpack.detect }.to raise_error(/Application can be run by more than one JRE/) end it 'returns no detections if no container can run an application' do @@ -84,12 +98,11 @@ before do allow(JavaBuildpack::Util::ConfigurationUtils) - .to receive(:load).with('components') - .and_return( - 'containers' => [], - 'frameworks' => ['JavaBuildpack::Framework::JavaOpts'], - 'jres' => [] - ) + .to receive(:load).with('components').and_return( + 'containers' => [], + 'frameworks' => ['JavaBuildpack::Framework::JavaOpts'], + 'jres' => [] + ) end it 'requires files needed for components' do @@ -102,12 +115,12 @@ allow(stub_framework1).to receive(:detect).and_return('stub-framework-1') allow(stub_jre1).to receive(:detect).and_return('stub-jre-1') - expect(stub_container1).to receive(:compile) - expect(stub_container2).not_to receive(:compile) - expect(stub_framework1).to receive(:compile) - expect(stub_framework2).not_to receive(:compile) - expect(stub_jre1).to receive(:compile) - expect(stub_jre2).not_to receive(:compile) + allow(stub_container1).to receive(:compile) + expect(stub_container2).not_to have_received(:compile) + allow(stub_framework1).to receive(:compile) + expect(stub_framework2).not_to have_received(:compile) + allow(stub_jre1).to receive(:compile) + expect(stub_jre2).not_to have_received(:compile) buildpack.compile end @@ -116,59 +129,67 @@ allow(stub_container1).to receive(:detect).and_return('stub-container-1') allow(stub_framework1).to receive(:detect).and_return('stub-framework-1') allow(stub_jre1).to receive(:detect).and_return('stub-jre-1') - allow(stub_container1).to receive(:release).and_return('test-command') - expect(stub_container1).to receive(:release) - expect(stub_container2).not_to receive(:release) - expect(stub_framework1).to receive(:release) - expect(stub_framework2).not_to receive(:release) - expect(stub_jre1).to receive(:release) - expect(stub_jre2).not_to receive(:release) + allow(stub_container1).to receive(:release).and_return('test-command') + expect(stub_container2).not_to have_received(:release) + allow(stub_framework1).to receive(:release) + expect(stub_framework2).not_to have_received(:release) + allow(stub_jre1).to receive(:release) + expect(stub_jre2).not_to have_received(:release) expect(buildpack.release) - .to eq({ 'addons' => [], 'config_vars' => {}, 'default_process_types' => { 'web' => 'test-command' } }.to_yaml) + .to eq({ 'addons' => [], + 'config_vars' => {}, + 'default_process_types' => { 'web' => 'JAVA_OPTS="" && test-command', + 'task' => 'JAVA_OPTS="" && test-command' } }.to_yaml) end it 'loads configuration file matching JRE class name' do - expect(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('stub_jre1') - expect(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('stub_jre2') - expect(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('stub_framework1') - expect(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('stub_framework2') - expect(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('stub_container1') - expect(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('stub_container2') + allow(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('stub_jre1') + allow(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('stub_jre2') + allow(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('stub_framework1') + allow(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('stub_framework2') + allow(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('stub_container1') + allow(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('stub_container2') buildpack.detect end it 'handles exceptions' do - expect { with_buildpack { |_buildpack| fail 'an exception' } }.to raise_error SystemExit + expect { with_buildpack { |_buildpack| raise 'an exception' } }.to raise_error SystemExit expect(stderr.string).to match(/an exception/) end - def with_buildpack(&block) - described_class.with_buildpack(app_dir, 'Error %s') do |buildpack| - block.call buildpack - end + def with_buildpack(&_) + # rubocop:disable Style/ExplicitBlockArgument + described_class.with_buildpack(app_dir, nil, nil, 'Error %s') { |buildpack| yield buildpack } + # rubocop:enable Style/ExplicitBlockArgument end end module Test - class StubContainer1 + class StubContainer1 < JavaBuildpack::Component::BaseComponent + attr_reader :component_name end - class StubContainer2 + class StubContainer2 < JavaBuildpack::Component::BaseComponent + attr_reader :component_name end - class StubJre1 + class StubJre1 < JavaBuildpack::Component::BaseComponent + attr_reader :component_name end - class StubJre2 + class StubJre2 < JavaBuildpack::Component::BaseComponent + attr_reader :component_name end - class StubFramework1 + class StubFramework1 < JavaBuildpack::Component::BaseComponent + attr_reader :component_name end - class StubFramework2 + class StubFramework2 < JavaBuildpack::Component::BaseComponent + attr_reader :component_name end end diff --git a/spec/java_buildpack/buildpack_version_spec.rb b/spec/java_buildpack/buildpack_version_spec.rb index cd87812e11..b7a776eaaf 100644 --- a/spec/java_buildpack/buildpack_version_spec.rb +++ b/spec/java_buildpack/buildpack_version_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013-2015 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,19 +22,21 @@ require 'pathname' describe JavaBuildpack::BuildpackVersion do - include_context 'application_helper' - include_context 'logging_helper' + include_context 'with application help' + include_context 'with console help' + include_context 'with logging help' let(:buildpack_version) { described_class.new } before do |example| configuration = example.metadata[:configuration] || {} - allow(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('version', true).and_return(configuration) + allow(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('version', true, true) + .and_return(configuration) end it 'creates offline version string from config/version.yml', - log_level: 'DEBUG', - configuration: { 'hash' => 'test-hash', 'offline' => true, + log_level: 'DEBUG', + configuration: { 'hash' => 'test-hash', 'offline' => true, 'remote' => 'test-remote', 'version' => 'test-version' } do expect(buildpack_version.to_s).to match(/test-version (offline) | test-remote#test-hash/) @@ -42,8 +45,8 @@ end it 'creates online version string from config/version.yml', - log_level: 'DEBUG', - configuration: { 'hash' => 'test-hash', 'offline' => false, + log_level: 'DEBUG', + configuration: { 'hash' => 'test-hash', 'offline' => false, 'remote' => 'test-remote', 'version' => 'test-version' } do expect(buildpack_version.to_s).to match(/test-version | test-remote#test-hash/) @@ -57,14 +60,14 @@ git_dir = Pathname.new('.git').expand_path allow_any_instance_of(described_class).to receive(:system) - .with('which git > /dev/null') - .and_return(true) + .with('which git > /dev/null') + .and_return(true) allow_any_instance_of(described_class).to receive(:`) - .with("git --git-dir=#{git_dir} rev-parse --short HEAD") - .and_return('test-hash') + .with("git --git-dir=#{git_dir} rev-parse --short HEAD") + .and_return('test-hash') allow_any_instance_of(described_class).to receive(:`) - .with("git --git-dir=#{git_dir} config --get remote.origin.url") - .and_return('test-remote') + .with("git --git-dir=#{git_dir} config --get remote.origin.url") + .and_return('test-remote') expect(buildpack_version.to_s).to match(/test-remote#test-hash/) expect(buildpack_version.to_s(false)).to match(/test-remote#test-hash/) @@ -82,7 +85,7 @@ end it 'creates a has from the values', - configuration: { 'hash' => 'test-hash', 'offline' => true, + configuration: { 'hash' => 'test-hash', 'offline' => true, 'remote' => 'test-remote', 'version' => 'test-version' } do |example| allow_any_instance_of(described_class).to receive(:system).with('which git > /dev/null').and_return(false) @@ -106,14 +109,14 @@ git_dir = Pathname.new('.git').expand_path allow_any_instance_of(described_class).to receive(:system) - .with('which git > /dev/null') - .and_return(true) + .with('which git > /dev/null') + .and_return(true) allow_any_instance_of(described_class).to receive(:`) - .with("git --git-dir=#{git_dir} rev-parse --short HEAD") - .and_return('test-hash') + .with("git --git-dir=#{git_dir} rev-parse --short HEAD") + .and_return('test-hash') allow_any_instance_of(described_class).to receive(:`) - .with("git --git-dir=#{git_dir} config --get remote.origin.url") - .and_return('test-remote') + .with("git --git-dir=#{git_dir} config --get remote.origin.url") + .and_return('test-remote') expect(buildpack_version.to_s).to eq('test-version | test-remote#test-hash') end @@ -125,7 +128,7 @@ it 'picks up offline from the environment' do allow_any_instance_of(described_class).to receive(:system).with('which git > /dev/null').and_return(false) - expect(buildpack_version.offline).to be + expect(buildpack_version.offline).to be_truthy end end diff --git a/spec/java_buildpack/component/additional_libraries_spec.rb b/spec/java_buildpack/component/additional_libraries_spec.rb index 069053a951..a1c57896cc 100644 --- a/spec/java_buildpack/component/additional_libraries_spec.rb +++ b/spec/java_buildpack/component/additional_libraries_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +20,7 @@ require 'java_buildpack/component/additional_libraries' describe JavaBuildpack::Component::AdditionalLibraries do - include_context 'droplet_helper' + include_context 'with droplet help' context do @@ -36,8 +37,8 @@ end it 'renders as classpath' do - additional_libraries << droplet.sandbox + 'jar-2.jar' - additional_libraries << droplet.sandbox + 'jar-1.jar' + additional_libraries << (droplet.sandbox + 'jar-2.jar') + additional_libraries << (droplet.sandbox + 'jar-1.jar') expect(additional_libraries.as_classpath).to eq('-cp $PWD/.java-buildpack/additional_libraries/jar-1.jar:' \ '$PWD/.java-buildpack/additional_libraries/jar-2.jar') @@ -46,21 +47,21 @@ it 'renders empty string if classpath is empty' do additional_libraries.clear - expect(additional_libraries.as_classpath).not_to be + expect(additional_libraries.as_classpath).not_to be_truthy end it 'symbolically links additional libraries' do additional_libraries.link_to app_dir - test_jar_1 = app_dir + 'test-jar-1.jar' - test_jar_2 = app_dir + 'test-jar-2.jar' - expect(test_jar_1).to exist - expect(test_jar_1).to be_symlink - expect(test_jar_1.readlink).to eq((additional_libs_directory + 'test-jar-1.jar').relative_path_from(app_dir)) + test_jar1 = app_dir + 'test-jar-1.jar' + test_jar2 = app_dir + 'test-jar-2.jar' + expect(test_jar1).to exist + expect(test_jar1).to be_symlink + expect(test_jar1.readlink).to eq((additional_libs_directory + 'test-jar-1.jar').relative_path_from(app_dir)) - expect(test_jar_2).to exist - expect(test_jar_2).to be_symlink - expect(test_jar_2.readlink).to eq((additional_libs_directory + 'test-jar-2.jar').relative_path_from(app_dir)) + expect(test_jar2).to exist + expect(test_jar2).to be_symlink + expect(test_jar2.readlink).to eq((additional_libs_directory + 'test-jar-2.jar').relative_path_from(app_dir)) end end diff --git a/spec/java_buildpack/component/application_spec.rb b/spec/java_buildpack/component/application_spec.rb index 5e549038e1..9104d86b13 100644 --- a/spec/java_buildpack/component/application_spec.rb +++ b/spec/java_buildpack/component/application_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,7 +21,7 @@ require 'java_buildpack/component/application' describe JavaBuildpack::Component::Application do - include_context 'application_helper' + include_context 'with application help' it 'returns a parsed version of VCAP_APPLICATION as details' do expect(application.details).to eq(vcap_application) @@ -69,7 +70,7 @@ end it 'returns a parsed version of VCAP_SERVICES as services' do - expect(application.services.find_service(/test-service/)).to be + expect(application.services.find_service(/test-service/)).to be_truthy end end diff --git a/spec/java_buildpack/component/base_component_spec.rb b/spec/java_buildpack/component/base_component_spec.rb index ac303962e1..301f9687f6 100644 --- a/spec/java_buildpack/component/base_component_spec.rb +++ b/spec/java_buildpack/component/base_component_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +20,7 @@ require 'java_buildpack/component/base_component' describe JavaBuildpack::Component::BaseComponent do - include_context 'component_helper' + include_context 'with component help' let(:component) { StubBaseComponent.new context } @@ -40,9 +41,9 @@ end it 'fails if methods are unimplemented' do - expect { component.detect }.to raise_error - expect { component.compile }.to raise_error - expect { component.release }.to raise_error + expect { component.detect }.to raise_error RuntimeError + expect { component.compile }.to raise_error RuntimeError + expect { component.release }.to raise_error RuntimeError end it 'downloads file and yield it', @@ -83,7 +84,7 @@ it 'prints timing information' do expect { |b| component.with_timing('test-caption', &b) }.to yield_control - expect(stdout.string).to match(/ test-caption \([\d]\.[\d]s\)/) + expect(stdout.string).to match(/ test-caption \(\d\.\ds\)/) end end diff --git a/spec/java_buildpack/component/droplet_spec.rb b/spec/java_buildpack/component/droplet_spec.rb index bea9f88a29..08bb370acb 100644 --- a/spec/java_buildpack/component/droplet_spec.rb +++ b/spec/java_buildpack/component/droplet_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,8 +23,8 @@ require 'pathname' describe JavaBuildpack::Component::Droplet do - include_context 'application_helper' - include_context 'droplet_helper' + include_context 'with application help' + include_context 'with droplet help' it 'returns additional_libraries' do expect(droplet.additional_libraries).to equal(additional_libraries) @@ -33,6 +34,10 @@ expect(droplet.component_id).to eq(component_id) end + it 'returns extension_directories' do + expect(droplet.extension_directories).to equal(extension_directories) + end + it 'returns java_home' do expect(droplet.java_home).to equal(java_home) end @@ -41,6 +46,14 @@ expect(droplet.java_opts).to equal(java_opts) end + it 'returns environment_variables' do + expect(droplet.environment_variables).to equal(environment_variables) + end + + it 'returns security_providers' do + expect(droplet.security_providers).to equal(security_providers) + end + it 'returns an existent child if in application' do FileUtils.touch(app_dir + 'test-file') diff --git a/spec/java_buildpack/component/environment_variables_spec.rb b/spec/java_buildpack/component/environment_variables_spec.rb new file mode 100644 index 0000000000..e492517f2c --- /dev/null +++ b/spec/java_buildpack/component/environment_variables_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'droplet_helper' +require 'java_buildpack/component/environment_variables' + +describe JavaBuildpack::Component::EnvironmentVariables do + include_context 'with droplet help' + + let(:variables) { described_class.new droplet.root } + + it 'adds a variable to the collection' do + variables.add_environment_variable 'test-key', 'test-value' + + expect(variables).to include('test-key=test-value') + end + + it 'adds a qualified variable value to the collection' do + variables.add_environment_variable 'test-key', droplet.sandbox + + expect(variables).to include('test-key=$PWD/.java-buildpack/environment_variables') + end + + it 'renders the collection as an environment variable' do + variables.add_environment_variable 'test-key-2', 'test-value-2' + variables.add_environment_variable 'test-key-1', 'test-value-1' + + expect(variables.as_env_vars).to eq('test-key-2=test-value-2 test-key-1=test-value-1') + end + +end diff --git a/spec/java_buildpack/component/extension_directories_spec.rb b/spec/java_buildpack/component/extension_directories_spec.rb new file mode 100644 index 0000000000..a033e49aad --- /dev/null +++ b/spec/java_buildpack/component/extension_directories_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'droplet_helper' +require 'java_buildpack/component/extension_directories' + +describe JavaBuildpack::Component::ExtensionDirectories do + include_context 'with droplet help' + + context do + + before do + extension_directories.clear + end + + it 'contains an added path' do + extension_directories << droplet.sandbox + + expect(extension_directories).to include(droplet.sandbox) + end + + it 'renders as path' do + extension_directories << (droplet.sandbox + 'extension-directories-1') + extension_directories << (droplet.sandbox + 'extension-directories-2') + + expect(extension_directories.as_paths).to eq('$PWD/.java-buildpack/extension_directories/' \ + 'extension-directories-1:$PWD/.java-buildpack/' \ + 'extension_directories/extension-directories-2') + end + end + + it 'renders empty string if path is empty' do + extension_directories.clear + expect(extension_directories.as_paths).not_to be_truthy + end + +end diff --git a/spec/java_buildpack/component/immutable_java_home_spec.rb b/spec/java_buildpack/component/immutable_java_home_spec.rb index 19a9c7eeff..ed87ac7840 100644 --- a/spec/java_buildpack/component/immutable_java_home_spec.rb +++ b/spec/java_buildpack/component/immutable_java_home_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,10 +17,18 @@ require 'spec_helper' require 'java_buildpack/component/immutable_java_home' +require 'java_buildpack/component/mutable_java_home' +require 'java_buildpack/util/tokenized_version' describe JavaBuildpack::Component::ImmutableJavaHome do - let(:delegate) { double('delegate', root: Pathname.new('test-java-home'), version: %w(1 2 3 u04)) } + let(:delegate) do + instance_double('JavaBuildpack::Component::MutableJavaHome', + root: Pathname.new('test-java-home'), + java_8_or_later?: true, + java_9_or_later?: true, + version: JavaBuildpack::Util::TokenizedVersion.new('1.2.3_u04')) + end let(:immutable_java_home) { described_class.new delegate, Pathname.new('.') } @@ -27,18 +36,20 @@ expect(immutable_java_home.as_env_var).to eq('JAVA_HOME=$PWD/test-java-home') end - it 'sets JAVA_HOME environment variable' do - immutable_java_home.do_with do - expect(ENV['JAVA_HOME']).to eq('test-java-home') - end - end - it 'returns the qualified delegate root' do - expect(immutable_java_home.root).to eq('$PWD/test-java-home') + expect(immutable_java_home.root.to_s).to eq('test-java-home') end it 'returns the delegate version' do - expect(immutable_java_home.version).to eq(%w(1 2 3 u04)) + expect(immutable_java_home.version).to eq(%w[1 2 3 u04]) + end + + it 'returns the delegate Java 8 or later' do + expect(immutable_java_home).to be_java_8_or_later + end + + it 'returns the delegate Java 9 or later' do + expect(immutable_java_home).to be_java_9_or_later end end diff --git a/spec/java_buildpack/component/java_opts_spec.rb b/spec/java_buildpack/component/java_opts_spec.rb index 1785f50754..babc152f0a 100644 --- a/spec/java_buildpack/component/java_opts_spec.rb +++ b/spec/java_buildpack/component/java_opts_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +20,7 @@ require 'java_buildpack/component/java_opts' describe JavaBuildpack::Component::JavaOpts do - include_context 'droplet_helper' + include_context 'with droplet help' let(:opts) { described_class.new droplet.root } @@ -29,6 +30,24 @@ expect(opts).to include('-javaagent:$PWD/.java-buildpack/java_opts/test-java-agent') end + it 'adds a qualified javaagent with properties to the collection' do + opts.add_javaagent_with_props(droplet.sandbox + 'test-java-agent', 'key1' => 'value1', 'key2' => 'value2') + + expect(opts).to include('-javaagent:$PWD/.java-buildpack/java_opts/test-java-agent=key1=value1,key2=value2') + end + + it 'adds a qualified agentpath to the collection' do + opts.add_agentpath droplet.sandbox + 'test-agentpath' + + expect(opts).to include('-agentpath:$PWD/.java-buildpack/java_opts/test-agentpath') + end + + it 'adds a qualified agentpath with properties to the collection' do + opts.add_agentpath_with_props(droplet.sandbox + 'test-agentpath', 'key1' => 'value1', 'key2' => 'value2') + + expect(opts).to include('-agentpath:$PWD/.java-buildpack/java_opts/test-agentpath=key1=value1,key2=value2') + end + it 'adds a qualified system property to the collection' do opts.add_system_property 'test-key', droplet.sandbox @@ -41,6 +60,12 @@ expect(opts).to include('-Dtest-key=test-value') end + it 'adds a bootclasspath property to the collection' do + opts.add_bootclasspath_p droplet.sandbox + 'test-bootclasspath' + + expect(opts).to include('-Xbootclasspath/p:$PWD/.java-buildpack/java_opts/test-bootclasspath') + end + it 'adds a qualified option to the collection' do opts.add_option 'test-key', droplet.sandbox diff --git a/spec/java_buildpack/component/modular_component_spec.rb b/spec/java_buildpack/component/modular_component_spec.rb index 71137faf21..65df02ffa5 100644 --- a/spec/java_buildpack/component/modular_component_spec.rb +++ b/spec/java_buildpack/component/modular_component_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,12 +20,12 @@ require 'java_buildpack/component/modular_component' describe JavaBuildpack::Component::ModularComponent do - include_context 'component_helper' + include_context 'with component help' let(:component) { StubModularComponent.new context } it 'fails if supports? is unimplemented' do - expect { component.supports? }.to raise_error + expect { component.supports? }.to raise_error RuntimeError end context do @@ -38,14 +39,14 @@ end it 'fails if methods are unimplemented' do - expect { component.command }.to raise_error - expect { component.sub_components(context) }.to raise_error + expect { component.command }.to raise_error RuntimeError + expect { component.sub_components(context) }.to raise_error RuntimeError end end context do - let(:sub_component) { double('sub_component') } + let(:sub_component) { instance_double('sub_component') } before do allow_any_instance_of(StubModularComponent).to receive(:supports?).and_return(true) @@ -62,13 +63,13 @@ end it 'calls compile on each sub_component' do - expect(sub_component).to receive(:compile).twice + allow(sub_component).to receive(:compile).twice component.compile end it 'calls release on each sub_component and then command' do - expect(sub_component).to receive(:release).twice + allow(sub_component).to receive(:release).twice allow_any_instance_of(StubModularComponent).to receive(:command).and_return('test-command') expect(component.release).to eq('test-command') @@ -77,8 +78,20 @@ end +# rubocop:disable Lint/UselessMethodDefinition class StubModularComponent < JavaBuildpack::Component::ModularComponent - public :command, :sub_components, :supports? + def command + super + end + + def sub_components(context) + super context + end + + def supports? + super + end end +# rubocop:enable Lint/UselessMethodDefinition diff --git a/spec/java_buildpack/component/mutable_java_home_spec.rb b/spec/java_buildpack/component/mutable_java_home_spec.rb index 4c11364798..7a857a5231 100644 --- a/spec/java_buildpack/component/mutable_java_home_spec.rb +++ b/spec/java_buildpack/component/mutable_java_home_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,13 +17,14 @@ require 'spec_helper' require 'java_buildpack/component/mutable_java_home' +require 'java_buildpack/util/tokenized_version' require 'pathname' describe JavaBuildpack::Component::MutableJavaHome do let(:path) { Pathname.new('foo/bar') } - let(:java_version) { %w(1 2 3 u04) } + let(:java_version) { JavaBuildpack::Util::TokenizedVersion.new('1.2.3_u04') } let(:mutable_java_home) { described_class.new } @@ -36,4 +38,44 @@ expect(mutable_java_home.version).to eq(java_version) end + it 'recognizes Java 6 as earlier than Java 8' do + mutable_java_home.version = JavaBuildpack::Util::TokenizedVersion.new('1.6.0') + expect(mutable_java_home).not_to be_java_8_or_later + end + + it 'recognizes Java 7 as earlier than Java 8' do + mutable_java_home.version = JavaBuildpack::Util::TokenizedVersion.new('1.7.0') + expect(mutable_java_home).not_to be_java_8_or_later + end + + it 'recognizes Java 8 as later than or equal to Java 8' do + mutable_java_home.version = JavaBuildpack::Util::TokenizedVersion.new('1.8.0') + expect(mutable_java_home).to be_java_8_or_later + end + + it 'recognizes Java 9 as later than or equal to Java 8' do + mutable_java_home.version = JavaBuildpack::Util::TokenizedVersion.new('9.0.0') + expect(mutable_java_home).to be_java_8_or_later + end + + it 'recognizes Java 6 as earlier than Java 9' do + mutable_java_home.version = JavaBuildpack::Util::TokenizedVersion.new('1.6.0') + expect(mutable_java_home).not_to be_java_9_or_later + end + + it 'recognizes Java 7 as earlier than Java 9' do + mutable_java_home.version = JavaBuildpack::Util::TokenizedVersion.new('1.7.0') + expect(mutable_java_home).not_to be_java_9_or_later + end + + it 'recognizes Java 8 as earlier than Java 9' do + mutable_java_home.version = JavaBuildpack::Util::TokenizedVersion.new('1.8.0') + expect(mutable_java_home).not_to be_java_9_or_later + end + + it 'recognizes Java 9 as later than or equal to Java 9' do + mutable_java_home.version = JavaBuildpack::Util::TokenizedVersion.new('9.0.0') + expect(mutable_java_home).to be_java_9_or_later + end + end diff --git a/spec/java_buildpack/component/security_providers_spec.rb b/spec/java_buildpack/component/security_providers_spec.rb new file mode 100644 index 0000000000..8fe3f67390 --- /dev/null +++ b/spec/java_buildpack/component/security_providers_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'droplet_helper' +require 'java_buildpack/component/security_providers' + +describe JavaBuildpack::Component::SecurityProviders do + include_context 'with droplet help' + + context do + + before do + security_providers.clear + end + + it 'contains an added provider' do + security_providers << 'test-security-provider' + + expect(security_providers).to include('test-security-provider') + end + end + + it 'symbolically links additional libraries' do + security_file = app_dir + 'java.security' + + security_providers.write_to security_file + + expect(security_file.read).to eq("security.provider.1=test-security-provider-1\n" \ + "security.provider.2=test-security-provider-2\n") + end + +end diff --git a/spec/java_buildpack/component/services_spec.rb b/spec/java_buildpack/component/services_spec.rb index fe038ff42e..f5df67321c 100644 --- a/spec/java_buildpack/component/services_spec.rb +++ b/spec/java_buildpack/component/services_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,78 +20,361 @@ require 'java_buildpack/component/services' describe JavaBuildpack::Component::Services do - include_context 'logging_helper' + include_context 'with logging help' - let(:service) do - { 'name' => 'test-name', 'label' => 'test-label', 'tags' => ['test-tag'], 'plan' => 'test-plan', - 'credentials' => { 'uri' => 'test-uri', 'h1' => 'foo', 'h2' => 'foo' } } - end + let(:services) { described_class.new('test' => service_payload) } - let(:services) { described_class.new('test' => [service]) } + context('when find_service') do - it 'returns false from one_service? if there is no service that matches' do - expect(services.one_service? 'bad-test').not_to be - expect(services.one_service?(/bad-test/)).not_to be - end + context('with single service') do - it 'returns true from one_service? if there is a matching name' do - expect(services.one_service? 'test-name').to be - expect(services.one_service?(/test-name/)).to be - end + let(:service_payload) do + [{ 'name' => 'test-name', 'label' => 'test-label', 'tags' => ['test-tag'], 'plan' => 'test-plan', + 'credentials' => { 'uri' => 'test-uri', 'h1' => 'foo', 'h2' => 'foo' } }] + end - it 'returns true from one_service? if there is a matching label' do - expect(services.one_service? 'test-label').to be - expect(services.one_service?(/test-label/)).to be - end + it 'returns nil from find_service? if there is no service that matches' do + expect(services.find_service('bad-test')).to be_nil + expect(services.find_service(/bad-test/)).to be_nil + end - it 'returns true from one_service? if there is a matching tag' do - expect(services.one_service? 'test-tag').to be - expect(services.one_service?(/test-tag/)).to be - end + it 'returns service from find_service? if there is a matching name' do + expect(services.find_service('test-name')).to be(service_payload[0]) + expect(services.find_service(/test-name/)).to be(service_payload[0]) + end - it 'returns false from one_service? if there is a matching service without required credentials' do - expect(services.one_service? 'test-tag', 'bad-credential').not_to be - expect(services.one_service?(/test-tag/, 'bad-credential')).not_to be - end + it 'returns service from find_service? if there is a matching label' do + expect(services.find_service('test-label')).to be(service_payload[0]) + expect(services.find_service(/test-label/)).to be(service_payload[0]) + end - it 'returns true from one_service? if there is a matching service with required credentials' do - expect(services.one_service? 'test-tag', 'uri').to be - expect(services.one_service?(/test-tag/, 'uri')).to be - end + it 'returns service from find_service? if there is a matching tag' do + expect(services.find_service('test-tag')).to be(service_payload[0]) + expect(services.find_service(/test-tag/)).to be(service_payload[0]) + end - it 'returns true from one_service? if there is a matching service with one required group credentials' do - expect(services.one_service? 'test-tag', %w(uri other)).to be - expect(services.one_service?(/test-tag/, %w(uri other))).to be - end + end - it 'returns true from one_service? if there is a matching service with two required group credentials' do - expect(services.one_service? 'test-tag', %w(h1 h2)).to be - expect(services.one_service?(/test-tag/, %w(h1 h2))).to be - end + context('with two services') do - it 'returns false from one_service? if there is a matching service with no required group credentials' do - expect(services.one_service? 'test-tag', %w(foo bar)).not_to be - expect(services.one_service?(/test-tag/, %w(foo bar))).not_to be - end + let(:service_payload) do + [{ 'name' => 'test-name', 'label' => 'test-label', 'tags' => ['test-tag'], 'plan' => 'test-plan' }, + { 'name' => 'test-name', 'label' => 'test-label', 'tags' => ['test-tag'], 'plan' => 'test-plan', + 'credentials' => { 'uri' => 'test-uri', 'h1' => 'foo', 'h2' => 'foo' } }] + end + + it 'returns nil from find_service? if there is no service that matches' do + expect(services.find_service('bad-test')).to be_nil + expect(services.find_service(/bad-test/)).to be_nil + end + + it 'returns service from find_service? if there is a matching name' do + expect(services.find_service('test-name')).to be(service_payload[1]) + expect(services.find_service(/test-name/)).to be(service_payload[1]) + end + + it 'returns service from find_service? if there is a matching label' do + expect(services.find_service('test-label')).to be(service_payload[1]) + expect(services.find_service(/test-label/)).to be(service_payload[1]) + end + + it 'returns service from find_service? if there is a matching tag' do + expect(services.find_service('test-tag')).to be(service_payload[1]) + expect(services.find_service(/test-tag/)).to be(service_payload[1]) + end + + end - it 'returns nil from find_service? if there is no service that matches' do - expect(services.find_service 'bad-test').to be_nil - expect(services.find_service(/bad-test/)).to be_nil end - it 'returns service from find_service? if there is a matching name' do - expect(services.find_service 'test-name').to be(service) - expect(services.find_service(/test-name/)).to be(service) + context('with find_volume_service') do + + context('with single service') do + + let(:service_payload) do + [{ 'name' => 'test-name', 'label' => 'test-label', 'tags' => ['test-tag'], 'plan' => 'test-plan', + 'credentials' => { 'uri' => 'test-uri', 'h1' => 'foo', 'h2' => 'foo' }, + 'volume_mounts' => [{ 'container_dir' => '/var/vcap/data/9ae0b817-1446-4915-9990-74c1bb26f147', + 'device_type' => 'shared', + 'mode' => 'rw' }] }] + end + + it 'returns nil from find_service? if there is no service that matches' do + expect(services.find_volume_service('bad-test')).to be_nil + expect(services.find_volume_service(/bad-test/)).to be_nil + end + + it 'returns service from find_service? if there is a matching name' do + expect(services.find_volume_service('test-name')).to be(service_payload[0]) + expect(services.find_volume_service(/test-name/)).to be(service_payload[0]) + end + + it 'returns service from find_service? if there is a matching label' do + expect(services.find_volume_service('test-label')).to be(service_payload[0]) + expect(services.find_volume_service(/test-label/)).to be(service_payload[0]) + end + + it 'returns service from find_service? if there is a matching tag' do + expect(services.find_volume_service('test-tag')).to be(service_payload[0]) + expect(services.find_volume_service(/test-tag/)).to be(service_payload[0]) + end + + end + + context('with two services') do + + let(:service_payload) do + [{ 'name' => 'test-name', 'label' => 'test-label', 'tags' => ['test-tag'], 'plan' => 'test-plan', + 'credentials' => { 'uri' => 'test-uri', 'h1' => 'foo', 'h2' => 'foo' }, + 'volume_mounts' => [] }, + { 'name' => 'test-name', 'label' => 'test-label', 'tags' => ['test-tag'], 'plan' => 'test-plan', + 'credentials' => { 'uri' => 'test-uri', 'h1' => 'foo', 'h2' => 'foo' }, + 'volume_mounts' => [{ 'container_dir' => '/var/vcap/data/9ae0b817-1446-4915-9990-74c1bb26f147', + 'device_type' => 'shared', + 'mode' => 'rw' }] }] + end + + it 'returns nil from find_service? if there is no service that matches' do + expect(services.find_volume_service('bad-test')).to be_nil + expect(services.find_volume_service(/bad-test/)).to be_nil + end + + it 'returns service from find_service? if there is a matching name' do + expect(services.find_volume_service('test-name')).to be(service_payload[1]) + expect(services.find_volume_service(/test-name/)).to be(service_payload[1]) + end + + it 'returns service from find_service? if there is a matching label' do + expect(services.find_volume_service('test-label')).to be(service_payload[1]) + expect(services.find_volume_service(/test-label/)).to be(service_payload[1]) + end + + it 'returns service from find_service? if there is a matching tag' do + expect(services.find_volume_service('test-tag')).to be(service_payload[1]) + expect(services.find_volume_service(/test-tag/)).to be(service_payload[1]) + end + + end + end - it 'returns service from find_service? if there is a matching label' do - expect(services.find_service 'test-label').to be(service) - expect(services.find_service(/test-label/)).to be(service) + context('with one_service') do + + context('with single service') do + + let(:service_payload) do + [{ 'name' => 'test-name', 'label' => 'test-label', 'tags' => ['test-tag'], 'plan' => 'test-plan', + 'credentials' => { 'uri' => 'test-uri', 'h1' => 'foo', 'h2' => 'foo' } }] + end + + it 'returns false from one_service? if there is no service that matches' do + expect(services).not_to be_one_service('bad-test') + expect(services).not_to be_one_service(/bad-test/) + end + + it 'returns true from one_service? if there is a matching name' do + expect(services).to be_one_service('test-name') + expect(services).to be_one_service(/test-name/) + end + + it 'returns true from one_service? if there is a matching label' do + expect(services).to be_one_service('test-label') + expect(services).to be_one_service(/test-label/) + end + + it 'returns true from one_service? if there is a matching tag' do + expect(services).to be_one_service('test-tag') + expect(services).to be_one_service(/test-tag/) + end + + it 'returns false from one_service? if there is a matching service without required credentials' do + expect(services).not_to be_one_service('test-tag', 'bad-credential') + expect(services).not_to be_one_service(/test-tag/, 'bad-credential') + end + + it 'returns true from one_service? if there is a matching service with required credentials' do + expect(services).to be_one_service('test-tag', 'uri') + expect(services).to be_one_service(/test-tag/, 'uri') + end + + it 'returns true from one_service? if there is a matching service with one required group credentials' do + expect(services).to be_one_service('test-tag', %w[uri other]) + expect(services).to be_one_service(/test-tag/, %w[uri other]) + end + + it 'returns true from one_service? if there is a matching service with two required group credentials' do + expect(services).to be_one_service('test-tag', %w[h1 h2]) + expect(services).to be_one_service(/test-tag/, %w[h1 h2]) + end + + it 'returns false from one_service? if there is a matching service with no required group credentials' do + expect(services).not_to be_one_service('test-tag', %w[foo bar]) + expect(services).not_to be_one_service(/test-tag/, %w[foo bar]) + end + + end + + context('with two services') do + + let(:service_payload) do + [{ 'name' => 'test-name', 'label' => 'test-label', 'tags' => ['test-tag'], 'plan' => 'test-plan' }, + { 'name' => 'test-name', 'label' => 'test-label', 'tags' => ['test-tag'], 'plan' => 'test-plan', + 'credentials' => { 'uri' => 'test-uri', 'h1' => 'foo', 'h2' => 'foo' } }] + end + + it 'returns false from one_service? if there is no service that matches' do + expect(services).not_to be_one_service('bad-test') + expect(services).not_to be_one_service(/bad-test/) + end + + it 'returns true from one_service? if there is a matching name' do + expect(services).to be_one_service('test-name') + expect(services).to be_one_service(/test-name/) + end + + it 'returns true from one_service? if there is a matching label' do + expect(services).to be_one_service('test-label') + expect(services).to be_one_service(/test-label/) + end + + it 'returns true from one_service? if there is a matching tag' do + expect(services).to be_one_service('test-tag') + expect(services).to be_one_service(/test-tag/) + end + + it 'returns false from one_service? if there is a matching service without required credentials' do + expect(services).not_to be_one_service('test-tag', 'bad-credential') + expect(services).not_to be_one_service(/test-tag/, 'bad-credential') + end + + it 'returns true from one_service? if there is a matching service with required credentials' do + expect(services).to be_one_service('test-tag', 'uri') + expect(services).to be_one_service(/test-tag/, 'uri') + end + + it 'returns true from one_service? if there is a matching service with one required group credentials' do + expect(services).to be_one_service('test-tag', %w[uri other]) + expect(services).to be_one_service(/test-tag/, %w[uri other]) + end + + it 'returns true from one_service? if there is a matching service with two required group credentials' do + expect(services).to be_one_service('test-tag', %w[h1 h2]) + expect(services).to be_one_service(/test-tag/, %w[h1 h2]) + end + + it 'returns false from one_service? if there is a matching service with no required group credentials' do + expect(services).not_to be_one_service('test-tag', %w[foo bar]) + expect(services).not_to be_one_service(/test-tag/, %w[foo bar]) + end + + end + end - it 'returns service from find_service? if there is a matching tag' do - expect(services.find_service 'test-tag').to be(service) - expect(services.find_service(/test-tag/)).to be(service) + context('with one_volume_service') do + + context('with no volume mounts') do + let(:service_payload) do + [{ 'name' => 'test-name', 'label' => 'test-label', 'tags' => ['test-tag'], 'plan' => 'test-plan', + 'credentials' => { 'uri' => 'test-uri', 'h1' => 'foo', 'h2' => 'foo' } }] + end + + it 'returns true from one_volume_service? if there is a matching name and empty volume_mounts' do + expect(services).not_to be_one_volume_service('test-name') + expect(services).not_to be_one_volume_service(/test-name/) + end + + it 'returns true from one_volume_service? if there is a matching label and empty volume_mounts' do + expect(services).not_to be_one_volume_service('test-label') + expect(services).not_to be_one_volume_service(/test-label/) + end + + it 'returns false from one_volume_service? if there is a matching tag and empty volume_mounts' do + expect(services).not_to be_one_volume_service('test-tag') + expect(services).not_to be_one_volume_service(/test-tag/) + end + + end + + context('with empty volume mounts') do + let(:service_payload) do + [{ 'name' => 'test-name', 'label' => 'test-label', 'tags' => ['test-tag'], 'plan' => 'test-plan', + 'credentials' => { 'uri' => 'test-uri', 'h1' => 'foo', 'h2' => 'foo' }, + 'volume_mounts' => [] }] + end + + it 'returns true from one_volume_service? if there is a matching name and empty volume_mounts' do + expect(services).not_to be_one_volume_service('test-name') + expect(services).not_to be_one_volume_service(/test-name/) + end + + it 'returns true from one_volume_service? if there is a matching label and empty volume_mounts' do + expect(services).not_to be_one_volume_service('test-label') + expect(services).not_to be_one_volume_service(/test-label/) + end + + it 'returns false from one_volume_service? if there is a matching tag and empty volume_mounts' do + expect(services).not_to be_one_volume_service('test-tag') + expect(services).not_to be_one_volume_service(/test-tag/) + end + + end + + context('with one volume mount') do + let(:service_payload) do + [{ 'name' => 'test-name', 'label' => 'test-label', 'tags' => ['test-tag'], 'plan' => 'test-plan', + 'credentials' => { 'uri' => 'test-uri', 'h1' => 'foo', 'h2' => 'foo' }, + 'volume_mounts' => [{ 'container_dir' => '/var/vcap/data/9ae0b817-1446-4915-9990-74c1bb26f147', + 'device_type' => 'shared', + 'mode' => 'rw' }] }] + end + + it 'returns true from one_volume_service? if there is a matching name and empty volume_mounts' do + expect(services).to be_one_volume_service('test-name') + expect(services).to be_one_volume_service(/test-name/) + end + + it 'returns true from one_volume_service? if there is a matching label and empty volume_mounts' do + expect(services).to be_one_volume_service('test-label') + expect(services).to be_one_volume_service(/test-label/) + end + + it 'returns false from one_volume_service? if there is a matching tag and empty volume_mounts' do + expect(services).to be_one_volume_service('test-tag') + expect(services).to be_one_volume_service(/test-tag/) + end + + end + + context('with two volume mounts') do + let(:service_payload) do + [{ 'name' => 'test-name', 'label' => 'test-label', 'tags' => ['test-tag'], 'plan' => 'test-plan', + 'credentials' => { 'uri' => 'test-uri', 'h1' => 'foo', 'h2' => 'foo' }, + 'volume_mounts' => [{ 'container_dir' => '/var/vcap/data/9ae0b817-1446-4915-9990-74c1bb26f147', + 'device_type' => 'shared', + 'mode' => 'rw' }, + { 'container_dir' => '/var/vcap/data/9ae0b817-1446-4915-9990-74c1bb26f147', + 'device_type' => 'shared', + 'mode' => 'rw' }] }] + end + + it 'returns true from one_volume_service? if there is a matching name and empty volume_mounts' do + expect(services).not_to be_one_volume_service('test-name') + expect(services).not_to be_one_volume_service(/test-name/) + end + + it 'returns true from one_volume_service? if there is a matching label and empty volume_mounts' do + expect(services).not_to be_one_volume_service('test-label') + expect(services).not_to be_one_volume_service(/test-label/) + end + + it 'returns false from one_volume_service? if there is a matching tag and empty volume_mounts' do + expect(services).not_to be_one_volume_service('test-tag') + expect(services).not_to be_one_volume_service(/test-tag/) + end + + end + end end diff --git a/spec/java_buildpack/component/versioned_dependency_component_spec.rb b/spec/java_buildpack/component/versioned_dependency_component_spec.rb index 87ceb72b48..87e502be1d 100644 --- a/spec/java_buildpack/component/versioned_dependency_component_spec.rb +++ b/spec/java_buildpack/component/versioned_dependency_component_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,14 +20,14 @@ require 'java_buildpack/component/versioned_dependency_component' describe JavaBuildpack::Component::VersionedDependencyComponent do - include_context 'component_helper' + include_context 'with component help' let(:component) { StubVersionedDependencyComponent.new context } it 'fails if methods are unimplemented' do - expect { component.compile }.to raise_error - expect { component.release }.to raise_error - expect { component.supports? }.to raise_error + expect { component.compile }.to raise_error RuntimeError + expect { component.release }.to raise_error RuntimeError + expect { component.supports? }.to raise_error RuntimeError end context do diff --git a/spec/java_buildpack/container/dist_zip_like_spec.rb b/spec/java_buildpack/container/dist_zip_like_spec.rb index aecdaf9bf5..6130eb67cd 100644 --- a/spec/java_buildpack/container/dist_zip_like_spec.rb +++ b/spec/java_buildpack/container/dist_zip_like_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +20,7 @@ require 'java_buildpack/container/dist_zip_like' describe JavaBuildpack::Container::DistZipLike do - include_context 'component_helper' + include_context 'with component help' it 'raises error if id method is unimplemented' do expect { component.send(:id) }.to raise_error "Method 'id' must be defined" @@ -35,7 +36,8 @@ component.compile expect((app_dir + 'bin/application').read) - .to match 'CLASSPATH=\$APP_HOME/.additional_libs/test-jar-1.jar:\$APP_HOME/.additional_libs/test-jar-2.jar:' + .to match 'CLASSPATH=\$APP_HOME/.additional_libs/test-jar-1.jar:\$APP_HOME/.additional_libs/test-jar-2.jar:' \ + '\$APP_HOME/.root_libs/test-jar-3.jar:\$APP_HOME/.root_libs/test-jar-4.jar:' end it 'extends the app_classpath', @@ -45,13 +47,25 @@ expect((app_dir + 'application-root/bin/application').read) .to match 'declare -r app_classpath="\$app_home/../../.additional_libs/test-jar-1.jar:' \ - '\$app_home/../../.additional_libs/test-jar-2.jar:' + '\$app_home/../../.additional_libs/test-jar-2.jar:\$app_home/../../.root_libs/test-jar-3.jar:' \ + '\$app_home/../../.root_libs/test-jar-4.jar:' end it 'returns command', app_fixture: 'container_dist_zip' do - expect(component.release).to eq("#{java_home.as_env_var} JAVA_OPTS=\"test-opt-2 test-opt-1\" $PWD/bin/application") + expect(component.release).to eq("test-var-2 test-var-1 JAVA_OPTS=$JAVA_OPTS #{java_home.as_env_var} exec " \ + '$PWD/bin/application') + end + + context do + let(:configuration) { { 'arguments' => 'some arguments' } } + + it 'returns command line arguments when they are specified', + app_fixture: 'container_dist_zip' do + + expect(component.release).to end_with('some arguments') + end end end diff --git a/spec/java_buildpack/container/dist_zip_spec.rb b/spec/java_buildpack/container/dist_zip_spec.rb index 0dad661a94..6c9551cfa1 100644 --- a/spec/java_buildpack/container/dist_zip_spec.rb +++ b/spec/java_buildpack/container/dist_zip_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +20,7 @@ require 'java_buildpack/container/dist_zip' describe JavaBuildpack::Container::DistZip do - include_context 'component_helper' + include_context 'with component help' it 'detects a distZip application', app_fixture: 'container_dist_zip' do @@ -51,4 +52,14 @@ expect(component.detect).to be_nil end + context do + let(:configuration) { { 'arguments' => 'some arguments' } } + + it 'returns command line arguments when they are specified', + app_fixture: 'container_dist_zip' do + + expect(component.release).to end_with('some arguments') + end + end + end diff --git a/spec/java_buildpack/container/groovy_spec.rb b/spec/java_buildpack/container/groovy_spec.rb index f60a27f8f4..5c9b96ec0f 100644 --- a/spec/java_buildpack/container/groovy_spec.rb +++ b/spec/java_buildpack/container/groovy_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +20,7 @@ require 'java_buildpack/container/groovy' describe JavaBuildpack::Container::Groovy do - include_context 'component_helper' + include_context 'with component help' it 'does not detect a non-Groovy project', app_fixture: 'container_main' do @@ -80,7 +81,7 @@ end it 'extracts Groovy from a ZIP', - app_fixture: 'container_groovy_main_method', + app_fixture: 'container_groovy_main_method', cache_fixture: 'stub-groovy.zip' do component.compile @@ -91,23 +92,21 @@ it 'returns command', app_fixture: 'container_groovy_main_method' do - expect(component.release).to eq("#{java_home.as_env_var} JAVA_OPTS=#{java_opts_str} " \ + expect(component.release).to eq("test-var-2 test-var-1 JAVA_OPTS=$JAVA_OPTS #{java_home.as_env_var} exec " \ '$PWD/.java-buildpack/groovy/bin/groovy -cp $PWD/.additional_libs/test-jar-1.jar:' \ - '$PWD/.additional_libs/test-jar-2.jar Application.groovy Alpha.groovy ' \ + '$PWD/.additional_libs/test-jar-2.jar:$PWD/.root_libs/test-jar-3.jar:' \ + '$PWD/.root_libs/test-jar-4.jar Application.groovy Alpha.groovy ' \ 'directory/Beta.groovy invalid.groovy') end it 'returns command with included JARs', app_fixture: 'container_groovy_with_jars' do - expect(component.release).to eq("#{java_home.as_env_var} JAVA_OPTS=#{java_opts_str} " \ + expect(component.release).to eq("test-var-2 test-var-1 JAVA_OPTS=$JAVA_OPTS #{java_home.as_env_var} exec " \ '$PWD/.java-buildpack/groovy/bin/groovy -cp $PWD/.additional_libs/test-jar-1.jar:' \ - '$PWD/.additional_libs/test-jar-2.jar:$PWD/Alpha.jar:$PWD/directory/Beta.jar ' \ + '$PWD/.additional_libs/test-jar-2.jar:$PWD/Alpha.jar:$PWD/directory/Beta.jar:' \ + '$PWD/.root_libs/test-jar-3.jar:$PWD/.root_libs/test-jar-4.jar ' \ 'Application.groovy invalid.groovy') end - def java_opts_str - "\"#{java_opts.join(' ')}\"" - end - end diff --git a/spec/java_buildpack/container/java_main_spec.rb b/spec/java_buildpack/container/java_main_spec.rb index 2ae241a38f..b743e29897 100644 --- a/spec/java_buildpack/container/java_main_spec.rb +++ b/spec/java_buildpack/container/java_main_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,16 +18,18 @@ require 'spec_helper' require 'component_helper' require 'java_buildpack/container/java_main' +require 'java_buildpack/util/qualify_path' describe JavaBuildpack::Container::JavaMain do - include_context 'component_helper' + include JavaBuildpack::Util + include_context 'with component help' - shared_context 'explicit_main_class' do + shared_context 'with explicit main class' do let(:configuration) { { 'java_main_class' => 'test-java-main-class' } } end context do - include_context 'explicit_main_class' + include_context 'with explicit main class' it 'detects with main class configuration' do @@ -52,23 +55,55 @@ expect(component.detect).to be_nil end + it 'links additional libraries to the lib directory', + app_fixture: 'container_main_spring_boot_jar_launcher' do + + component.compile + + lib = app_dir + 'lib' + + test_jar1 = lib + 'test-jar-1.jar' + test_jar2 = lib + 'test-jar-2.jar' + expect(test_jar1).to exist + expect(test_jar1).to be_symlink + expect(test_jar1.readlink).to eq((additional_libs_directory + 'test-jar-1.jar').relative_path_from(lib)) + + expect(test_jar2).to exist + expect(test_jar2).to be_symlink + expect(test_jar2.readlink).to eq((additional_libs_directory + 'test-jar-2.jar').relative_path_from(lib)) + end + + it 'caches Spring Boot Thin Launcher dependencies', + app_fixture: 'container_main_spring_boot_thin_launcher' do + + expect_any_instance_of(JavaBuildpack::Util::SpringBootUtils).to receive(:cache_thin_dependencies) + .with(java_home.root, application.root, sandbox + 'repository') + + component.compile + end + context do - include_context 'explicit_main_class' + include_context 'with explicit main class' it 'returns command' do - expect(component.release).to eq("#{java_home.root}/bin/java -cp $PWD/.:$PWD/.additional_libs/test-jar-1.jar:" \ - "$PWD/.additional_libs/test-jar-2.jar #{java_opts_str} " \ - 'test-java-main-class') + expect(component.release).to eq('test-var-2 test-var-1 ' \ + "eval exec #{qualify_path java_home.root, droplet.root}/bin/java $JAVA_OPTS " \ + '-cp $PWD/.:$PWD/.additional_libs/test-jar-1.jar:$PWD/' \ + '.additional_libs/test-jar-2.jar:$PWD/.root_libs/test-jar-3.jar:' \ + '$PWD/.root_libs/test-jar-4.jar test-java-main-class') end end it 'returns additional classpath entries when Class-Path is specified', app_fixture: 'container_main' do - expect(component.release).to eq("#{java_home.root}/bin/java -cp $PWD/.:$PWD/.additional_libs/test-jar-1.jar:" \ - '$PWD/.additional_libs/test-jar-2.jar:$PWD/alpha.jar:$PWD/bravo.jar:' \ - "$PWD/charlie.jar #{java_opts_str} test-main-class") + expect(component.release).to eq('test-var-2 test-var-1 ' \ + "eval exec #{qualify_path java_home.root, droplet.root}/bin/java $JAVA_OPTS " \ + '-cp $PWD/.:$PWD/.additional_libs/test-jar-1.jar:$PWD/' \ + '.additional_libs/test-jar-2.jar:$PWD/alpha.jar:$PWD/bravo.jar:$PWD/' \ + 'charlie.jar:$PWD/.root_libs/test-jar-3.jar:$PWD/.root_libs/test-jar-4.jar ' \ + 'test-main-class') end context do @@ -76,71 +111,48 @@ it 'returns command line arguments when they are specified' do - expect(component.release).to eq("#{java_home.root}/bin/java -cp $PWD/.:$PWD/.additional_libs/test-jar-1.jar:" \ - "$PWD/.additional_libs/test-jar-2.jar #{java_opts_str} " \ - 'test-java-main-class some arguments') + expect(component.release).to eq('test-var-2 test-var-1 ' \ + "eval exec #{qualify_path java_home.root, droplet.root}/bin/java $JAVA_OPTS " \ + '-cp $PWD/.:$PWD/.additional_libs/test-jar-1.jar:$PWD/.additional_libs/' \ + 'test-jar-2.jar:$PWD/.root_libs/test-jar-3.jar:' \ + '$PWD/.root_libs/test-jar-4.jar test-java-main-class some arguments') end end it 'releases Spring boot applications with a JarLauncher in the MANIFEST.MF by specifying a port', app_fixture: 'container_main_spring_boot_jar_launcher' do - expect(component.release).to eq("SERVER_PORT=$PORT #{java_home.root}/bin/java -cp $PWD/.:" \ - '$PWD/.additional_libs/test-jar-1.jar:$PWD/.additional_libs/test-jar-2.jar ' \ - "#{java_opts_str} org.springframework.boot.loader.JarLauncher") + expect(component.release).to eq('test-var-2 test-var-1 SERVER_PORT=$PORT ' \ + "eval exec #{qualify_path java_home.root, droplet.root}/bin/java $JAVA_OPTS " \ + '-cp $PWD/.:$PWD/.root_libs/test-jar-3.jar:$PWD/.root_libs/test-jar-4.jar ' \ + 'org.springframework.boot.loader.JarLauncher') end it 'releases Spring boot applications with a WarLauncher in the MANIFEST.MF by specifying a port', app_fixture: 'container_main_spring_boot_war_launcher' do - expect(component.release).to eq("SERVER_PORT=$PORT #{java_home.root}/bin/java -cp $PWD/.:" \ - '$PWD/.additional_libs/test-jar-1.jar:$PWD/.additional_libs/test-jar-2.jar ' \ - "#{java_opts_str} org.springframework.boot.loader.WarLauncher") + expect(component.release).to eq('test-var-2 test-var-1 SERVER_PORT=$PORT ' \ + "eval exec #{qualify_path java_home.root, droplet.root}/bin/java $JAVA_OPTS " \ + '-cp $PWD/.:$PWD/.root_libs/test-jar-3.jar:$PWD/.root_libs/test-jar-4.jar ' \ + 'org.springframework.boot.loader.WarLauncher') end it 'releases Spring boot applications with a PropertiesLauncher in the MANIFEST.MF by specifying a port', app_fixture: 'container_main_spring_boot_properties_launcher' do - expect(component.release).to eq("SERVER_PORT=$PORT #{java_home.root}/bin/java -cp $PWD/.:" \ - '$PWD/.additional_libs/test-jar-1.jar:$PWD/.additional_libs/test-jar-2.jar ' \ - "#{java_opts_str} org.springframework.boot.loader.PropertiesLauncher") - end - - context do - let(:configuration) { { 'java_main_class' => 'org.springframework.boot.loader.JarLauncher' } } - - it 'releases Spring boot applications with a JarLauncher in the configuration by specifying a port' do - - expect(component.release).to eq("SERVER_PORT=$PORT #{java_home.root}/bin/java -cp $PWD/.:" \ - '$PWD/.additional_libs/test-jar-1.jar:$PWD/.additional_libs/test-jar-2.jar ' \ - "#{java_opts_str} org.springframework.boot.loader.JarLauncher") - end - end - - context do - let(:configuration) { { 'java_main_class' => 'org.springframework.boot.loader.WarLauncher' } } - - it 'releases Spring boot applications with a WarLauncher in the configuration by specifying a port' do - - expect(component.release).to eq("SERVER_PORT=$PORT #{java_home.root}/bin/java -cp $PWD/.:" \ - '$PWD/.additional_libs/test-jar-1.jar:$PWD/.additional_libs/test-jar-2.jar ' \ - "#{java_opts_str} org.springframework.boot.loader.WarLauncher") - end + expect(component.release).to eq('test-var-2 test-var-1 SERVER_PORT=$PORT ' \ + "eval exec #{qualify_path java_home.root, droplet.root}/bin/java $JAVA_OPTS " \ + '-cp $PWD/.:$PWD/.root_libs/test-jar-3.jar:$PWD/.root_libs/test-jar-4.jar ' \ + 'org.springframework.boot.loader.PropertiesLauncher') end - context do - let(:configuration) { { 'java_main_class' => 'org.springframework.boot.loader.PropertiesLauncher' } } + it 'releases Spring Boot thin applications by specifying thin.root', + app_fixture: 'container_main_spring_boot_thin_launcher' do - it 'releases Spring boot applications with a PropertiesLauncher in the configuration by specifying a port' do - - expect(component.release).to eq("SERVER_PORT=$PORT #{java_home.root}/bin/java -cp $PWD/.:" \ - '$PWD/.additional_libs/test-jar-1.jar:$PWD/.additional_libs/test-jar-2.jar ' \ - "#{java_opts_str} org.springframework.boot.loader.PropertiesLauncher") - end - end + component.release - def java_opts_str - java_opts.join(' ') + expect(java_opts).to include('-Dthin.offline=true') + expect(java_opts).to include('-Dthin.root=$PWD/.java-buildpack/java_main/repository') end end diff --git a/spec/java_buildpack/container/play_framework_spec.rb b/spec/java_buildpack/container/play_framework_spec.rb index 12104eed8f..2f3602f151 100644 --- a/spec/java_buildpack/container/play_framework_spec.rb +++ b/spec/java_buildpack/container/play_framework_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,9 +21,9 @@ require 'java_buildpack/util/play/factory' describe JavaBuildpack::Container::PlayFramework do - include_context 'component_helper' + include_context 'with component help' - let(:delegate) { double('delegate') } + let(:delegate) { instance_double('delegate') } context do @@ -31,19 +32,19 @@ end it 'delegates detect' do - expect(delegate).to receive(:version).and_return('0.0.0') + allow(delegate).to receive(:version).and_return('0.0.0') expect(component.detect).to eq('play-framework=0.0.0') end it 'delegates compile' do - expect(delegate).to receive(:compile) + allow(delegate).to receive(:compile) component.compile end it 'delegates release' do - expect(delegate).to receive(:release) + allow(delegate).to receive(:release) component.release end diff --git a/spec/java_buildpack/container/ratpack_spec.rb b/spec/java_buildpack/container/ratpack_spec.rb index 04f76d9ac4..ba3999c243 100644 --- a/spec/java_buildpack/container/ratpack_spec.rb +++ b/spec/java_buildpack/container/ratpack_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +20,7 @@ require 'java_buildpack/container/ratpack' describe JavaBuildpack::Container::Ratpack do - include_context 'component_helper' + include_context 'with component help' it 'detects a dist Ratpack application', app_fixture: 'container_ratpack_dist' do @@ -63,13 +64,15 @@ component.compile expect((app_dir + 'bin/application').read) - .to match 'CLASSPATH=\$APP_HOME/.additional_libs/test-jar-1.jar:\$APP_HOME/.additional_libs/test-jar-2.jar:' + .to match 'CLASSPATH=\$APP_HOME/.additional_libs/test-jar-1.jar:\$APP_HOME/.additional_libs/test-jar-2.jar:' \ + '\$APP_HOME/.root_libs/test-jar-3.jar:\$APP_HOME/.root_libs/test-jar-4.jar:' end it 'returns command', app_fixture: 'container_ratpack_staged' do - expect(component.release).to eq("#{java_home.as_env_var} JAVA_OPTS=\"test-opt-2 test-opt-1\" $PWD/bin/application") + expect(component.release).to eq("test-var-2 test-var-1 JAVA_OPTS=$JAVA_OPTS #{java_home.as_env_var} exec " \ + '$PWD/bin/application') end end diff --git a/spec/java_buildpack/container/spring_boot_cli_spec.rb b/spec/java_buildpack/container/spring_boot_cli_spec.rb index 9377a96036..ae684b1cb9 100644 --- a/spec/java_buildpack/container/spring_boot_cli_spec.rb +++ b/spec/java_buildpack/container/spring_boot_cli_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +20,7 @@ require 'java_buildpack/container/spring_boot_cli' describe JavaBuildpack::Container::SpringBootCLI do - include_context 'component_helper' + include_context 'with component help' it 'does not detect a non-Groovy project', app_fixture: 'container_main' do @@ -51,6 +52,12 @@ expect(component.detect).to be_nil end + it 'does not detect Logback Groovy files', + app_fixture: 'container_groovy_logback' do + + expect(component.detect).to be_nil + end + it 'does not detect a Groovy file which has a shebang but which also contains a class', app_fixture: 'container_groovy_shebang_containing_class' do @@ -76,7 +83,7 @@ end it 'extracts Spring Boot CLI from a ZIP', - app_fixture: 'container_spring_boot_cli_valid_app', + app_fixture: 'container_spring_boot_cli_valid_app', cache_fixture: 'stub-spring-boot-cli.tar.gz' do component.compile @@ -87,12 +94,17 @@ it 'returns command', app_fixture: 'container_spring_boot_cli_valid_app' do - expect(component.release).to eq("#{java_home.as_env_var} JAVA_OPTS=#{java_opts_str} SERVER_PORT=$PORT " \ - '$PWD/.java-buildpack/spring_boot_cli/bin/spring run ' \ - '-cp $PWD/.additional_libs/test-jar-1.jar:$PWD/.additional_libs/test-jar-2.jar ' \ + expect(component.release).to eq("#{env_vars_str} #{java_home.as_env_var} " \ + 'exec $PWD/.java-buildpack/spring_boot_cli/bin/spring run ' \ + '-cp $PWD/.additional_libs/test-jar-1.jar:$PWD/.additional_libs/test-jar-2.jar:' \ + '$PWD/.root_libs/test-jar-3.jar:$PWD/.root_libs/test-jar-4.jar ' \ 'directory/pogo_4.groovy invalid.groovy pogo_1.groovy pogo_2.groovy pogo_3.groovy') end + def env_vars_str + environment_variables.join(' ') + end + def java_opts_str "\"#{java_opts.join(' ')}\"" end diff --git a/spec/java_buildpack/container/spring_boot_spec.rb b/spec/java_buildpack/container/spring_boot_spec.rb index 09b6ce1093..45d2b19281 100644 --- a/spec/java_buildpack/container/spring_boot_spec.rb +++ b/spec/java_buildpack/container/spring_boot_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +20,7 @@ require 'java_buildpack/container/spring_boot' describe JavaBuildpack::Container::SpringBoot do - include_context 'component_helper' + include_context 'with component help' it 'detects a dist Spring Boot application', app_fixture: 'container_spring_boot_dist' do @@ -63,14 +64,22 @@ component.compile expect((app_dir + 'bin/application').read) - .to match 'CLASSPATH=\$APP_HOME/.additional_libs/test-jar-1.jar:\$APP_HOME/.additional_libs/test-jar-2.jar:' + .to match 'CLASSPATH=\$APP_HOME/.additional_libs/test-jar-1.jar:\$APP_HOME/.additional_libs/test-jar-2.jar:' \ + '\$APP_HOME/.root_libs/test-jar-3.jar:\$APP_HOME/.root_libs/test-jar-4.jar:' end it 'returns command', app_fixture: 'container_spring_boot_staged' do - expect(component.release).to eq("SERVER_PORT=$PORT #{java_home.as_env_var} JAVA_OPTS=\"test-opt-2 test-opt-1\" " \ - '$PWD/bin/application') + expect(component.release).to eq("#{env_vars_str} #{java_home.as_env_var} exec $PWD/bin/application") + end + + def env_vars_str + environment_variables.join(' ') + end + + def java_opts_str + "\"#{java_opts.join(' ')}\"" end end diff --git a/spec/java_buildpack/container/tomcat/gemfire/gemfire_logging_spec.rb b/spec/java_buildpack/container/tomcat/gemfire/gemfire_logging_spec.rb deleted file mode 100644 index 10e2b74435..0000000000 --- a/spec/java_buildpack/container/tomcat/gemfire/gemfire_logging_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'spec_helper' -require 'component_helper' -require 'java_buildpack/container/tomcat/gemfire/gemfire_logging' - -describe JavaBuildpack::Container::GemFireLogging do - include_context 'component_helper' - - let(:component_id) { 'tomcat' } - - it 'always detects' do - expect(component.detect).to eq("gem-fire-logging=#{version}") - end - - it 'copies resources', - cache_fixture: 'stub-gemfire-slf4j-jdk14.jar' do - - component.compile - - expect(sandbox + "lib/slf4j-jdk14-#{version}.jar").to exist - end - - it 'does nothing during release' do - component.release - end - -end diff --git a/spec/java_buildpack/container/tomcat/gemfire/gemfire_modules_spec.rb b/spec/java_buildpack/container/tomcat/gemfire/gemfire_modules_spec.rb deleted file mode 100644 index 19611ed9a0..0000000000 --- a/spec/java_buildpack/container/tomcat/gemfire/gemfire_modules_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'spec_helper' -require 'component_helper' -require 'java_buildpack/container/tomcat/gemfire/gemfire_modules' - -describe JavaBuildpack::Container::GemFireModules do - include_context 'component_helper' - - let(:component_id) { 'tomcat' } - - it 'always detects' do - expect(component.detect).to eq("gem-fire-modules=#{version}") - end - - it 'copies resources', - cache_fixture: 'stub-gemfire-modules.jar' do - - component.compile - - expect(sandbox + "lib/gemfire-modules-#{version}.jar").to exist - end - - it 'does nothing during release' do - component.release - end - -end diff --git a/spec/java_buildpack/container/tomcat/gemfire/gemfire_modules_tomcat7_spec.rb b/spec/java_buildpack/container/tomcat/gemfire/gemfire_modules_tomcat7_spec.rb deleted file mode 100644 index 9ae3ade253..0000000000 --- a/spec/java_buildpack/container/tomcat/gemfire/gemfire_modules_tomcat7_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'spec_helper' -require 'component_helper' -require 'java_buildpack/container/tomcat/gemfire/gemfire_modules_tomcat7' - -describe JavaBuildpack::Container::GemFireModulesTomcat7 do - include_context 'component_helper' - - let(:component_id) { 'tomcat' } - - it 'always detects' do - expect(component.detect).to eq("gem-fire-modules-tomcat7=#{version}") - end - - it 'copies resources', - cache_fixture: 'stub-gemfire-modules-tomcat7.jar' do - - component.compile - - expect(sandbox + "lib/gemfire-modules-tomcat7-#{version}.jar").to exist - end - - it 'does nothing during release' do - component.release - end - -end diff --git a/spec/java_buildpack/container/tomcat/gemfire/gemfire_security_spec.rb b/spec/java_buildpack/container/tomcat/gemfire/gemfire_security_spec.rb deleted file mode 100644 index ca3fc077d0..0000000000 --- a/spec/java_buildpack/container/tomcat/gemfire/gemfire_security_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'spec_helper' -require 'component_helper' -require 'java_buildpack/container/tomcat/gemfire/gemfire_security' - -describe JavaBuildpack::Container::GemFireSecurity do - include_context 'component_helper' - - let(:component_id) { 'tomcat' } - - it 'always detects' do - expect(component.detect).to eq("gem-fire-security=#{version}") - end - - it 'copies resources', - cache_fixture: 'stub-gemfire-security.jar' do - - component.compile - - expect(sandbox + "lib/gemfire-security-#{version}.jar").to exist - end - - it 'does nothing during release' do - component.release - end - -end diff --git a/spec/java_buildpack/container/tomcat/gemfire/gemfire_spec.rb b/spec/java_buildpack/container/tomcat/gemfire/gemfire_spec.rb deleted file mode 100644 index 9ea6acc099..0000000000 --- a/spec/java_buildpack/container/tomcat/gemfire/gemfire_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'spec_helper' -require 'component_helper' -require 'java_buildpack/container/tomcat/gemfire/gemfire' - -describe JavaBuildpack::Container::GemFire do - include_context 'component_helper' - - let(:component_id) { 'tomcat' } - - it 'always detects' do - expect(component.detect).to eq("gem-fire=#{version}") - end - - it 'copies resources', - cache_fixture: 'stub-gemfire.jar' do - - component.compile - - expect(sandbox + "lib/gemfire-#{version}.jar").to exist - end - - it 'does nothing during release' do - component.release - end - -end diff --git a/spec/java_buildpack/container/tomcat/tomcat_access_logging_support_spec.rb b/spec/java_buildpack/container/tomcat/tomcat_access_logging_support_spec.rb index 398f0de608..244f7fa9b3 100644 --- a/spec/java_buildpack/container/tomcat/tomcat_access_logging_support_spec.rb +++ b/spec/java_buildpack/container/tomcat/tomcat_access_logging_support_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +20,7 @@ require 'java_buildpack/container/tomcat/tomcat_access_logging_support' describe JavaBuildpack::Container::TomcatAccessLoggingSupport do - include_context 'component_helper' + include_context 'with component help' let(:component_id) { 'tomcat' } diff --git a/spec/java_buildpack/container/tomcat/tomcat_external_configuration_spec.rb b/spec/java_buildpack/container/tomcat/tomcat_external_configuration_spec.rb new file mode 100644 index 0000000000..fb97731e91 --- /dev/null +++ b/spec/java_buildpack/container/tomcat/tomcat_external_configuration_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'internet_availability_helper' +require 'java_buildpack/container/tomcat/tomcat_external_configuration' + +describe JavaBuildpack::Container::TomcatExternalConfiguration do + include_context 'with component help' + + let(:component_id) { 'tomcat' } + + it 'always detects' do + expect(component.detect).to eq("tomcat-external-configuration=#{version}") + end + + it 'does guarantee that internet access is available when downloading', + app_fixture: 'container_tomcat', + cache_fixture: 'stub-tomcat-external-configuration.tar.gz' do + + expect_any_instance_of(JavaBuildpack::Util::Cache::InternetAvailability) + .to receive(:available).with(true, 'The Tomcat External Configuration download location is always accessible') + .twice + + component.compile + end + + it 'extracts Tomcat external configuration files from a GZipped TAR', + app_fixture: 'container_tomcat', + cache_fixture: 'stub-tomcat-external-configuration.tar.gz' do + + component.compile + + expect(sandbox + 'conf/context.xml').to exist + expect(sandbox + 'conf/server.xml').to exist + end + +end diff --git a/spec/java_buildpack/container/tomcat/tomcat_gemfire_store_spec.rb b/spec/java_buildpack/container/tomcat/tomcat_gemfire_store_spec.rb deleted file mode 100644 index 08c89d2473..0000000000 --- a/spec/java_buildpack/container/tomcat/tomcat_gemfire_store_spec.rb +++ /dev/null @@ -1,139 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'spec_helper' -require 'component_helper' -require 'java_buildpack/container/tomcat/tomcat_gemfire_store' -require 'java_buildpack/container/tomcat/gemfire/gemfire' -require 'java_buildpack/container/tomcat/gemfire/gemfire_logging' -require 'java_buildpack/container/tomcat/gemfire/gemfire_logging_api' -require 'java_buildpack/container/tomcat/gemfire/gemfire_modules' -require 'java_buildpack/container/tomcat/gemfire/gemfire_modules_tomcat7' -require 'java_buildpack/container/tomcat/gemfire/gemfire_security' - -describe JavaBuildpack::Container::TomcatGemfireStore do - include_context 'component_helper' - - let(:component_id) { 'tomcat' } - - let(:component) { StubGemfireStore.new context } - - let(:configuration) do - { 'gemfire' => gemfire_configuration, - 'gemfire_modules' => gemfire_modules_configuration, - 'gemfire_modules_tomcat7' => gemfire_modules_tomcat7_configuration, - 'gemfire_security' => gemfire_security_configuration, - 'gemfire_logging' => gemfire_logging_configuration, - 'gemfire_logging_api' => gemfire_logging_api_configuration - } - end - - let(:gemfire_configuration) { double('gemfire-configuration') } - - let(:gemfire_modules_configuration) { double('gemfire-modules-configuration') } - - let(:gemfire_modules_tomcat7_configuration) { double('gemfire-modules_tomcat7-configuration') } - - let(:gemfire_security_configuration) { double('gemfire-security-configuration') } - - let(:gemfire_logging_configuration) { double('gemfire-logging-configuration') } - - let(:gemfire_logging_api_configuration) { double('gemfire-logging-api-configuration') } - - it 'does not detect without a session_replication service' do - expect(component.detect).to be_nil - end - - it 'does nothing without a session_replication service during release' do - expect(component.command).to be_nil - end - - it 'creates submodules' do - expect(JavaBuildpack::Container::GemFire) - .to receive(:new).with(sub_configuration_context(gemfire_configuration)) - expect(JavaBuildpack::Container::GemFireModules) - .to receive(:new).with(sub_configuration_context(gemfire_modules_configuration)) - expect(JavaBuildpack::Container::GemFireModulesTomcat7) - .to receive(:new).with(sub_configuration_context(gemfire_modules_tomcat7_configuration)) - expect(JavaBuildpack::Container::GemFireSecurity) - .to receive(:new).with(sub_configuration_context(gemfire_security_configuration)) - expect(JavaBuildpack::Container::GemFireLogging) - .to receive(:new).with(sub_configuration_context(gemfire_logging_configuration)) - expect(JavaBuildpack::Container::GemFireLoggingApi) - .to receive(:new).with(sub_configuration_context(gemfire_logging_api_configuration)) - - component.sub_components context - end - - context do - - before do - allow(services).to receive(:one_service?).with(/session_replication/, 'locators', 'username', 'password') - .and_return(true) - allow(services).to receive(:find_service).and_return('credentials' => { 'locators' => %w(1.0.0.2[45] 1.0.0.4[54]), - 'username' => 'test-username', - 'password' => 'test-password' }) - allow(application_cache).to receive(:get).with('test-uri').and_return('f') - end - - it 'detect with a session_replication service' do - expect(component.detect).to be - end - - # rubocop:disable Metrics/LineLength - it 'returns command' do - expect(component.command).to eq(%w(test-opt-2 - test-opt-1 - -Dgemfire.security-username=test-username - -Dgemfire.security-password=test-password - -Dgemfire.security-client-auth-init=templates.security.UserPasswordAuthInit.create)) - end - # rubocop:enable Metrics/LineLength - - it 'mutates context.xml', - app_fixture: 'container_tomcat_gemfire_store' do - component.compile - expect((sandbox + 'conf/context.xml').read) - .to eq(Pathname.new('spec/fixtures/container_tomcat_gemfire_store_context_after.xml').read) - end - - it 'mutates server.xml', - app_fixture: 'container_tomcat_gemfire_store' do - component.compile - expect((sandbox + 'conf/server.xml').read) - .to eq(Pathname.new('spec/fixtures/container_tomcat_gemfire_store_server_after.xml').read) - end - - it 'creates cache-client.xml', - app_fixture: 'container_tomcat_gemfire_store' do - component.compile - expect((sandbox + 'conf/cache-client.xml').read) - .to eq(Pathname.new('spec/fixtures/container_tomcat_gemfire_store_cache_client_after.xml').read) - end - - end - -end - -class StubGemfireStore < JavaBuildpack::Container::TomcatGemfireStore - public :command, :sub_components, :supports? -end - -def sub_configuration_context(configuration) - c = context.clone - c[:configuration] = configuration - c -end diff --git a/spec/java_buildpack/container/tomcat/tomcat_geode_store_spec.rb b/spec/java_buildpack/container/tomcat/tomcat_geode_store_spec.rb new file mode 100644 index 0000000000..8463b23156 --- /dev/null +++ b/spec/java_buildpack/container/tomcat/tomcat_geode_store_spec.rb @@ -0,0 +1,156 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2021 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/container/tomcat/tomcat_geode_store' + +describe JavaBuildpack::Container::TomcatGeodeStore do + include_context 'with component help' + + let(:component) { described_class.new(context, '9') } + + let(:component_id) { 'tomcat' } + + let(:configuration) do + { 'database' => 'test-database', + 'timeout' => 'test-timeout', + 'connection_pool_size' => 'test-connection-pool-size' } + end + + it 'does not detect without a session-replication service' do + expect(component.detect).to be_nil + end + + context 'when there is a session-replication service' do + before do + allow(services).to receive(:one_service?).with(/session-replication/, 'locators', 'users') + .and_return(true) + allow(services).to receive(:find_service).and_return( + 'credentials' => { + 'locators' => ['some-locator[some-port]', 'some-other-locator[some-other-port]'], + 'users' => + [ + { + 'password' => 'some-password', + 'username' => 'some-username', + 'roles' => ['cluster_operator'] + } + ] + } + ) + + end + + it 'detect with a session-replication service' do + expect(component.detect).to eq("tomcat-geode-store=#{version}") + end + + it 'copies resources', + app_fixture: 'container_tomcat_geode_store', + cache_fixture: 'stub-geode-store.tar' do + + component.compile + + expect(sandbox + 'lib/stub-jar-1.jar').to exist + expect(sandbox + 'lib/stub-jar-2.jar').to exist + expect(sandbox + 'lib/geode-modules-tomcat9.jar').to exist + end + + it 'mutates context.xml', + app_fixture: 'container_tomcat_geode_store', + cache_fixture: 'stub-geode-store.tar' do + + component.compile + + expect((sandbox + 'conf/context.xml').read) + .to eq(Pathname.new('spec/fixtures/container_tomcat_geode_store_context_after.xml').read) + end + + it 'prints warning when Tomcat version in buildpack is different from Geode Tomcat module version', + app_fixture: 'container_tomcat_geode_store', + cache_fixture: 'stub-geode-store.tar' do + + component = described_class.new(context, '8') + + expect { component.compile }.to output( + # rubocop:disable Layout/LineLength + /WARNING: Tomcat version 8 does not match Geode Tomcat 9 module\. If you encounter compatibility issues, please make sure these versions match\./ + # rubocop:enable Layout/LineLength + ).to_stdout + end + + it 'does not add Geode Tomcat module version to Session Manager classname if version is empty', + app_fixture: 'container_tomcat_geode_store', + cache_fixture: 'stub-geode-store-no-tomcat-version.tar' do + + component.compile + + expect((sandbox + 'conf/context.xml').read) + .to eq(Pathname.new('spec/fixtures/container_no_tomcat_version_geode_store_context_after.xml').read) + end + + it 'raises runtime error if multiple Geode Tomcat module jars are detected', + app_fixture: 'container_tomcat_geode_store', + cache_fixture: 'stub-geode-store-tomcat-multi-version.tar' do + + # rubocop:disable Layout/LineLength + expect { component.compile }.to raise_error RuntimeError, 'Multiple versions of geode-modules-tomcat jar found. Please verify your geode_store tar only contains one geode-modules-tomcat jar.' + # rubocop:enable Layout/LineLength + end + + it 'raises runtime error if no Geode Tomcat module jar is detected', + app_fixture: 'container_tomcat_geode_store', + cache_fixture: 'stub-geode-store-no-geode-tomcat.tar' do + + # rubocop:disable Layout/LineLength + expect { component.compile }.to raise_error RuntimeError, 'Geode Tomcat module not found. Please verify your geode_store tar contains a geode-modules-tomcat jar.' + # rubocop:enable Layout/LineLength + end + + it 'mutates server.xml', + app_fixture: 'container_tomcat_geode_store', + cache_fixture: 'stub-geode-store.tar' do + + component.compile + + expect((sandbox + 'conf/server.xml').read) + .to eq(Pathname.new('spec/fixtures/container_tomcat_geode_store_server_after.xml').read) + end + + it 'adds a cache-client.xml', + app_fixture: 'container_tomcat_geode_store', + cache_fixture: 'stub-geode-store.tar' do + + component.compile + + expect((sandbox + 'conf/cache-client.xml').read) + .to eq(Pathname.new('spec/fixtures/container_tomcat_geode_store_cache_client_after.xml').read) + end + + it 'passes client auth class to the release', + app_fixture: 'container_tomcat_geode_store', + cache_fixture: 'stub-geode-store.tar' do + + component.release + + expect(java_opts).to include( + '-Dgemfire.security-client-auth-init=io.pivotal.cloudcache.ClientAuthInitialize.create' + ) + end + end +end diff --git a/spec/java_buildpack/container/tomcat/tomcat_insight_support_spec.rb b/spec/java_buildpack/container/tomcat/tomcat_insight_support_spec.rb index 391dd7de8f..78e8f604e9 100644 --- a/spec/java_buildpack/container/tomcat/tomcat_insight_support_spec.rb +++ b/spec/java_buildpack/container/tomcat/tomcat_insight_support_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,7 +21,7 @@ require 'java_buildpack/container/tomcat/tomcat_insight_support' describe JavaBuildpack::Container::TomcatInsightSupport do - include_context 'component_helper' + include_context 'with component help' let(:component_id) { 'tomcat' } diff --git a/spec/java_buildpack/container/tomcat/tomcat_instance_spec.rb b/spec/java_buildpack/container/tomcat/tomcat_instance_spec.rb index 4bc923f813..c65d0c2d88 100644 --- a/spec/java_buildpack/container/tomcat/tomcat_instance_spec.rb +++ b/spec/java_buildpack/container/tomcat/tomcat_instance_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +20,7 @@ require 'java_buildpack/container/tomcat/tomcat_instance' describe JavaBuildpack::Container::TomcatInstance do - include_context 'component_helper' + include_context 'with component help' let(:component_id) { 'tomcat' } @@ -38,7 +39,7 @@ end it 'extracts Tomcat from a GZipped TAR', - app_fixture: 'container_tomcat', + app_fixture: 'container_tomcat', cache_fixture: 'stub-tomcat.tar.gz' do component.compile @@ -49,31 +50,31 @@ end it 'configures for Tomcat 7', - app_fixture: 'container_tomcat', + app_fixture: 'container_tomcat', cache_fixture: 'stub-tomcat.tar.gz' do component.compile expect((sandbox + 'conf/context.xml').read).to match(//) expect((sandbox + 'conf/server.xml').read) - .to match(//) + .to match(%r{}) end context do let(:version) { '8.0.12' } it 'configures for Tomcat 8', - app_fixture: 'container_tomcat', + app_fixture: 'container_tomcat', cache_fixture: 'stub-tomcat.tar.gz' do component.compile - expect((sandbox + 'conf/context.xml').read).to match(/[\s]*/) + expect((sandbox + 'conf/context.xml').read).to match(%r{\s*}) expect((sandbox + 'conf/server.xml').read) - .not_to match(//) + .not_to match(%r{}) end end it 'links only the application files and directories to the ROOT webapp', - app_fixture: 'container_tomcat_with_index', + app_fixture: 'container_tomcat_with_index', cache_fixture: 'stub-tomcat.tar.gz' do FileUtils.touch(app_dir + '.test-file') @@ -95,21 +96,48 @@ expect(root_webapp + '.test-file').not_to exist end + context do + let(:configuration) { { 'context_path' => '/first-segment/second-segment' } } + + it 'links only the application files and directories to the first-segment#second-segment webapp', + app_fixture: 'container_tomcat_with_index', + cache_fixture: 'stub-tomcat.tar.gz' do + + FileUtils.touch(app_dir + '.test-file') + + component.compile + + root_webapp = app_dir + '.java-buildpack/tomcat/webapps/first-segment#second-segment' + + web_inf = root_webapp + 'WEB-INF' + expect(web_inf).to exist + expect(web_inf).to be_symlink + expect(web_inf.readlink).to eq((app_dir + 'WEB-INF').relative_path_from(root_webapp)) + + index = root_webapp + 'index.html' + expect(index).to exist + expect(index).to be_symlink + expect(index.readlink).to eq((app_dir + 'index.html').relative_path_from(root_webapp)) + + expect(root_webapp + '.test-file').not_to exist + end + end + it 'links the Tomcat datasource JAR to the ROOT webapp when that JAR is present', - app_fixture: 'container_tomcat', + app_fixture: 'container_tomcat', cache_fixture: 'stub-tomcat7.tar.gz' do component.compile web_inf_lib = app_dir + 'WEB-INF/lib' - app_jar = web_inf_lib + 'tomcat-jdbc.jar' + app_jar = web_inf_lib + 'tomcat-jdbc.jar' expect(app_jar).to exist expect(app_jar).to be_symlink expect(app_jar.readlink).to eq((sandbox + 'lib/tomcat-jdbc.jar').relative_path_from(web_inf_lib)) end it 'does not link the Tomcat datasource JAR to the ROOT webapp when that JAR is absent', - app_fixture: 'container_tomcat', + app_fixture: 'container_tomcat', cache_fixture: 'stub-tomcat.tar.gz' do component.compile @@ -119,22 +147,22 @@ end it 'links additional libraries to the ROOT webapp', - app_fixture: 'container_tomcat', + app_fixture: 'container_tomcat', cache_fixture: 'stub-tomcat.tar.gz' do component.compile web_inf_lib = app_dir + 'WEB-INF/lib' - test_jar_1 = web_inf_lib + 'test-jar-1.jar' - test_jar_2 = web_inf_lib + 'test-jar-2.jar' - expect(test_jar_1).to exist - expect(test_jar_1).to be_symlink - expect(test_jar_1.readlink).to eq((additional_libs_directory + 'test-jar-1.jar').relative_path_from(web_inf_lib)) + test_jar1 = web_inf_lib + 'test-jar-1.jar' + test_jar2 = web_inf_lib + 'test-jar-2.jar' + expect(test_jar1).to exist + expect(test_jar1).to be_symlink + expect(test_jar1.readlink).to eq((additional_libs_directory + 'test-jar-1.jar').relative_path_from(web_inf_lib)) - expect(test_jar_2).to exist - expect(test_jar_2).to be_symlink - expect(test_jar_2.readlink).to eq((additional_libs_directory + 'test-jar-2.jar').relative_path_from(web_inf_lib)) + expect(test_jar2).to exist + expect(test_jar2).to be_symlink + expect(test_jar2.readlink).to eq((additional_libs_directory + 'test-jar-2.jar').relative_path_from(web_inf_lib)) end end diff --git a/spec/java_buildpack/container/tomcat/tomcat_lifecycle_support_spec.rb b/spec/java_buildpack/container/tomcat/tomcat_lifecycle_support_spec.rb index 4b43ea19a5..4fb72d964b 100644 --- a/spec/java_buildpack/container/tomcat/tomcat_lifecycle_support_spec.rb +++ b/spec/java_buildpack/container/tomcat/tomcat_lifecycle_support_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +20,7 @@ require 'java_buildpack/container/tomcat/tomcat_lifecycle_support' describe JavaBuildpack::Container::TomcatLifecycleSupport do - include_context 'component_helper' + include_context 'with component help' let(:component_id) { 'tomcat' } diff --git a/spec/java_buildpack/container/tomcat/tomcat_logging_support_spec.rb b/spec/java_buildpack/container/tomcat/tomcat_logging_support_spec.rb index 731cafd41e..d1c10fa651 100644 --- a/spec/java_buildpack/container/tomcat/tomcat_logging_support_spec.rb +++ b/spec/java_buildpack/container/tomcat/tomcat_logging_support_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +20,7 @@ require 'java_buildpack/container/tomcat/tomcat_logging_support' describe JavaBuildpack::Container::TomcatLoggingSupport do - include_context 'component_helper' + include_context 'with component help' let(:component_id) { 'tomcat' } @@ -32,11 +33,15 @@ component.compile - expect(sandbox + "endorsed/tomcat_logging_support-#{version}.jar").to exist + expect(sandbox + "bin/tomcat_logging_support-#{version}.jar").to exist end - it 'does nothing during release' do - component.release + it 'downloads JAR', + cache_fixture: 'stub-logging-support.jar' do + + component.compile + + expect(droplet.root_libraries).to include(sandbox + "bin/tomcat_logging_support-#{version}.jar") end end diff --git a/spec/java_buildpack/container/tomcat/tomcat_redis_store_spec.rb b/spec/java_buildpack/container/tomcat/tomcat_redis_store_spec.rb index f095ca18f1..762ab6c91e 100644 --- a/spec/java_buildpack/container/tomcat/tomcat_redis_store_spec.rb +++ b/spec/java_buildpack/container/tomcat/tomcat_redis_store_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,13 +20,13 @@ require 'java_buildpack/container/tomcat/tomcat_redis_store' describe JavaBuildpack::Container::TomcatRedisStore do - include_context 'component_helper' + include_context 'with component help' let(:component_id) { 'tomcat' } let(:configuration) do - { 'database' => 'test-database', - 'timeout' => 'test-timeout', + { 'database' => 'test-database', + 'timeout' => 'test-timeout', 'connection_pool_size' => 'test-connection-pool-size' } end @@ -36,10 +37,10 @@ context do before do - allow(services).to receive(:one_service?).with(/session-replication/, %w(hostname host), 'port', 'password') - .and_return(true) + allow(services).to receive(:one_service?).with(/session-replication/, %w[hostname host], 'port', 'password') + .and_return(true) allow(services).to receive(:find_service).and_return('credentials' => { 'hostname' => 'test-host', - 'port' => 'test-port', + 'port' => 'test-port', 'password' => 'test-password' }) end @@ -48,7 +49,7 @@ end it 'copies resources', - app_fixture: 'container_tomcat_redis_store', + app_fixture: 'container_tomcat_redis_store', cache_fixture: 'stub-redis-store.jar' do component.compile @@ -57,7 +58,7 @@ end it 'mutates context.xml', - app_fixture: 'container_tomcat_redis_store', + app_fixture: 'container_tomcat_redis_store', cache_fixture: 'stub-redis-store.jar' do component.compile @@ -71,10 +72,10 @@ context do before do - allow(services).to receive(:one_service?).with(/session-replication/, %w(hostname host), 'port', 'password') - .and_return(true) - allow(services).to receive(:find_service).and_return('credentials' => { 'host' => 'test-host', - 'port' => 'test-port', + allow(services).to receive(:one_service?).with(/session-replication/, %w[hostname host], 'port', 'password') + .and_return(true) + allow(services).to receive(:find_service).and_return('credentials' => { 'host' => 'test-host', + 'port' => 'test-port', 'password' => 'test-password' }) end diff --git a/spec/java_buildpack/container/tomcat/gemfire/gemfire_logging_api_spec.rb b/spec/java_buildpack/container/tomcat/tomcat_setenv_spec.rb similarity index 58% rename from spec/java_buildpack/container/tomcat/gemfire/gemfire_logging_api_spec.rb rename to spec/java_buildpack/container/tomcat/tomcat_setenv_spec.rb index 5a2d3a0515..391577115b 100644 --- a/spec/java_buildpack/container/tomcat/gemfire/gemfire_logging_api_spec.rb +++ b/spec/java_buildpack/container/tomcat/tomcat_setenv_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,27 +17,26 @@ require 'spec_helper' require 'component_helper' -require 'java_buildpack/container/tomcat/gemfire/gemfire_logging_api' +require 'java_buildpack/container/tomcat/tomcat_setenv' -describe JavaBuildpack::Container::GemFireLoggingApi do - include_context 'component_helper' +describe JavaBuildpack::Container::TomcatSetenv do + include_context 'with component help' let(:component_id) { 'tomcat' } it 'always detects' do - expect(component.detect).to eq("gem-fire-logging-api=#{version}") + expect(component.detect).to eq('tomcat-setenv') end - it 'copies resources', - cache_fixture: 'stub-gemfire-slf4j-api.jar' do - + it 'creates setenv.sh' do component.compile - expect(sandbox + "lib/slf4j-api-#{version}.jar").to exist - end + expect(sandbox + 'bin/setenv.sh').to exist + expect((sandbox + 'bin/setenv.sh').read).to eq <<~SH + #!/bin/sh - it 'does nothing during release' do - component.release + CLASSPATH=$CLASSPATH:$PWD/.root_libs/test-jar-3.jar:$PWD/.root_libs/test-jar-4.jar + SH end end diff --git a/spec/java_buildpack/container/tomcat_spec.rb b/spec/java_buildpack/container/tomcat_spec.rb index 6f49ef475c..37dded2c4c 100644 --- a/spec/java_buildpack/container/tomcat_spec.rb +++ b/spec/java_buildpack/container/tomcat_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2021 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,74 +19,99 @@ require 'component_helper' require 'fileutils' require 'java_buildpack/container/tomcat' +require 'java_buildpack/container/tomcat/tomcat_access_logging_support' +require 'java_buildpack/container/tomcat/tomcat_geode_store' require 'java_buildpack/container/tomcat/tomcat_insight_support' require 'java_buildpack/container/tomcat/tomcat_instance' require 'java_buildpack/container/tomcat/tomcat_lifecycle_support' require 'java_buildpack/container/tomcat/tomcat_logging_support' -require 'java_buildpack/container/tomcat/tomcat_access_logging_support' require 'java_buildpack/container/tomcat/tomcat_redis_store' describe JavaBuildpack::Container::Tomcat do - include_context 'component_helper' + include_context 'with component help' let(:component) { StubTomcat.new context } let(:configuration) do - { 'tomcat' => tomcat_configuration, - 'lifecycle_support' => lifecycle_support_configuration, - 'logging_support' => logging_support_configuration, - 'access_logging_support' => access_logging_support_configuration, - 'redis_store' => redis_store_configuration } + { 'access_logging_support' => access_logging_support_configuration, + 'external_configuration' => tomcat_external_configuration, + 'geode_store' => geode_store_configuration, + 'lifecycle_support' => lifecycle_support_configuration, + 'logging_support' => logging_support_configuration, + 'redis_store' => redis_store_configuration, + 'tomcat' => tomcat_configuration } end - let(:tomcat_configuration) { double('tomcat-configuration') } + let(:access_logging_support_configuration) { instance_double('logging-support-configuration') } - let(:lifecycle_support_configuration) { double('lifecycle-support-configuration') } + let(:lifecycle_support_configuration) { instance_double('lifecycle-support-configuration') } - let(:logging_support_configuration) { double('logging-support-configuration') } + let(:logging_support_configuration) { instance_double('logging-support-configuration') } - let(:access_logging_support_configuration) { double('logging-support-configuration') } + let(:geode_store_configuration) { instance_double('geode_store_configuration') } - let(:redis_store_configuration) { double('redis-store-configuration') } + let(:redis_store_configuration) { instance_double('redis-store-configuration') } + + let(:tomcat_configuration) { { 'external_configuration_enabled' => false } } + + let(:tomcat_external_configuration) { instance_double('tomcat_external_configuration') } it 'detects WEB-INF', app_fixture: 'container_tomcat' do - expect(component.supports?).to be + expect(component).to be_supports end it 'does not detect when WEB-INF is absent', app_fixture: 'container_main' do - expect(component.supports?).not_to be + expect(component).not_to be_supports end it 'does not detect when WEB-INF is present in a Java main application', app_fixture: 'container_main_with_web_inf' do - expect(component.supports?).not_to be + expect(component).not_to be_supports end it 'creates submodules' do - expect(JavaBuildpack::Container::TomcatInstance) - .to receive(:new).with(sub_configuration_context(tomcat_configuration)) - expect(JavaBuildpack::Container::TomcatLifecycleSupport) + # create Tomcat instance double so we can pull version when creating submodules + tomcat_instance = instance_double('JavaBuildpack::Container::TomcatInstance') + tomcat_instance.instance_variable_set(:@version, %w[9 0 44]) + + allow(JavaBuildpack::Container::TomcatInstance) + .to receive(:new).with(sub_configuration_context(tomcat_configuration)).and_return(tomcat_instance) + allow(JavaBuildpack::Container::TomcatAccessLoggingSupport) + .to receive(:new).with(sub_configuration_context(access_logging_support_configuration)) + allow(JavaBuildpack::Container::TomcatGeodeStore) + .to receive(:new).with(sub_configuration_context(geode_store_configuration), '9') + allow(JavaBuildpack::Container::TomcatInsightSupport).to receive(:new).with(context) + allow(JavaBuildpack::Container::TomcatLifecycleSupport) .to receive(:new).with(sub_configuration_context(lifecycle_support_configuration)) - expect(JavaBuildpack::Container::TomcatLoggingSupport) + allow(JavaBuildpack::Container::TomcatLoggingSupport) .to receive(:new).with(sub_configuration_context(logging_support_configuration)) - expect(JavaBuildpack::Container::TomcatAccessLoggingSupport) - .to receive(:new).with(sub_configuration_context(access_logging_support_configuration)) - expect(JavaBuildpack::Container::TomcatRedisStore) + allow(JavaBuildpack::Container::TomcatRedisStore) .to receive(:new).with(sub_configuration_context(redis_store_configuration)) - expect(JavaBuildpack::Container::TomcatInsightSupport).to receive(:new).with(context) + allow(JavaBuildpack::Container::TomcatSetenv).to receive(:new).with(context) component.sub_components context end it 'returns command' do + expect(component.command).to eq("test-var-2 test-var-1 JAVA_OPTS=$JAVA_OPTS #{java_home.as_env_var} exec " \ + '$PWD/.java-buildpack/tomcat/bin/catalina.sh run') + end + + context do + + let(:tomcat_configuration) { { 'external_configuration_enabled' => true } } + + it 'creates submodule TomcatExternalConfiguration' do + allow(JavaBuildpack::Container::TomcatExternalConfiguration) + .to receive(:new).with(sub_configuration_context(tomcat_external_configuration)) - expect(component.command).to eq("#{java_home.as_env_var} JAVA_OPTS=\"test-opt-2 test-opt-1 -Dhttp.port=$PORT\" " \ - '$PWD/.java-buildpack/tomcat/bin/catalina.sh run') + component.sub_components context + end end end @@ -97,7 +123,7 @@ class StubTomcat < JavaBuildpack::Container::Tomcat end def sub_configuration_context(configuration) - c = context.clone + c = context.clone c[:configuration] = configuration c end diff --git a/spec/java_buildpack/framework/app_dynamics_agent_spec.rb b/spec/java_buildpack/framework/app_dynamics_agent_spec.rb index 53719b444f..96194396be 100644 --- a/spec/java_buildpack/framework/app_dynamics_agent_spec.rb +++ b/spec/java_buildpack/framework/app_dynamics_agent_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,9 +20,13 @@ require 'java_buildpack/framework/app_dynamics_agent' describe JavaBuildpack::Framework::AppDynamicsAgent do - include_context 'component_helper' + include_context 'with component help' - let(:configuration) { { 'default_tier_name' => 'test-tier-name' } } + let(:configuration) do + { 'default_tier_name' => nil, + 'default_node_name' => "$(expr \"$VCAP_APPLICATION\" : '.*instance_index[\": ]*\\([[:digit:]]*\\).*')", + 'default_application_name' => nil } + end it 'does not detect without app-dynamics-n/a service' do expect(component.detect).to be_nil @@ -32,7 +37,7 @@ let(:credentials) { {} } before do - allow(services).to receive(:one_service?).with(/app-dynamics/, 'host-name').and_return(true) + allow(services).to receive(:one_service?).with(/app-?dynamics/, 'host-name').and_return(true) allow(services).to receive(:find_service).and_return('credentials' => credentials) end @@ -61,8 +66,8 @@ expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/app_dynamics_agent/javaagent.jar') expect(java_opts).to include('-Dappdynamics.controller.hostName=test-host-name') - expect(java_opts).to include("-Dappdynamics.agent.applicationName='test-application-name'") - expect(java_opts).to include("-Dappdynamics.agent.tierName='test-tier-name'") + expect(java_opts).to include('-Dappdynamics.agent.applicationName=test-application-name') + expect(java_opts).to include('-Dappdynamics.agent.tierName=test-application-name') expect(java_opts).to include('-Dappdynamics.agent.nodeName=$(expr "$VCAP_APPLICATION" : ' \ '\'.*instance_index[": ]*\\([[:digit:]]*\\).*\')') end @@ -73,7 +78,93 @@ it 'adds tier_name from credentials to JAVA_OPTS if specified' do component.release - expect(java_opts).to include("-Dappdynamics.agent.tierName='another-test-tier-name'") + expect(java_opts).to include('-Dappdynamics.agent.tierName=another-test-tier-name') + end + end + + context do + let(:credentials) { super().merge 'tier-name' => 'another-test tier-name' } + + it 'adds tier_name from credentials with space in name to JAVA_OPTS if specified' do + component.release + + expect(java_opts).to include('-Dappdynamics.agent.tierName=another-test\ tier-name') + end + end + + context do + let(:credentials) { super().merge 'tier-name' => '--> ${SOME_VAR} <--' } + + it 'adds tier_name from credentials with shell variable in it to JAVA_OPTS if specified' do + component.release + + expect(java_opts).to include('-Dappdynamics.agent.tierName=\"--> ${SOME_VAR} <--\"') + end + end + + context do + let(:credentials) { super().merge 'tier-name' => '$(echo \'Hello World!\') and stuff' } + + it 'adds tier_name from credentials with subshell in it to JAVA_OPTS if specified' do + component.release + + expect(java_opts).to include('-Dappdynamics.agent.tierName=\"$(echo \'Hello World!\') and stuff\"') + end + end + + context do + let(:credentials) { super().merge 'application-name' => 'another-test application-name' } + + it 'adds application_name from credentials with space in name to JAVA_OPTS if specified' do + component.release + + expect(java_opts).to include('-Dappdynamics.agent.applicationName=another-test\ application-name') + end + end + + context do + let(:credentials) { super().merge 'application-name' => '$(echo \'Hello World!\') and stuff' } + + it 'adds application_name from credentials with subshell in value to JAVA_OPTS if specified' do + component.release + + expect(java_opts).to include('-Dappdynamics.agent.applicationName=\"$(echo \'Hello World!\') and stuff\"') + end + end + + context do + let(:credentials) { super().merge 'application-name' => 'Name ${MY_APP_NAME}' } + + it 'adds application_name from credentials with env variable in value to JAVA_OPTS if specified' do + component.release + + expect(java_opts).to include('-Dappdynamics.agent.applicationName=\"Name ${MY_APP_NAME}\"') + end + end + + context do + let(:configuration) do + { 'default_tier_name' => nil, + 'default_node_name' => nil, + 'default_application_name' => 'default application-name' } + end + + it 'adds application_name from default config to JAVA_OPTS if specified' do + component.release + + # should not be escaped, escaping happens at runtime because default value is a sub-command + # executed in the runtime container + expect(java_opts).to include('-Dappdynamics.agent.applicationName=default application-name') + end + end + + context do + let(:credentials) { super().merge 'node-name' => 'another-test-node-name' } + + it 'adds node_name from credentials to JAVA_OPTS if specified' do + component.release + + expect(java_opts).to include('-Dappdynamics.agent.nodeName=another-test-node-name') end end @@ -116,8 +207,102 @@ expect(java_opts).to include('-Dappdynamics.agent.accountAccessKey=test-account-access-key') end end - end - end + context do + + let(:environment) { { 'APPD_CONF_HTTP_URL' => 'http://foo.com' } } + let(:conf_files) { described_class.instance_variable_get(:@conf_files) } + + it 'sets APPD_CONF_HTTP_URL env var to download config files from', + cache_fixture: 'stub-app-dynamics-agent.zip' do + + config_files = %w[logging/log4j2.xml logging/log4j.xml app-agent-config.xml controller-info.xml + service-endpoint.xml transactions.xml custom-interceptors.xml + custom-activity-correlation.xml] + + config_files.each do |file| + uri = "http://foo.com/java/#{file}" + allow(application_cache).to receive(:get) + .with(uri) + stub_request(:head, uri) + .with(headers: { 'Accept' => '*/*', 'Host' => 'foo.com', 'User-Agent' => 'Ruby' }) + .to_return(status: 200, body: '', headers: {}) + end + component.compile + end + + end + + context do + let(:environment) { { 'APPD_CONF_HTTP_URL' => 'https://foo.com' } } + + it 'sets APPD_CONF_HTTP_URL env var to download config files over HTTPS', + cache_fixture: 'stub-app-dynamics-agent.zip' do + + config_files = %w[logging/log4j2.xml logging/log4j.xml app-agent-config.xml controller-info.xml + service-endpoint.xml transactions.xml custom-interceptors.xml + custom-activity-correlation.xml] + + config_files.each do |file| + uri = "https://foo.com/java/#{file}" + allow(application_cache).to receive(:get) + .with(uri) + allow(Net::HTTP).to receive(:start).with('foo.com', 443, use_ssl: true).and_call_original + stub_request(:head, uri) + .with(headers: { 'Accept' => '*/*', 'Host' => 'foo.com', 'User-Agent' => 'Ruby' }) + .to_return(status: 200, body: '', headers: {}) + end + component.compile + end + end + + context do + let(:environment) { { 'APPD_CONF_HTTP_URL' => 'https://user:pass@foo.com' } } + + it 'sets APPD_CONF_HTTP_URL env var to download config files over HTTPS with Basic Auth', + cache_fixture: 'stub-app-dynamics-agent.zip' do + + config_files = %w[logging/log4j2.xml logging/log4j.xml app-agent-config.xml controller-info.xml + service-endpoint.xml transactions.xml custom-interceptors.xml + custom-activity-correlation.xml] + + config_files.each do |file| + allow(application_cache).to receive(:get) + .with("https://user:pass@foo.com/java/#{file}") + allow(Net::HTTP).to receive(:start).with('foo.com', 443, use_ssl: true).and_call_original + stub_request(:head, "https://foo.com/java/#{file}") + .with(headers: { 'Accept' => '*/*', 'Host' => 'foo.com', 'User-Agent' => 'Ruby', + 'Authorization' => 'Basic dXNlcjpwYXNz' }) + .to_return(status: 200, body: '', headers: {}) + end + component.compile + end + end + + context do + + let(:environment) { { 'APPD_CONF_DIR' => 'BOOT-INF/classes/appdynamics/conf' } } + + it 'sets APPD_CONF_DIR env var to copy config files from app dir', + app_fixture: 'framework_app_dynamics_agent', + cache_fixture: 'stub-app-dynamics-agent.zip' do + component.compile + expect(File.read(sandbox + 'ver21.1.0.31582/conf/app-agent-config.xml')).to include 'sourced by APPD_CONF_DIR' + end + end + + context do + + let(:environment) { { 'APPD_CONF_DIR' => 'BOOT-INF/classes/appdynamics/conf-false' } } + + it 'sets APPD_CONF_DIR env var to copy config files from incorrect app dir', + app_fixture: 'framework_app_dynamics_agent', + cache_fixture: 'stub-app-dynamics-agent.zip' do + + expect { component.compile }.to raise_error(RuntimeError, /AppDynamics configuration source dir/) + end + end + end + end end diff --git a/spec/java_buildpack/framework/aspectj_weaver_agent_spec.rb b/spec/java_buildpack/framework/aspectj_weaver_agent_spec.rb new file mode 100644 index 0000000000..1f4bfd7c02 --- /dev/null +++ b/spec/java_buildpack/framework/aspectj_weaver_agent_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/aspectj_weaver_agent' + +describe JavaBuildpack::Framework::AspectjWeaverAgent do + include_context 'with component help' + + it 'does not detect if not enabled' do + expect(component.detect).to be_nil + end + + context do + let(:configuration) { { 'enabled' => true } } + + it 'does not detect if aop.xml only', + app_fixture: 'framework_aspectj_weaver_aop_xml_only' do + + expect(component.detect).to be_nil + end + + it 'detects when aop.xml in BOOT-INF classes', + app_fixture: 'framework_aspectj_weaver_boot_inf_classes' do + + expect(component.detect).to eq('aspectj-weaver-agent=1.8.10') + end + + it 'detects when aop.xml in BOOT-INF/classes/META-INF', + app_fixture: 'framework_aspectj_weaver_boot_inf_classes_meta_inf' do + + expect(component.detect).to eq('aspectj-weaver-agent=1.8.10') + end + + it 'does not detect if JAR only', + app_fixture: 'framework_aspectj_weaver_jar_only' do + + expect(component.detect).to be_nil + end + + it 'detects when aop.xml in META-INF', + app_fixture: 'framework_aspectj_weaver_meta_inf' do + + expect(component.detect).to eq('aspectj-weaver-agent=1.8.10') + end + + it 'detects when aop.xml in classes', + app_fixture: 'framework_aspectj_weaver_classes' do + + expect(component.detect).to eq('aspectj-weaver-agent=1.8.10') + end + + it 'adds java agent', + app_fixture: 'framework_aspectj_weaver_boot_inf_classes' do + + component.release + expect(java_opts).to include('-javaagent:$PWD/BOOT-INF/lib/aspectjweaver-1.8.10.jar') + end + end + +end diff --git a/spec/java_buildpack/framework/azure_application_insights_agent_spec.rb b/spec/java_buildpack/framework/azure_application_insights_agent_spec.rb new file mode 100644 index 0000000000..06b2a957f4 --- /dev/null +++ b/spec/java_buildpack/framework/azure_application_insights_agent_spec.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/azure_application_insights_agent' + +describe JavaBuildpack::Framework::AzureApplicationInsightsAgent do + include_context 'with component help' + + let(:configuration) do + { 'default_application_name' => nil } + end + + it 'does not detect without azure-application-insights service' do + expect(component.detect).to be_nil + end + + context do + + before do + allow(services).to receive(:one_service?).with(/azure-application-insights/, + %w[connection_string instrumentation_key]) + .and_return(true) + end + + it 'detects with azure-application-insights service' do + expect(component.detect).to eq("azure-application-insights-agent=#{version}") + end + + it 'downloads Azure Application Insights agent JAR', + cache_fixture: 'stub-azure-application-insights-agent.jar' do + + component.compile + + expect(sandbox + "azure_application_insights_agent-#{version}.jar").to exist + end + + it 'copies resources', + cache_fixture: 'stub-azure-application-insights-agent.jar' do + + component.compile + + expect(sandbox + 'AI-Agent.xml').to exist + end + + it 'updates JAVA_OPTS' do + allow(services).to receive(:find_service) + .and_return('credentials' => { 'connection_string' => 'test-connection-string', + 'instrumentation_key' => 'test-instrumentation-key' }) + + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/azure_application_insights_agent/' \ + "azure_application_insights_agent-#{version}.jar") + expect(java_opts).to include('-Dapplicationinsights.connection.string=test-connection-string') + expect(java_opts).to include('-DAPPLICATION_INSIGHTS_IKEY=test-instrumentation-key') + end + + it 'updates environment variables' do + allow(services).to receive(:find_service) + .and_return('credentials' => { 'instrumentation_key' => 'test-instrumentation-key' }) + + component.release + + expect(environment_variables).to include('APPINSIGHTS_INSTRUMENTATIONKEY=test-instrumentation-key') + end + + end + +end diff --git a/spec/java_buildpack/framework/checkmarx_iast_agent_spec.rb b/spec/java_buildpack/framework/checkmarx_iast_agent_spec.rb new file mode 100644 index 0000000000..631ecd62b5 --- /dev/null +++ b/spec/java_buildpack/framework/checkmarx_iast_agent_spec.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/checkmarx_iast_agent' + +describe JavaBuildpack::Framework::CheckmarxIastAgent do + include_context 'with component help' + + it 'does not detect without checkmarx-iast service' do + expect(component.detect).to be_nil + end + + context do + + before do + allow(services).to receive(:one_service?).with(/^checkmarx-iast$/, 'server').and_return(true) + allow(services).to receive(:find_service).and_return('credentials' => { 'server' => 'test-server' }) + + allow(application_cache).to receive(:get) + .with('test-server/iast/compilation/download/JAVA') + .and_yield(Pathname.new('spec/fixtures/stub-checkmarx-agent.zip').open, false) + end + + it 'detects with checkmarx-iast service' do + expect(component.detect).to eq('checkmarx-iast-agent=') + end + + it 'downloads agent', + cache_fixture: 'stub-checkmarx-agent.zip' do + + component.compile + + expect(sandbox + 'cx-launcher.jar').to exist + end + + it 'appends override configuration', + cache_fixture: 'stub-checkmarx-agent.zip' do + + component.compile + + expect(File.read(sandbox + 'cx_agent.override.properties')).to eq('test-data + +enableWeavedClassCache=false +') + end + + it 'updates JAVA_OPTS' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/checkmarx_iast_agent/cx-launcher.jar') + expect(java_opts).to include('-Dcx.logToConsole=true') + expect(java_opts).to include('-Dcx.appName=test-application-name') + expect(java_opts).to include('-DcxAppTag=test-application-name') + expect(java_opts).to include('-DcxTeam=CxServer') + end + + end + +end diff --git a/spec/java_buildpack/framework/client_certificate_mapper_spec.rb b/spec/java_buildpack/framework/client_certificate_mapper_spec.rb new file mode 100644 index 0000000000..37120b3709 --- /dev/null +++ b/spec/java_buildpack/framework/client_certificate_mapper_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/client_certificate_mapper' + +describe JavaBuildpack::Framework::ClientCertificateMapper do + include_context 'with component help' + + it 'always detects' do + expect(component.detect).to eq("client-certificate-mapper=#{version}") + end + + it 'adds the jar to the additional libraries during compile', + cache_fixture: 'stub-client-certificate-mapper.jar' do + + component.compile + + expect(sandbox + "client_certificate_mapper-#{version}.jar").to exist + expect(additional_libraries).to include(sandbox + "client_certificate_mapper-#{version}.jar") + end + + it 'adds the jar to the additional libraries during release', + cache_fixture: 'stub-client-certificate-mapper.jar' do + + component.release + + expect(additional_libraries).to include(sandbox + "client_certificate_mapper-#{version}.jar") + end + +end diff --git a/spec/java_buildpack/framework/container_customizer_spec.rb b/spec/java_buildpack/framework/container_customizer_spec.rb new file mode 100644 index 0000000000..cca668fd98 --- /dev/null +++ b/spec/java_buildpack/framework/container_customizer_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/container_customizer' + +describe JavaBuildpack::Framework::ContainerCustomizer do + include_context 'with component help' + + it 'does not detect without Spring Boot WAR' do + expect(component.detect).to be_nil + end + + it 'detects with Spring Boot WAR', + app_fixture: 'framework_container_customizer' do + + expect(component.detect).to eq("container-customizer=#{version}") + end + + it 'downloads the container customizer', + app_fixture: 'framework_container_customizer', + cache_fixture: 'stub-container-customizer.jar' do + + component.compile + + expect(sandbox + "container_customizer-#{version}.jar").to exist + end + + it 'adds container customizer to the additional libraries', + app_fixture: 'framework_container_customizer', + cache_fixture: 'stub-container-customizer.jar' do + + component.release + + expect(additional_libraries).to include(sandbox + "container_customizer-#{version}.jar") + end + +end diff --git a/spec/java_buildpack/framework/container_security_provider_spec.rb b/spec/java_buildpack/framework/container_security_provider_spec.rb new file mode 100644 index 0000000000..7c2968a00f --- /dev/null +++ b/spec/java_buildpack/framework/container_security_provider_spec.rb @@ -0,0 +1,125 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/container_security_provider' + +describe JavaBuildpack::Framework::ContainerSecurityProvider do + include_context 'with component help' + + it 'always detects' do + expect(component.detect).to eq("container-security-provider=#{version}") + end + + it 'adds extension directory' do + component.release + + expect(extension_directories).to include(droplet.sandbox) + end + + it 'adds security provider', + cache_fixture: 'stub-container-security-provider.jar' do + + component.compile + expect(security_providers[1]).to eq('org.cloudfoundry.security.CloudFoundryContainerProvider') + end + + context do + + let(:java_home_delegate) do + delegate = JavaBuildpack::Component::MutableJavaHome.new + delegate.root = app_dir + '.test-java-home' + delegate.version = JavaBuildpack::Util::TokenizedVersion.new('9.0.0') + + delegate + end + + it 'adds JAR to classpath during compile in Java 9', + cache_fixture: 'stub-container-security-provider.jar' do + + component.compile + + expect(root_libraries).to include(droplet.sandbox + "container_security_provider-#{version}.jar") + end + + it 'adds JAR to classpath during release in Java 9' do + component.release + + expect(root_libraries).to include(droplet.sandbox + "container_security_provider-#{version}.jar") + end + + it 'does not add extension directory in Java 9' do + component.release + + expect(extension_directories).not_to include(droplet.sandbox) + end + + end + + it 'does not manager system properties' do + component.release + + expect(java_opts).not_to include('-Dorg.cloudfoundry.security.keymanager.enabled=false') + expect(java_opts).not_to include('-Dorg.cloudfoundry.security.trustmanager.enabled=false') + end + + context 'when KeyManager disabled' do + let(:configuration) { { 'key_manager_enabled' => false } } + + it 'adds system property' do + component.release + + expect(java_opts).to include('-Dorg.cloudfoundry.security.keymanager.enabled=false') + end + + end + + context 'when TrustManager disabled' do + let(:configuration) { { 'trust_manager_enabled' => false } } + + it 'adds system property' do + component.release + + expect(java_opts).to include('-Dorg.cloudfoundry.security.trustmanager.enabled=false') + end + + end + + context 'when KeyManager enabled' do + let(:configuration) { { 'key_manager_enabled' => true } } + + it 'adds system property' do + component.release + + expect(java_opts).to include('-Dorg.cloudfoundry.security.keymanager.enabled=true') + end + + end + + context 'when TrustManager enabled' do + let(:configuration) { { 'trust_manager_enabled' => true } } + + it 'adds system property' do + component.release + + expect(java_opts).to include('-Dorg.cloudfoundry.security.trustmanager.enabled=true') + end + + end + +end diff --git a/spec/java_buildpack/framework/contrast_security_agent_spec.rb b/spec/java_buildpack/framework/contrast_security_agent_spec.rb new file mode 100644 index 0000000000..9ffb86cb90 --- /dev/null +++ b/spec/java_buildpack/framework/contrast_security_agent_spec.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/contrast_security_agent' +require 'java_buildpack/util/tokenized_version' + +describe JavaBuildpack::Framework::ContrastSecurityAgent do + include_context 'with component help' + + it 'does not detect without contrastsecurity service' do + expect(component.detect).to be_nil + end + + context do + + before do + allow(services).to receive(:one_service?).with(/contrast-security/, 'api_key', 'service_key', 'teamserver_url', + 'username').and_return(true) + allow(services).to receive(:find_service).and_return('credentials' => { 'teamserver_url' => 'a_url', + 'username' => 'contrast_user', + 'api_key' => 'api_test', + 'service_key' => 'service_test' }) + end + + it 'detects with contrastsecurity service' do + expect(component.detect).to eq("contrast-security-agent=#{version}") + end + + it 'downloads Contrast Security agent JAR', + cache_fixture: 'stub-contrast-security-agent.jar' do + + component.compile + expect(sandbox + 'contrast-engine-0.0.0.jar').to exist + end + + it 'uses contrast-engine for versions < 3.4.3' do + + tokenized_version = JavaBuildpack::Util::TokenizedVersion.new('3.4.2_756') + allow(JavaBuildpack::Repository::ConfiguredItem).to receive(:find_item) do |&block| + block&.call(tokenized_version) + end.and_return([tokenized_version, uri]) + + component.release + expect(java_opts.to_s).to include('contrast-engine-3.4.2.jar') + end + + it 'uses java-agent for versions >= 3.4.3' do + tokenized_version = JavaBuildpack::Util::TokenizedVersion.new('3.4.3_000') + allow(JavaBuildpack::Repository::ConfiguredItem).to receive(:find_item) do |&block| + block&.call(tokenized_version) + end.and_return([tokenized_version, uri]) + + component.release + expect(java_opts.to_s).to include('java-agent-3.4.3.jar') + end + + it 'updates JAVA_OPTS' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/contrast_security_agent/contrast-engine-0.0.0.jar' \ + '=$PWD/.java-buildpack/contrast_security_agent/contrast.config') + expect(java_opts).to include('-Dcontrast.dir=$TMPDIR') + expect(java_opts).to include('-Dcontrast.override.appname=test-application-name') + end + + it 'created contrast.config', + cache_fixture: 'stub-contrast-security-agent.jar' do + + component.compile + expect(sandbox + 'contrast.config').to exist + end + + it 'does not override app name if there is an existing appname' do + java_opts.add_system_property('contrast.override.appname', 'NAME_ALREADY_OVERRIDDEN') + + component.release + + expect(java_opts).to include('-Dcontrast.override.appname=NAME_ALREADY_OVERRIDDEN') + expect(java_opts).not_to include('-Dcontrast.override.appname=test-application-name') + end + + end + +end diff --git a/spec/java_buildpack/framework/datadog_javaagent_spec.rb b/spec/java_buildpack/framework/datadog_javaagent_spec.rb new file mode 100644 index 0000000000..75ec1889ed --- /dev/null +++ b/spec/java_buildpack/framework/datadog_javaagent_spec.rb @@ -0,0 +1,175 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2021 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/datadog_javaagent' +require 'java_buildpack/util/tokenized_version' + +describe JavaBuildpack::Framework::DatadogJavaagent do + include_context 'with component help' + + let(:configuration) do + { 'default_application_version' => nil, + 'default_application_name' => nil } + end + + describe '#detect' do + subject(:detect) { component.detect } + + it 'does not detect without an api key' do + expect(detect).to be_nil + end + + context 'when api key is empty' do + let(:environment) { { 'DD_API_KEY' => '' } } + + it { is_expected.to be_nil } + end + + context 'when apm is disabled' do + let(:environment) { { 'DD_API_KEY' => 'foo', 'DD_APM_ENABLED' => 'false' } } + + it { is_expected.to be_nil } + end + + context 'when apm is enabled with no api key' do + let(:environment) { { 'DD_APM_ENABLED' => 'true' } } + + it { is_expected.to be_nil } + end + + context 'when apm key is provided' do + let(:environment) { { 'DD_API_KEY' => 'foo' } } + + it { is_expected.to eq("datadog-javaagent=#{version}") } + end + end + + context 'when apm key is provided' do + let(:environment) do + super().update({ 'DD_API_KEY' => 'foo' }) + end + + context 'when datadog buildpack is present' do + before do + FileUtils.mkdir_p File.join(context[:droplet].root, '.datadog') + end + + after do + FileUtils.rmdir File.join(context[:droplet].root, '.datadog') + end + + it 'compile downloads datadog-javaagent JAR', cache_fixture: 'stub-datadog-javaagent.jar' do + component.compile + expect(sandbox + "datadog_javaagent-#{version}.jar").to exist + end + + it 'makes a jar with fake class files', cache_fixture: 'stub-datadog-javaagent.jar' do + component.compile + expect(sandbox + "datadog_javaagent-#{version}.jar").to exist + expect(sandbox + 'datadog_fakeclasses.jar').to exist + expect(sandbox + 'datadog_fakeclasses').not_to exist + + cnt = `unzip -l #{sandbox}/datadog_fakeclasses.jar | grep '\\(\\.class\\)$' | wc -l`.to_i + expect(cnt).to equal(34) + end + + it 'release updates JAVA_OPTS' do + component.release + + expect(java_opts).to include( + "-javaagent:$PWD/.java-buildpack/datadog_javaagent/datadog_javaagent-#{version}.jar" + ) + expect(java_opts).to include('-Ddd.service=\"test-application-name\"') + expect(java_opts).to include('-Ddd.version=test-application-version') + end + end + + context 'when datadog buildpack 4.22.0 (or older) is present' do + before do + FileUtils.mkdir_p File.join(context[:droplet].root, 'datadog') + end + + after do + FileUtils.rmdir File.join(context[:droplet].root, 'datadog') + end + + it 'compile downloads datadog-javaagent JAR', cache_fixture: 'stub-datadog-javaagent.jar' do + component.compile + expect(sandbox + "datadog_javaagent-#{version}.jar").to exist + end + + it 'makes a jar with fake class files', cache_fixture: 'stub-datadog-javaagent.jar' do + component.compile + expect(sandbox + "datadog_javaagent-#{version}.jar").to exist + expect(sandbox + 'datadog_fakeclasses.jar').to exist + expect(sandbox + 'datadog_fakeclasses').not_to exist + + cnt = `unzip -l #{sandbox}/datadog_fakeclasses.jar | grep '\\(\\.class\\)$' | wc -l`.to_i + expect(cnt).to equal(34) + end + + it 'release updates JAVA_OPTS' do + component.release + + expect(java_opts).to include( + "-javaagent:$PWD/.java-buildpack/datadog_javaagent/datadog_javaagent-#{version}.jar" + ) + expect(java_opts).to include('-Ddd.service=\"test-application-name\"') + expect(java_opts).to include('-Ddd.version=test-application-version') + end + end + + context 'when datadog buildpack is not present' do + it 'compile downloads datadog-javaagent JAR', cache_fixture: 'stub-datadog-javaagent.jar' do + component.compile + expect(sandbox + "datadog_javaagent-#{version}.jar").not_to exist + end + + it 'release updates JAVA_OPTS' do + component.release + + expect(java_opts).not_to include( + "-javaagent:$PWD/.java-buildpack/datadog_javaagent/datadog_javaagent-#{version}.jar" + ) + expect(java_opts).not_to include('-Ddd.service=\"test-application-name\"') + expect(java_opts).not_to include('-Ddd.version=test-application-version') + end + end + end + + context 'when dd_version environment variable is provided' do + let(:environment) do + super().update({ 'DD_VERSION' => 'env-variable-version' }) + end + + before do + FileUtils.mkdir_p File.join(context[:droplet].root, '.datadog') + end + + after do + FileUtils.rmdir File.join(context[:droplet].root, '.datadog') + end + + it 'release updates JAVA_OPTS with env variable version' do + component.release + + expect(java_opts).to include('-Ddd.version=env-variable-version') + end + end +end diff --git a/spec/java_buildpack/framework/debug_spec.rb b/spec/java_buildpack/framework/debug_spec.rb new file mode 100644 index 0000000000..c5037d1f9c --- /dev/null +++ b/spec/java_buildpack/framework/debug_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/debug' + +describe JavaBuildpack::Framework::Debug do + include_context 'with component help' + + it 'does not detect if not enabled' do + expect(component.detect).to be_nil + end + + context do + let(:configuration) { { 'enabled' => true } } + + it 'detects when enabled' do + expect(component.detect).to eq('debug=8000') + end + + it 'uses 8000 as the default port and does not suspend by default' do + component.release + expect(java_opts).to include('-agentlib:jdwp=transport=dt_socket,server=y,address=8000,suspend=n') + end + end + + context do + let(:configuration) { { 'enabled' => true, 'port' => 8001 } } + + it 'uses configured port' do + component.release + expect(java_opts).to include('-agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n') + end + end + + context do + let(:configuration) { { 'enabled' => true, 'suspend' => true } } + + it 'uses configured suspend' do + component.release + expect(java_opts).to include('-agentlib:jdwp=transport=dt_socket,server=y,address=8000,suspend=y') + end + end + +end diff --git a/spec/java_buildpack/framework/dynatrace_one_agent_spec.rb b/spec/java_buildpack/framework/dynatrace_one_agent_spec.rb new file mode 100644 index 0000000000..53c686a1ce --- /dev/null +++ b/spec/java_buildpack/framework/dynatrace_one_agent_spec.rb @@ -0,0 +1,169 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/dynatrace_one_agent' +require 'java_buildpack/util/tokenized_version' + +describe JavaBuildpack::Framework::DynatraceOneAgent do + include_context 'with component help' + + it 'does not detect without dynatrace-n/a service' do + expect(component.detect).to be_nil + end + + context do + + before do + allow(services).to receive(:one_service?).with(/dynatrace/, 'apitoken', 'environmentid').and_return(true) + allow(services).to receive(:find_service).and_return('credentials' => { 'environmentid' => 'test-environmentid', + 'apiurl' => 'test-apiurl', + 'apitoken' => 'test-apitoken' }) + + allow(application_cache).to receive(:get) + .with('test-apiurl/v1/deployment/installer/agent/unix/paas/latest?include=java&bitness=64&' \ + 'Api-Token=test-apitoken') + .and_yield(Pathname.new('spec/fixtures/stub-dynatrace-one-agent.zip').open, false) + end + + it 'detects with dynatrace-n/a service' do + expect(component.detect).to eq('dynatrace-one-agent=latest') + end + + it 'downloads Dynatrace agent zip', + cache_fixture: 'stub-dynatrace-one-agent.zip' do + + component.compile + + expect(sandbox + 'agent/lib64/liboneagentproc.so').to exist + expect(sandbox + 'manifest.json').to exist + end + + it 'sets LD_PRELOAD with liboneagentproc', + app_fixture: 'framework_dynatrace_one_agent' do + + component.release + + expect(environment_variables).to include('LD_PRELOAD=$PWD/.java-buildpack/dynatrace_one_agent/agent/lib64/' \ + 'liboneagentproc.so') + end + + it 'updates environment variables', + app_fixture: 'framework_dynatrace_one_agent' do + + component.release + + expect(environment_variables).to include('DT_APPLICATIONID=test-application-name') + expect(environment_variables).to include('DT_TENANT=test-environmentid') + expect(environment_variables).to include('DT_TENANTTOKEN=token-from-file') + expect(environment_variables).to include('DT_CONNECTION_POINT=' \ + '"https://endpoint1/communication;https://endpoint2/communication"') + end + + context do + + let(:environment) do + { 'DT_APPLICATIONID' => 'test-application-id' } + end + + it 'does not update environment variables if they exist', + app_fixture: 'framework_dynatrace_one_agent' do + + component.release + + expect(environment_variables).not_to include(/DT_APPLICATIONID/) + end + + end + + context do + + before do + allow(services).to receive(:one_service?).with(/dynatrace/, 'apitoken', 'environmentid').and_return(true) + allow(services).to receive(:find_service).and_return('credentials' => { 'environmentid' => 'test-environmentid', + 'apiurl' => 'test-apiurl', + 'apitoken' => 'test-apitoken', + 'networkzone' => 'test-network-zone' }) + + allow(application_cache).to receive(:get) + .with('test-apiurl/v1/deployment/installer/agent/unix/paas/latest?include=java&bitness=64&' \ + 'Api-Token=test-apitoken&networkZone=test-network-zone') + .and_yield(Pathname.new('spec/fixtures/stub-dynatrace-one-agent.zip').open, false) + end + + it 'downloads Dynatrace agent zip with networkzone', + cache_fixture: 'stub-dynatrace-one-agent.zip' do + + component.compile + + expect(sandbox + 'agent/lib64/liboneagentproc.so').to exist + expect(sandbox + 'manifest.json').to exist + end + end + + context do + + before do + allow(services).to receive(:one_service?).with(/dynatrace/, 'apitoken', 'environmentid').and_return(true) + allow(services).to receive(:find_service).and_return('credentials' => { 'environmentid' => 'test-environmentid', + 'apiurl' => 'test-apiurl', + 'apitoken' => 'test-apitoken' }) + allow(application_cache).to receive(:get) + .with('test-apiurl/v1/deployment/installer/agent/unix/paas/latest?include=java&bitness=64' \ + '&Api-Token=test-apitoken') + .and_raise(RuntimeError.new('service interrupt')) + end + + it 'fails on download error on default' do + expect { component.compile }.to raise_error(RuntimeError) + end + + end + + context do + + before do + allow(services).to receive(:one_service?).with(/dynatrace/, 'apitoken', 'environmentid').and_return(true) + allow(services).to receive(:find_service).and_return('credentials' => { 'environmentid' => 'test-environmentid', + 'apiurl' => 'test-apiurl', + 'apitoken' => 'test-apitoken', + 'skiperrors' => 'true' }) + allow(application_cache).to receive(:get) + .with('test-apiurl/v1/deployment/installer/agent/unix/paas/latest?include=java&bitness=64' \ + '&Api-Token=test-apitoken') + .and_raise(RuntimeError.new('service interrupt')) + end + + it 'skips errors during compile and writes error file' do + component.compile + expect(sandbox + 'dynatrace_download_error').to exist + end + + it 'does not do anything during release' do + component.compile + component.release + + expect(java_opts).not_to include('-agentpath:$PWD/.java-buildpack/dynatrace_one_agent/agent/lib64/' \ + 'liboneagentloader.so') + end + + end + + end + +end diff --git a/spec/java_buildpack/framework/elastic_apm_agent_spec.rb b/spec/java_buildpack/framework/elastic_apm_agent_spec.rb new file mode 100644 index 0000000000..98c5c3caf9 --- /dev/null +++ b/spec/java_buildpack/framework/elastic_apm_agent_spec.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/elastic_apm_agent' +require 'java_buildpack/util/tokenized_version' + +describe JavaBuildpack::Framework::ElasticApmAgent do + include_context 'with component help' + + it 'does not detect without elastic-apm-n/a service' do + expect(component.detect).to be_nil + end + + context do + + before do + allow(services).to receive(:one_service?).with(/elastic-apm/, %w[server_urls secret_token]).and_return(true) + end + + it 'detects with elastic-apm-n/a service' do + expect(component.detect).to eq("elastic-apm-agent=#{version}") + end + + it 'downloads elastic-apm agent JAR', + cache_fixture: 'stub-elastic-apm-agent.jar' do + + component.compile + + expect(sandbox + "elastic_apm_agent-#{version}.jar").to exist + end + + it 'updates JAVA_OPTS' do + allow(services).to receive(:find_service).and_return('credentials' => { 'server_urls' => 'serverurl', + 'secret_token' => 'secret_token' }) + + component.release + + expect(java_opts).to include("-javaagent:$PWD/.java-buildpack/elastic_apm_agent/elastic_apm_agent-#{version}.jar") + expect(java_opts).to include('-Delastic.apm.home=$PWD/.java-buildpack/elastic_apm_agent') + expect(java_opts).to include('-Delastic.apm.server_urls=serverurl') + expect(java_opts).to include('-Delastic.apm.secret_token=secret_token') + expect(java_opts).to include('-Delastic.apm.service_name=test-application-name') + expect(java_opts).to include('-Delastic.apm.log_file_name=STDOUT') + end + + context do + let(:creds) do + { 'secret_token' => 'test-secret_token', + 'server_urls' => 'different-serverurl', + 'service_name' => 'different-name', + 'foo' => 'bar', + 'capture_jmx_metrics' => 'object_name[java.lang:type=Memory] ' \ + 'attribute[HeapMemoryUsage:metric_name=test_heap_metric]', + 'sub' => '$(echo \'Hello world!\') and stuff', + 'var' => '--> ${SOME_VAR} <--', + 'bool' => false, + 'nil' => nil } + end + + it 'updates JAVA_OPTS with additional options' do + allow(services).to receive(:find_service).and_return('credentials' => creds) + + component.release + + expect(java_opts).to include('-Delastic.apm.secret_token=test-secret_token') + expect(java_opts).to include('-Delastic.apm.server_urls=different-serverurl') + expect(java_opts).to include('-Delastic.apm.service_name=different-name') + expect(java_opts).to include('-Delastic.apm.foo=bar') + escaped = 'object_name\[java.lang:type\=Memory\]\ attribute\[HeapMemoryUsage:metric_name\=test_heap_metric\]' + expect(java_opts).to include("-Delastic.apm.capture_jmx_metrics=#{escaped}") + expect(java_opts).to include('-Delastic.apm.sub=\"$(echo \'Hello world!\') and stuff\"') + expect(java_opts).to include('-Delastic.apm.var=\"--> ${SOME_VAR} <--\"') + end + end + end +end diff --git a/spec/java_buildpack/framework/google_stackdriver_debugger_spec.rb b/spec/java_buildpack/framework/google_stackdriver_debugger_spec.rb new file mode 100644 index 0000000000..68aa50a4fd --- /dev/null +++ b/spec/java_buildpack/framework/google_stackdriver_debugger_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/google_stackdriver_debugger' + +describe JavaBuildpack::Framework::GoogleStackdriverDebugger do + include_context 'with component help' + + it 'does not detect without google-stackdriver-debugger-n/a service' do + expect(component.detect).to be_nil + end + + context do + + before do + allow(services).to receive(:one_service?) + .with(/google-stackdriver-debugger/, 'PrivateKeyData').and_return(true) + + allow(services).to receive(:find_service).and_return( + 'credentials' => { + 'PrivateKeyData' => 'dGVzdC1wcml2YXRlLWtleS1kYXRh' + } + ) + end + + it 'detects with google-stackdriver-debugger-c-n/a service' do + expect(component.detect).to eq("google-stackdriver-debugger=#{version}") + end + + it 'unpacks the google stackdriver debugger tar', + cache_fixture: 'stub-google-stackdriver-debugger.tar.gz' do + + component.compile + + expect(sandbox + 'cdbg_java_agent.so').to exist + end + + it 'writes JSOn file', + cache_fixture: 'stub-google-stackdriver-debugger.tar.gz' do + + component.compile + + expect(sandbox + 'svc.json').to exist + expect(File.read(sandbox + 'svc.json')).to eq("test-private-key-data\n") + end + + it 'updates JAVA_OPTS' do + component.release + expect(java_opts).to include('-agentpath:$PWD/.java-buildpack/google_stackdriver_debugger/cdbg_java_agent.so=' \ + '--logtostderr=1') + expect(java_opts).to include('-Dcom.google.cdbg.auth.serviceaccount.enable=true') + expect(java_opts).to include('-Dcom.google.cdbg.auth.serviceaccount.jsonfile=' \ + '$PWD/.java-buildpack/google_stackdriver_debugger/svc.json') + expect(java_opts).to include('-Dcom.google.cdbg.module=test-application-name') + expect(java_opts).to include('-Dcom.google.cdbg.version=test-application-version') + end + + end + +end diff --git a/spec/java_buildpack/framework/google_stackdriver_profiler_spec.rb b/spec/java_buildpack/framework/google_stackdriver_profiler_spec.rb new file mode 100644 index 0000000000..aadfb945a3 --- /dev/null +++ b/spec/java_buildpack/framework/google_stackdriver_profiler_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/google_stackdriver_profiler' + +describe JavaBuildpack::Framework::GoogleStackdriverProfiler do + include_context 'with component help' + + it 'does not detect without google-stackdriver-profiler service' do + expect(component.detect).to be_nil + end + + context do + + before do + allow(services).to receive(:one_service?) + .with(/google-stackdriver-profiler/, 'PrivateKeyData').and_return(true) + + allow(services).to receive(:find_service).and_return( + 'credentials' => { + 'PrivateKeyData' => 'eyJwcm9qZWN0X2lkIjoidGVzdC1wcm9qZWN0LWlkIn0=' + } + ) + end + + it 'detects with google-stackdriver-profiler service' do + expect(component.detect).to eq("google-stackdriver-profiler=#{version}") + end + + it 'unpacks the google stackdriver debugger tar', + cache_fixture: 'stub-google-stackdriver-profiler.tar.gz' do + + component.compile + + expect(sandbox + 'profiler_java_agent.so').to exist + end + + it 'writes JSON file', + cache_fixture: 'stub-google-stackdriver-profiler.tar.gz' do + + component.compile + + expect(sandbox + 'svc.json').to exist + expect(File.read(sandbox + 'svc.json')).to eq("{\"project_id\":\"test-project-id\"}\n") + end + + it 'updates JAVA_OPTS' do + component.release + expect(java_opts).to include('-agentpath:$PWD/.java-buildpack/google_stackdriver_profiler/' \ + 'profiler_java_agent.so=--logtostderr=1,-cprof_project_id=test-project-id,' \ + '-cprof_service=test-application-name,' \ + '-cprof_service_version=test-application-version') + + expect(environment_variables).to include('GOOGLE_APPLICATION_CREDENTIALS=' \ + '$PWD/.java-buildpack/google_stackdriver_profiler/svc.json') + end + + end + +end diff --git a/spec/java_buildpack/framework/introscope_agent_spec.rb b/spec/java_buildpack/framework/introscope_agent_spec.rb new file mode 100644 index 0000000000..a529082344 --- /dev/null +++ b/spec/java_buildpack/framework/introscope_agent_spec.rb @@ -0,0 +1,338 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/introscope_agent' + +describe JavaBuildpack::Framework::IntroscopeAgent do + include_context 'with component help' + + let(:configuration) do + { 'default_agent_name' => "$(expr \"$VCAP_APPLICATION\" : '.*application_name[\": ]*\\([A-Za-z0-9_-]*\\).*')" } + end + + let(:vcap_application) do + { 'application_name' => 'test-application-name', + 'application_uris' => %w[test-application-uri-0 test-application-uri-1] } + end + + it 'does not detect without introscope-n/a service' do + expect(component.detect).to be_nil + end + + context do + + let(:credentials) { {} } + + before do + allow(services).to receive(:one_service?).with(/introscope/, + %w[agentManager_url_1 agent_manager_url]).and_return(true) + allow(services).to receive(:find_service).and_return('credentials' => credentials) + end + + it 'detects with introscope-n/a service' do + expect(component.detect).to eq("introscope-agent=#{version}") + end + + it 'expands Introscope agent zip', cache_fixture: 'stub-introscope-agent.tar' do + + component.compile + + expect(sandbox + 'Agent.jar').to exist + end + + context do + let(:credentials) { { 'agent_name' => 'another-test-agent-name', 'agent_manager_url' => 'default-host:5001' } } + + it 'adds agent_name from credentials to JAVA_OPTS if specified' do + component.release + + expect(java_opts).to include('-Dcom.wily.introscope.agent.agentName=another-test-agent-name') + end + end + + context do + + let(:credentials) { { 'agent_manager_url' => 'test-host-name:5001' } } + + it 'parses the url and sets host port and default socket factory' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/introscope_agent/Agent.jar') + expect(java_opts).to include('-Dcom.wily.introscope.agentProfile=$PWD/.java-buildpack/introscope_agent/core' \ + '/config/IntroscopeAgent.profile') + expect(java_opts).to include('-Dintroscope.agent.defaultProcessName=test-application-name') + expect(java_opts).to include('-Dintroscope.agent.hostName=test-application-uri-0') + + expect(java_opts).to include('-DagentManager.url.1=test-host-name:5001') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.host.DEFAULT=test-host-name') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.port.DEFAULT=5001') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.socketfactory.DEFAULT=' \ + 'com.wily.isengard.postofficehub.link.net.DefaultSocketFactory') + + expect(java_opts).to include('-Dcom.wily.introscope.agent.agentName=$(expr "$VCAP_APPLICATION" : ' \ + '\'.*application_name[": ]*\\([A-Za-z0-9_-]*\\).*\')') + end + end + + context do + + let(:credentials) { { 'agent_manager_url' => 'test-host-name:5001' } } + + it 'parses the agent_manager_url and sets host port and default socket factory' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/introscope_agent/Agent.jar') + expect(java_opts).to include('-Dcom.wily.introscope.agentProfile=$PWD/.java-buildpack/introscope_agent/core' \ + '/config/IntroscopeAgent.profile') + expect(java_opts).to include('-Dintroscope.agent.defaultProcessName=test-application-name') + expect(java_opts).to include('-Dintroscope.agent.hostName=test-application-uri-0') + + expect(java_opts).to include('-DagentManager.url.1=test-host-name:5001') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.host.DEFAULT=test-host-name') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.port.DEFAULT=5001') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.socketfactory.DEFAULT=' \ + 'com.wily.isengard.postofficehub.link.net.DefaultSocketFactory') + + expect(java_opts).to include('-Dcom.wily.introscope.agent.agentName=$(expr "$VCAP_APPLICATION" : ' \ + '\'.*application_name[": ]*\\([A-Za-z0-9_-]*\\).*\')') + end + end + + context do + let(:credentials) { { 'agent_manager_url' => 'ssl://test-host-name:5443' } } + + it 'parses the url and sets host, port, and ssl socket factory' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/introscope_agent/Agent.jar') + expect(java_opts).to include('-Dcom.wily.introscope.agentProfile=$PWD/.java-buildpack/introscope_agent/core' \ + '/config/IntroscopeAgent.profile') + expect(java_opts).to include('-Dintroscope.agent.defaultProcessName=test-application-name') + expect(java_opts).to include('-Dintroscope.agent.hostName=test-application-uri-0') + + expect(java_opts).to include('-DagentManager.url.1=ssl://test-host-name:5443') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.host.DEFAULT=test-host-name') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.port.DEFAULT=5443') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.socketfactory.DEFAULT=' \ + 'com.wily.isengard.postofficehub.link.net.SSLSocketFactory') + + expect(java_opts).to include('-Dcom.wily.introscope.agent.agentName=$(expr "$VCAP_APPLICATION" : ' \ + '\'.*application_name[": ]*\\([A-Za-z0-9_-]*\\).*\')') + end + end + + context do + let(:credentials) { { 'agent_manager_url' => 'ssl://test-host-name:5443' } } + + it 'parses the agent_manager_url and sets host, port, and ssl socket factory' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/introscope_agent/Agent.jar') + expect(java_opts).to include('-Dcom.wily.introscope.agentProfile=$PWD/.java-buildpack/introscope_agent/core' \ + '/config/IntroscopeAgent.profile') + expect(java_opts).to include('-Dintroscope.agent.defaultProcessName=test-application-name') + expect(java_opts).to include('-Dintroscope.agent.hostName=test-application-uri-0') + + expect(java_opts).to include('-DagentManager.url.1=ssl://test-host-name:5443') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.host.DEFAULT=test-host-name') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.port.DEFAULT=5443') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.socketfactory.DEFAULT=' \ + 'com.wily.isengard.postofficehub.link.net.SSLSocketFactory') + + expect(java_opts).to include('-Dcom.wily.introscope.agent.agentName=$(expr "$VCAP_APPLICATION" : ' \ + '\'.*application_name[": ]*\\([A-Za-z0-9_-]*\\).*\')') + end + end + + context do + let(:credentials) { { 'agent_manager_url' => 'http://test-host-name:8081' } } + + it 'parses the url and sets host, port, and http socket factory' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/introscope_agent/Agent.jar') + expect(java_opts).to include('-Dcom.wily.introscope.agentProfile=$PWD/.java-buildpack/introscope_agent/core' \ + '/config/IntroscopeAgent.profile') + expect(java_opts).to include('-Dintroscope.agent.defaultProcessName=test-application-name') + expect(java_opts).to include('-Dintroscope.agent.hostName=test-application-uri-0') + + expect(java_opts).to include('-DagentManager.url.1=http://test-host-name:8081') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.host.DEFAULT=test-host-name') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.port.DEFAULT=8081') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.socketfactory.DEFAULT=' \ + 'com.wily.isengard.postofficehub.link.net.HttpTunnelingSocketFactory') + + expect(java_opts).to include('-Dcom.wily.introscope.agent.agentName=$(expr "$VCAP_APPLICATION" : ' \ + '\'.*application_name[": ]*\\([A-Za-z0-9_-]*\\).*\')') + end + end + + context do + let(:credentials) { { 'agent_manager_url' => 'http://test-host-name:8081' } } + + it 'parses the agent_manager_url and sets host, port, and http socket factory' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/introscope_agent/Agent.jar') + expect(java_opts).to include('-Dcom.wily.introscope.agentProfile=$PWD/.java-buildpack/introscope_agent/core' \ + '/config/IntroscopeAgent.profile') + expect(java_opts).to include('-Dintroscope.agent.defaultProcessName=test-application-name') + expect(java_opts).to include('-Dintroscope.agent.hostName=test-application-uri-0') + + expect(java_opts).to include('-DagentManager.url.1=http://test-host-name:8081') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.host.DEFAULT=test-host-name') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.port.DEFAULT=8081') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.socketfactory.DEFAULT=' \ + 'com.wily.isengard.postofficehub.link.net.HttpTunnelingSocketFactory') + + expect(java_opts).to include('-Dcom.wily.introscope.agent.agentName=$(expr "$VCAP_APPLICATION" : ' \ + '\'.*application_name[": ]*\\([A-Za-z0-9_-]*\\).*\')') + end + end + + context do + let(:credentials) { { 'agent_manager_url' => 'https://test-host-name:8444' } } + + it 'parses the url and sets host, port, and https socket factory' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/introscope_agent/Agent.jar') + expect(java_opts).to include('-Dcom.wily.introscope.agentProfile=$PWD/.java-buildpack/introscope_agent/core' \ + '/config/IntroscopeAgent.profile') + expect(java_opts).to include('-Dintroscope.agent.defaultProcessName=test-application-name') + expect(java_opts).to include('-Dintroscope.agent.hostName=test-application-uri-0') + + expect(java_opts).to include('-DagentManager.url.1=https://test-host-name:8444') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.host.DEFAULT=test-host-name') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.port.DEFAULT=8444') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.socketfactory.DEFAULT=' \ + 'com.wily.isengard.postofficehub.link.net.HttpsTunnelingSocketFactory') + + expect(java_opts).to include('-Dcom.wily.introscope.agent.agentName=$(expr "$VCAP_APPLICATION" : ' \ + '\'.*application_name[": ]*\\([A-Za-z0-9_-]*\\).*\')') + end + end + + context do + let(:credentials) { { 'agent_manager_url' => 'https://test-host-name:8444' } } + + it 'parses the agent_manager_url and sets host, port, and https socket factory' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/introscope_agent/Agent.jar') + expect(java_opts).to include('-Dcom.wily.introscope.agentProfile=$PWD/.java-buildpack/introscope_agent/core' \ + '/config/IntroscopeAgent.profile') + expect(java_opts).to include('-Dintroscope.agent.defaultProcessName=test-application-name') + expect(java_opts).to include('-Dintroscope.agent.hostName=test-application-uri-0') + + expect(java_opts).to include('-DagentManager.url.1=https://test-host-name:8444') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.host.DEFAULT=test-host-name') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.port.DEFAULT=8444') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.socketfactory.DEFAULT=' \ + 'com.wily.isengard.postofficehub.link.net.HttpsTunnelingSocketFactory') + + expect(java_opts).to include('-Dcom.wily.introscope.agent.agentName=$(expr "$VCAP_APPLICATION" : ' \ + '\'.*application_name[": ]*\\([A-Za-z0-9_-]*\\).*\')') + end + end + + context do + let(:credentials) do + { 'agent_manager_url' => 'https://test-host-name:8444', + 'agent_manager_credential' => 'test-credential-cccf-88-ae' } + end + + it 'sets the url and also the credential' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/introscope_agent/Agent.jar') + expect(java_opts).to include('-Dcom.wily.introscope.agentProfile=$PWD/.java-buildpack/introscope_agent/core' \ + '/config/IntroscopeAgent.profile') + expect(java_opts).to include('-Dintroscope.agent.defaultProcessName=test-application-name') + expect(java_opts).to include('-Dintroscope.agent.hostName=test-application-uri-0') + + expect(java_opts).to include('-DagentManager.url.1=https://test-host-name:8444') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.host.DEFAULT=test-host-name') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.port.DEFAULT=8444') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.socketfactory.DEFAULT=' \ + 'com.wily.isengard.postofficehub.link.net.HttpsTunnelingSocketFactory') + + expect(java_opts).to include('-Dcom.wily.introscope.agent.agentName=$(expr "$VCAP_APPLICATION" : ' \ + '\'.*application_name[": ]*\\([A-Za-z0-9_-]*\\).*\')') + expect(java_opts).to include('-DagentManager.credential=test-credential-cccf-88-ae') + end + end + + context do + let(:credentials) do + { 'agent_manager_url' => 'https://test-host-name:8444', + 'agent_manager_credential' => 'test-credential-cccf-88-ae' } + end + + it 'sets the agent_manager_url and also the agent_manager_credential' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/introscope_agent/Agent.jar') + expect(java_opts).to include('-Dcom.wily.introscope.agentProfile=$PWD/.java-buildpack/introscope_agent/core' \ + '/config/IntroscopeAgent.profile') + expect(java_opts).to include('-Dintroscope.agent.defaultProcessName=test-application-name') + expect(java_opts).to include('-Dintroscope.agent.hostName=test-application-uri-0') + + expect(java_opts).to include('-DagentManager.url.1=https://test-host-name:8444') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.host.DEFAULT=test-host-name') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.port.DEFAULT=8444') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.socketfactory.DEFAULT=' \ + 'com.wily.isengard.postofficehub.link.net.HttpsTunnelingSocketFactory') + + expect(java_opts).to include('-Dcom.wily.introscope.agent.agentName=$(expr "$VCAP_APPLICATION" : ' \ + '\'.*application_name[": ]*\\([A-Za-z0-9_-]*\\).*\')') + expect(java_opts).to include('-DagentManager.credential=test-credential-cccf-88-ae') + end + end + + context do + let(:credentials) do + { 'agentManager_url_1' => 'https://test-host-name:8444', + 'agent_manager_credential' => 'test-credential-cccf-88-ae', + 'agent_default_process_name' => 'TestProcess' } + end + + it 'sets the agent_manager_url, agent_manager_credential, and agent_process_name' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/introscope_agent/Agent.jar') + expect(java_opts).to include('-Dcom.wily.introscope.agentProfile=$PWD/.java-buildpack/introscope_agent/core' \ + '/config/IntroscopeAgent.profile') + expect(java_opts).to include('-Dintroscope.agent.defaultProcessName=TestProcess') + expect(java_opts).to include('-Dintroscope.agent.hostName=test-application-uri-0') + + expect(java_opts).to include('-DagentManager.url.1=https://test-host-name:8444') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.host.DEFAULT=test-host-name') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.port.DEFAULT=8444') + expect(java_opts).to include('-Dintroscope.agent.enterprisemanager.transport.tcp.socketfactory.DEFAULT=' \ + 'com.wily.isengard.postofficehub.link.net.HttpsTunnelingSocketFactory') + + expect(java_opts).to include('-Dcom.wily.introscope.agent.agentName=$(expr "$VCAP_APPLICATION" : ' \ + '\'.*application_name[": ]*\\([A-Za-z0-9_-]*\\).*\')') + expect(java_opts).to include('-DagentManager.credential=test-credential-cccf-88-ae') + end + end + + end +end diff --git a/spec/java_buildpack/framework/jacoco_agent_spec.rb b/spec/java_buildpack/framework/jacoco_agent_spec.rb new file mode 100644 index 0000000000..05957fa272 --- /dev/null +++ b/spec/java_buildpack/framework/jacoco_agent_spec.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/jacoco_agent' +require 'java_buildpack/util/tokenized_version' + +describe JavaBuildpack::Framework::JacocoAgent do + include_context 'with component help' + + it 'does not detect without jacoco service' do + expect(component.detect).to be_nil + end + + context do + + before do + allow(services).to receive(:one_service?).with(/jacoco/, 'address').and_return(true) + end + + it 'detects with jacoco service' do + expect(component.detect).to eq("jacoco-agent=#{version}") + end + + it 'downloads JaCoco agent JAR', + cache_fixture: 'stub-jacoco-agent.jar' do + + component.compile + + expect(sandbox + 'jacocoagent.jar').to exist + end + + it 'updates JAVA_OPTS' do + allow(services).to receive(:find_service).and_return('credentials' => { 'address' => 'test-address' }) + + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/jacoco_agent/jacocoagent.jar=' \ + 'address=test-address,output=tcpclient,sessionid=$CF_INSTANCE_GUID') + end + + it 'updates JAVA_OPTS with additional options' do + allow(services).to receive(:find_service).and_return('credentials' => { 'address' => 'test-address', + 'output' => 'test-output', + 'excludes' => 'test-excludes', + 'includes' => 'test-includes', + 'port' => 6300 }) + + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/jacoco_agent/jacocoagent.jar=' \ + 'address=test-address,output=test-output,sessionid=$CF_INSTANCE_GUID,' \ + 'excludes=test-excludes,includes=test-includes,port=6300') + end + + end + +end diff --git a/spec/java_buildpack/framework/java_cfenv_spec.rb b/spec/java_buildpack/framework/java_cfenv_spec.rb new file mode 100644 index 0000000000..e461823fd2 --- /dev/null +++ b/spec/java_buildpack/framework/java_cfenv_spec.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'logging_helper' +require 'java_buildpack/framework/java_cf_env' + +describe JavaBuildpack::Framework::JavaCfEnv do + include_context 'with component help' + include_context 'with console help' + include_context 'with logging help' + + let(:configuration) { { 'enabled' => true } } + + it 'detects with Spring Boot 3 JAR', + app_fixture: 'framework_java_cf_boot_3' do + + expect(component.detect).to eq("java-cf-env=#{version}") + end + + it 'does not detect with Spring Boot < 3', + app_fixture: 'framework_java_cf_boot_2' do + + expect(component.detect).to be_nil + end + + it 'does not detect with Spring Boot 3 named jar but not Spring Boot 3 Manifest', + app_fixture: 'framework_java_not_spring_boot3' do + + expect(component.detect).to be_nil + end + + it 'does not detect with Spring Boot 3 & java-cfenv present', + app_fixture: 'framework_java_cf_exists' do + + expect(component.detect).to be_nil + end + + context do + let(:configuration) { { 'enabled' => false } } + + it 'does not detect if disabled', + app_fixture: 'framework_java_cf_boot_3' do + + expect(component.detect).to be_nil + end + end + + it 'downloads additional libraries', + app_fixture: 'framework_java_cf_boot_3', + cache_fixture: 'stub-java-cfenv.jar' do + + component.compile + + expect(sandbox + "java_cf_env-#{version}.jar").to exist + end + + it 'adds to additional libraries', + app_fixture: 'framework_java_cf_boot_3', + cache_fixture: 'stub-java-cfenv.jar' do + + component.release + + expect(additional_libraries).to include(sandbox + "java_cf_env-#{version}.jar") + end + +end diff --git a/spec/java_buildpack/framework/java_memory_assistant/agent_spec.rb b/spec/java_buildpack/framework/java_memory_assistant/agent_spec.rb new file mode 100644 index 0000000000..c70c640f79 --- /dev/null +++ b/spec/java_buildpack/framework/java_memory_assistant/agent_spec.rb @@ -0,0 +1,288 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'application_helper' +require 'component_helper' +require 'logging_helper' +require 'java_buildpack/component/environment_variables' +require 'java_buildpack/framework/java_memory_assistant/agent' + +describe JavaBuildpack::Framework::JavaMemoryAssistantAgent do + include_context 'with application help' + include_context 'with component help' + include_context 'with logging help' + + let(:vcap_application) do + { + 'instance_index' => '42', + 'instance_id' => '406beca7-7692-41f4-9482-f32ae0a1da93' + } + end + + context do + + let(:configuration) do + { + 'check_interval' => '5s', + 'max_frequency' => '1/1m', + 'thresholds' => { + 'heap' => 90, + 'old_gen' => 90 + } + } + end + + let(:version) { '1.2.3' } + + it 'updates JAVA_OPTS with default values' do + component.release + + expect(java_opts).not_to include('--add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED') + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/java_memory_assistant_agent/' \ + 'java-memory-assistant-1.2.3.jar') + expect(java_opts).to include('-Djma.enabled=true') + + expect(java_opts).to include('-Djma.check_interval=5s') + expect(java_opts).to include('\'-Djma.max_frequency=1/1m\'') + + expect(java_opts).to include('\'-Djma.thresholds.heap=90\'') + expect(java_opts).to include('\'-Djma.thresholds.old_gen=90\'') + + end + + context do + + let(:java_home_delegate) do + delegate = JavaBuildpack::Component::MutableJavaHome.new + delegate.root = app_dir + '.test-java-home' + delegate.version = JavaBuildpack::Util::TokenizedVersion.new('1.8.0_55') + + delegate + end + + it 'does not add the --add-opens on Java 8' do + component.release + + expect(java_opts).not_to include('--add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED') + end + + end + + context do + + let(:java_home_delegate) do + delegate = JavaBuildpack::Component::MutableJavaHome.new + delegate.root = app_dir + '.test-java-home' + delegate.version = JavaBuildpack::Util::TokenizedVersion.new('9.0.1') + + delegate + end + + it 'adds the --add-opens on Java 11' do + component.release + + expect(java_opts).to include('--add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED') + end + + end + + end + + context do + let(:configuration) do + { + 'check_interval' => '10m', + 'max_frequency' => '4/10h', + 'heap_dump_folder' => 'test/folder', + 'log_level' => 'DEBUG', + 'thresholds' => { + 'heap' => 60, + 'code_cache' => 30, + 'metaspace' => 5, + 'perm_gen' => 45.5, + 'eden' => 90, + 'survivor' => 95.5, + 'old_gen' => 30 + } + } + end + + let(:version) { '0.1.0' } + + it 'updates JAVA_OPTS with configured values' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/java_memory_assistant_agent/' \ + 'java-memory-assistant-0.1.0.jar') + expect(java_opts).to include('-Djma.enabled=true') + expect(java_opts).to include('-Djma.check_interval=10m') + expect(java_opts).to include('\'-Djma.max_frequency=4/10h\'') + expect(java_opts).to include('-Djma.log_level=DEBUG') + expect(java_opts).to include('\'-Djma.thresholds.heap=60\'') + expect(java_opts).to include('\'-Djma.thresholds.code_cache=30\'') + expect(java_opts).to include('\'-Djma.thresholds.metaspace=5\'') + expect(java_opts).to include('\'-Djma.thresholds.perm_gen=45.5\'') + expect(java_opts).to include('\'-Djma.thresholds.eden=90\'') + expect(java_opts).to include('\'-Djma.thresholds.survivor=95.5\'') + expect(java_opts).to include('\'-Djma.thresholds.old_gen=30\'') + end + + end + + context do + let(:configuration) do + { + 'log_level' => 'debug' + } + end + + it 'maps log-level case-insensitive' do + component.release + + expect(java_opts).to include('-Djma.log_level=DEBUG') + end + + end + + context do + let(:configuration) do + { + 'thresholds' => { + 'heap' => '>600MB', + 'eden' => '< 30MB' + } + } + end + + let(:version) { '0.1.0' } + + it 'escapses redirection characters' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/java_memory_assistant_agent/' \ + 'java-memory-assistant-0.1.0.jar') + + expect(java_opts).to include('\'-Djma.thresholds.heap=>600MB\'') + expect(java_opts).to include('\'-Djma.thresholds.eden=< 30MB\'') + end + + end + + context do + let(:configuration) do + { + 'log_level' => 'WARN' + } + end + + let(:version) { '0.1.0' } + + it 'maps log-level WARN to WARNING' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/java_memory_assistant_agent/' \ + 'java-memory-assistant-0.1.0.jar') + + expect(java_opts).to include('-Djma.enabled=true') + expect(java_opts).to include('-Djma.log_level=WARNING') + end + + end + + context do + let(:configuration) do + { + 'log_level' => 'INFO' + } + end + + let(:version) { '0.1.0' } + + it 'maps log-level INFO to INFO' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/java_memory_assistant_agent/' \ + 'java-memory-assistant-0.1.0.jar') + + expect(java_opts).to include('-Djma.enabled=true') + expect(java_opts).to include('-Djma.log_level=INFO') + end + + end + + context do + let(:configuration) do + { + 'log_level' => 'ERROR' + } + end + + it 'maps log-level ERROR to ERROR' do + component.release + + expect(java_opts).to include('-Djma.log_level=ERROR') + end + + end + + context do + let(:configuration) do + { + 'log_level' => 'FATAL' + } + end + + it 'maps log-level FATAL to ERROR' do + component.release + + expect(java_opts).to include('-Djma.log_level=ERROR') + end + + end + + context do + + let(:configuration) do + {} + end + + it 'falls back on JBP log_level when no log_level specified via configuration', + :enable_log_file, log_level: 'WARN' do + component.release + + expect(java_opts).to include('-Djma.log_level=WARNING') + end + + end + + context do + let(:configuration) do + { + 'log_level' => 'ciao' + } + end + + it 'fails if log_level is not recognized' do + expect { component.release }.to raise_exception 'Invalid value of the \'log_level\'' \ + ' property: \'ciao\'' + end + + end + +end diff --git a/spec/java_buildpack/framework/java_memory_assistant/clean_up_spec.rb b/spec/java_buildpack/framework/java_memory_assistant/clean_up_spec.rb new file mode 100644 index 0000000000..9838d33a67 --- /dev/null +++ b/spec/java_buildpack/framework/java_memory_assistant/clean_up_spec.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'application_helper' +require 'component_helper' +require 'java_buildpack/framework/java_memory_assistant/clean_up' + +describe JavaBuildpack::Framework::JavaMemoryAssistantCleanUp do + include_context 'with application help' + include_context 'with component help' + + let(:version) { '1.2.3' } + + context do + + let(:configuration) do + { + 'max_dump_count' => 1 + } + end + + it 'downloads and unpacks the cleanup command', + cache_fixture: 'stub-java-memory-assistant-cleanup.zip' do + + component.compile + + expect(sandbox + 'cleanup').to exist + end + + end + + context do + + let(:configuration) do + { + 'max_dump_count' => 1 + } + end + + it 'configures clean up' do + component.release + + expect(java_opts).to include('-Djma.command.interpreter=') + expect(java_opts).to include('-Djma.execute.before=$PWD/.java-buildpack/java_memory_assistant_clean_up/' \ + 'cleanup') + end + + end + + context do + + let(:configuration) do + { + 'max_dump_count' => 0 + } + end + + it 'does not configure clean up when max_dump_count is zero' do + component.release + + expect(java_opts).not_to include('-Djma.command.interpreter=') + expect(java_opts).not_to include('-Djma.execute.before=$PWD/.java-buildpack/java_memory_assistant_clean_up/' \ + 'cleanup') + end + + end + + context do + + let(:configuration) do + {} + end + + it 'does not configure clean up when max_dump_count is not set' do + component.release + + expect(java_opts).not_to include('-Djma.command.interpreter=') + expect(java_opts).not_to include('-Djma.execute.before=$PWD/.java-buildpack/java_memory_assistant_clean_up/' \ + 'cleanup') + end + + end + +end diff --git a/spec/java_buildpack/framework/java_memory_assistant/heap_dump_folder_spec.rb b/spec/java_buildpack/framework/java_memory_assistant/heap_dump_folder_spec.rb new file mode 100644 index 0000000000..ac1b671d8d --- /dev/null +++ b/spec/java_buildpack/framework/java_memory_assistant/heap_dump_folder_spec.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'application_helper' +require 'component_helper' +require 'logging_helper' +require 'java_buildpack/framework/java_memory_assistant/heap_dump_folder' + +describe JavaBuildpack::Framework::JavaMemoryAssistantHeapDumpFolder do + include_context 'with application help' + include_context 'with component help' + include_context 'with logging help' + + let(:logger) { described_class.instance.get_logger String } + + context do + let(:vcap_application) do + { + 'space_name' => '1234567890', + 'space_id' => '0987654321', + 'application_name' => 'abcdefghi', + 'application_id' => 'ihgfedcba' + } + end + + let(:configuration) do + { + 'heap_dump_folder' => nil + } + end + + it 'uses the default for \'jma.heap_dump_folder\' if no value is specified', :enable_log_file, log_level: 'INFO' do + + component.release + + expect(java_opts).to include('-Djma.heap_dump_folder="1234567890-09876543/abcdefghi-ihgfedcb"') + expect(environment_variables).to include('JMA_HEAP_DUMP_FOLDER=1234567890-09876543/abcdefghi-ihgfedcb') + + expect(log_contents).to match(%r{Heap dumps will be stored under '1234567890-09876543/abcdefghi-ihgfedcb'}) + end + + end + + context do + let(:configuration) do + { + 'heap_dump_folder' => 'test/folder' + } + end + + it 'adds \'jma.heap_dump_folder\' with verbatim value', :enable_log_file, log_level: 'INFO' do + + component.release + + expect(java_opts).to include('-Djma.heap_dump_folder="test/folder"') + expect(environment_variables).to include('JMA_HEAP_DUMP_FOLDER=test/folder') + + expect(log_contents).to match(%r{Heap dumps will be stored under 'test/folder'}) + end + + end + + context do + let(:configuration) do + { + 'heap_dump_folder' => 'test/folder' + } + end + + before do + allow(services).to receive(:find_volume_service).with('heap-dump') + .and_return('volume_mounts' => + [ + { + 'container_dir' => '/my_volume', + 'mode' => 'rw' + } + ]) + end + + it 'adds \'jma.heap_dump_folder\' with volume container_dir as path root', :enable_log_file, log_level: 'INFO' do + + component.release + + expect(java_opts).to include('-Djma.heap_dump_folder="/my_volume/test/folder"') + expect(environment_variables).to include('JMA_HEAP_DUMP_FOLDER=/my_volume/test/folder') + + expect(log_contents).to match(%r{Heap dumps will be stored under '/my_volume/test/folder'}) + end + + end + + context do + let(:configuration) do + { + 'heap_dump_folder' => 'test/folder' + } + end + + before do + allow(services).to receive(:find_volume_service).with('heap-dump') + .and_return('volume_mounts' => + [ + { + 'container_dir' => '/my_volume', + 'mode' => 'r' + } + ]) + end + + it 'fails if volume does not have write mode active', :enable_log_file, log_level: 'DEBUG' do + expect { component.release }.to raise_error 'Volume mounted under \'/my_volume\' not in write mode' + expect(log_contents).not_to match(/Heap dumps will be stored under/) + end + + end + +end diff --git a/spec/java_buildpack/framework/java_memory_assistant_spec.rb b/spec/java_buildpack/framework/java_memory_assistant_spec.rb new file mode 100644 index 0000000000..46ec183291 --- /dev/null +++ b/spec/java_buildpack/framework/java_memory_assistant_spec.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'application_helper' +require 'component_helper' +require 'java_buildpack/framework/java_memory_assistant' +require 'java_buildpack/framework/java_memory_assistant/agent' +require 'java_buildpack/framework/java_memory_assistant/clean_up' +require 'java_buildpack/framework/java_memory_assistant/heap_dump_folder' + +describe JavaBuildpack::Framework::JavaMemoryAssistant do + include_context 'with component help' + + let(:component) { StubJavaMemoryAssistant.new context } + + context do + + let(:configuration) do + { + 'enabled' => false + } + end + + it 'does not activate submodules if it is disabled in the configuration' do + expect(component.detect).not_to be_truthy + end + + end + + context do + + let(:configuration) do + { 'enabled' => true, + 'agent' => agent_configuration, + 'clean_up' => clean_up_configuration } + end + + let(:agent_configuration) { instance_double('agent_configuration') } + + let(:clean_up_configuration) { instance_double('clean_up_configuration') } + + it 'creates submodules' do + allow(JavaBuildpack::Framework::JavaMemoryAssistantAgent) + .to receive(:new).with(sub_configuration_context(agent_configuration)) + allow(JavaBuildpack::Framework::JavaMemoryAssistantHeapDumpFolder) + .to receive(:new).with(sub_configuration_context(agent_configuration)) + allow(JavaBuildpack::Framework::JavaMemoryAssistantCleanUp) + .to receive(:new).with(sub_configuration_context(clean_up_configuration)) + + component.sub_components context + end + end + +end + +class StubJavaMemoryAssistant < JavaBuildpack::Framework::JavaMemoryAssistant + + public :command, :sub_components, :supports? + +end + +def sub_configuration_context(configuration) + c = context.clone + c[:configuration] = configuration + c +end diff --git a/spec/java_buildpack/framework/java_opts_spec.rb b/spec/java_buildpack/framework/java_opts_spec.rb index 2d5763ebbb..23d08bb8a2 100644 --- a/spec/java_buildpack/framework/java_opts_spec.rb +++ b/spec/java_buildpack/framework/java_opts_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +20,7 @@ require 'java_buildpack/framework/java_opts' describe JavaBuildpack::Framework::JavaOpts do - include_context 'component_helper' + include_context 'with component help' context do let(:configuration) { { 'java_opts' => '-Xmx1024M' } } @@ -38,107 +39,77 @@ end end - context do - let(:environment) { { 'JAVA_OPTS' => '-Dalpha=bravo' } } - - it 'does not detect with ENV and without from_environment configuration' do - expect(component.detect).to be_nil - end - end - - it 'does not detect without java_opts configuration' do - expect(component.detect).to be_nil - end - context do let(:configuration) do { 'java_opts' => '-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=y ' \ - "-XX:OnOutOfMemoryError='kill -9 %p'" } + "-XX:OnOutOfMemoryError='kill -9 %p'" } end it 'adds split java_opts to context' do component.release - expect(java_opts).to include('-Xdebug') expect(java_opts).to include('-Xnoagent') - expect(java_opts).to include('-Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=y') - expect(java_opts).to include('-XX:OnOutOfMemoryError=kill\ -9\ %p') + expect(java_opts).to include('-Xrunjdwp:transport=dt_socket,server\=y,address\=8000,suspend\=y') + expect(java_opts).to include('-XX:OnOutOfMemoryError=kill\ -9\ \%p') end end context do - let(:configuration) { { 'java_opts' => '-Xms1024M' } } - - it 'raises an error if a -Xms is configured' do - expect { component.compile }.to raise_error(/-Xms/) + let(:configuration) do + { 'java_opts' => '-Dtest=!£%^&*()<>[]{};~`' } end - end - context do - let(:configuration) { { 'java_opts' => '-Xmx1024M' } } - - it 'raises an error if a -Xmx is configured' do - expect { component.compile }.to raise_error(/-Xmx/) + it 'escapes special characters' do + component.release + expect(java_opts).to include('-Dtest=\!\£\%\^\&\*\(\)\<\>\[\]\{\}\;\~\`') end end context do - let(:configuration) { { 'java_opts' => '-XX:MaxMetaspaceSize=128M' } } - - it 'raises an error if a -XX:MaxMetaspaceSize is configured' do - expect { component.compile }.to raise_error(/-XX:MaxMetaspaceSize/) + let(:configuration) do + { 'java_opts' => '-Dtest=$DOLLAR\\\SLASH' } end - end - - context do - let(:configuration) { { 'java_opts' => '-XX:MetaspaceSize=128M' } } - it 'raises an error if a -XX:MetaspaceSize is configured' do - expect { component.compile }.to raise_error(/-XX:MetaspaceSize/) + it 'does not escape the shell variable character from configuration' do + component.release + expect(java_opts).to include('-Dtest=$DOLLAR\SLASH') end end context do - let(:configuration) { { 'java_opts' => '-XX:MaxPermSize=128M' } } - - it 'raises an error if a -XX:MaxPermSize is configured' do - expect { component.compile }.to raise_error(/-XX:MaxPermSize/) + let(:configuration) do + { 'java_opts' => '-Dtest=something.\\\$dollar.\\\\\\\slash' } end - end - context do - let(:configuration) { { 'java_opts' => '-XX:PermSize=128M' } } - - it 'raises an error if a -XX:PermSize is configured' do - expect { component.compile }.to raise_error(/-XX:PermSize/) + it 'can escape non-escaped characters' do + component.release + expect(java_opts).to include('-Dtest=something.\\$dollar.\\\slash') end end context do - let(:configuration) { { 'java_opts' => '-Xss1M' } } + let(:configuration) do + { 'java_opts' => '-javaagent:agent.jar=port=$PORT,host=localhost' } + end - it 'raises an error if a -Xss is configured' do - expect { component.compile }.to raise_error(/-Xss/) + it 'escapes equal signs after the first one' do + component.release + expect(java_opts).to include('-javaagent:agent.jar=port\\=$PORT,host\\=localhost') end end context do let(:configuration) { { 'from_environment' => true } } - let(:environment) { { 'JAVA_OPTS' => '-Dalpha=bravo' } } - it 'includes values specified in ENV[JAVA_OPTS]' do + it 'includes $JAVA_OPTS with from_environment' do component.release - expect(java_opts).to include('-Dalpha=bravo') + expect(java_opts).to include('$JAVA_OPTS') end end - context do - let(:environment) { { 'JAVA_OPTS' => '-Dalpha=bravo' } } - - it 'does not include values specified in ENV[JAVA_OPTS] without from_environment' do - component.release - expect(java_opts).not_to include('-Dalpha=bravo') - end + it 'does not include $JAVA_OPTS without from_environment' do + component.release + expect(java_opts).not_to include('$JAVA_OPTS') end end diff --git a/spec/java_buildpack/framework/java_security_spec.rb b/spec/java_buildpack/framework/java_security_spec.rb new file mode 100644 index 0000000000..b4d1c7a725 --- /dev/null +++ b/spec/java_buildpack/framework/java_security_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'fileutils' +require 'java_buildpack/framework/java_security' + +describe JavaBuildpack::Framework::JavaSecurity do + include_context 'with component help' + + it 'adds extension directories to system properties' do + component.release + + expect(java_opts).to include('-Djava.ext.dirs=$PWD/.java-buildpack/java_security/test-extension-directory-1:' \ + '$PWD/.java-buildpack/java_security/test-extension-directory-2') + end + + it 'writes security provider security properties' do + component.compile + + expect(sandbox + 'java.security').to exist + expect(File.read(sandbox + 'java.security')) + .to eq File.read('spec/fixtures/framework_java_security_security_providers') + end + + it 'writes networking security properties' do + networking.networkaddress_cache_ttl = -1 + networking.networkaddress_cache_negative_ttl = -2 + + component.compile + + expect(sandbox + 'java.security').to exist + expect(File.read(sandbox + 'java.security')).to eq File.read('spec/fixtures/framework_java_security_networking') + end + + it 'adds security properties to system properties' do + component.release + + expect(java_opts).to include('-Djava.security.properties=$PWD/.java-buildpack/java_security/' \ + 'java.security') + end + +end diff --git a/spec/java_buildpack/framework/jmx_spec.rb b/spec/java_buildpack/framework/jmx_spec.rb new file mode 100644 index 0000000000..02315bcc06 --- /dev/null +++ b/spec/java_buildpack/framework/jmx_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/jmx' + +describe JavaBuildpack::Framework::Jmx do + include_context 'with component help' + + it 'does not detect if not enabled' do + expect(component.detect).to be_nil + end + + context do + let(:configuration) { { 'enabled' => true } } + + it 'detects when enabled' do + expect(component.detect).to eq('jmx=5000') + end + + it 'uses 5000 as the default port' do + component.release + expect(java_opts).to include '-Djava.rmi.server.hostname=127.0.0.1' + expect(java_opts).to include '-Dcom.sun.management.jmxremote.authenticate=false' + expect(java_opts).to include '-Dcom.sun.management.jmxremote.ssl=false' + expect(java_opts).to include '-Dcom.sun.management.jmxremote.port=5000' + expect(java_opts).to include '-Dcom.sun.management.jmxremote.rmi.port=5000' + end + end + + context do + let(:configuration) { { 'enabled' => true, 'port' => 5001 } } + + it 'uses configured port' do + component.release + expect(java_opts).to include '-Djava.rmi.server.hostname=127.0.0.1' + expect(java_opts).to include '-Dcom.sun.management.jmxremote.authenticate=false' + expect(java_opts).to include '-Dcom.sun.management.jmxremote.ssl=false' + expect(java_opts).to include '-Dcom.sun.management.jmxremote.port=5001' + expect(java_opts).to include '-Dcom.sun.management.jmxremote.rmi.port=5001' + end + end + +end diff --git a/spec/java_buildpack/framework/jprofiler_profiler_spec.rb b/spec/java_buildpack/framework/jprofiler_profiler_spec.rb new file mode 100644 index 0000000000..3c38d4ba5b --- /dev/null +++ b/spec/java_buildpack/framework/jprofiler_profiler_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/jprofiler_profiler' + +describe JavaBuildpack::Framework::JprofilerProfiler do + include_context 'with component help' + + it 'does not detect if not enabled' do + expect(component.detect).to be_nil + end + + context do + let(:configuration) { { 'enabled' => true } } + + it 'detects when enabled' do + expect(component.detect).to eq("jprofiler-profiler=#{version}") + end + + it 'downloads YourKit agent', + cache_fixture: 'stub-jprofiler-profiler.tar.gz' do + + component.compile + + expect(sandbox + 'bin/linux-x64/libjprofilerti.so').to exist + end + + context do + it 'updates JAVA_OPTS' do + component.release + + expect(java_opts).to include('-agentpath:$PWD/.java-buildpack/jprofiler_profiler/bin/linux-x64/' \ + 'libjprofilerti.so=port=8849,nowait') + + end + + context do + let(:configuration) { super().merge 'port' => 8_850 } + + it 'adds port from configuration to JAVA_OPTS if specified' do + component.release + + expect(java_opts).to include('-agentpath:$PWD/.java-buildpack/jprofiler_profiler/bin/linux-x64/' \ + 'libjprofilerti.so=port=8850,nowait') + end + end + + context do + let(:configuration) { super().merge 'nowait' => false } + + it 'disables nowait in JAVA_OPTS if specified' do + component.release + + expect(java_opts).to include('-agentpath:$PWD/.java-buildpack/jprofiler_profiler/bin/linux-x64/' \ + 'libjprofilerti.so=port=8849') + end + end + + end + + end + +end diff --git a/spec/java_buildpack/framework/jrebel_agent_spec.rb b/spec/java_buildpack/framework/jrebel_agent_spec.rb new file mode 100644 index 0000000000..b51947a82e --- /dev/null +++ b/spec/java_buildpack/framework/jrebel_agent_spec.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/jrebel_agent' + +describe JavaBuildpack::Framework::JrebelAgent do + include_context 'with component help' + + it 'does not detect when rebel-remote.xml is not present' do + expect(component.detect).to be_nil + end + + it 'detects when rebel-remote.xml is present in the top-level directory', + app_fixture: 'framework_jrebel_app_simple' do + + expect(component.detect).to eq("jrebel-agent=#{version}") + end + + it 'detects when rebel-remote.xml is present in WEB-INF/classes', + app_fixture: 'framework_jrebel_app_war' do + + expect(component.detect).to eq("jrebel-agent=#{version}") + end + + it 'detects when rebel-remote.xml is present inside an embedded JAR', + app_fixture: 'framework_jrebel_app_war_with_jar' do + + expect(component.detect).to eq("jrebel-agent=#{version}") + end + + context do + let(:configuration) { { 'enabled' => false } } + + it 'does not detect when not enabled', + app_fixture: 'framework_jrebel_app_simple' do + + expect(component.detect).to be_nil + end + end + + it 'downloads the JRebel JAR and the native agent', + app_fixture: 'framework_jrebel_app_simple', + cache_fixture: 'stub-jrebel-archive.zip' do + + component.compile + + expect(sandbox + 'lib/libjrebel64.so').to exist + expect(sandbox + 'lib/libjrebel32.so').to exist + end + + it 'adds correct arguments to JAVA_OPTS', + app_fixture: 'framework_jrebel_app_simple', + cache_fixture: 'stub-jrebel-archive.zip' do + + allow(component).to receive(:architecture).and_return('x86_64') + + component.release + + expect(java_opts).to include('-agentpath:$PWD/.java-buildpack/jrebel_agent/lib/libjrebel64.so') + expect(java_opts).to include('-Drebel.remoting_plugin=true') + expect(java_opts).to include('-Drebel.cloud.platform=cloudfoundry/java-buildpack') + end + + it 'does not throw an error when a directory ends in .jar', + app_fixture: 'framework_jrebel_jar_directory' do + + expect_any_instance_of(described_class).not_to receive(:`).with(start_with("unzip -l #{app_dir + 'directory.jar'}")) + + component.detect + end + +end diff --git a/spec/java_buildpack/framework/luna_security_provider_spec.rb b/spec/java_buildpack/framework/luna_security_provider_spec.rb new file mode 100644 index 0000000000..b38b7e7333 --- /dev/null +++ b/spec/java_buildpack/framework/luna_security_provider_spec.rb @@ -0,0 +1,357 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/luna_security_provider' + +describe JavaBuildpack::Framework::LunaSecurityProvider do + include_context 'with component help' + + it 'does not detect without luna-n/a service' do + expect(component.detect).to be_nil + end + + context do + let(:vcap_services) do + { 'test-service-n/a' => [ + { + 'name' => 'luna-service', 'label' => 'luna-service-n/a', + 'tags' => ['luna-service-tag'], 'plan' => 'luna-plan', + 'credentials' => { + 'client' => { + 'certificate' => "-----BEGIN CERTIFICATE-----\ntest-client-cert\n-----END CERTIFICATE-----", + 'private-key' => "-----BEGIN RSA PRIVATE KEY-----\ntest-client-private-key\n-----END RSA PRIVATE KEY-----" + }, + 'servers' => [ + { + 'name' => 'test-server-1', + 'certificate' => "-----BEGIN CERTIFICATE-----\ntest-server-1-cert\n-----END CERTIFICATE-----" + }, { + 'name' => 'test-server-2', + 'certificate' => "-----BEGIN CERTIFICATE-----\ntest-server-2-cert\n-----END CERTIFICATE-----" + } + ], + 'groups' => [{ + 'label' => 'test-group-1', + 'members' => %w[test-group-1-member-1 test-group-1-member-2] + }, { + 'label' => 'test-group-2', + 'members' => %w[test-group-2-member-1 test-group-2-member-2] + }] + } + } + ] } + end + + it 'detects with luna-n/a service with client, servers and groups' do + expect(component.detect).to eq("luna-security-provider=#{version}") + end + end + + context do + let(:vcap_services) do + { 'test-service-n/a' => [ + { 'name' => 'luna-service', 'label' => 'luna-service-n/a', + 'tags' => ['luna-service-tag'], 'plan' => 'luna-plan', + 'credentials' => { + 'client' => { + 'certificate' => "-----BEGIN CERTIFICATE-----\ntest-client-cert\n-----END CERTIFICATE-----", + 'private-key' => "-----BEGIN RSA PRIVATE KEY-----\ntest-client-private-key\n-----END RSA PRIVATE KEY-----" + }, + 'servers' => [ + { + 'name' => 'test-server-1', + 'certificate' => "-----BEGIN CERTIFICATE-----\ntest-server-1-cert\n-----END CERTIFICATE-----" + }, { + 'name' => 'test-server-2', + 'certificate' => "-----BEGIN CERTIFICATE-----\ntest-server-2-cert\n-----END CERTIFICATE-----" + } + ] + } } + ] } + end + + it 'detects with luna-n/a service with client and servers' do + expect(component.detect).to eq("luna-security-provider=#{version}") + end + end + + context do + let(:vcap_services) do + { 'test-service-n/a' => [ + { 'name' => 'luna-service', 'label' => 'luna-service-n/a', + 'tags' => ['luna-service-tag'], 'plan' => 'luna-plan', + 'credentials' => { + 'client' => { + 'certificate' => "-----BEGIN CERTIFICATE-----\ntest-client-cert\n-----END CERTIFICATE-----", + 'private-key' => "-----BEGIN RSA PRIVATE KEY-----\ntest-client-private-key\n-----END RSA PRIVATE KEY-----" + } + } } + ] } + end + + it 'detects with luna-n/a service with just client' do + expect(component.detect).to eq("luna-security-provider=#{version}") + end + end + + context do + before do + allow(services).to receive(:one_service?).with(/luna/, 'client', 'servers', 'groups').and_return(true) + + allow(services).to receive(:find_service).and_return( + 'credentials' => { + 'client' => { + 'certificate' => "-----BEGIN CERTIFICATE-----\ntest-client-cert\n-----END CERTIFICATE-----", + 'private-key' => "-----BEGIN RSA PRIVATE KEY-----\ntest-client-private-key\n-----END RSA PRIVATE KEY-----" + }, + 'servers' => [ + { + 'name' => 'test-server-1', + 'certificate' => "-----BEGIN CERTIFICATE-----\ntest-server-1-cert\n-----END CERTIFICATE-----" + }, { + 'name' => 'test-server-2', + 'certificate' => "-----BEGIN CERTIFICATE-----\ntest-server-2-cert\n-----END CERTIFICATE-----" + } + ], + 'groups' => [ + { + 'label' => 'test-group-1', + 'members' => %w[test-group-1-member-1 test-group-1-member-2] + }, { + 'label' => 'test-group-2', + 'members' => %w[test-group-2-member-1 test-group-2-member-2] + } + ] + } + ) + end + + it 'detects with luna-n/a service' do + expect(component.detect).to eq("luna-security-provider=#{version}") + end + + it 'copies resources', + cache_fixture: 'stub-luna-security-provider.tar' do + + component.compile + + expect(sandbox + 'Chrystoki.conf').to exist + end + + it 'unpacks the luna tar', + cache_fixture: 'stub-luna-security-provider.tar' do + + component.compile + + expect(sandbox + 'libs/64/libCryptoki2.so').to exist + expect(sandbox + 'libs/64/libcklog2.so').to exist + expect(sandbox + 'jsp/LunaProvider.jar').to exist + expect(sandbox + 'jsp/64/libLunaAPI.so').to exist + end + + it 'write certificate files', + cache_fixture: 'stub-luna-security-provider.tar' do + + component.compile + + expect(sandbox + 'client-certificate.pem').to exist + expect(sandbox + 'client-private-key.pem').to exist + expect(sandbox + 'server-certificates.pem').to exist + + check_file_contents(sandbox + 'client-certificate.pem', + 'spec/fixtures/framework_luna_security_provider/client-certificate.pem') + check_file_contents(sandbox + 'client-private-key.pem', + 'spec/fixtures/framework_luna_security_provider/client-private-key.pem') + check_file_contents(sandbox + 'server-certificates.pem', + 'spec/fixtures/framework_luna_security_provider/server-certificates.pem') + end + + it 'writes configuration', + cache_fixture: 'stub-luna-security-provider.tar' do + + component.compile + + expect(sandbox + 'Chrystoki.conf').to exist + check_file_contents(sandbox + 'Chrystoki.conf', 'spec/fixtures/framework_luna_security_provider/Chrystoki.conf') + end + + it 'updates environment variables' do + component.release + expect(environment_variables).to include('ChrystokiConfigurationPath=$PWD/.java-buildpack/luna_security_provider') + end + + it 'adds security provider', + cache_fixture: 'stub-luna-security-provider.tar' do + + component.compile + expect(security_providers.last).to eq('com.safenetinc.luna.provider.LunaProvider') + end + + it 'adds extension directory' do + component.release + + expect(extension_directories).to include(droplet.sandbox + 'ext') + end + + context do + + let(:java_home_delegate) do + delegate = JavaBuildpack::Component::MutableJavaHome.new + delegate.root = app_dir + '.test-java-home' + delegate.version = JavaBuildpack::Util::TokenizedVersion.new('9.0.0') + + delegate + end + + it 'adds JAR to classpath during compile in Java 9+', + cache_fixture: 'stub-luna-security-provider.tar' do + + component.compile + + expect(root_libraries).to include(droplet.sandbox + 'jsp/LunaProvider.jar') + end + + it 'adds JAR to classpath during release in Java 9+' do + component.release + + expect(root_libraries).to include(droplet.sandbox + 'jsp/LunaProvider.jar') + end + + it 'does not add extension directory in Java 9+' do + component.release + + expect(extension_directories).not_to include(droplet.sandbox + 'ext') + end + + it 'updates environment variables for Java 9+' do + component.release + expect(environment_variables).to include( + 'LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/.java-buildpack/luna_security_provider/jsp/64/' + ) + end + end + + context do + + let(:environment) { { 'LUNA_CONF_HTTP_URL' => 'http://foo.com' } } + let(:conf_files) { described_class.instance_variable_get(:@conf_files) } + + it 'sets LUNA_CONF_HTTP_URL env var to download config files from', + cache_fixture: 'stub-luna-security-provider.tar' do + + config_files = %w[Chrystoki.conf server-certificates.pem] + + config_files.each do |file| + uri = "http://foo.com/#{file}" + allow(application_cache).to receive(:get).with(uri) + stub_request(:head, uri) + .with(headers: { 'Accept' => '*/*', 'Host' => 'foo.com', 'User-Agent' => 'Ruby' }) + .to_return(status: 200, body: '', headers: {}) + end + component.compile + end + + end + + context do + let(:environment) { { 'LUNA_CONF_HTTP_URL' => 'https://foo.com/' } } + + it 'sets LUNA_CONF_HTTP_URL env var to download config files over HTTPS', + cache_fixture: 'stub-luna-security-provider.tar' do + + config_files = %w[Chrystoki.conf server-certificates.pem] + + config_files.each do |file| + uri = "https://foo.com/#{file}" + allow(application_cache).to receive(:get).with(uri) + allow(Net::HTTP).to receive(:start).with('foo.com', 443, use_ssl: true).and_call_original + stub_request(:head, uri) + .with(headers: { 'Accept' => '*/*', 'Host' => 'foo.com', 'User-Agent' => 'Ruby' }) + .to_return(status: 200, body: '', headers: {}) + end + component.compile + end + end + + context do + let(:environment) { { 'LUNA_CONF_HTTP_URL' => 'https://user:pass@foo.com' } } + + it 'sets LUNA_CONF_HTTP_URL env var to download config files over HTTPS with Basic Auth', + cache_fixture: 'stub-luna-security-provider.tar' do + + config_files = %w[Chrystoki.conf server-certificates.pem] + + config_files.each do |file| + allow(application_cache).to receive(:get).with("https://user:pass@foo.com/#{file}") + allow(Net::HTTP).to receive(:start).with('foo.com', 443, use_ssl: true).and_call_original + stub_request(:head, "https://foo.com/#{file}") + .with(headers: { 'Accept' => '*/*', 'Host' => 'foo.com', 'User-Agent' => 'Ruby', + 'Authorization' => 'Basic dXNlcjpwYXNz' }) + .to_return(status: 200, body: '', headers: {}) + end + component.compile + end + end + + context do + let(:configuration) do + { + 'logging_enabled' => true, + 'ha_logging_enabled' => true, + 'tcp_keep_alive_enabled' => false + } + end + + it 'writes configuration', + cache_fixture: 'stub-luna-security-provider.tar' do + + component.compile + + expect(sandbox + 'Chrystoki.conf').to exist + check_file_contents(sandbox + 'Chrystoki.conf', + 'spec/fixtures/framework_luna_security_provider_logging/Chrystoki.conf') + end + end + + context do + let(:configuration) do + { + 'logging_enabled' => true, + 'ha_logging_enabled' => true, + 'tcp_keep_alive_enabled' => true + } + end + + it 'writes configuration with client tcp keep alive', + cache_fixture: 'stub-luna-security-provider.tar' do + + component.compile + + expect(sandbox + 'Chrystoki.conf').to exist + check_file_contents(sandbox + 'Chrystoki.conf', + 'spec/fixtures/framework_luna_security_provider_tcp_keep_alive/Chrystoki.conf') + end + end + + def check_file_contents(actual, expected) + expect(File.read(actual)).to eq File.read(expected) + end + + end +end diff --git a/spec/java_buildpack/framework/maria_db_jdbc_spec.rb b/spec/java_buildpack/framework/maria_db_jdbc_spec.rb index 173fa3a990..0ecdc315da 100644 --- a/spec/java_buildpack/framework/maria_db_jdbc_spec.rb +++ b/spec/java_buildpack/framework/maria_db_jdbc_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +20,7 @@ require 'java_buildpack/framework/maria_db_jdbc' describe JavaBuildpack::Framework::MariaDbJDBC do - include_context 'component_helper' + include_context 'with component help' it 'does not detect without a service containing a mysql tag' do expect(component.detect).to be_nil @@ -48,6 +49,12 @@ expect(component.detect).to be_nil end + it 'does not detect if the application has a new style -j MySQL driver', + app_fixture: 'framework_mariadb_jdbc_with_new_mysql_driver' do + + expect(component.detect).to be_nil + end + it 'downloads the MariaDB driver when needed', cache_fixture: 'stub-mariadb-java-client.jar' do diff --git a/spec/java_buildpack/framework/metric_writer_spec.rb b/spec/java_buildpack/framework/metric_writer_spec.rb new file mode 100644 index 0000000000..3df422554a --- /dev/null +++ b/spec/java_buildpack/framework/metric_writer_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/metric_writer' + +describe JavaBuildpack::Framework::MetricWriter do + include_context 'with component help' + + let(:configuration) { { 'enabled' => true } } + + it 'detects with Micrometer JAR', + app_fixture: 'framework_metric_writer' do + + expect(component.detect).to eq("metric-writer=#{version}") + end + + it 'does not detect without Micrometer JAR' do + expect(component.detect).to be_nil + end + + context do + let(:configuration) { { 'enabled' => false } } + + it 'does not detect if disabled', + app_fixture: 'framework_metric_writer' do + + expect(component.detect).to be_nil + end + end + + it 'downloads additional libraries', + app_fixture: 'framework_metric_writer', + cache_fixture: 'stub-metric-writer.jar' do + + component.compile + + expect(sandbox + "metric_writer-#{version}.jar").to exist + end + + it 'adds to additional libraries', + app_fixture: 'framework_metric_writer', + cache_fixture: 'stub-metric-writer.jar' do + + component.release + + expect(additional_libraries).to include(sandbox + "metric_writer-#{version}.jar") + end + +end diff --git a/spec/java_buildpack/framework/multi_buildpack_spec.rb b/spec/java_buildpack/framework/multi_buildpack_spec.rb new file mode 100644 index 0000000000..c795a08359 --- /dev/null +++ b/spec/java_buildpack/framework/multi_buildpack_spec.rb @@ -0,0 +1,303 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'pathname' +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/multi_buildpack' + +describe JavaBuildpack::Framework::MultiBuildpack do + include_context 'with component help' + + let(:dep_dirs) do + Dir.mktmpdir + ret = [] + [1, 2, 3].each do |_| + ret.push dep_dir + end + ret + end + + def dep_dir + ddirpath = Dir.mktmpdir + '/deps' + Dir.mkdir(ddirpath, 0o0755) + Pathname.new ddirpath + end + + before do |example| + app_fixture = example.metadata[:app_fixture] + if app_fixture + (0..2).each do |i| + FileUtils.cp_r "spec/fixtures/#{app_fixture.chomp}/#{i}/.", dep_dirs[i] if dep_dirs[i] + end + end + + allow(Pathname).to receive(:glob).with('/tmp/*/deps').and_return(dep_dirs) + end + + it 'does not detect without deps' do + expect(component.detect).to be_nil + end + + it 'detects when deps with config.yml exist', + app_fixture: 'framework_multi_buildpack_deps' do + + expect(component.detect).to include('test-buildpack-0-0', + 'test-buildpack-0-2', 'test-buildpack-1-0', + 'test-buildpack-1-2', 'test-buildpack-2-0', + 'test-buildpack-2-2') + end + + it 'adds bin/ directory to $PATH during compile if it exists', + app_fixture: 'framework_multi_buildpack_deps' do + + component.compile + + expect(environment_variables).to include('PATH=$PATH:$PWD/../deps/0/bin') + end + + it 'adds bin/ directory to $PATH during release if it exists', + app_fixture: 'framework_multi_buildpack_deps' do + + component.release + + expect(environment_variables).to include('PATH=$PATH:$PWD/../deps/0/bin') + end + + it 'adds lib/ directory to $LD_LIBRARY_PATH during compile if it exists', + app_fixture: 'framework_multi_buildpack_deps' do + + component.compile + + expect(environment_variables).to include('LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/../deps/0/lib') + end + + it 'adds lib/ directory to $LD_LIBRARY_PATH during release if it exists', + app_fixture: 'framework_multi_buildpack_deps' do + + component.release + + expect(environment_variables).to include('LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/../deps/0/lib') + end + + it 'adds additional_libraries during compile', + app_fixture: 'framework_multi_buildpack_deps' do + + component.compile + + expect(additional_libraries).to include(Pathname.new('/multi-test-additional-library-1')) + expect(additional_libraries).to include(Pathname.new('/multi-test-additional-library-2')) + end + + it 'adds additional_libraries during release', + app_fixture: 'framework_multi_buildpack_deps' do + + component.release + + expect(additional_libraries).to include(Pathname.new('/multi-test-additional-library-1')) + expect(additional_libraries).to include(Pathname.new('/multi-test-additional-library-2')) + end + + it 'adds agentpaths during compile', + app_fixture: 'framework_multi_buildpack_deps' do + + component.compile + + expect(java_opts).to include("-agentpath:$PWD/#{qualify_path('/multi-test-agent-1')}") + expect(java_opts).to include("-agentpath:$PWD/#{qualify_path('/multi-test-agent-2')}") + end + + it 'adds agentpaths during release', + app_fixture: 'framework_multi_buildpack_deps' do + + component.release + + expect(java_opts).to include("-agentpath:$PWD/#{qualify_path('/multi-test-agent-1')}") + expect(java_opts).to include("-agentpath:$PWD/#{qualify_path('/multi-test-agent-2')}") + end + + it 'adds agentpaths_with_props during compile', + app_fixture: 'framework_multi_buildpack_deps' do + + component.compile + + expect(java_opts).to include("-agentpath:$PWD/#{qualify_path('/multi-test-agent-1')}=" \ + 'test-key-1=test-value-1,test-key-2=test-value-2') + expect(java_opts).to include("-agentpath:$PWD/#{qualify_path('/multi-test-agent-2')}=" \ + 'test-key-1=test-value-1,test-key-2=test-value-2') + end + + it 'adds agentpaths_with_props during release', + app_fixture: 'framework_multi_buildpack_deps' do + + component.release + + expect(java_opts).to include("-agentpath:$PWD/#{qualify_path('/multi-test-agent-1')}=" \ + 'test-key-1=test-value-1,test-key-2=test-value-2') + expect(java_opts).to include("-agentpath:$PWD/#{qualify_path('/multi-test-agent-2')}=" \ + 'test-key-1=test-value-1,test-key-2=test-value-2') + end + + it 'adds bootclasspath_ps during compile', + app_fixture: 'framework_multi_buildpack_deps' do + + component.compile + + expect(java_opts).to include("-Xbootclasspath/p:$PWD/#{qualify_path('/multi-test-bootclasspath-p-1')}") + expect(java_opts).to include("-Xbootclasspath/p:$PWD/#{qualify_path('/multi-test-bootclasspath-p-2')}") + end + + it 'adds bootclasspath_ps during release', + app_fixture: 'framework_multi_buildpack_deps' do + + component.release + + expect(java_opts).to include("-Xbootclasspath/p:$PWD/#{qualify_path('/multi-test-bootclasspath-p-1')}") + expect(java_opts).to include("-Xbootclasspath/p:$PWD/#{qualify_path('/multi-test-bootclasspath-p-2')}") + end + + it 'adds environment_variables during compile', + app_fixture: 'framework_multi_buildpack_deps' do + + component.compile + + expect(environment_variables).to include('multi-test-key-1=multi-test-value-1') + expect(environment_variables).to include('multi-test-key-2=multi-test-value-2') + end + + it 'adds environment_variables during release', + app_fixture: 'framework_multi_buildpack_deps' do + + component.release + + expect(environment_variables).to include('multi-test-key-1=multi-test-value-1') + expect(environment_variables).to include('multi-test-key-2=multi-test-value-2') + end + + it 'adds extension_directories during compile', + app_fixture: 'framework_multi_buildpack_deps' do + + component.compile + + expect(extension_directories).to include(Pathname.new('/multi-test-extension-directory-1')) + expect(extension_directories).to include(Pathname.new('/multi-test-extension-directory-2')) + end + + it 'adds extension_directories during release', + app_fixture: 'framework_multi_buildpack_deps' do + + component.release + + expect(extension_directories).to include(Pathname.new('/multi-test-extension-directory-1')) + expect(extension_directories).to include(Pathname.new('/multi-test-extension-directory-2')) + end + + it 'adds javaagents during compile', + app_fixture: 'framework_multi_buildpack_deps' do + + component.compile + + expect(java_opts).to include("-javaagent:$PWD/#{qualify_path('/multi-test-java-agent-1')}") + expect(java_opts).to include("-javaagent:$PWD/#{qualify_path('/multi-test-java-agent-2')}") + end + + it 'adds javaagents during release', + app_fixture: 'framework_multi_buildpack_deps' do + + component.release + + expect(java_opts).to include("-javaagent:$PWD/#{qualify_path('/multi-test-java-agent-1')}") + expect(java_opts).to include("-javaagent:$PWD/#{qualify_path('/multi-test-java-agent-2')}") + end + + it 'adds options during compile', + app_fixture: 'framework_multi_buildpack_deps' do + + component.compile + + expect(java_opts).to include('multi-test-key-1=multi-test-value-1') + expect(java_opts).to include('multi-test-key-2=multi-test-value-2') + end + + it 'adds options during release', + app_fixture: 'framework_multi_buildpack_deps' do + + component.release + + expect(java_opts).to include('multi-test-key-1=multi-test-value-1') + expect(java_opts).to include('multi-test-key-2=multi-test-value-2') + end + + it 'adds preformatted_options during compile', + app_fixture: 'framework_multi_buildpack_deps' do + + component.compile + + expect(java_opts).to include('multi-test-preformatted-option-1') + expect(java_opts).to include('multi-test-preformatted-option-2') + end + + it 'adds preformatted_options during release', + app_fixture: 'framework_multi_buildpack_deps' do + + component.release + + expect(java_opts).to include('multi-test-preformatted-option-1') + expect(java_opts).to include('multi-test-preformatted-option-2') + end + + it 'adds security_providers during compile', + app_fixture: 'framework_multi_buildpack_deps' do + + component.compile + + expect(security_providers).to include('multi-test-security-provider-1') + expect(security_providers).to include('multi-test-security-provider-2') + end + + it 'adds security_providers during release', + app_fixture: 'framework_multi_buildpack_deps' do + + component.release + + expect(security_providers).to include('multi-test-security-provider-1') + expect(security_providers).to include('multi-test-security-provider-2') + end + + it 'adds system_properties during compile', + app_fixture: 'framework_multi_buildpack_deps' do + + component.compile + + expect(java_opts).to include('-Dmulti-test-key-1=multi-test-value-1') + expect(java_opts).to include('-Dmulti-test-key-2=multi-test-value-2') + end + + it 'adds system_properties during release', + app_fixture: 'framework_multi_buildpack_deps' do + + component.release + + expect(java_opts).to include('-Dmulti-test-key-1=multi-test-value-1') + expect(java_opts).to include('-Dmulti-test-key-2=multi-test-value-2') + end + + def qualify_path(path) + Pathname.new(path).relative_path_from(application.root) + end + +end diff --git a/spec/java_buildpack/framework/new_relic_agent_spec.rb b/spec/java_buildpack/framework/new_relic_agent_spec.rb index 77f9665286..7aeb0a035d 100644 --- a/spec/java_buildpack/framework/new_relic_agent_spec.rb +++ b/spec/java_buildpack/framework/new_relic_agent_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,10 +17,12 @@ require 'spec_helper' require 'component_helper' +require 'internet_availability_helper' require 'java_buildpack/framework/new_relic_agent' +require 'java_buildpack/util/tokenized_version' describe JavaBuildpack::Framework::NewRelicAgent do - include_context 'component_helper' + include_context 'with component help' it 'does not detect without newrelic-n/a service' do expect(component.detect).to be_nil @@ -28,7 +31,7 @@ context do before do - allow(services).to receive(:one_service?).with(/newrelic/, 'licenseKey').and_return(true) + allow(services).to receive(:one_service?).with(/newrelic/, %w[licenseKey license_key]).and_return(true) end it 'detects with newrelic-n/a service' do @@ -51,21 +54,44 @@ expect(sandbox + 'newrelic.yml').to exist end + it 'ignores unspecified extensions', + cache_fixture: 'stub-new-relic-agent.jar' do + + component.compile + + expect(sandbox + 'extensions').not_to exist + end + it 'updates JAVA_OPTS' do allow(services).to receive(:find_service).and_return('credentials' => { 'licenseKey' => 'test-license-key' }) + allow(java_home).to receive(:java_8_or_later?).and_return(JavaBuildpack::Util::TokenizedVersion.new('1.7.0_u10')) component.release expect(java_opts).to include("-javaagent:$PWD/.java-buildpack/new_relic_agent/new_relic_agent-#{version}.jar") expect(java_opts).to include('-Dnewrelic.home=$PWD/.java-buildpack/new_relic_agent') expect(java_opts).to include('-Dnewrelic.config.license_key=test-license-key') - expect(java_opts).to include("-Dnewrelic.config.app_name='test-application-name'") - expect(java_opts).to include('-Dnewrelic.config.log_file_path=$PWD/.java-buildpack/new_relic_agent/logs') + expect(java_opts).to include('-Dnewrelic.config.app_name=test-application-name') + expect(java_opts).to include('-Dnewrelic.config.log_file_name=STDOUT') + end + + it 'updates JAVA_OPTS with additional options' do + allow(services).to receive(:find_service).and_return('credentials' => { 'licenseKey' => 'test-license-key', + 'license_key' => 'different-license-key', + 'app_name' => 'different-name', + 'foo' => 'bar' }) + allow(java_home).to receive(:java_8_or_later?).and_return(JavaBuildpack::Util::TokenizedVersion.new('1.7.0_u10')) + + component.release + + expect(java_opts).to include('-Dnewrelic.config.license_key=different-license-key') + expect(java_opts).to include('-Dnewrelic.config.app_name=different-name') + expect(java_opts).to include('-Dnewrelic.config.foo=bar') end it 'updates JAVA_OPTS on Java 8' do allow(services).to receive(:find_service).and_return('credentials' => { 'licenseKey' => 'test-license-key' }) - allow(java_home).to receive(:version).and_return(%w(1 8 0 u10)) + allow(java_home).to receive(:java_8_or_later?).and_return(JavaBuildpack::Util::TokenizedVersion.new('1.8.0_u10')) component.release @@ -74,4 +100,39 @@ end + describe JavaBuildpack::Framework::NewRelicAgentExtensions do + include_context 'with component help' + + it 'does not support if repository_root not specified' do + expect(component).not_to be_supports + end + + context 'when enabled' do + let(:configuration) { { 'repository_root' => 'test-repository-root' } } + + it 'supports if repository_root specified' do + expect(component).to be_supports + end + + it 'downloads extensions TAR', + cache_fixture: 'stub-new-relic-extensions.tar.gz' do + + component.compile + + expect(sandbox + 'extensions/extension-example.xml').to exist + end + + it 'does guarantee that internet access is available when downloading', + cache_fixture: 'stub-new-relic-extensions.tar.gz' do + + expect_any_instance_of(JavaBuildpack::Util::Cache::InternetAvailability) + .to receive(:available).with(true, 'The New Relic Extensions download location is always accessible').twice + + component.compile + end + + end + + end + end diff --git a/spec/java_buildpack/framework/open_telemetry_javaagent_spec.rb b/spec/java_buildpack/framework/open_telemetry_javaagent_spec.rb new file mode 100644 index 0000000000..9f8f260550 --- /dev/null +++ b/spec/java_buildpack/framework/open_telemetry_javaagent_spec.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/open_telemetry_javaagent' +require 'java_buildpack/util/tokenized_version' + +describe JavaBuildpack::Framework::OpenTelemetryJavaagent do + include_context 'with component help' + + let(:configuration) { { 'version' => '1.27.0' } } + let(:vcap_application) { { 'application_name' => 'GreatServiceTM' } } + + it 'does not detect without otel-collector service bind' do + expect(component.detect).to be_nil + end + + context 'when detected' do + + before do + allow(services).to receive(:one_service?).with(/otel-collector/).and_return(true) + end + + it 'detects with opentelemetry-javaagent' do + expect(component.detect).to eq("open-telemetry-javaagent=#{version}") + end + + it 'downloads the opentelemetry javaagent jar', cache_fixture: 'stub-download.jar' do + + component.compile + + expect(sandbox + "open_telemetry_javaagent-#{version}.jar").to exist + end + + it 'updates JAVA_OPTS' do + allow(services).to receive(:find_service).and_return('credentials' => { 'otel.exporter.otlp.endpoint' => 'https://my-collector-endpoint', + 'ignored' => 'not used', + 'otel.foo' => 'bar' }) + component.release + + expect(java_opts).to include( + "-javaagent:$PWD/.java-buildpack/open_telemetry_javaagent/open_telemetry_javaagent-#{version}.jar" + ) + expect(java_opts).to include('-Dotel.exporter.otlp.endpoint=https://my-collector-endpoint') + expect(java_opts).to include('-Dotel.foo=bar') + end + + it 'sets the service name from the application name' do + allow(services).to receive(:find_service).and_return('credentials' => { 'otel.exporter.otlp.endpoint' => 'https://my-collector-endpoint', + 'ignored' => 'not used', + 'otel.foo' => 'bar' }) + component.release + + expect(java_opts).to include('-Dotel.service.name=GreatServiceTM') + end + + end + +end diff --git a/spec/java_buildpack/framework/play_framework_auto_reconfiguration_spec.rb b/spec/java_buildpack/framework/play_framework_auto_reconfiguration_spec.rb deleted file mode 100644 index 551f739730..0000000000 --- a/spec/java_buildpack/framework/play_framework_auto_reconfiguration_spec.rb +++ /dev/null @@ -1,66 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'spec_helper' -require 'component_helper' -require 'java_buildpack/framework/play_framework_auto_reconfiguration' - -describe JavaBuildpack::Framework::PlayFrameworkAutoReconfiguration do - include_context 'component_helper' - - let(:configuration) { { 'enabled' => true } } - - it 'detects with application configuration', - app_fixture: 'container_play_2.1_dist' do - - expect(component.detect).to eq("play-framework-auto-reconfiguration=#{version}") - end - - it 'does not detect without application configuration', - app_fixture: 'container_play_too_deep' do - - expect(component.detect).to be_nil - end - - context do - let(:configuration) { { 'enabled' => false } } - - it 'does not detect if disabled', - app_fixture: 'container_play_2.1_dist' do - - expect(component.detect).to be_nil - end - end - - it 'downloads additional libraries', - app_fixture: 'container_play_2.1_dist', - cache_fixture: 'stub-auto-reconfiguration.jar' do - - component.compile - - expect(sandbox + "play_framework_auto_reconfiguration-#{version}.jar").to exist - end - - it 'adds to the additional libraries', - app_fixture: 'container_play_2.1_dist', - cache_fixture: 'stub-auto-reconfiguration.jar' do - - component.release - - expect(additional_libraries).to include(sandbox + "play_framework_auto_reconfiguration-#{version}.jar") - end - -end diff --git a/spec/java_buildpack/framework/play_framework_jpa_plugin_spec.rb b/spec/java_buildpack/framework/play_framework_jpa_plugin_spec.rb deleted file mode 100644 index a0ee3e57fd..0000000000 --- a/spec/java_buildpack/framework/play_framework_jpa_plugin_spec.rb +++ /dev/null @@ -1,78 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'spec_helper' -require 'component_helper' -require 'java_buildpack/framework/play_framework_jpa_plugin' - -describe JavaBuildpack::Framework::PlayFrameworkJPAPlugin do - include_context 'component_helper' - - let(:configuration) { { 'enabled' => true } } - - it 'detects Play 2.0 application', - app_fixture: 'framework_play_jpa_plugin_play20' do - - expect(component.detect).to eq("play-framework-jpa-plugin=#{version}") - end - - context do - let(:configuration) { { 'enabled' => false } } - - it 'does not detect if disabled', - app_fixture: 'framework_play_jpa_plugin_play20' do - - expect(component.detect).to be_nil - end - end - - it 'detects staged application', - app_fixture: 'framework_play_jpa_plugin_staged' do - - expect(component.detect).to eq("play-framework-jpa-plugin=#{version}") - end - - it 'detects dist application', - app_fixture: 'framework_play_jpa_plugin_dist' do - - expect(component.detect).to eq("play-framework-jpa-plugin=#{version}") - end - - it 'does not detect non-JPA application', - app_fixture: 'container_play_2.1_dist' do - - expect(component.detect).to be_nil - end - - it 'downloads additional libraries', - app_fixture: 'framework_play_jpa_plugin_dist', - cache_fixture: 'stub-play-jpa-plugin.jar' do - - component.compile - - expect(sandbox + "play_framework_jpa_plugin-#{version}.jar").to exist - end - - it 'adds to additional libraries', - app_fixture: 'framework_play_jpa_plugin_dist', - cache_fixture: 'stub-play-jpa-plugin.jar' do - - component.release - - expect(additional_libraries).to include(sandbox + "play_framework_jpa_plugin-#{version}.jar") - end - -end diff --git a/spec/java_buildpack/framework/postgresql_jdbc_spec.rb b/spec/java_buildpack/framework/postgresql_jdbc_spec.rb index 8136a6b0b6..aa8c3d4f2b 100644 --- a/spec/java_buildpack/framework/postgresql_jdbc_spec.rb +++ b/spec/java_buildpack/framework/postgresql_jdbc_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +20,7 @@ require 'java_buildpack/framework/postgresql_jdbc' describe JavaBuildpack::Framework::PostgresqlJDBC do - include_context 'component_helper' + include_context 'with component help' it 'does not detect without a postgres service' do expect(component.detect).to be_nil diff --git a/spec/java_buildpack/framework/protect_app_security_provider_spec.rb b/spec/java_buildpack/framework/protect_app_security_provider_spec.rb new file mode 100644 index 0000000000..c69b98123e --- /dev/null +++ b/spec/java_buildpack/framework/protect_app_security_provider_spec.rb @@ -0,0 +1,154 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/protect_app_security_provider' + +describe JavaBuildpack::Framework::ProtectAppSecurityProvider do + include_context 'with component help' + + it 'does not detect without protectapp-n/a service' do + expect(component.detect).to be_nil + end + + context do + + before do + allow(services).to receive(:one_service?).with(/protectapp/, 'client', 'trusted_certificates').and_return(true) + + allow(services).to receive(:find_service).and_return( + 'credentials' => { + 'client' => { + 'certificate' => "-----BEGIN CERTIFICATE-----\ntest-client-cert\n-----END CERTIFICATE-----", + 'private_key' => "-----BEGIN RSA PRIVATE KEY-----\ntest-client-private-key\n-----END RSA PRIVATE KEY-----" + }, + 'trusted_certificates' => [ + "-----BEGIN CERTIFICATE-----\ntest-server-1-cert\n-----END CERTIFICATE-----", + "-----BEGIN CERTIFICATE-----\ntest-server-2-cert\n-----END CERTIFICATE-----" + ], + 'NAE_IP.1' => 'server_ip', + 'foo' => 'bar' + } + ) + end + + it 'detects with protectapp-n/a service' do + expect(component.detect).to eq("protect-app-security-provider=#{version}") + end + + it 'unpacks the protectapp zip', + cache_fixture: 'stub-protect-app-security-provider.zip' do + + allow(component).to receive(:shell).with(start_with('unzip -qq')).and_call_original + allow(component).to receive(:shell).with(start_with('openssl pkcs12')) + allow(component).to receive(:shell).with(start_with("#{java_home.root}/bin/keytool -importkeystore")) + allow(component).to receive(:shell).with(start_with("#{java_home.root}/bin/keytool -importcert")) + + component.compile + + expect(sandbox + "ext/IngrianNAE-#{version}.jar").to exist + expect(sandbox + 'ext/Ingrianlog4j-core-2.1.jar').to exist + expect(sandbox + 'ext/Ingrianlog4j-api-2.1.jar').to exist + end + + it 'adds security provider', + cache_fixture: 'stub-protect-app-security-provider.zip' do + + allow(component).to receive(:shell).with(start_with('unzip -qq')).and_call_original + allow(component).to receive(:shell).with(start_with('openssl pkcs12')) + allow(component).to receive(:shell).with(start_with("#{java_home.root}/bin/keytool -importkeystore")) + allow(component).to receive(:shell).with(start_with("#{java_home.root}/bin/keytool -importcert")) + + component.compile + + expect(security_providers.last).to eq('com.ingrian.security.nae.IngrianProvider') + end + + it 'copies resources', + cache_fixture: 'stub-protect-app-security-provider.zip' do + + allow(component).to receive(:shell).with(start_with('unzip -qq')).and_call_original + allow(component).to receive(:shell).with(start_with('openssl pkcs12')) + allow(component).to receive(:shell).with(start_with("#{java_home.root}/bin/keytool -importkeystore")) + allow(component).to receive(:shell).with(start_with("#{java_home.root}/bin/keytool -importcert")) + + component.compile + + expect(sandbox + 'IngrianNAE.properties').to exist + end + + it 'adds extension directory' do + component.release + + expect(extension_directories).to include(droplet.sandbox + 'ext') + end + + it 'updates JAVA_OPTS with additional options' do + component.release + + expect(java_opts).to include('-Dcom.ingrian.security.nae.IngrianNAE_Properties_Conf_Filename=' \ + '$PWD/.java-buildpack/protect_app_security_provider/IngrianNAE.properties') + expect(java_opts).to include('-Dcom.ingrian.security.nae.Key_Store_Location=' \ + '$PWD/.java-buildpack/protect_app_security_provider/nae-keystore.jks') + expect(java_opts).to include('-Dcom.ingrian.security.nae.Key_Store_Password=nae-keystore-password') + expect(java_opts).to include('-Dcom.ingrian.security.nae.NAE_IP.1=server_ip') + expect(java_opts).to include('-Dcom.ingrian.security.nae.foo=bar') + + expect(java_opts).not_to include(start_with('-Dcom.ingrian.security.nae.client')) + expect(java_opts).not_to include(start_with('-Dcom.ingrian.security.nae.trusted_certificates')) + end + + context do + + let(:java_home_delegate) do + delegate = JavaBuildpack::Component::MutableJavaHome.new + delegate.root = app_dir + '.test-java-home' + delegate.version = JavaBuildpack::Util::TokenizedVersion.new('9.0.0') + + delegate + end + + it 'adds JAR to classpath during compile in Java 9', + cache_fixture: 'stub-protect-app-security-provider.zip' do + + allow(component).to receive(:shell).with(start_with('unzip -qq')).and_call_original + allow(component).to receive(:shell).with(start_with('openssl pkcs12')) + allow(component).to receive(:shell).with(start_with("#{java_home.root}/bin/keytool -importkeystore")) + allow(component).to receive(:shell).with(start_with("#{java_home.root}/bin/keytool -importcert")) + + component.compile + + expect(additional_libraries).to include(droplet.sandbox + "ext/IngrianNAE-#{version}.000.jar") + end + + it 'adds JAR to classpath during release in Java 9' do + component.release + + expect(additional_libraries).to include(droplet.sandbox + "ext/IngrianNAE-#{version}.000.jar") + end + + it 'adds does not add extension directory in Java 9' do + component.release + + expect(extension_directories).not_to include(droplet.sandbox + 'ext') + end + + end + + end +end diff --git a/spec/java_buildpack/framework/riverbed_appinternals_agent_spec.rb b/spec/java_buildpack/framework/riverbed_appinternals_agent_spec.rb new file mode 100644 index 0000000000..993a6daf89 --- /dev/null +++ b/spec/java_buildpack/framework/riverbed_appinternals_agent_spec.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/riverbed_appinternals_agent' + +describe JavaBuildpack::Framework::RiverbedAppinternalsAgent do + include_context 'with component help' + + it 'does detect riverbed-appinternals-agent service' do + expect(component.detect).to be_nil + end + + context do + + before do + allow(services).to receive(:one_service?).with(/appinternals/).and_return(true) + + p = Pathname.new('spec/fixtures/stub-riverbed-appinternals-agent.zip') + allow(application_cache).to receive(:get).with('http://testfoobar/profiler.zip') + .and_yield(p.open, false) + end + + it 'detects with riverbed-appinternals-agent service' do + expect(component.detect).to eq("riverbed-appinternals-agent=#{version}") + end + + it 'unzips riverbed appinternals agent zip file', + cache_fixture: 'stub-riverbed-appinternals-agent.zip' do + + component.compile + + expect(sandbox + 'agent/lib/librpilj64.so').to exist + end + + it 'updates JAVA_OPTS' do + allow(services).to receive(:find_service).and_return('credentials' => {}) + + component.release + + expect(environment_variables).to include('AIX_INSTRUMENT_ALL=1') + expect(environment_variables).to include('DSA_PORT=2111') + expect(environment_variables).to include('RVBD_AGENT_FILES=1') + expect(environment_variables).to include('RVBD_AGENT_PORT=7073') + expect(environment_variables).to include('RVBD_JBP_VERSION=0.0.0') + + expect(java_opts).to include('-agentpath:$PWD/.java-buildpack/riverbed_appinternals_agent/agent/lib/' \ + 'librpilj64.so') + end + + it 'updates JAVA_OPTS with credentials' do + allow(services).to receive(:find_service).and_return('credentials' => { 'rvbd_dsa_port' => '10000', \ + 'rvbd_agent_port' => '20000', \ + 'rvbd_moniker' => 'special_name' }) + + component.release + + expect(environment_variables).to include('DSA_PORT=10000') + expect(environment_variables).to include('RVBD_AGENT_PORT=20000') + expect(java_opts).to include('-Driverbed.moniker=special_name') + end + end +end diff --git a/spec/java_buildpack/framework/sealights_agent_spec.rb b/spec/java_buildpack/framework/sealights_agent_spec.rb new file mode 100644 index 0000000000..a6d5fb8870 --- /dev/null +++ b/spec/java_buildpack/framework/sealights_agent_spec.rb @@ -0,0 +1,156 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/sealights_agent' +require 'java_buildpack/util/tokenized_version' + +describe JavaBuildpack::Framework::SealightsAgent do + include_context 'with component help' + + it 'does not detect without sealights service' do + expect(component.detect).to be_nil + end + + context do + + let(:credentials) { { 'token' => 'my_token' } } + + let(:configuration) do + { 'build_session_id' => '1234', + 'proxy' => '127.0.0.1:8888', + 'lab_id' => 'lab1', + 'enable_upgrade' => true } + end + + before do + allow(services).to receive(:one_service?).with(/sealights/, 'token').and_return(true) + allow(services).to receive(:find_service).and_return('credentials' => credentials) + end + + it 'detects with sealights service' do + expect(component.detect).to eq("sealights-agent=#{version}") + end + + context do + it 'updates JAVA_OPTS sl.tags' do + component.release + + expect(java_opts).to include('-Dsl.tags=pivotal_cloud_foundry') + end + + it 'updates JAVA_OPTS sl.buildSessionId' do + component.release + + expect(java_opts).to include("-Dsl.buildSessionId=#{configuration['build_session_id']}") + end + + it 'updates JAVA_OPTS sl.labId' do + component.release + + expect(java_opts).to include("-Dsl.labId=#{configuration['lab_id']}") + end + + it 'updates JAVA_OPTS sl.proxy' do + component.release + + expect(java_opts).to include("-Dsl.proxy=#{configuration['proxy']}") + end + + it 'updates JAVA_OPTS sl.enableUpgrade' do + component.release + + expect(java_opts).to include("-Dsl.enableUpgrade=#{configuration['enable_upgrade']}") + end + + it 'updates JAVA_OPTS sl.token' do + component.release + + expect(java_opts).to include("-Dsl.token=#{credentials['token']}") + end + end + + context do + let(:configuration) { {} } + + it 'does not specify JAVA_OPTS sl.buildSessionId if one was not specified' do + component.release + + expect(java_opts).not_to include(/buildSessionId/) + end + + it 'does not specify JAVA_OPTS sl.labId if one was not specified' do + component.release + + expect(java_opts).not_to include(/labId/) + end + + it 'does not specify JAVA_OPTS sl.proxy if one was not specified' do + component.release + + expect(java_opts).not_to include(/proxy/) + end + + it 'sets JAVA_OPTS sl.enableUpgrade to false by default' do + component.release + + expect(java_opts).to include('-Dsl.enableUpgrade=false') + end + end + + context do + let(:credentials) { { 'token' => 'my_token', 'proxy' => 'my_proxy', 'lab_id' => 'my_lab' } } + let(:configuration) { {} } + + it 'updates JAVA_OPTS sl.labId from the user provisioned service' do + component.release + + expect(java_opts).to include("-Dsl.labId=#{credentials['lab_id']}") + end + + it 'updates JAVA_OPTS sl.proxy from the user provisioned service' do + component.release + + expect(java_opts).to include("-Dsl.proxy=#{credentials['proxy']}") + end + end + + context do + let(:credentials) { { 'token' => 'my_token', 'proxy' => 'my_proxy', 'lab_id' => 'my_lab' } } + + let(:configuration) do + { 'proxy' => '127.0.0.1:8888', + 'lab_id' => 'lab1' } + end + + it 'updates JAVA_OPTS sl.labId from config (and not user provisioned service)' do + component.release + + expect(java_opts).to include("-Dsl.labId=#{configuration['lab_id']}") + end + + it 'updates JAVA_OPTS sl.proxy from config (and not user provisioned service)' do + component.release + + expect(java_opts).to include("-Dsl.proxy=#{configuration['proxy']}") + end + end + + end + +end diff --git a/spec/java_buildpack/framework/seeker_security_agent_spec.rb b/spec/java_buildpack/framework/seeker_security_agent_spec.rb new file mode 100644 index 0000000000..52bfac092b --- /dev/null +++ b/spec/java_buildpack/framework/seeker_security_agent_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/seeker_security_provider' + +describe JavaBuildpack::Framework::SeekerSecurityProvider do + include_context 'with component help' + + it 'does not detect without seeker service' do + expect(component.detect).to be_nil + end + + context do + + before do + allow(services).to receive(:one_service?).with(/seeker/i, 'seeker_server_url').and_return(true) + + allow(services).to receive(:find_service).and_return('credentials' => { 'seeker_server_url' => + 'http://localhost' }) + + allow(application_cache).to receive(:get).with('http://localhost/rest/api/latest/installers/agents/binaries/JAVA') + .and_yield(Pathname.new('spec/fixtures/stub-seeker-agent.zip').open, + false) + end + + it 'detects with seeker service' do + expect(component.detect).to eq('seeker-security-provider') + end + + it 'expands Seeker agent zip for agent direct download' do + component.compile + + expect(sandbox + 'seeker-agent.jar').to exist + end + + it 'updates JAVA_OPTS' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/seeker_security_provider/seeker-agent.jar') + expect(environment_variables).to include('SEEKER_SERVER_URL=http://localhost') + end + + end + +end diff --git a/spec/java_buildpack/framework/sky_walking_agent_spec.rb b/spec/java_buildpack/framework/sky_walking_agent_spec.rb new file mode 100644 index 0000000000..1ddff1cac2 --- /dev/null +++ b/spec/java_buildpack/framework/sky_walking_agent_spec.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/sky_walking_agent' + +describe JavaBuildpack::Framework::SkyWalkingAgent do + include_context 'with component help' + + let(:configuration) do + { 'default_application_name' => nil } + end + + it 'does not detect without skywalking-n/a service' do + expect(component.detect).to be_nil + end + + context do + + let(:credentials) { {} } + + before do + allow(services).to receive(:one_service?).with(/sky-?walking/, 'servers').and_return(true) + allow(services).to receive(:find_service).and_return('credentials' => credentials) + end + + it 'detects with skywalking-n/a service' do + expect(component.detect).to eq("sky-walking-agent=#{version}") + end + + it 'expands Skywalking agent tar', + cache_fixture: 'stub-skywalking-agent.tar.gz' do + + component.compile + + expect(sandbox + 'skywalking-agent.jar').to exist + end + + it 'raises error if servers not specified' do + expect { component.release }.to raise_error(/'servers' credential must be set/) + end + + context do + + let(:credentials) { { 'servers' => 'test-servers' } } + + it 'updates JAVA_OPTS' do + component.release + + expect(java_opts).to include('-javaagent:$PWD/.java-buildpack/sky_walking_agent/skywalking-agent.jar') + expect(java_opts).to include('-Dskywalking.collector.servers=test-servers') + expect(java_opts).to include('-Dskywalking.agent.application_code=test-application-name') + end + + context do + let(:credentials) { super().merge 'sample-n-per-3-secs' => '10' } + + it 'adds sample_n_per_3_secs from credentials to JAVA_OPTS if specified' do + component.release + + expect(java_opts).to include('-Dskywalking.agent.sample_n_per_3_secs=10') + end + end + + context do + let(:credentials) { super().merge 'application-name' => 'another-test-application-name' } + + it 'adds application_name from credentials to JAVA_OPTS if specified' do + component.release + + expect(java_opts).to include('-Dskywalking.agent.application_code=another-test-application-name') + end + end + + context do + let(:credentials) { super().merge 'span-limit-per-segment' => '300' } + + it 'adds span_limit_per_segment from credentials to JAVA_OPTS if specified' do + component.release + + expect(java_opts).to include('-Dskywalking.agent.span_limit_per_segment=300') + end + end + + context do + let(:credentials) { super().merge 'ignore-suffix' => '.html' } + + it 'adds ignore_suffix to JAVA_OPTS if specified' do + component.release + + expect(java_opts).to include('-Dskywalking.agent.ignore_suffix=.html') + end + end + + context do + let(:credentials) { super().merge 'is-open-debugging-class' => 'true' } + + it 'adds is_open_debugging_class to JAVA_OPTS if specified' do + component.release + + expect(java_opts).to include('-Dskywalking.agent.is_open_debugging_class=true') + end + end + + context do + let(:credentials) { super().merge 'logging-level' => 'DEBUG' } + + it 'adds logging_level to JAVA_OPTS if specified' do + component.release + + expect(java_opts).to include('-Dskywalking.logging.level=DEBUG') + end + end + end + + end + +end diff --git a/spec/java_buildpack/framework/splunk_otel_java_agent_spec.rb b/spec/java_buildpack/framework/splunk_otel_java_agent_spec.rb new file mode 100644 index 0000000000..eb75ada8e5 --- /dev/null +++ b/spec/java_buildpack/framework/splunk_otel_java_agent_spec.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/splunk_otel_java_agent' +require 'java_buildpack/util/tokenized_version' + +describe JavaBuildpack::Framework::SplunkOtelJavaAgent do + include_context 'with component help' + + let(:configuration) { { 'version' => '1.16.0' } } + let(:vcap_application) { { 'application_name' => 'GreatServiceTM' } } + + it 'does not detect without splunk-o11y service bind' do + expect(component.detect).to be_nil + end + + context 'when detected' do + + before do + allow(services).to receive(:one_service?).with(/splunk-o11y/).and_return(true) + end + + it 'detects with splunk-otel-java' do + expect(component.detect).to eq("splunk-otel-java-agent=#{version}") + end + + it 'downloads the splunk otel javaagent jar', cache_fixture: 'stub-splunk-otel-javaagent.jar' do + + component.compile + + expect(sandbox + "splunk_otel_java_agent-#{version}.jar").to exist + end + + it 'updates JAVA_OPTS' do + allow(services).to receive(:find_service).and_return('credentials' => { 'splunk.access.token' => 'sekret', + 'ignored' => 'not used', + 'otel.foo' => 'bar' }) + component.release + + expect(java_opts).to include( + "-javaagent:$PWD/.java-buildpack/splunk_otel_java_agent/splunk_otel_java_agent-#{version}.jar" + ) + expect(java_opts).to include('-Dsplunk.access.token=sekret') + expect(java_opts).to include('-Dotel.foo=bar') + end + + it 'sets the service name from the application name' do + allow(services).to receive(:find_service).and_return('credentials' => { 'splunk.access.token' => 'sekret' }) + + component.release + + expect(java_opts).to include('-Dotel.service.name=GreatServiceTM') + end + + it 'prefers credentials over application_name for service name' do + creds = { 'credentials' => { 'otel.service.name' => 'sweet', 'splunk.access.token' => 'sekret' } } + allow(services).to receive(:find_service).and_return(creds) + + component.release + + expect(java_opts).to include('-Dotel.service.name=sweet') + end + + end + +end diff --git a/spec/java_buildpack/framework/spring_auto_reconfiguration/web_xml_modifier_spec.rb b/spec/java_buildpack/framework/spring_auto_reconfiguration/web_xml_modifier_spec.rb deleted file mode 100644 index d72b95dcdb..0000000000 --- a/spec/java_buildpack/framework/spring_auto_reconfiguration/web_xml_modifier_spec.rb +++ /dev/null @@ -1,51 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'spec_helper' -require 'java_buildpack/framework/spring_auto_reconfiguration/web_xml_modifier' - -describe JavaBuildpack::Framework::WebXmlModifier do - - it 'does not modify root if there is no ContextLoaderListener' do - assert_equality('web_root_no_contextLoaderListener') { |modifier| modifier.augment_root_context } - end - - it 'does not modify a servlet if is not a DispatcherServlet' do - assert_equality('web_servlet_no_DispatcherServlet') { |modifier| modifier.augment_root_context } - end - - it 'adds a new contextInitializerClasses if it does not exist' do - assert_equality('web_root_no_params') { |modifier| modifier.augment_root_context } - assert_equality('web_servlet_no_params') { |modifier| modifier.augment_servlet_contexts } - end - - it 'updates existing contextInitializerClasses if it does exist' do - assert_equality('web_root_existing_params') { |modifier| modifier.augment_root_context } - assert_equality('web_servlet_existing_params') { |modifier| modifier.augment_servlet_contexts } - end - - def assert_equality(fixture) - modifier = described_class.new(Pathname.new("spec/fixtures/#{fixture}_before.xml").read) - - yield modifier - - actual = modifier.to_s - expected = Pathname.new("spec/fixtures/#{fixture}_after.xml").read - - expect(actual).to eq(expected) - end - -end diff --git a/spec/java_buildpack/framework/spring_auto_reconfiguration_spec.rb b/spec/java_buildpack/framework/spring_auto_reconfiguration_spec.rb index 40c245a313..17a2946b04 100644 --- a/spec/java_buildpack/framework/spring_auto_reconfiguration_spec.rb +++ b/spec/java_buildpack/framework/spring_auto_reconfiguration_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,11 +17,13 @@ require 'spec_helper' require 'component_helper' +require 'logging_helper' require 'java_buildpack/framework/spring_auto_reconfiguration' -require 'java_buildpack/framework/spring_auto_reconfiguration/web_xml_modifier' describe JavaBuildpack::Framework::SpringAutoReconfiguration do - include_context 'component_helper' + include_context 'with component help' + include_context 'with console help' + include_context 'with logging help' let(:configuration) { { 'enabled' => true } } @@ -36,10 +39,43 @@ expect(component.detect).to eq("spring-auto-reconfiguration=#{version}") end + it 'does not detect with Spring JAR and user java-cfenv', + app_fixture: 'framework_auto_reconfiguration_java_cfenv' do + + expect(component.detect).to be_nil + end + it 'does not detect without Spring JAR' do expect(component.detect).to be_nil end + it 'warns if SCC is present', + cache_fixture: 'stub-auto-reconfiguration.jar', + app_fixture: 'framework_auto_reconfiguration_scc' do + + component.compile + + expect(stderr.string).to match(/ATTENTION: The Spring Cloud Connectors library is present in your application/) + end + + it 'does not warn when SCC is missing', + cache_fixture: 'stub-auto-reconfiguration.jar', + app_fixture: 'framework_auto_reconfiguration_servlet_3' do + + component.compile + + expect(stderr.string).not_to match(/ATTENTION: The Spring Cloud Connectors library is present in your application/) + end + + it 'warns if SAR is contributed', + cache_fixture: 'stub-auto-reconfiguration.jar', + app_fixture: 'framework_auto_reconfiguration_servlet_3' do + + component.compile + + expect(stderr.string).to match(/ATTENTION: The Spring Auto Reconfiguration and shaded Spring Cloud/) + end + context do let(:configuration) { { 'enabled' => false } } @@ -51,7 +87,7 @@ end it 'downloads additional libraries', - app_fixture: 'framework_auto_reconfiguration_servlet_3', + app_fixture: 'framework_auto_reconfiguration_servlet_3', cache_fixture: 'stub-auto-reconfiguration.jar' do component.compile @@ -60,7 +96,7 @@ end it 'adds to additional libraries', - app_fixture: 'framework_auto_reconfiguration_servlet_3', + app_fixture: 'framework_auto_reconfiguration_servlet_3', cache_fixture: 'stub-auto-reconfiguration.jar' do component.release @@ -68,26 +104,21 @@ expect(additional_libraries).to include(sandbox + "spring_auto_reconfiguration-#{version}.jar") end - context do - - let(:web_xml_modifier) { double('WebXmlModifier') } + context('when java-cfenv injects its lib') do before do - allow(JavaBuildpack::Framework::WebXmlModifier).to receive(:new).and_return(web_xml_modifier) - expect(web_xml_modifier).to receive(:augment_root_context) - expect(web_xml_modifier).to receive(:augment_servlet_contexts) - allow(web_xml_modifier).to receive(:to_s).and_return('Test Content') + additional_libraries.insert 0, additional_libs_directory + 'java_cf_env.jar' end - it 'updates web.xml if it exists', - app_fixture: 'framework_auto_reconfiguration_servlet_2', - cache_fixture: 'stub-auto-reconfiguration.jar' do + after do + additional_libraries.delete additional_libs_directory + 'java_cf_env.jar' + end - component.compile + it 'does not detect with Spring JAR and injected cfenv', + app_fixture: 'framework_auto_reconfiguration_servlet_3' do - expect((app_dir + 'WEB-INF/web.xml').read).to eq('Test Content') + expect(component.detect).to be_nil end end - end diff --git a/spec/java_buildpack/framework/spring_insight_spec.rb b/spec/java_buildpack/framework/spring_insight_spec.rb index 13b32a5db3..92ae5735ae 100644 --- a/spec/java_buildpack/framework/spring_insight_spec.rb +++ b/spec/java_buildpack/framework/spring_insight_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,8 +22,8 @@ require 'java_buildpack/framework/spring_insight' describe JavaBuildpack::Framework::SpringInsight do - include_context 'component_helper' - include_context 'internet_availability_helper' + include_context 'with component help' + include_context 'with internet availability help' it 'does not detect without spring-insight-n/a service' do expect(component.detect).to be_nil @@ -31,39 +32,43 @@ context do before do - allow(services).to receive(:one_service?).with(/insight/, 'dashboard_url', 'agent_username', 'agent_password') - .and_return(true) - allow(services).to receive(:find_service).and_return('label' => 'insight-1.0', - 'credentials' => { 'dashboard_url' => 'test-uri', - 'agent_password' => 'foo', - 'agent_username' => 'bar' }) - allow(application_cache).to receive(:get).with('test-uri/services/config/agent-download') - .and_yield(Pathname.new('spec/fixtures/stub-insight-agent.jar').open, false) + allow(services).to receive(:one_service?) + .with(/p-insight/, 'agent_download_url', 'service_instance_id').and_return(true) + allow(services).to receive(:find_service).and_return( + 'label' => 'p-insight', + 'credentials' => { + 'version' => '2.0.0', + 'agent_download_url' => 'test-uri/services/config/agent-download', + 'agent_password' => 'foo', + 'agent_username' => 'bar', + 'service_instance_id' => '12345' + } + ) + allow(application_cache).to receive(:get) + .with('test-uri/services/config/agent-download') + .and_yield(Pathname.new('spec/fixtures/stub-insight-agent.jar').open, false) end - it 'detects with spring-insight-n/a service' do - expect(component.detect).to eq('spring-insight=1.0') + it 'does detect with spring-insight-n/a service' do + expect(component.detect).to eq('spring-insight=2.0.0') end - it 'extracts Spring Insight from the Uber Agent zip file inside the Agent Installer jar' do + it 'does extract Spring Insight from the Uber Agent zip file inside the Agent Installer jar' do component.compile - container_libs_dir = app_dir + '.spring-insight/container-libs' - - expect(sandbox + 'weaver/insight-weaver-cf-2.0.0-CI-SNAPSHOT.jar').to exist - expect(container_libs_dir + 'insight-bootstrap-generic-2.0.0-CI-SNAPSHOT.jar').to exist - expect(container_libs_dir + 'insight-bootstrap-tomcat-common-2.0.0-CI-SNAPSHOT.jar').to exist + expect(sandbox + 'weaver/insight-weaver-2.0.0-CI-SNAPSHOT.jar').to exist expect(sandbox + 'insight/conf/insight.properties').to exist + expect(sandbox + 'insight/agent-plugins/insight-agent-rabbitmq-core-2.0.0-CI-SNAPSHOT.jar').to exist end - it 'guarantees that internet access is available when downloading' do + it 'does guarantee that internet access is available when downloading' do expect_any_instance_of(JavaBuildpack::Util::Cache::InternetAvailability) .to receive(:available).with(true, 'The Spring Insight download location is always accessible') component.compile end - it 'updates JAVA_OPTS', + it 'does update JAVA_OPTS', app_fixture: 'framework_spring_insight' do component.release @@ -74,9 +79,36 @@ expect(java_opts).to include('-Dinsight.logs=$PWD/.java-buildpack/spring_insight/insight/logs') expect(java_opts).to include('-Daspectj.overweaving=true') expect(java_opts).to include('-Dorg.aspectj.tracing.factory=default') - expect(java_opts).to include('-Dagent.http.username=bar') - expect(java_opts).to include('-Dagent.http.password=foo') end end + context do + + it 'does extract Spring Insight from the Uber Agent zip file and copy the ActiveMQ plugin' do + allow(services).to receive(:one_service?) + .with(/p-insight/, 'agent_download_url', 'service_instance_id').and_return(true) + allow(services).to receive(:find_service).and_return( + 'label' => 'p-insight', + 'credentials' => { + 'version' => '2.0.0', + 'agent_download_url' => 'test-uri/services/config/agent-download', + 'agent_password' => 'foo', + 'agent_username' => 'bar', + 'service_instance_id' => '12345', + 'agent_transport' => 'activemq' + } + ) + allow(application_cache).to receive(:get) + .with('test-uri/services/config/agent-download') + .and_yield(Pathname.new('spec/fixtures/stub-insight-agent.jar').open, false) + + component.compile + + expect(sandbox + 'weaver/insight-weaver-2.0.0-CI-SNAPSHOT.jar').to exist + expect(sandbox + 'insight/conf/insight.properties').to exist + expect(sandbox + 'insight/agent-plugins/insight-agent-activemq-2.0.0-CI-SNAPSHOT.jar').to exist + end + + end + end diff --git a/spec/java_buildpack/framework/takipi_agent_spec.rb b/spec/java_buildpack/framework/takipi_agent_spec.rb new file mode 100644 index 0000000000..26c3e2ceaa --- /dev/null +++ b/spec/java_buildpack/framework/takipi_agent_spec.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/takipi_agent' + +describe JavaBuildpack::Framework::TakipiAgent do + include_context 'with component help' + + let(:configuration) { { 'node_name_prefix' => nil } } + + it 'does not detect without takipi-n/a service' do + expect(component.detect).to be_nil + end + + context do + + let(:credentials) { {} } + + before do + allow(services).to receive(:one_service?).with(/takipi/, %w[secret_key collector_host]).and_return(true) + allow(services).to receive(:find_service).and_return('credentials' => credentials) + end + + it 'detects with takipi service' do + expect(component.detect).to eq("takipi-agent=#{version}") + end + + it 'expands Takipi agent tarball', + cache_fixture: 'stub-takipi-agent.tar.gz' do + + component.compile + + expect(sandbox + 'lib/libTakipiAgent.so').to exist + end + + context do + let(:credentials) { { 'collector_host' => 'test-host' } } + + it 'updates default environment variables' do + component.release + + expect(environment_variables) + .to include('LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/.java-buildpack/takipi_agent/lib') + expect(environment_variables).to include('JVM_LIB_FILE=$PWD/.test-java-home/lib/amd64/server/libjvm.so') + expect(environment_variables).to include('TAKIPI_HOME=$PWD/.java-buildpack/takipi_agent') + end + + it 'updates user environment variables' do + component.release + + expect(environment_variables).to include('TAKIPI_COLLECTOR_HOST=test-host') + end + + context 'with secret key' do + let(:credentials) { super().merge 'secret_key' => 'test-key' } + + it 'secret key set' do + component.release + + expect(environment_variables).to include('TAKIPI_SECRET_KEY=test-key') + end + end + + context 'with configuration overrides' do + + let(:configuration) { { 'node_name_prefix' => 'test-name', 'application_name' => 'test-name' } } + + it 'update application name' do + component.release + expect(java_opts).to include('-agentpath:$PWD/.java-buildpack/takipi_agent/lib/libTakipiAgent.so') + expect(java_opts).to include('-Dtakipi.name=test-name') + end + + end + end + + end + +end diff --git a/spec/java_buildpack/framework/your_kit_profiler_spec.rb b/spec/java_buildpack/framework/your_kit_profiler_spec.rb new file mode 100644 index 0000000000..46976f07ae --- /dev/null +++ b/spec/java_buildpack/framework/your_kit_profiler_spec.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/your_kit_profiler' + +describe JavaBuildpack::Framework::YourKitProfiler do + include_context 'with component help' + + it 'does not detect if not enabled' do + expect(component.detect).to be_nil + end + + context do + let(:configuration) { { 'enabled' => true } } + + it 'detects when enabled' do + expect(component.detect).to eq("your-kit-profiler=#{version}") + end + + it 'downloads YourKit agent', + cache_fixture: 'stub-your-kit-profiler.so' do + + component.compile + + expect(sandbox + "your_kit_profiler-#{version}").to exist + end + + context do + it 'updates JAVA_OPTS' do + component.release + + # rubocop:disable Layout/LineLength + expect(java_opts).to include("-agentpath:$PWD/.java-buildpack/your_kit_profiler/your_kit_profiler-#{version}=" \ + 'dir=$PWD/.java-buildpack/your_kit_profiler/snapshots,logdir=$PWD/.java-buildpack/your_kit_profiler/logs,' \ + 'port=10001,sessionname=test-application-name') + # rubocop:enable Layout/LineLength + end + + context do + let(:configuration) { super().merge 'port' => 10_002 } + + it 'adds port from configuration to JAVA_OPTS if specified' do + component.release + + # rubocop:disable Layout/LineLength + expect(java_opts).to include('-agentpath:$PWD/.java-buildpack/your_kit_profiler/your_kit_profiler-' \ + "#{version}=dir=$PWD/.java-buildpack/your_kit_profiler/snapshots,logdir=$PWD/.java-buildpack/" \ + 'your_kit_profiler/logs,port=10002,sessionname=test-application-name') + # rubocop:enable Layout/LineLength + end + end + + context do + let(:configuration) { super().merge 'default_session_name' => 'alternative-session-name' } + + it 'adds session name from configuration to JAVA_OPTS if specified' do + component.release + + # rubocop:disable Layout/LineLength + expect(java_opts).to include('-agentpath:$PWD/.java-buildpack/your_kit_profiler/your_kit_profiler-' \ + "#{version}=dir=$PWD/.java-buildpack/your_kit_profiler/snapshots,logdir=$PWD/.java-buildpack/" \ + 'your_kit_profiler/logs,port=10001,sessionname=alternative-session-name') + # rubocop:enable Layout/LineLength + end + end + + end + + end + +end diff --git a/spec/java_buildpack/jre/ibm_jre_initializer_spec.rb b/spec/java_buildpack/jre/ibm_jre_initializer_spec.rb new file mode 100644 index 0000000000..f6f455afe9 --- /dev/null +++ b/spec/java_buildpack/jre/ibm_jre_initializer_spec.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/component/mutable_java_home' +require 'java_buildpack/jre/ibm_jre_initializer' + +describe JavaBuildpack::Jre::IbmJreInitializer do + include_context 'with component help' + + let(:java_home) { JavaBuildpack::Component::MutableJavaHome.new } + + it 'detects with id of ibm-jre-initializer-' do + expect(component.detect).to eq("ibm-jre-initializer=#{version}") + end + + it 'installs java from bin', + cache_fixture: 'stub-java.bin' do + + allow(component).to receive(:shell).with(%r{spec/fixtures/stub-java.bin -i silent -f .* 2>&1}) + + component.detect + component.compile + end + + it 'adds JAVA_HOME to java_home' do + component + + expect(java_home.root).to eq(sandbox + 'jre/') + end + + it 'adds java.io.tmpdir to java_opts' do + component.detect + component.release + + expect(java_opts).to include('-Djava.io.tmpdir=$TMPDIR') + end + + it 'adds Xtune to java_opts' do + component.detect + component.release + + expect(java_opts).to include('-Xtune:virtualized') + end + + it 'adds Xshareclasses to java_opts' do + component.detect + component.release + + expect(java_opts).to include('-Xshareclasses:none') + end + +end diff --git a/spec/java_buildpack/jre/ibm_jre_spec.rb b/spec/java_buildpack/jre/ibm_jre_spec.rb new file mode 100644 index 0000000000..c671f4a391 --- /dev/null +++ b/spec/java_buildpack/jre/ibm_jre_spec.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'fileutils' +require 'java_buildpack/component/mutable_java_home' +require 'java_buildpack/jre/ibm_jre_initializer' +require 'java_buildpack/jre/ibm_jre' + +describe JavaBuildpack::Jre::IbmJRE do + include_context 'with component help' + + let(:component) { StubIbmJRE.new context } + + let(:java_home) { JavaBuildpack::Component::MutableJavaHome.new } + + let(:configuration) do + { 'jre' => jre_configuration, + 'jvmkill_agent' => jvmkill_agent_configuration } + end + + let(:jre_configuration) { instance_double('jre_configuration') } + + let(:jvmkill_agent_configuration) { {} } + + it 'supports anyway' do + expect(component).to be_supports + end + + it 'creates IbmJreInitializer instance' do + allow_any_instance_of(StubIbmJRE).to receive(:supports?).and_return false + + allow(JavaBuildpack::Jre::IbmJreInitializer) + .to receive(:new).with(sub_configuration_context(jre_configuration).merge(component_name: 'Stub Ibm JRE')) + allow(JavaBuildpack::Jre::JvmkillAgent) + .to receive(:new).with(sub_configuration_context(jvmkill_agent_configuration)) + + component.sub_components context + end + +end + +class StubIbmJRE < JavaBuildpack::Jre::IbmJRE + + public :command, :sub_components + + # rubocop:disable Lint/UselessMethodDefinition + def supports? + super + end + # rubocop:enable Lint/UselessMethodDefinition + +end + +def sub_configuration_context(configuration) + cntxt = context.clone + cntxt[:configuration] = configuration + cntxt +end diff --git a/spec/java_buildpack/jre/jvmkill_agent_spec.rb b/spec/java_buildpack/jre/jvmkill_agent_spec.rb new file mode 100644 index 0000000000..76736ccc2f --- /dev/null +++ b/spec/java_buildpack/jre/jvmkill_agent_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/jre/jvmkill_agent' + +describe JavaBuildpack::Jre::JvmkillAgent do + include_context 'with component help' + + it 'copies executable to bin directory', + cache_fixture: 'stub-jvmkill-agent' do + + component.compile + + expect(sandbox + "bin/jvmkill-#{version}").to exist + end + + it 'chmods executable to 0755', + cache_fixture: 'stub-jvmkill-agent' do + + component.compile + + expect(File.stat(sandbox + "bin/jvmkill-#{version}").mode).to eq(0o100755) + end + + it 'adds agent parameters to the JAVA_OPTS' do + component.release + + expect(java_opts).to include('-agentpath:$PWD/.java-buildpack/jvmkill_agent/bin/jvmkill-0.0.0=printHeapHistogram=1') + end + + it 'adds heap dump parameter to JAVA_OPTS when volume service available' do + allow(services).to receive(:one_volume_service?).with(/heap-dump/).and_return(true) + allow(services).to receive(:find_volume_service).and_return('volume_mounts' => + [{ 'container_dir' => 'test-container-dir' }]) + + component.release + + expect(java_opts).to include('-agentpath:$PWD/.java-buildpack/jvmkill_agent/bin/jvmkill-0.0.0=' \ + 'printHeapHistogram=1,heapDumpPath=test-container-dir/test-space-name-test-spa/' \ + 'test-application-name-test-app/$CF_INSTANCE_INDEX-%FT%T%z-' \ + '${CF_INSTANCE_GUID:0:8}.hprof') + end + +end diff --git a/spec/java_buildpack/jre/memory/memory_bucket_spec.rb b/spec/java_buildpack/jre/memory/memory_bucket_spec.rb deleted file mode 100644 index 21194a2f48..0000000000 --- a/spec/java_buildpack/jre/memory/memory_bucket_spec.rb +++ /dev/null @@ -1,59 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'spec_helper' -require 'logging_helper' -require 'java_buildpack/jre/memory/memory_bucket' -require 'java_buildpack/jre/memory/memory_range' -require 'java_buildpack/jre/memory/memory_size' - -describe JavaBuildpack::Jre::MemoryBucket do - include_context 'logging_helper' - - let(:test_name) { 'bucket-name' } - let(:test_weighting) { 0.5 } - let(:test_range) { JavaBuildpack::Jre::MemoryRange.new('10M..10M') } - - it 'fails to construct if name is nil' do - expect { described_class.new(nil, test_weighting, test_range) }.to raise_error(/Invalid MemoryBucket name/) - end - - it 'fails to construct if name is the empty string' do - expect { described_class.new('', test_weighting, test_range) }.to raise_error(/Invalid MemoryBucket name/) - end - - it 'fails to construct if weighting is nil' do - expect { described_class.new(test_name, nil, test_range) }.to raise_error(/Invalid weighting/) - end - - it 'fails to construct if weighting is not numeric' do - expect { described_class.new(test_name, 'x', test_range) }.to raise_error(/Invalid weighting/) - end - - it 'fails to construct if weighting is negative' do - expect { described_class.new(test_name, -0.1, test_range) }.to raise_error(/Invalid weighting/) - end - - it 'initializes size to nil' do - memory_bucket = described_class.new(test_name, test_weighting, test_range) - expect(memory_bucket.size).to eq(nil) - end - - it 'fails to construct if range is invalid' do - expect { described_class.new(test_name, test_weighting, 'x') }.to raise_error(/Invalid\ 'range'\ parameter/) - end - -end diff --git a/spec/java_buildpack/jre/memory/memory_limit_spec.rb b/spec/java_buildpack/jre/memory/memory_limit_spec.rb deleted file mode 100644 index 86ed6afe5a..0000000000 --- a/spec/java_buildpack/jre/memory/memory_limit_spec.rb +++ /dev/null @@ -1,79 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'spec_helper' -require 'memory_limit_helper' -require 'java_buildpack/jre/memory/memory_limit' -require 'java_buildpack/jre/memory/memory_size' - -describe JavaBuildpack::Jre::MemoryLimit do - include_context 'memory_limit_helper' - - it 'accepts memory with an uppercase G', - memory_limit: '1G' do - - expect(described_class.memory_limit).to eq(JavaBuildpack::Jre::MemorySize.new('1048576K')) - end - - it 'accepts memory with an lowercase G', - memory_limit: '1g' do - - expect(described_class.memory_limit).to eq(JavaBuildpack::Jre::MemorySize.new('1048576K')) - end - - it 'accepts memory with an uppercase M', - memory_limit: '1M' do - - expect(described_class.memory_limit).to eq(JavaBuildpack::Jre::MemorySize.new('1024K')) - end - - it 'accepts memory with an lowercase M', - memory_limit: '1m' do - - expect(described_class.memory_limit).to eq(JavaBuildpack::Jre::MemorySize.new('1024K')) - end - - it 'returns nil if a memory limit is not specified', - memory_limit: nil do - - expect(described_class.memory_limit).to be_nil - end - - it 'fails if a memory limit does not have a unit', - memory_limit: '-1' do - - expect { described_class.memory_limit }.to raise_error(/Invalid/) - end - - it 'fails if a memory limit is not an number', - memory_limit: 'xm' do - - expect { described_class.memory_limit }.to raise_error(/Invalid/) - end - - it 'fails if a memory limit is not an integer', - memory_limit: '-1.1m' do - - expect { described_class.memory_limit }.to raise_error(/Invalid/) - end - - it 'fails if a memory limit is negative', - memory_limit: '-1m' do - - expect { described_class.memory_limit }.to raise_error(/Invalid/) - end - -end diff --git a/spec/java_buildpack/jre/memory/memory_range_spec.rb b/spec/java_buildpack/jre/memory/memory_range_spec.rb deleted file mode 100644 index c9bfd9aff0..0000000000 --- a/spec/java_buildpack/jre/memory/memory_range_spec.rb +++ /dev/null @@ -1,161 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'spec_helper' -require 'java_buildpack/jre/memory/memory_range' -require 'java_buildpack/jre/memory/memory_size' - -describe JavaBuildpack::Jre::MemoryRange do - - let(:low) { JavaBuildpack::Jre::MemorySize.new('1m') } - - let(:mid) { JavaBuildpack::Jre::MemorySize.new('4m') } - - let(:test_lower_bound) { JavaBuildpack::Jre::MemorySize.new('3m') } - - let(:test_upper_bound) { JavaBuildpack::Jre::MemorySize.new('5m') } - - it 'accepts an absolute memory size and produce the corresponding tight range' do - range = described_class.new('3m') - expect(range.floor).to eq(test_lower_bound) - expect(range.ceiling).to eq(test_lower_bound) - end - - it 'accepts a range with specified lower and upper bounds' do - range = described_class.new('3m..5m') - expect(range.floor).to eq(test_lower_bound) - expect(range.ceiling).to eq(test_upper_bound) - end - - it 'accepts a range with specified lower bound, but no upper bound' do - range = described_class.new('3m..') - expect(range.floor).to eq(test_lower_bound) - expect(range.bounded?).not_to be - expect(range.ceiling).to be_nil - end - - it 'accepts a range with specified upper bound, but no lower bound' do - range = described_class.new('..5m') - expect(range.floor).to eq(0) - expect(range.ceiling).to eq(test_upper_bound) - end - - it 'accepts a range with no lower or upper bounds' do - range = described_class.new('..') - expect(range.floor).to eq(0) - expect(range.bounded?).not_to be - expect(range.ceiling).to be_nil - end - - it 'detects a memory size lower than a range to lie outside the range' do - range = described_class.new('3m..5m') - expect(range.contains?(0)).to eq(false) - end - - it 'detects a memory size higher than a range to lie outside the range' do - range = described_class.new('3m..5m') - expect(range.contains?(test_upper_bound * 2)).to eq(false) - end - - it 'detects a memory size within a range as lying inside the range' do - range = described_class.new('3m..5m') - expect(range.contains?(mid)).to eq(true) - end - - it 'constrains a memory size lower than a range to the lower bound of the range' do - range = described_class.new('3m..5m') - expect(range.constrain(low)).to eq(test_lower_bound) - end - - it 'constrains a memory size higher than a range to the upper bound of the range' do - range = described_class.new('3m..5m') - expect(range.constrain(test_upper_bound * 2)).to eq(test_upper_bound) - end - - it 'constrains a memory size within the range to be the memory size itself' do - range = described_class.new('3m..5m') - expect(range.constrain(mid)).to eq(mid) - end - - it 'detects a degenerate range' do - range = described_class.new('3m..3m') - expect(range.degenerate?).to eq(true) - end - - it 'detects a non-degenerate range' do - range = described_class.new('3m..5m') - expect(range.degenerate?).to eq(false) - end - - it 'fails if the range string is empty' do - expect { described_class.new('2m..1m') }.to raise_error(/Invalid range/) - end - - it 'fails if the range is empty' do - expect { described_class.new(test_upper_bound, test_lower_bound) }.to raise_error(/Invalid range/) - end - - it 'fails if the lower bound is not a MemorySize' do - expect { described_class.new('', test_upper_bound) }.to raise_error(/Invalid combination of parameter types/) - end - - it 'fails if the upper bound is not a MemorySize' do - expect { described_class.new(test_lower_bound, '') }.to raise_error(/Invalid MemorySize parameter of type/) - end - - it 'accepts valid lower and upper bounds' do - range = described_class.new(test_lower_bound, test_upper_bound) - expect(range.floor).to eq(test_lower_bound) - expect(range.ceiling).to eq(test_upper_bound) - expect(range.bounded?).to be - end - - it 'accepts a lower bound and no upper bound' do - range = described_class.new(test_lower_bound) - expect(range.floor).to eq(test_lower_bound) - expect(range.ceiling).to be_nil - expect(range.bounded?).not_to be - end - - it 'detects a degenerate range constructed from MemorySizes' do - range = described_class.new(test_lower_bound, test_lower_bound) - expect(range.degenerate?).to eq(true) - end - - it 'detects a non-degenerate range constructed from MemorySizes' do - range = described_class.new(test_lower_bound, test_upper_bound) - expect(range.degenerate?).to eq(false) - end - - it 'produces a string representation' do - range = described_class.new('3m..5m') - expect(range.to_s).to eq('3M..5M') - end - - it 'supports multiplication by a numeric' do - range = described_class.new('3m..5m') - expect(range * 2).to eq(described_class.new('6m..10m')) - end - - it 'compares bounded ranges for equality' do - expect(described_class.new('3m..5m')).to eq(described_class.new(test_lower_bound, test_upper_bound)) - end - - it 'compares unbounded ranges for equality' do - expect(described_class.new('3m..')).to eq(described_class.new(test_lower_bound)) - end - -end diff --git a/spec/java_buildpack/jre/memory/memory_size_spec.rb b/spec/java_buildpack/jre/memory/memory_size_spec.rb deleted file mode 100644 index 6dea4f4f45..0000000000 --- a/spec/java_buildpack/jre/memory/memory_size_spec.rb +++ /dev/null @@ -1,138 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'spec_helper' -require 'java_buildpack/jre/memory/memory_size' - -describe JavaBuildpack::Jre::MemorySize do - - let(:half_meg) { described_class.new('512K') } - - let(:one_meg) { described_class.new('1M') } - - it 'accepts a memory size in bytes, kilobytes, megabytes, or gigabytes' do - expect(described_class.new('1024B')).to eq(described_class.new('1k')) - expect(described_class.new('1024b')).to eq(described_class.new('1k')) - expect(described_class.new('1M')).to eq(described_class.new('1024k')) - expect(described_class.new('1m')).to eq(described_class.new('1024k')) - expect(described_class.new('1G')).to eq(described_class.new('1048576k')) - expect(described_class.new('1g')).to eq(described_class.new('1048576k')) - end - - it 'fails if nil is passed to the constructor' do - expect { described_class.new(nil) }.to raise_error(/Invalid/) - end - - it 'accepts a zero memory size with no unit' do - expect(described_class.new('0')).to eq(described_class.new('0k')) - end - - it 'fails if a non-zero memory size does not have a unit' do - expect { described_class.new('1') }.to raise_error(/Invalid/) - end - - it 'fails if a memory size has an invalid unit' do - expect { described_class.new('1A') }.to raise_error(/Invalid/) - end - - it 'fails if a memory size is not an number' do - expect { described_class.new('xm') }.to raise_error(/Invalid/) - end - - it 'fails if a memory size is not an integer' do - expect { described_class.new('1.1m') }.to raise_error(/Invalid/) - end - - it 'fails if a memory size has embedded whitespace' do - expect { described_class.new('1 1m') }.to raise_error(/Invalid/) - end - - it 'accepts a negative value' do - expect(described_class.new('-1M')).to eq(described_class.new('-1024k')) - end - - it 'compares values' do - expect(one_meg).to be < described_class.new('1025K') - expect(described_class.new('1025K')).to be > one_meg - end - - it 'compares a described_class to 0' do - expect(one_meg).to be > 0 - end - - it 'fails when a memory size is compared to a non-zero numeric' do - expect { described_class.new('1B') < 2 }.to raise_error(/Cannot compare/) - end - - it 'multiplies values' do - expect(one_meg * 2).to eq(described_class.new('2M')) - end - - it 'fails when a memory size is multiplied by a memory size' do - expect { one_meg * one_meg }.to raise_error(/Cannot multiply/) - end - - it 'subtracts memory values' do - expect(one_meg - half_meg).to eq(half_meg) - end - - it 'fails when a numeric is subtracted from a memory size' do - expect { one_meg - 1 }.to raise_error(/Invalid parameter: instance of Fixnum is not a MemorySize/) - end - - it 'adds memory values' do - expect(half_meg + half_meg).to eq(one_meg) - end - - it 'fails when a numeric is added to a memory size' do - expect { one_meg + 1 }.to raise_error(/Invalid parameter: instance of Fixnum is not a MemorySize/) - end - - it 'divides a memory size by a numeric' do - expect(one_meg / 2).to eq(half_meg) - end - - it 'divides a memory size by a numeric using floating point' do - expect(described_class.new('3B') / 2).to eq(described_class.new('2B')) - end - - it 'divides a memory size by another memory size' do - expect(one_meg / half_meg).to eq(2) - end - - it 'divides a memory size by another memory size using floating point' do - expect(half_meg / one_meg).to eq(0.5) - end - - it 'fails when a memory size is divided by an incorrect type' do - expect { described_class.new('1B') / '' }.to raise_error(/Cannot divide/) - end - - it 'provides a zero memory size' do - expect(described_class::ZERO).to eq(described_class.new('0B')) - end - - it 'converts the memory size to a string' do - expect(described_class::ZERO.to_s).to eq('0') - expect(described_class.new('1K').to_s).to eq('1K') - expect(described_class.new('1k').to_s).to eq('1K') - expect(described_class.new('1M').to_s).to eq('1M') - expect(described_class.new('1m').to_s).to eq('1M') - expect(described_class.new('1G').to_s).to eq('1G') - expect(described_class.new('1g').to_s).to eq('1G') - end - -end diff --git a/spec/java_buildpack/jre/memory/openjdk_memory_heuristic_factory_spec.rb b/spec/java_buildpack/jre/memory/openjdk_memory_heuristic_factory_spec.rb deleted file mode 100644 index 3e7a755121..0000000000 --- a/spec/java_buildpack/jre/memory/openjdk_memory_heuristic_factory_spec.rb +++ /dev/null @@ -1,67 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'spec_helper' -require 'java_buildpack/jre/memory/openjdk_memory_heuristic_factory' -require 'java_buildpack/jre/memory/weight_balancing_memory_heuristic' -require 'java_buildpack/util/tokenized_version' -require 'rspec/expectations' - -RSpec::Matchers.define :be_a_hash_like do |expected| - match do |actual| - expected['heap']['1m'] == actual['heap']['1m'] && - expected['metaspace']['2m'] == actual['metaspace']['2m'] && - expected['permgen']['3m'] == actual['permgen']['3m'] && - expected['stack']['4m'] == actual['stack']['4m'] - end -end - -describe JavaBuildpack::Jre::OpenJDKMemoryHeuristicFactory do - - let(:heuristics) { { 'c' => 'd' } } - - let(:post_8) { JavaBuildpack::Util::TokenizedVersion.new('1.8.0') } - - let(:pre_8) { JavaBuildpack::Util::TokenizedVersion.new('1.7.0') } - - let(:sizes) { { 'a' => 'b' } } - - let(:expected_java_memory_options) do - { - 'heap' => ->(v) { %W(-Xmx#{v} -Xms#{v}) }, - 'metaspace' => ->(v) { %W(-XX:MaxMetaspaceSize=#{v} -XX:MetaspaceSize=#{v}) }, - 'permgen' => ->(v) { %W(-XX:MaxPermSize=#{v} -XX:PermSize=#{v}) }, - 'stack' => ->(v) { %W(-Xss#{v}) } - } - end - - it 'passes the appropriate constructor parameters for versions prior to 1.8' do - allow(JavaBuildpack::Jre::WeightBalancingMemoryHeuristic) - .to receive(:new).with(sizes, heuristics, %w(heap stack native permgen), - be_a_hash_like(expected_java_memory_options)) - - described_class.create_memory_heuristic(sizes, heuristics, pre_8) - end - - it 'passes the appropriate constructor parameters for versions 1.8 and higher' do - allow(JavaBuildpack::Jre::WeightBalancingMemoryHeuristic) - .to receive(:new).with(sizes, heuristics, %w(heap stack native metaspace), - be_a_hash_like(expected_java_memory_options)) - - described_class.create_memory_heuristic(sizes, heuristics, post_8) - end - -end diff --git a/spec/java_buildpack/jre/memory/stack_memory_bucket_spec.rb b/spec/java_buildpack/jre/memory/stack_memory_bucket_spec.rb deleted file mode 100644 index f29ad9d260..0000000000 --- a/spec/java_buildpack/jre/memory/stack_memory_bucket_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'spec_helper' -require 'logging_helper' -require 'java_buildpack/jre/memory/stack_memory_bucket' -require 'java_buildpack/jre/memory/memory_bucket' -require 'java_buildpack/jre/memory/memory_range' -require 'java_buildpack/jre/memory/memory_size' - -describe JavaBuildpack::Jre::StackMemoryBucket do - include_context 'logging_helper' - - let(:test_stack_bucket_weighting) { 0.05 } - let(:test_stack_size) { JavaBuildpack::Jre::MemorySize.new('2M') } - let(:test_stack_size_range) { JavaBuildpack::Jre::MemoryRange.new(test_stack_size, test_stack_size) } - - it 'calls the superclass constructor' do - # since we can't easily stub the superclass, test the superclass behaves as expected - stack_memory_bucket = described_class.new(test_stack_bucket_weighting, test_stack_size_range) - expect(stack_memory_bucket.range).to eq(test_stack_size_range) - end - -end diff --git a/spec/java_buildpack/jre/memory/weight_balancing_memory_heuristic_spec.rb b/spec/java_buildpack/jre/memory/weight_balancing_memory_heuristic_spec.rb deleted file mode 100644 index 5a44b5ebd3..0000000000 --- a/spec/java_buildpack/jre/memory/weight_balancing_memory_heuristic_spec.rb +++ /dev/null @@ -1,440 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'spec_helper' -require 'logging_helper' -require 'memory_limit_helper' -require 'java_buildpack/jre/memory/weight_balancing_memory_heuristic' - -describe JavaBuildpack::Jre::WeightBalancingMemoryHeuristic do - include_context 'logging_helper' - include_context 'memory_limit_helper' - - let(:heuristic) do |example| - sizes = example.metadata[:sizes] || {} - weightings = example.metadata[:weightings] || { 'heap' => 5, 'permgen' => 3, 'stack' => 1, 'native' => 1 } - valid_types = %w(heap permgen stack native) - java_opts = { 'heap' => ->(v) { "-Xmx#{v}" }, 'permgen' => ->(v) { "-XX:MaxPermSize=#{v}" }, - 'stack' => ->(v) { "-Xss#{v}" } } - - described_class.new(sizes, weightings, valid_types, java_opts) - end - - it 'fails if a memory limit is negative', - memory_limit: '-1m' do - - expect { heuristic.resolve }.to raise_error(/Invalid/) - end - - it 'fails if the heap weighting is less than 0', - with_memory_limit: '1m', - weightings: { 'heap' => -1 } do - - expect { heuristic.resolve }.to raise_error(/Invalid/) - end - - it 'fails if the permgen weighting is less than 0', - memory_limit: '1m', - weightings: { 'permgen' => -1 } do - - expect { heuristic.resolve }.to raise_error(/Invalid/) - end - - it 'fails if the stack weighting is less than 0', - memory_limit: '1m', - weightings: { 'stack' => -1 } do - - expect { heuristic.resolve }.to raise_error(/Invalid/) - end - - it 'fails if the native weighting is less than 0', - memory_limit: '1m', - weightings: { 'native' => -1 } do - - expect { heuristic.resolve }.to raise_error(/Invalid/) - end - - it 'fails if a configured weighting is invalid', - memory_limit: '1m', - weightings: { 'native' => 'x' } do - - expect { heuristic.resolve }.to raise_error(/Invalid/) - end - - it 'defaults maximum heap size and permgen size according to the configured weightings', - memory_limit: '1024m' do - - output = heuristic.resolve - - expect(output).to include('-Xmx512M') - expect(output).to include('-XX:MaxPermSize=314572K') - end - - it 'defaults the stack size even with a small memory limit', - memory_limit: '10m' do - - output = heuristic.resolve - - expect(output).to include('-Xss1M') - end - - it 'defaults permgen size according to the configured weightings when maximum heap size is specified', - memory_limit: '4096m', - sizes: { 'stack' => '1m', 'heap' => "#{(4096 * 3 / 4).to_i}m" } do - - output = heuristic.resolve - - expect(output).to include('-Xmx3G') - expect(output).to include('-XX:MaxPermSize=471859K') - end - - it 'defaults maximum heap size according to the configured weightings when maximum permgen size is specified', - memory_limit: '4096m', - sizes: { 'stack' => '1m', 'permgen' => '2g' } do - - output = heuristic.resolve - - expect(output).to include('-XX:MaxPermSize=2G') - expect(output).to include('-Xmx1398101K') - end - - it 'defaults maximum heap size and permgen size according to the configured weightings when thread stack size is ' \ - 'specified', - memory_limit: '4096m', - sizes: { 'stack' => '2m' } do - - output = heuristic.resolve - - expect(output).to include('-Xmx2G') - expect(output).to include('-XX:MaxPermSize=1258291K') - end - - it 'defaults maximum heap size and permgen size according to the configured weightings when thread stack size is ' \ - 'specified as a range', - memory_limit: '4096m', - sizes: { 'stack' => '2m..3m' } do - - output = heuristic.resolve - - expect(output).to include('-Xmx2G') - expect(output).to include('-XX:MaxPermSize=1258291K') - expect(output).to include('-Xss2M') - end - - it 'defaults maximum heap size and permgen size according to the configured weightings when thread stack size is ' \ - 'specified as a range which impinges on heap and permgen', - memory_limit: '4096m', - sizes: { 'stack' => '1g..2g' } do - - output = heuristic.resolve - - expect(output).to include('-Xmx1747626K') - expect(output).to include('-XX:MaxPermSize=1G') - expect(output).to include('-Xss1G') - end - - it 'defaults stack size to the top of its range when heap size and permgen size allow for excess memory', - memory_limit: '4096m', - sizes: { 'heap' => '50m', 'permgen' => '50m', 'stack' => '400m..500m' } do - - output = heuristic.resolve - - expect(output).to include('-Xmx50M') - expect(output).to include('-XX:MaxPermSize=50M') - expect(output).to include('-Xss500M') - end - - it 'defaults stack size strictly within its range when heap size and permgen size allow for just enough excess ' \ - 'memory', - memory_limit: '4096m', - sizes: { 'heap' => '3000m', 'permgen' => '196m', 'stack' => '400m..500m' } do - - output = heuristic.resolve - - expect(output).to include('-Xss450000K') - end - - it 'does not apply any defaults when maximum heap size, maximum permgen size, and thread stack size are specified', - memory_limit: '4096m', - sizes: { 'heap' => '1m', 'permgen' => '1m', 'stack' => '2m' } do - - output = heuristic.resolve - - expect(output).to include('-Xmx1M') - expect(output).to include('-XX:MaxPermSize=1M') - expect(output).to include('-Xss2M') - end - - it 'works with a single memory type', - memory_limit: '4096m', - weightings: { 'heap' => 5 } do - - output = heuristic.resolve - - expect(output).to include('-Xmx4G') - end - - it 'works with no memory types', - memory_limit: '4096m', - weightings: {} do - - output = heuristic.resolve - - expect(output).to eq([]) - end - - it 'issues a warning when the specified maximum memory sizes imply the total memory size may be too large', - memory_limit: '4096m', - sizes: { 'heap' => '800m', 'permgen' => '800m' } do - - output = heuristic.resolve - - expect(output).to include('-Xmx800M') - expect(output).to include('-XX:MaxPermSize=800M') - expect(log_contents).to match(/There is more than .* times more spare native memory than the default/) - end - - it 'issues a warning when the specified maximum memory sizes, including native, imply the total memory size may be ' \ - 'too large', - memory_limit: '4096m', - sizes: { 'heap' => '1m', 'permgen' => '1m', 'stack' => '2m', 'native' => '2000m' } do - - output = heuristic.resolve - - expect(output).to include('-Xmx1M') - expect(output).to include('-XX:MaxPermSize=1M') - expect(output).to include('-Xss2M') - expect(log_contents).to match(/allocated Java memory sizes total .* which is less than/) - end - - it 'allows native memory to be fixed', - memory_limit: '4096m', - sizes: { 'permgen' => '1m', 'stack' => '2m', 'native' => '10m' } do - - output = heuristic.resolve - - expect(output).to include('-Xmx3763609K') - expect(output).to include('-XX:MaxPermSize=1M') - expect(output).to include('-Xss2M') - end - - it 'allows native memory to be specified as a range with an upper bound', - memory_limit: '4096m', - sizes: { 'permgen' => '1m', 'stack' => '2m', 'native' => '10m..20m' } do - - output = heuristic.resolve - - expect(output).to include('-Xmx3753369K') - expect(output).to include('-XX:MaxPermSize=1M') - expect(output).to include('-Xss2M') - end - - it 'issues a warning when the specified maximum heap size is close to the default', - memory_limit: '4096m', - sizes: { 'heap' => '2049m' } do - - heuristic.resolve - - expect(log_contents).to match(/WARN.*is close to the default/) - end - - it 'issues a warning when the specified maximum permgen size is close to the default', - memory_limit: '4096m', - sizes: { 'permgen' => '1339m' } do - - heuristic.resolve - - expect(log_contents).to match(/WARN.*is close to the default/) - end - - it 'does not issue a warning when the specified maximum permgen size is not close to the default', - memory_limit: '1G', - sizes: { 'permgen' => '128M' } do - - heuristic.resolve - - expect(log_contents).not_to match(/WARN.*is close to the default/) - end - - it 'fails when the specified maximum memory is larger than the total memory size', - memory_limit: '4096m', - sizes: { 'heap' => '5g' } do - - expect { heuristic.resolve }.to raise_error(/exceeded/) - end - - it 'defaults nothing when the total memory size is not available', - with_memory_limit: nil do - - output = heuristic.resolve - - expect(output).to eq([]) - end - - it 'uses the calculated default when this falls within a specified range', - memory_limit: '4096m', - sizes: { 'permgen' => '1g..1250m' } do - - output = heuristic.resolve - - expect(output).to include('-XX:MaxPermSize=1258291K') - end - - it 'uses the upper bound of a range when the calculated default exceeds the upper bound of the range', - memory_limit: '5120m', - sizes: { 'permgen' => '1g..1250m' } do - - output = heuristic.resolve - - expect(output).to include('-XX:MaxPermSize=1250M') - end - - it 'uses the lower bound of a range when the calculated default is smaller than the lower bound of the range', - memory_limit: '2048m', - sizes: { 'permgen' => '1g..1250m' } do - - output = heuristic.resolve - - expect(output).to include('-XX:MaxPermSize=1G') - end - - it 'uses the calculated default when this exceeds the lower bound of a specified open range', - memory_limit: '4096m', - sizes: { 'permgen' => '1g..' } do - - output = heuristic.resolve - - expect(output).to include('-XX:MaxPermSize=1258291K') - end - - it 'uses the lower bound of an open range when the calculated default is smaller than the lower bound of the range', - memory_limit: '2048m', - sizes: { 'permgen' => '1g..' } do - - output = heuristic.resolve - - expect(output).to include('-XX:MaxPermSize=1G') - end - - it 'uses the calculated default when this falls below the upper bound of an open range', - memory_limit: '4096m', - sizes: { 'permgen' => '..1250m' } do - - output = heuristic.resolve - - expect(output).to include('-XX:MaxPermSize=1258291K') - end - - it 'uses the upper bound of an open range when the calculated default exceeds the upper bound of the range', - memory_limit: '5120m', - sizes: { 'permgen' => '..1250m' } do - - output = heuristic.resolve - - expect(output).to include('-XX:MaxPermSize=1250M') - end - - it 'allows open range with no lower or upper bound', - memory_limit: '4096m', - sizes: { 'permgen' => '..' } do - - output = heuristic.resolve - - expect(output).to include('-XX:MaxPermSize=1258291K') - end - - it 'allows a zero lower bound to be specified without units', - memory_limit: '5120m', - sizes: { 'permgen' => '0..1250m' } do - - output = heuristic.resolve - - expect(output).to include('-XX:MaxPermSize=1250M') - end - - it 'fails if a range is empty', - memory_limit: '4096m', - sizes: { 'permgen' => '2m..1m' } do - - expect { heuristic.resolve }.to raise_error(/Invalid range/) - end - - it 'defaults maximum heap size and permgen size according to the configured weightings and range lower bounds', - memory_limit: '1024m', - sizes: { 'stack' => '1m', 'permgen' => '400m..500m' } do - - output = heuristic.resolve - - expect(output).to include('-Xmx445098K') - expect(output).to include('-XX:MaxPermSize=400M') - end - - it 'defaults maximum heap size and permgen size according to the configured weightings and range upper bounds', - memory_limit: '1024m', - sizes: { 'stack' => '1m', 'permgen' => '100m..285m' } do - - output = heuristic.resolve - - expect(output).to include('-Xmx543232K') - expect(output).to include('-XX:MaxPermSize=285M') - end - - it 'does not apply any defaults when maximum heap size, maximum permgen size, and thread stack size are specified ' \ - 'as tight ranges', - memory_limit: '4096m', - sizes: { 'heap' => '1m..1024k', 'permgen' => '1024k..1m', 'stack' => '2m..2m' } do - - output = heuristic.resolve - - expect(output).to include('-Xmx1M') - expect(output).to include('-XX:MaxPermSize=1M') - expect(output).to include('-Xss2M') - end - - it 'allows native memory to be specified with no upper bound', - memory_limit: '5120m', - sizes: { 'stack' => '1m..1m', 'native' => '4000m..' } do - - output = heuristic.resolve - - expect(output).to include('-Xmx380M') - expect(output).to include('-XX:MaxPermSize=228M') - expect(output).to include('-Xss1M') - end - - it 'respects lower bounds when there is no memory limit', - memory_limit: nil, - sizes: { 'heap' => '30m..', 'permgen' => '10m', 'stack' => '1m..1m', 'native' => '10m..' } do - - output = heuristic.resolve - - expect(output).to include('-Xmx30M') - expect(output).to include('-XX:MaxPermSize=10M') - expect(output).to include('-Xss1M') - end - - it 'works with other weightings', - memory_limit: '256m', - weightings: { 'heap' => 75, 'permgen' => 10, 'stack' => 5, 'native' => 10 } do - - output = heuristic.resolve - - expect(output).to include('-Xmx192M') - expect(output).to include('-XX:MaxPermSize=26214K') - expect(output).to include('-Xss1M') - end - -end diff --git a/spec/java_buildpack/jre/open_jdk_jre_spec.rb b/spec/java_buildpack/jre/open_jdk_jre_spec.rb deleted file mode 100644 index 62208cbefc..0000000000 --- a/spec/java_buildpack/jre/open_jdk_jre_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'spec_helper' -require 'component_helper' -require 'java_buildpack/component/mutable_java_home' -require 'java_buildpack/jre/open_jdk_jre' -require 'java_buildpack/jre/memory/weight_balancing_memory_heuristic' - -describe JavaBuildpack::Jre::OpenJdkJRE do - include_context 'component_helper' - - let(:java_home) { JavaBuildpack::Component::MutableJavaHome.new } - - let(:memory_heuristic) { double('MemoryHeuristic', resolve: %w(opt-1 opt-2)) } - - before do - allow(JavaBuildpack::Jre::WeightBalancingMemoryHeuristic).to receive(:new).and_return(memory_heuristic) - end - - it 'detects with id of openjdk_jre-' do - expect(component.detect).to eq("open-jdk-jre=#{version}") - end - - it 'extracts Java from a GZipped TAR', - cache_fixture: 'stub-java.tar.gz' do - - component.detect - component.compile - - expect(sandbox + 'bin/java').to exist - end - - it 'adds the JAVA_HOME to java_home' do - component - - expect(java_home.root).to eq(sandbox) - end - - it 'adds memory options to java_opts' do - component.detect - component.release - - expect(java_opts).to include('opt-1') - expect(java_opts).to include('opt-2') - end - - it 'adds OnOutOfMemoryError to java_opts' do - component.detect - component.release - - expect(java_opts).to include('-XX:OnOutOfMemoryError=$PWD/.java-buildpack/open_jdk_jre/bin/killjava.sh') - end - - it 'places the killjava script (with appropriately substituted content) in the diagnostics directory', - cache_fixture: 'stub-java.tar.gz' do - - component.detect - component.compile - - expect(sandbox + 'bin/killjava.sh').to exist - end - - it 'adds java.io.tmpdir to java_opts' do - component.detect - component.release - - expect(java_opts).to include('-Djava.io.tmpdir=$TMPDIR') - end - -end diff --git a/spec/java_buildpack/jre/open_jdk_like_jre_spec.rb b/spec/java_buildpack/jre/open_jdk_like_jre_spec.rb new file mode 100644 index 0000000000..df43a4dc2b --- /dev/null +++ b/spec/java_buildpack/jre/open_jdk_like_jre_spec.rb @@ -0,0 +1,119 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/component/mutable_java_home' +require 'java_buildpack/jre/open_jdk_like_jre' +require 'java_buildpack/util/tokenized_version' +require 'resolv' + +describe JavaBuildpack::Jre::OpenJDKLikeJre do + include_context 'with component help' + + let(:java_home) { JavaBuildpack::Component::MutableJavaHome.new } + + it 'detects with id of openjdk_like_jre-' do + expect(component.detect).to eq("open-jdk-like-jre=#{version}") + end + + it 'extracts Java from a GZipped TAR', + cache_fixture: 'stub-java.tar.gz' do + + component.detect + component.compile + + expect(sandbox + 'bin/java').to exist + end + + it 'adds the JAVA_HOME to java_home' do + component + + expect(java_home.root).to eq(sandbox) + end + + it 'adds java.io.tmpdir to java_opts' do + component.detect + component.release + + expect(java_opts).to include('-Djava.io.tmpdir=$TMPDIR') + end + + it 'does not disable dns caching if no BOSH DNS', + cache_fixture: 'stub-java.tar.gz' do + + allow_any_instance_of(Resolv::DNS::Config).to receive(:nameserver_port).and_return([['8.8.8.8', 53]]) + + component.detect + component.compile + + expect(networking.networkaddress_cache_ttl).not_to be_truthy + expect(networking.networkaddress_cache_negative_ttl).not_to be_truthy + end + + it 'disables dns caching if BOSH DNS', + cache_fixture: 'stub-java.tar.gz' do + + allow_any_instance_of(Resolv::DNS::Config).to receive(:nameserver_port).and_return([['169.254.0.2', 53]]) + + component.detect + component.compile + + expect(networking.networkaddress_cache_ttl).to eq 0 + expect(networking.networkaddress_cache_negative_ttl).to eq 0 + end + + it 'does not set active processor count before Java 1.8.0_191', + cache_fixture: 'stub-java.tar.gz' do + + component.detect + component.release + + expect(java_opts).not_to include('-XX:ActiveProcessorCount=$(nproc)') + end + + context 'with Java 1.8.0_191' do + + let(:version) { '1.8.0_191' } + + it 'sets active processor count at Java 1.8.0_191', + cache_fixture: 'stub-java.tar.gz' do + + component.detect + component.release + + expect(java_opts).to include('-XX:ActiveProcessorCount=$(nproc)') + end + + end + + context 'with Java 11.0.0' do + + let(:version) { '11.0.0' } + + it 'sets active processor count after Java 1.8.0_191', + cache_fixture: 'stub-java.tar.gz' do + + component.detect + component.release + + expect(java_opts).to include('-XX:ActiveProcessorCount=$(nproc)') + end + + end + +end diff --git a/spec/java_buildpack/jre/open_jdk_like_memory_calculator_spec.rb b/spec/java_buildpack/jre/open_jdk_like_memory_calculator_spec.rb new file mode 100644 index 0000000000..8c4a68a33e --- /dev/null +++ b/spec/java_buildpack/jre/open_jdk_like_memory_calculator_spec.rb @@ -0,0 +1,150 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/component/mutable_java_home' +require 'java_buildpack/jre/open_jdk_like_memory_calculator' +require 'java_buildpack/util/qualify_path' + +describe JavaBuildpack::Jre::OpenJDKLikeMemoryCalculator do + include_context 'with component help' + include JavaBuildpack::Util + + let(:configuration) { { 'stack_threads' => '200' } } + + let(:java_home) do + java_home = JavaBuildpack::Component::MutableJavaHome.new + java_home.version = version8 + return java_home + end + + let(:version8) { JavaBuildpack::Util::TokenizedVersion.new('1.8.0_162') } + + let(:version9) { JavaBuildpack::Util::TokenizedVersion.new('9.0.4_11') } + + it 'copies executable to bin directory', + cache_fixture: 'stub-memory-calculator.tar.gz' do + + allow(component).to receive(:show_settings) + + component.compile + + expect(sandbox + "bin/java-buildpack-memory-calculator-#{version}").to exist + end + + it 'chmods executable to 0755', + cache_fixture: 'stub-memory-calculator.tar.gz' do + + allow(component).to receive(:show_settings) + + component.compile + + expect(File.stat(sandbox + "bin/java-buildpack-memory-calculator-#{version}").mode).to eq(0o100755) + end + + context do + + let(:version) { '3.0.0' } + + it 'copies executable to bin directory from a compressed archive', + cache_fixture: 'stub-memory-calculator.tar.gz' do + + allow(component).to receive(:show_settings) + + component.compile + + expect(sandbox + "bin/java-buildpack-memory-calculator-#{version}").to exist + end + + it 'chmods executable to 0755 from a compressed archive', + cache_fixture: 'stub-memory-calculator.tar.gz' do + + allow(component).to receive(:show_settings) + + component.compile + + expect(File.stat(sandbox + "bin/java-buildpack-memory-calculator-#{version}").mode).to eq(0o100755) + end + + end + + it 'creates memory calculation command', + app_fixture: 'jre_memory_calculator_application' do + + java_home.version = version8 + + command = component.memory_calculation_command + + expect(command).to eq('CALCULATED_MEMORY=$($PWD/.java-buildpack/open_jdk_like_memory_calculator/bin/' \ + 'java-buildpack-memory-calculator-0.0.0 -totMemory=$MEMORY_LIMIT -loadedClasses=2 ' \ + '-poolType=metaspace -stackThreads=200 -vmOptions="$JAVA_OPTS") && echo JVM Memory ' \ + 'Configuration: $CALCULATED_MEMORY && JAVA_OPTS="$JAVA_OPTS $CALCULATED_MEMORY"') + end + + it 'does not throw an error when a directory ends in .jar', + app_fixture: 'jre_memory_calculator_jar_directory', + cache_fixture: 'stub-memory-calculator.tar.gz' do + + expect_any_instance_of(described_class).not_to receive(:`).with(start_with("unzip -l #{app_dir + 'directory.jar'}")) + + component.compile + end + + it 'adds MALLOC_ARENA_MAX to environment' do + component.release + + expect(environment_variables).to include('MALLOC_ARENA_MAX=2') + end + + context 'with headroom' do + + let(:configuration) { { 'headroom' => '11', 'stack_threads' => '200' } } + + it 'creates memory calculation command with headroom', + app_fixture: 'jre_memory_calculator_application' do + + java_home.version = version8 + + command = component.memory_calculation_command + + expect(command).to eq('CALCULATED_MEMORY=$($PWD/.java-buildpack/open_jdk_like_memory_calculator/bin/' \ + 'java-buildpack-memory-calculator-0.0.0 -totMemory=$MEMORY_LIMIT -headRoom=11 ' \ + '-loadedClasses=2 -poolType=metaspace -stackThreads=200 -vmOptions="$JAVA_OPTS") && echo ' \ + 'JVM Memory Configuration: $CALCULATED_MEMORY && JAVA_OPTS="$JAVA_OPTS $CALCULATED_MEMORY"') + end + + end + + context 'when java 9' do + + it 'creates memory calculation command', + app_fixture: 'jre_memory_calculator_application' do + + java_home.version = version9 + + command = component.memory_calculation_command + + expect(command).to eq('CALCULATED_MEMORY=$($PWD/.java-buildpack/open_jdk_like_memory_calculator/bin/' \ + 'java-buildpack-memory-calculator-0.0.0 -totMemory=$MEMORY_LIMIT -loadedClasses=14777 ' \ + '-poolType=metaspace -stackThreads=200 -vmOptions="$JAVA_OPTS") && echo JVM Memory ' \ + 'Configuration: $CALCULATED_MEMORY && JAVA_OPTS="$JAVA_OPTS $CALCULATED_MEMORY"') + end + + end + +end diff --git a/spec/java_buildpack/jre/open_jdk_like_security_providers_spec.rb b/spec/java_buildpack/jre/open_jdk_like_security_providers_spec.rb new file mode 100644 index 0000000000..21ac204076 --- /dev/null +++ b/spec/java_buildpack/jre/open_jdk_like_security_providers_spec.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'fileutils' +require 'java_buildpack/jre/open_jdk_like_security_providers' + +describe JavaBuildpack::Jre::OpenJDKLikeSecurityProviders do + include_context 'with component help' + + it 'does not add extension directories with no JRE default' do + component.release + + expect(extension_directories).to contain_exactly(sandbox + 'test-extension-directory-1', + sandbox + 'test-extension-directory-2') + end + + it 'adds security providers' do + FileUtils.mkdir_p(java_home.root + 'lib/security') + FileUtils.cp 'spec/fixtures/java.security', java_home.root + 'lib/security' + + component.compile + + expect(security_providers).to eq %w[test-security-provider-1 + test-security-provider-2 + sun.security.provider.Sun + sun.security.rsa.SunRsaSign sun.security.ec.SunEC + com.sun.net.ssl.internal.ssl.Provider + com.sun.crypto.provider.SunJCE + sun.security.jgss.SunProvider + com.sun.security.sasl.Provider + org.jcp.xml.dsig.internal.dom.XMLDSigRI + sun.security.smartcardio.SunPCSC + apple.security.AppleProvider] + end + + it 'adds extension directories with JRE default to system properties' do + FileUtils.mkdir_p(java_home.root + 'lib/security/java.security') + + component.release + + expect(extension_directories).to include(java_home.root + 'lib/ext') + end + + it 'adds extension directories with Server JRE default to system properties' do + FileUtils.mkdir_p(java_home.root + 'jre/lib/security/java.security') + + component.release + + expect(extension_directories).to include(java_home.root + 'jre/lib/ext') + end + + context do + + let(:java_home_delegate) do + delegate = JavaBuildpack::Component::MutableJavaHome.new + delegate.root = app_dir + '.test-java-home' + delegate.version = JavaBuildpack::Util::TokenizedVersion.new('9.0.0') + + delegate + end + + it 'does not add extension directory for Java 9' do + extension_directories.clear + + component.release + + expect(extension_directories).to be_empty + end + + end + +end diff --git a/spec/java_buildpack/jre/open_jdk_like_spec.rb b/spec/java_buildpack/jre/open_jdk_like_spec.rb new file mode 100644 index 0000000000..e8cebf0447 --- /dev/null +++ b/spec/java_buildpack/jre/open_jdk_like_spec.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'fileutils' +require 'java_buildpack/component/mutable_java_home' +require 'java_buildpack/jre/open_jdk_like' +require 'java_buildpack/jre/open_jdk_like_jre' +require 'java_buildpack/jre/open_jdk_like_memory_calculator' +require 'java_buildpack/jre/open_jdk_like_security_providers' + +describe JavaBuildpack::Jre::OpenJDKLike do + include_context 'with component help' + + let(:component) { StubOpenJDKLike.new context } + + let(:java_home) { JavaBuildpack::Component::MutableJavaHome.new } + + let(:version7) { JavaBuildpack::Util::TokenizedVersion.new('1.7.0_+') } + + let(:version8) { JavaBuildpack::Util::TokenizedVersion.new('1.8.0_+') } + + let(:configuration) do + { 'jre' => jre_configuration, + 'memory_calculator' => memory_calculator_configuration, + 'jvmkill_agent' => jvmkill_agent_configuration } + end + + let(:jre_configuration) { instance_double('jre_configuration') } + + let(:jvmkill_agent_configuration) { {} } + + let(:memory_calculator_configuration) { { 'stack_threads' => '200' } } + + it 'always supports' do + expect(component).to be_supports + end + + it 'creates submodules' do + allow_any_instance_of(StubOpenJDKLike).to receive(:supports?).and_return false + + allow(JavaBuildpack::Jre::JvmkillAgent) + .to receive(:new).with(sub_configuration_context(jvmkill_agent_configuration)) + allow(JavaBuildpack::Jre::OpenJDKLikeJre) + .to receive(:new).with(sub_configuration_context(jre_configuration).merge(component_name: 'Stub Open JDK Like')) + allow(JavaBuildpack::Jre::OpenJDKLikeMemoryCalculator) + .to receive(:new).with(sub_configuration_context(memory_calculator_configuration)) + allow(JavaBuildpack::Jre::OpenJDKLikeSecurityProviders) + .to receive(:new).with(context) + + component.sub_components context + end + + it 'returns command for Java 7' do + java_home.version = version7 + expect(component.command).to eq('CALCULATED_MEMORY=$($PWD/.java-buildpack/open_jdk_like/bin/' \ + 'java-buildpack-memory-calculator-0.0.0 -totMemory=$MEMORY_LIMIT' \ + ' -loadedClasses=0 -poolType=permgen -stackThreads=200 -vmOptions="$JAVA_OPTS")' \ + ' && echo JVM Memory Configuration: $CALCULATED_MEMORY && ' \ + 'JAVA_OPTS="$JAVA_OPTS $CALCULATED_MEMORY"') + + end + + it 'returns command for Java 8' do + java_home.version = version8 + expect(component.command).to eq('CALCULATED_MEMORY=$($PWD/.java-buildpack/open_jdk_like/bin/' \ + 'java-buildpack-memory-calculator-0.0.0 -totMemory=$MEMORY_LIMIT' \ + ' -loadedClasses=0 -poolType=metaspace -stackThreads=200 -vmOptions="$JAVA_OPTS")' \ + ' && echo JVM Memory Configuration: $CALCULATED_MEMORY && ' \ + 'JAVA_OPTS="$JAVA_OPTS $CALCULATED_MEMORY"') + + end + +end + +class StubOpenJDKLike < JavaBuildpack::Jre::OpenJDKLike + + public :command, :sub_components + + # rubocop:disable Lint/UselessMethodDefinition + def supports? + super + end + # rubocop:enable Lint/UselessMethodDefinition +end + +def sub_configuration_context(configuration) + c = context.clone + c[:configuration] = configuration + c +end diff --git a/spec/java_buildpack/jre/oracle_jre_spec.rb b/spec/java_buildpack/jre/oracle_jre_spec.rb deleted file mode 100644 index efec872043..0000000000 --- a/spec/java_buildpack/jre/oracle_jre_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -# Encoding: utf-8 -# Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'spec_helper' -require 'component_helper' -require 'java_buildpack/component/mutable_java_home' -require 'java_buildpack/jre/oracle_jre' -require 'java_buildpack/jre/memory/weight_balancing_memory_heuristic' - -describe JavaBuildpack::Jre::OracleJRE do - include_context 'component_helper' - - let(:java_home) { JavaBuildpack::Component::MutableJavaHome.new } - - let(:memory_heuristic) { double('MemoryHeuristic', resolve: %w(opt-1 opt-2)) } - - before do - allow(JavaBuildpack::Jre::WeightBalancingMemoryHeuristic).to receive(:new).and_return(memory_heuristic) - end - - it 'detects with id of oracle-jre-' do - expect(component.detect).to eq("oracle-jre=#{version}") - end - - it 'extracts Java from a GZipped TAR', - cache_fixture: 'stub-java.tar.gz' do - - component.detect - component.compile - - expect(sandbox + 'bin/java').to exist - end - - it 'adds the JAVA_HOME to java_home' do - component - - expect(java_home.root).to eq(sandbox) - end - - it 'adds memory options to java_opts' do - component.detect - component.release - - expect(java_opts).to include('opt-1') - expect(java_opts).to include('opt-2') - end - - it 'adds OnOutOfMemoryError to java_opts' do - component.detect - component.release - - expect(java_opts).to include('-XX:OnOutOfMemoryError=$PWD/.java-buildpack/oracle_jre/bin/killjava.sh') - end - - it 'places the killjava script (with appropriately substituted content) in the diagnostics directory', - cache_fixture: 'stub-java.tar.gz' do - - component.detect - component.compile - - expect(sandbox + 'bin/killjava.sh').to exist - end - - it 'adds java.io.tmpdir to java_opts' do - component.detect - component.release - - expect(java_opts).to include('-Djava.io.tmpdir=$TMPDIR') - end - -end diff --git a/spec/java_buildpack/logging/delegating_logger_spec.rb b/spec/java_buildpack/logging/delegating_logger_spec.rb index 685fbaf662..d26fc2ace9 100644 --- a/spec/java_buildpack/logging/delegating_logger_spec.rb +++ b/spec/java_buildpack/logging/delegating_logger_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,42 +21,42 @@ describe JavaBuildpack::Logging::DelegatingLogger do - let(:block) { ->() { 'test-message' } } - let(:delegate1) { double('delegate1') } - let(:delegate2) { double('delegate2') } + let(:block) { -> { 'test-message' } } + let(:delegate1) { instance_double('delegate1') } + let(:delegate2) { instance_double('delegate2') } let(:delegating_logger) { described_class.new('test-klass', [delegate1, delegate2]) } it 'delegates FATAL calls' do - expect(delegate1).to receive(:add).with(Logger::FATAL, nil, 'test-klass') - expect(delegate2).to receive(:add).with(Logger::FATAL, nil, 'test-klass') + allow(delegate1).to receive(:add).with(Logger::FATAL, nil, 'test-klass') + allow(delegate2).to receive(:add).with(Logger::FATAL, nil, 'test-klass') delegating_logger.fatal end it 'delegates ERROR calls' do - expect(delegate1).to receive(:add).with(Logger::ERROR, nil, 'test-klass') - expect(delegate2).to receive(:add).with(Logger::ERROR, nil, 'test-klass') + allow(delegate1).to receive(:add).with(Logger::ERROR, nil, 'test-klass') + allow(delegate2).to receive(:add).with(Logger::ERROR, nil, 'test-klass') delegating_logger.error end it 'delegates WARN calls' do - expect(delegate1).to receive(:add).with(Logger::WARN, nil, 'test-klass') - expect(delegate2).to receive(:add).with(Logger::WARN, nil, 'test-klass') + allow(delegate1).to receive(:add).with(Logger::WARN, nil, 'test-klass') + allow(delegate2).to receive(:add).with(Logger::WARN, nil, 'test-klass') delegating_logger.warn end it 'delegates INFO calls' do - expect(delegate1).to receive(:add).with(Logger::INFO, nil, 'test-klass') - expect(delegate2).to receive(:add).with(Logger::INFO, nil, 'test-klass') + allow(delegate1).to receive(:add).with(Logger::INFO, nil, 'test-klass') + allow(delegate2).to receive(:add).with(Logger::INFO, nil, 'test-klass') delegating_logger.info end it 'delegates DEBUG calls' do - expect(delegate1).to receive(:add).with(Logger::DEBUG, nil, 'test-klass') - expect(delegate2).to receive(:add).with(Logger::DEBUG, nil, 'test-klass') + allow(delegate1).to receive(:add).with(Logger::DEBUG, nil, 'test-klass') + allow(delegate2).to receive(:add).with(Logger::DEBUG, nil, 'test-klass') delegating_logger.debug end diff --git a/spec/java_buildpack/logging/logger_factory_spec.rb b/spec/java_buildpack/logging/logger_factory_spec.rb index bb8e513b48..4df7ebeeac 100644 --- a/spec/java_buildpack/logging/logger_factory_spec.rb +++ b/spec/java_buildpack/logging/logger_factory_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,17 +22,17 @@ require 'java_buildpack/util/configuration_utils' describe JavaBuildpack::Logging::LoggerFactory do - include_context 'console_helper' - include_context 'logging_helper' + include_context 'with console help' + include_context 'with logging help' let(:logger) { described_class.instance.get_logger String } it 'maintains backwards compatibility' do - expect(described_class.get_logger String).to be + expect(described_class.get_logger(String)).to be_truthy end it 'logs all levels to file', - log_level: 'FATAL' do + :enable_log_file, log_level: 'FATAL' do trigger @@ -183,8 +184,8 @@ context do before do - allow(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('logging', false) - .and_return('default_log_level' => 'DEBUG') + allow(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('logging', true, false) + .and_return('default_log_level' => 'DEBUG') described_class.instance.setup app_dir end @@ -208,7 +209,7 @@ context do before do - allow(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('logging', false).and_return({}) + allow(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('logging', true, false).and_return({}) described_class.instance.setup app_dir end diff --git a/spec/java_buildpack/repository/configured_item_spec.rb b/spec/java_buildpack/repository/configured_item_spec.rb index 33605db8ee..dfe9655b07 100644 --- a/spec/java_buildpack/repository/configured_item_spec.rb +++ b/spec/java_buildpack/repository/configured_item_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,7 +22,7 @@ describe JavaBuildpack::Repository::ConfiguredItem do - let(:repository_index) { double('RepositoryIndex', find_item: [resolved_version, resolved_uri]) } + let(:repository_index) { instance_double('RepositoryIndex', find_item: [resolved_version, resolved_uri]) } let(:resolved_uri) { 'resolved-uri' } @@ -32,15 +33,14 @@ end it 'raises an error if no repository root is specified' do - expect { described_class.find_item('Test', {}) }.to raise_error + expect { described_class.find_item('Test', {}) }.to raise_error(/A repository root must be specified/) end it 'resolves a system.properties version if specified' do details = described_class.find_item('Test', - 'repository_root' => 'test-repository-root', + 'repository_root' => 'test-repository-root', 'java.runtime.version' => 'test-java-runtime-version', - 'version' => '1.7.0' - ) + 'version' => '1.7.0') expect(details[0]).to eq(resolved_version) expect(details[1]).to eq(resolved_uri) @@ -49,8 +49,7 @@ it 'resolves a configuration version if specified' do details = described_class.find_item('Test', 'repository_root' => 'test-repository-root', - 'version' => '1.7.0' - ) + 'version' => '1.7.0') expect(details[0]).to eq(resolved_version) expect(details[1]).to eq(resolved_uri) @@ -59,16 +58,14 @@ it 'drives the version validator block if supplied' do described_class.find_item('Test', 'repository_root' => 'test-repository-root', - 'version' => '1.7.0' - ) do |version| + 'version' => '1.7.0') do |version| expect(version).to eq(JavaBuildpack::Util::TokenizedVersion.new('1.7.0')) end end it 'resolves nil if no version is specified' do details = described_class.find_item('Test', - 'repository_root' => 'test-repository-root' - ) + 'repository_root' => 'test-repository-root') expect(details[0]).to eq(resolved_version) expect(details[1]).to eq(resolved_uri) diff --git a/spec/java_buildpack/repository/repository_index_spec.rb b/spec/java_buildpack/repository/repository_index_spec.rb index 126ca2376b..170380c379 100644 --- a/spec/java_buildpack/repository/repository_index_spec.rb +++ b/spec/java_buildpack/repository/repository_index_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -25,10 +26,10 @@ require 'java_buildpack/util/tokenized_version' describe JavaBuildpack::Repository::RepositoryIndex do - include_context 'application_helper' - include_context 'logging_helper' + include_context 'with application help' + include_context 'with logging help' - let(:application_cache) { double('ApplicationCache') } + let(:application_cache) { instance_double('ApplicationCache') } before do allow(JavaBuildpack::Util::Cache::DownloadCache).to receive(:new).and_return(application_cache) @@ -36,47 +37,48 @@ it 'loads index' do allow(application_cache).to receive(:get).with(%r{/test-uri/index\.yml}) - .and_yield(Pathname.new('spec/fixtures/test-index.yml').open) - allow(JavaBuildpack::Repository::VersionResolver).to receive(:resolve).with('test-version', %w(resolved-version)) - .and_return('resolved-version') + .and_yield(Pathname.new('spec/fixtures/test-index.yml').open) + allow(JavaBuildpack::Repository::VersionResolver).to receive(:resolve).with('test-version', %w[resolved-version]) + .and_return('resolved-version') repository_index = described_class.new('{platform}/{architecture}/test-uri') - expect(repository_index.find_item('test-version')).to eq(%w(resolved-version resolved-uri)) + expect(repository_index.find_item('test-version')).to eq(%w[resolved-version resolved-uri]) end it 'copes with trailing slash in repository URI' do allow(application_cache).to receive(:get).with(%r{/test-uri/index\.yml}) - .and_yield(Pathname.new('spec/fixtures/test-index.yml').open) - allow(JavaBuildpack::Repository::VersionResolver).to receive(:resolve).with('test-version', %w(resolved-version)) - .and_return('resolved-version') + .and_yield(Pathname.new('spec/fixtures/test-index.yml').open) + allow(JavaBuildpack::Repository::VersionResolver).to receive(:resolve).with('test-version', %w[resolved-version]) + .and_return('resolved-version') repository_index = described_class.new('{platform}/{architecture}/test-uri/') - expect(repository_index.find_item('test-version')).to eq(%w(resolved-version resolved-uri)) + expect(repository_index.find_item('test-version')).to eq(%w[resolved-version resolved-uri]) end it 'substitutes the default repository root' do allow(JavaBuildpack::Util::ConfigurationUtils) .to receive(:load).with('repository').and_return('default_repository_root' => 'http://default-repository-root/') - expect(application_cache).to receive(:get).with('http://default-repository-root/test-uri/index.yml') - .and_yield(Pathname.new('spec/fixtures/test-index.yml').open) + allow(application_cache).to receive(:get).with('http://default-repository-root/test-uri/index.yml') + .and_yield(Pathname.new('spec/fixtures/test-index.yml').open) described_class.new('{default.repository.root}/test-uri') end it 'handles Centos' do allow(Pathname).to receive(:new).and_call_original - redhat_release = double('redhat-release') + redhat_release = instance_double('redhat-release') allow(Pathname).to receive(:new).with('/etc/redhat-release').and_return(redhat_release) allow_any_instance_of(described_class).to receive(:`).with('uname -s').and_return('Linux') allow_any_instance_of(described_class).to receive(:`).with('uname -m').and_return('x86_64') - allow_any_instance_of(described_class).to receive(:`).with('which lsb_release 2> /dev/null').and_return('') + allow_any_instance_of(described_class).to receive(:`) + .with("cat /etc/os-release | grep '^ID=' | cut -d'=' -f 2").and_return('') allow(redhat_release).to receive(:exist?).and_return(true) allow(redhat_release).to receive(:read).and_return('CentOS release 6.4 (Final)') allow(application_cache).to receive(:get).with('centos6/x86_64/test-uri/index.yml') - .and_yield(Pathname.new('spec/fixtures/test-index.yml').open) + .and_yield(Pathname.new('spec/fixtures/test-index.yml').open) described_class.new('{platform}/{architecture}/test-uri') @@ -84,10 +86,14 @@ end it 'handles Mac OS X' do + allow(Pathname).to receive(:new).and_call_original + non_redhat = instance_double('non-redhat', exist?: false) + allow(Pathname).to receive(:new).with('/etc/redhat-release').and_return(non_redhat) + allow_any_instance_of(described_class).to receive(:`).with('uname -s').and_return('Darwin') allow_any_instance_of(described_class).to receive(:`).with('uname -m').and_return('x86_64') allow(application_cache).to receive(:get).with('mountainlion/x86_64/test-uri/index.yml') - .and_yield(Pathname.new('spec/fixtures/test-index.yml').open) + .and_yield(Pathname.new('spec/fixtures/test-index.yml').open) described_class.new('{platform}/{architecture}/test-uri') @@ -95,13 +101,18 @@ end it 'handles Ubuntu' do + allow(Pathname).to receive(:new).and_call_original + non_redhat = instance_double('non-redhat', exist?: false) + allow(Pathname).to receive(:new).with('/etc/redhat-release').and_return(non_redhat) + allow_any_instance_of(described_class).to receive(:`).with('uname -s').and_return('Linux') allow_any_instance_of(described_class).to receive(:`).with('uname -m').and_return('x86_64') - allow_any_instance_of(described_class).to receive(:`).with('which lsb_release 2> /dev/null') - .and_return('/usr/bin/lsb_release') - allow_any_instance_of(described_class).to receive(:`).with('lsb_release -cs').and_return('precise') + allow_any_instance_of(described_class).to receive(:`).with("cat /etc/os-release | grep '^ID=' | cut -d'=' -f 2") + .and_return('ubuntu') + allow_any_instance_of(described_class).to receive(:`) + .with("cat /etc/os-release | grep '^VERSION_CODENAME=' | cut -d'=' -f 2").and_return('precise') allow(application_cache).to receive(:get).with('precise/x86_64/test-uri/index.yml') - .and_yield(Pathname.new('spec/fixtures/test-index.yml').open) + .and_yield(Pathname.new('spec/fixtures/test-index.yml').open) described_class.new('{platform}/{architecture}/test-uri') @@ -109,9 +120,14 @@ end it 'handles unknown OS' do + allow(Pathname).to receive(:new).and_call_original + non_redhat = instance_double('non-redhat', exist?: false) + allow(Pathname).to receive(:new).with('/etc/redhat-release').and_return(non_redhat) + allow_any_instance_of(File).to receive(:exists?).with('/etc/redhat-release').and_return(false) allow_any_instance_of(described_class).to receive(:`).with('uname -s').and_return('Linux') - allow_any_instance_of(described_class).to receive(:`).with('which lsb_release 2> /dev/null').and_return('') + allow_any_instance_of(described_class).to receive(:`) + .with("cat /etc/os-release | grep '^ID=' | cut -d'=' -f 2").and_return('') expect { described_class.new('{platform}/{architecture}/test-uri') } .to raise_error('Unable to determine platform') diff --git a/spec/java_buildpack/repository/version_resolver_spec.rb b/spec/java_buildpack/repository/version_resolver_spec.rb index a56eed118b..3193456fcc 100644 --- a/spec/java_buildpack/repository/version_resolver_spec.rb +++ b/spec/java_buildpack/repository/version_resolver_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,13 +15,17 @@ # See the License for the specific language governing permissions and # limitations under the License. +require 'logging_helper' require 'spec_helper' require 'java_buildpack/repository/version_resolver' require 'java_buildpack/util/tokenized_version' describe JavaBuildpack::Repository::VersionResolver do + include_context 'with logging help' - let(:versions) { %w(1.6.0_26 1.6.0_27 1.6.1_14 1.7.0_19 1.7.0_21 1.8.0_M-7 1.8.0_05 2.0.0) } + let(:versions) do + %w[1.6.0_26 1.6.0_27 1.6.0_112 1.6.0_102 1.6.0_45RELEASE 1.6.1_14 1.7.0_19 1.7.0_21 1.8.0_M-7 1.8.0_05 2.0.0 2.0.0a] + end it 'resolves the default version if no candidate is supplied' do expect(described_class.resolve(nil, versions)).to eq(tokenized_version('2.0.0')) @@ -39,10 +44,14 @@ end it 'resolves a wildcard qualifier' do - expect(described_class.resolve(tokenized_version('1.6.0_+'), versions)).to eq(tokenized_version('1.6.0_27')) + expect(described_class.resolve(tokenized_version('1.6.0_+'), versions)).to eq(tokenized_version('1.6.0_112')) expect(described_class.resolve(tokenized_version('1.8.0_+'), versions)).to eq(tokenized_version('1.8.0_05')) end + it 'resolves a partial-wildcard qualifier' do + expect(described_class.resolve(tokenized_version('1.7.0_1+'), versions)).to eq(tokenized_version('1.7.0_19')) + end + it 'resolves a non-wildcard version' do expect(described_class.resolve(tokenized_version('1.6.0_26'), versions)).to eq(tokenized_version('1.6.0_26')) expect(described_class.resolve(tokenized_version('2.0.0'), versions)).to eq(tokenized_version('2.0.0')) @@ -56,8 +65,20 @@ expect(described_class.resolve(tokenized_version('2.1.0'), versions)).to be_nil end - it 'raises an exception when a wildcard is specified in the [] collection' do - expect { described_class.resolve(tokenized_version('1.6.0_25'), %w(+)) }.to raise_error(/Invalid/) + it 'ignores illegal versions' do + expect(described_class.resolve(tokenized_version('2.0.+'), versions)).to eq(tokenized_version('2.0.0')) + end + + it 'picks an exact match over a partial match' do + versions = %w[3.1.1 3.1.1_BETA 3.1.1_BETA.2 3.1.2 3.2.0] + expect(described_class.resolve(tokenized_version('3.1.1'), versions)).to eq(tokenized_version('3.1.1')) + expect(described_class.resolve(tokenized_version('3.1.1_BETA'), versions)).to eq(tokenized_version('3.1.1_BETA')) + end + + it 'picks the latest including qualifiers' do + versions = %w[3.1.1 3.1.1_BETA 3.1.1_BETA.2 3.1.2 3.2.0] + expect(described_class.resolve(tokenized_version('3.1.1_+'), versions)).to eq(tokenized_version('3.1.1_BETA.2')) + expect(described_class.resolve(tokenized_version('3.1.1_BE+'), versions)).to eq(tokenized_version('3.1.1_BETA.2')) end def tokenized_version(s) diff --git a/spec/java_buildpack/util/cache/application_cache_spec.rb b/spec/java_buildpack/util/cache/application_cache_spec.rb index 7f3ad8494e..651f77d282 100644 --- a/spec/java_buildpack/util/cache/application_cache_spec.rb +++ b/spec/java_buildpack/util/cache/application_cache_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,20 +22,21 @@ require 'java_buildpack/util/cache/application_cache' describe JavaBuildpack::Util::Cache::ApplicationCache do - include_context 'application_helper' - include_context 'internet_availability_helper' - include_context 'logging_helper' + include_context 'with application help' + include_context 'with internet availability help' + include_context 'with logging help' previous_arg_value = ARGV[1] before do ARGV[1] = nil - stub_request(:get, 'http://foo-uri/').with(headers: { 'Accept' => '*/*', 'User-Agent' => 'Ruby' }) + stub_request(:get, 'http://foo-uri/') + .with(headers: { 'Accept' => '*/*', 'User-Agent' => 'Ruby' }) .to_return(status: 200, body: 'foo-cached', headers: { Etag: 'foo-etag', 'Last-Modified' => 'foo-last-modified' }) stub_request(:head, 'http://foo-uri/') - .with(headers: { 'Accept' => '*/*', 'If-Modified-Since' => 'foo-last-modified', 'If-None-Match' => 'foo-etag', + .with(headers: { 'Accept' => '*/*', 'If-Modified-Since' => 'foo-last-modified', 'If-None-Match' => 'foo-etag', 'User-Agent' => 'Ruby' }) .to_return(status: 304, body: '', headers: {}) end @@ -44,7 +46,7 @@ end it 'raises an error if ARGV[1] is not defined' do - expect { described_class.new }.to raise_error + expect { described_class.new }.to raise_error RuntimeError end it 'uses ARGV[1] directory' do diff --git a/spec/java_buildpack/util/cache/cache_factory_spec.rb b/spec/java_buildpack/util/cache/cache_factory_spec.rb new file mode 100644 index 0000000000..e1c064e609 --- /dev/null +++ b/spec/java_buildpack/util/cache/cache_factory_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'application_helper' +require 'internet_availability_helper' +require 'logging_helper' +require 'java_buildpack/util/cache/application_cache' +require 'java_buildpack/util/cache/cache_factory' +require 'java_buildpack/util/cache/download_cache' + +describe JavaBuildpack::Util::Cache::CacheFactory do + include_context 'with application help' + include_context 'with internet availability help' + include_context 'with logging help' + + previous_arg_value = ARGV[1] + + before do + ARGV[1] = nil + end + + after do + ARGV[1] = previous_arg_value + end + + it 'returns an ApplicationCache if ARGV[1] is defined' do + ARGV[1] = app_dir + + expect(described_class.create).to be_instance_of JavaBuildpack::Util::Cache::ApplicationCache + end + + it 'returns a DownloadCache if ARGV[1] is not defined' do + expect(described_class.create).to be_instance_of JavaBuildpack::Util::Cache::DownloadCache + end + +end diff --git a/spec/java_buildpack/util/cache/cached_file_spec.rb b/spec/java_buildpack/util/cache/cached_file_spec.rb index 2a238aabcb..13b18b22cc 100644 --- a/spec/java_buildpack/util/cache/cached_file_spec.rb +++ b/spec/java_buildpack/util/cache/cached_file_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,18 +17,19 @@ require 'spec_helper' require 'application_helper' +require 'digest' require 'fileutils' require 'java_buildpack/util/cache/cached_file' describe JavaBuildpack::Util::Cache::CachedFile do - include_context 'application_helper' + include_context 'with application help' let(:cache_root) { app_dir + 'cache/root' } let(:file_cache) { described_class.new(app_dir, 'http://foo-uri/', true) } it 'does not create any files on initialization' do - %w(cached etag last_modified).each { |extension| expect(cache_file(extension)).not_to exist } + %w[cached etag last_modified].each { |extension| expect(cache_file(extension)).not_to exist } end it 'creates cache_root if mutable' do @@ -47,15 +49,15 @@ end it 'does not detect cached file' do - expect(file_cache.cached?).not_to be + expect(file_cache).not_to be_cached end it 'does not detect etag file' do - expect(file_cache.etag?).not_to be + expect(file_cache).not_to be_etag end it 'does not detect last_modified file' do - expect(file_cache.last_modified?).not_to be + expect(file_cache).not_to be_last_modified end context do @@ -71,19 +73,19 @@ end it 'detects cached file' do - expect(file_cache.cached?).to be + expect(file_cache).to be_cached end it 'destroys all files' do file_cache.destroy - %w(cached etag last_modified).each { |extension| expect(cache_file(extension)).not_to exist } + %w[cached etag last_modified].each { |extension| expect(cache_file(extension)).not_to exist } end it 'does not destroy all files if immutable' do described_class.new(app_dir, 'http://foo-uri/', false).destroy - %w(cached etag last_modified).each { |extension| expect(cache_file(extension)).to exist } + %w[cached etag last_modified].each { |extension| expect(cache_file(extension)).to exist } end it 'calls the block with the content of the etag file' do @@ -91,7 +93,7 @@ end it 'detects etag file' do - expect(file_cache.etag?).to be + expect(file_cache).to be_etag end it 'calls the block with the content of the last_modified file' do @@ -100,12 +102,12 @@ end it 'detects last_modified file' do - expect(file_cache.last_modified?).to be + expect(file_cache).to be_last_modified end end def cache_file(extension) - app_dir + "http%3A%2F%2Ffoo-uri%2F.#{extension}" + app_dir + "#{Digest::SHA256.hexdigest('http://foo-uri/')}.#{extension}" end def touch(extension, content = '') diff --git a/spec/java_buildpack/util/cache/download_cache_spec.rb b/spec/java_buildpack/util/cache/download_cache_spec.rb index 1c9c1e4e1a..b482e8834b 100644 --- a/spec/java_buildpack/util/cache/download_cache_spec.rb +++ b/spec/java_buildpack/util/cache/download_cache_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,16 +19,17 @@ require 'application_helper' require 'internet_availability_helper' require 'logging_helper' +require 'digest' require 'fileutils' require 'java_buildpack/util/cache/download_cache' require 'net/http' describe JavaBuildpack::Util::Cache::DownloadCache do - include_context 'application_helper' - include_context 'internet_availability_helper' - include_context 'logging_helper' + include_context 'with application help' + include_context 'with internet availability help' + include_context 'with logging help' - let(:ca_certs_directory) { double exist?: false, to_s: 'test-path' } + let(:ca_certs_directory) { instance_double('Pathname', exist?: false, to_s: 'test-path') } let(:mutable_cache_root) { app_dir + 'mutable' } @@ -39,7 +41,11 @@ let(:uri_secure) { 'https://foo-uri/' } - let(:download_cache) { described_class.new(mutable_cache_root, immutable_cache_root) } + let(:download_cache) do + download_cache = described_class.new(mutable_cache_root, immutable_cache_root) + download_cache.retry_max = 0 + download_cache + end before do described_class.const_set :CA_FILE, ca_certs_directory @@ -72,18 +78,19 @@ .to_return(status: 200, body: 'foo-cached', headers: { Etag: 'foo-etag', 'Last-Modified' => 'foo-last-modified' }) allow(Net::HTTP).to receive(:Proxy).and_call_original - expect(Net::HTTP).not_to receive(:Proxy).with('proxy', 9000, nil, nil) + expect(Net::HTTP).not_to have_received(:Proxy).with('proxy', 9000, nil, nil) expect { |b| download_cache.get uri, &b }.to yield_file_with_content(/foo-cached/) expect_complete_cache mutable_cache_root end it 'downloads with credentials if cached file does not exist' do - stub_request(:get, uri_credentials) + stub_request(:get, uri) + .with(headers: { 'Authorization' => 'Basic dGVzdC11c2VybmFtZTp0ZXN0LXBhc3N3b3Jk' }) .to_return(status: 200, body: 'foo-cached', headers: { Etag: 'foo-etag', 'Last-Modified' => 'foo-last-modified' }) allow(Net::HTTP).to receive(:Proxy).and_call_original - expect(Net::HTTP).not_to receive(:Proxy).with('proxy', 9000, nil, nil) + expect(Net::HTTP).not_to have_received(:Proxy).with('proxy', 9000, nil, nil) expect { |b| download_cache.get uri_credentials, &b }.to yield_file_with_content(/foo-cached/) expect_complete_cache mutable_cache_root @@ -161,8 +168,8 @@ it 'discards content with incorrect size' do stub_request(:get, uri) - .to_return(status: 200, body: 'foo-cac', headers: { Etag: 'foo-etag', - 'Last-Modified' => 'foo-last-modified', + .to_return(status: 200, body: 'foo-cac', headers: { Etag: 'foo-etag', + 'Last-Modified' => 'foo-last-modified', 'Content-Length' => 10 }) touch immutable_cache_root, 'cached', 'old-foo-cached' @@ -170,9 +177,21 @@ expect { |b| download_cache.get uri, &b }.to yield_file_with_content(/foo-cached/) end + it 'ignores incorrect size when encoded' do + stub_request(:get, uri) + .to_return(status: 200, body: 'foo-cac', headers: { Etag: 'foo-etag', + 'Content-Encoding' => 'gzip', + 'Last-Modified' => 'foo-last-modified', + 'Content-Length' => 10 }) + + touch immutable_cache_root, 'cached', 'old-foo-cached' + + expect { |b| download_cache.get uri, &b }.to yield_file_with_content(/foo-cac/) + end + context do - let(:environment) { { 'http_proxy' => 'http://proxy:9000' } } + let(:environment) { { 'http_proxy' => 'http://proxy:9000', 'HTTP_PROXY' => nil } } it 'uses http_proxy if specified' do stub_request(:get, uri) @@ -180,7 +199,7 @@ 'Last-Modified' => 'foo-last-modified' }) allow(Net::HTTP).to receive(:Proxy).and_call_original - expect(Net::HTTP).to receive(:Proxy).with('proxy', 9000, nil, nil).and_call_original + allow(Net::HTTP).to receive(:Proxy).with('proxy', 9000, nil, nil).and_call_original download_cache.get(uri) {} end @@ -189,7 +208,7 @@ context do - let(:environment) { { 'HTTP_PROXY' => 'http://proxy:9000' } } + let(:environment) { { 'HTTP_PROXY' => 'http://proxy:9000', 'http_proxy' => nil } } it 'uses HTTP_PROXY if specified' do stub_request(:get, uri) @@ -197,7 +216,7 @@ 'Last-Modified' => 'foo-last-modified' }) allow(Net::HTTP).to receive(:Proxy).and_call_original - expect(Net::HTTP).to receive(:Proxy).with('proxy', 9000, nil, nil).and_call_original + allow(Net::HTTP).to receive(:Proxy).with('proxy', 9000, nil, nil).and_call_original download_cache.get(uri) {} end @@ -206,7 +225,23 @@ context do - let(:environment) { { 'https_proxy' => 'http://proxy:9000' } } + let(:environment) { { 'HTTP_PROXY' => 'http://user%21:pass%40@proxy:9000', 'http_proxy' => nil } } + + it 'decodes user/pass from HTTP_PROXY if encoded' do + stub_request(:get, uri) + .to_return(status: 200, body: 'foo-cached', headers: { Etag: 'foo-etag', + 'Last-Modified' => 'foo-last-modified' }) + + allow(Net::HTTP).to receive(:Proxy).with('proxy', 9000, /user!/, /pass@/).and_call_original + + download_cache.get(uri) {} + end + + end + + context do + + let(:environment) { { 'https_proxy' => 'http://proxy:9000', 'HTTPS_PROXY' => nil } } it 'uses https_proxy if specified and URL is secure' do stub_request(:get, uri_secure) @@ -214,7 +249,7 @@ 'Last-Modified' => 'foo-last-modified' }) allow(Net::HTTP).to receive(:Proxy).and_call_original - expect(Net::HTTP).to receive(:Proxy).with('proxy', 9000, nil, nil).and_call_original + allow(Net::HTTP).to receive(:Proxy).with('proxy', 9000, nil, nil).and_call_original download_cache.get(uri_secure) {} end @@ -223,7 +258,7 @@ context do - let(:environment) { { 'HTTPS_PROXY' => 'http://proxy:9000' } } + let(:environment) { { 'HTTPS_PROXY' => 'http://proxy:9000', 'https_proxy' => nil } } it 'uses HTTPS_PROXY if specified and URL is secure' do stub_request(:get, uri_secure) @@ -231,7 +266,55 @@ 'Last-Modified' => 'foo-last-modified' }) allow(Net::HTTP).to receive(:Proxy).and_call_original - expect(Net::HTTP).to receive(:Proxy).with('proxy', 9000, nil, nil).and_call_original + allow(Net::HTTP).to receive(:Proxy).with('proxy', 9000, nil, nil).and_call_original + + download_cache.get(uri_secure) {} + end + + end + + context do + + let(:environment) { { 'HTTPS_PROXY' => 'http://user%21:pass%40@proxy:9000', 'https_proxy' => nil } } + + it 'decodes user/pass from HTTPS_PROXY if encoded' do + stub_request(:get, uri_secure) + .to_return(status: 200, body: 'foo-cached', headers: { Etag: 'foo-etag', + 'Last-Modified' => 'foo-last-modified' }) + + allow(Net::HTTP).to receive(:Proxy).with('proxy', 9000, /user!/, /pass@/).and_call_original + + download_cache.get(uri_secure) {} + end + + end + + context do + let(:environment) { { 'NO_PROXY' => '127.0.0.1,localhost,foo-uri,.foo-uri', 'HTTPS_PROXY' => 'http://proxy:9000' } } + + it 'does not use proxy if host in NO_PROXY' do + stub_request(:get, uri_secure) + .to_return(status: 200, body: 'foo-cached', headers: { Etag: 'foo-etag', + 'Last-Modified' => 'foo-last-modified' }) + + allow(Net::HTTP).to receive(:Proxy).and_call_original + expect(Net::HTTP).not_to have_received(:Proxy).with('proxy', 9000, nil, nil) + + download_cache.get(uri_secure) {} + end + + end + + context do + let(:environment) { { 'no_proxy' => '127.0.0.1,localhost,foo-uri,.foo-uri', 'https_proxy' => 'http://proxy:9000' } } + + it 'does not use proxy if host in no_proxy' do + stub_request(:get, uri_secure) + .to_return(status: 200, body: 'foo-cached', headers: { Etag: 'foo-etag', + 'Last-Modified' => 'foo-last-modified' }) + + allow(Net::HTTP).to receive(:Proxy).and_call_original + expect(Net::HTTP).not_to have_received(:Proxy).with('proxy', 9000, nil, nil) download_cache.get(uri_secure) {} end @@ -243,7 +326,7 @@ .to_return(status: 200, body: 'foo-cached', headers: { Etag: 'foo-etag', 'Last-Modified' => 'foo-last-modified' }) allow(Net::HTTP).to receive(:Proxy).and_call_original - expect(Net::HTTP).to receive(:start).with('foo-uri', 80, {}).and_call_original + allow(Net::HTTP).to receive(:start).with('foo-uri', 80).and_call_original download_cache.get(uri) {} end @@ -254,7 +337,7 @@ allow(ca_certs_directory).to receive(:exist?).and_return(true) allow(Net::HTTP).to receive(:Proxy).and_call_original - expect(Net::HTTP).to receive(:start).with('foo-uri', 80, {}).and_call_original + allow(Net::HTTP).to receive(:start).with('foo-uri', 80).and_call_original download_cache.get(uri) {} end @@ -264,7 +347,7 @@ .to_return(status: 200, body: 'foo-cached', headers: { Etag: 'foo-etag', 'Last-Modified' => 'foo-last-modified' }) allow(Net::HTTP).to receive(:Proxy).and_call_original - expect(Net::HTTP).to receive(:start).with('foo-uri', 443, use_ssl: true).and_call_original + allow(Net::HTTP).to receive(:start).with('foo-uri', 443, use_ssl: true).and_call_original download_cache.get(uri_secure) {} end @@ -275,7 +358,7 @@ allow(ca_certs_directory).to receive(:exist?).and_return(true) allow(Net::HTTP).to receive(:Proxy).and_call_original - expect(Net::HTTP).to receive(:start).with('foo-uri', 443, use_ssl: true, ca_file: 'test-path').and_call_original + allow(Net::HTTP).to receive(:start).with('foo-uri', 443, use_ssl: true, ca_file: 'test-path').and_call_original download_cache.get(uri_secure) {} end @@ -293,7 +376,7 @@ end def cache_file(root, extension) - root + "http%3A%2F%2Ffoo-uri%2F.#{extension}" + root + "#{Digest::SHA256.hexdigest('http://foo-uri/')}.#{extension}" end def expect_complete_cache(root) diff --git a/spec/java_buildpack/util/cache/internet_availability_spec.rb b/spec/java_buildpack/util/cache/internet_availability_spec.rb index 7a5b3e2b06..f4d3d8f349 100644 --- a/spec/java_buildpack/util/cache/internet_availability_spec.rb +++ b/spec/java_buildpack/util/cache/internet_availability_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,56 +22,60 @@ require 'java_buildpack/util/cache/internet_availability' describe JavaBuildpack::Util::Cache::InternetAvailability do - include_context 'internet_availability_helper' - include_context 'logging_helper' + include_context 'with internet availability help' + include_context 'with logging help' it 'uses internet by default' do - expect(described_class.instance.available?).to be + expect(described_class.instance).to be_available end context do before do allow(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('cache') - .and_return('remote_downloads' => 'disabled') + .and_return('remote_downloads' => 'disabled') described_class.instance.send :initialize end it 'does not use internet if remote downloads are disabled' do - expect(described_class.instance.available?).not_to be + expect(described_class.instance).not_to be_available end end - it 'records availability' do + it 'records availability', + :enable_log_file do + described_class.instance.available false - expect(described_class.instance.available?).not_to be + expect(described_class.instance).not_to be_available expect(log_contents).not_to match(/Internet availability set to false/) end - it 'records availability with message' do + it 'records availability with message', + :enable_log_file do + described_class.instance.available false, 'test message' - expect(described_class.instance.available?).not_to be + expect(described_class.instance).not_to be_available expect(log_contents).to match(/Internet availability set to false: test message/) end it 'temporarily sets internet unavailable' do - expect(described_class.instance.available?).to be + expect(described_class.instance).to be_available - described_class.instance.available(false) { expect(described_class.instance.available?).not_to be } + described_class.instance.available(false) { expect(described_class.instance).not_to be_available } - expect(described_class.instance.available?).to be + expect(described_class.instance).to be_available end it 'temporarily sets internet available', :disable_internet do - expect(described_class.instance.available?).not_to be + expect(described_class.instance).not_to be_available - described_class.instance.available(true) { expect(described_class.instance.available?).to be } + described_class.instance.available(true) { expect(described_class.instance).to be_available } - expect(described_class.instance.available?).not_to be + expect(described_class.instance).not_to be_available end end diff --git a/spec/java_buildpack/util/cache/yield_file_with_content.rb b/spec/java_buildpack/util/cache/yield_file_with_content.rb index c741c941fe..14010610a9 100644 --- a/spec/java_buildpack/util/cache/yield_file_with_content.rb +++ b/spec/java_buildpack/util/cache/yield_file_with_content.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/spec/java_buildpack/util/configuration_utils_spec.rb b/spec/java_buildpack/util/configuration_utils_spec.rb new file mode 100644 index 0000000000..9ebd64ada7 --- /dev/null +++ b/spec/java_buildpack/util/configuration_utils_spec.rb @@ -0,0 +1,204 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/util' +require 'java_buildpack/util/configuration_utils' +require 'fileutils' +require 'logging_helper' +require 'pathname' +require 'spec_helper' +require 'yaml' + +describe JavaBuildpack::Util::ConfigurationUtils do + include_context 'with logging help' + + let(:test_data) do + { 'foo' => { 'one' => '1', 'two' => 2 }, + 'bar' => { 'alpha' => { 'one' => 'cat', 'two' => 'dog' } }, + 'version' => '1.7.1', + 'not_here' => nil } + end + + it 'not load absent configuration file' do + pathname = instance_double('Pathname') + allow(Pathname).to receive(:new).and_return(pathname) + allow(pathname).to receive(:exist?).and_return(false) + + expect(described_class.load('test')).to eq({}) + end + + it 'write configuration file' do + test_file = Pathname.new(File.expand_path('../../../config/open_jdk_jre.yml', File.dirname(__FILE__))) + original_content = file_contents test_file + loaded_content = described_class.load('open_jdk_jre', false) + described_class.write('open_jdk_jre', loaded_content) + expect(described_class.load('open_jdk_jre', false)).to eq(loaded_content) + expect(file_contents(test_file)).to eq(original_content) + end + + context do + + before do + pathname = instance_double('Pathname') + allow(Pathname).to receive(:new).and_return(pathname) + allow(pathname).to receive(:exist?).and_return(true) + + allow(YAML).to receive(:load_file).and_return(test_data) + end + + it 'load configuration file' do + expect(described_class.load('test', false)).to eq(test_data) + end + + it 'load configuration file and clean nil values' do + expect(described_class.load('test', true)).to eq('foo' => { 'one' => '1', 'two' => 2 }, + 'bar' => { 'alpha' => { 'one' => 'cat', 'two' => 'dog' } }, + 'version' => '1.7.1') + end + + context do + + let(:environment) do + { 'JBP_CONFIG_TEST' => '{bar: {alpha: {one: 3, two: {one: 3}}, bravo: newValue}, foo: lion}' } + end + + it 'overlays matching environment variables' do + + expect(described_class.load('test')).to eq('foo' => { 'one' => '1', 'two' => 2 }, + 'bar' => { 'alpha' => { 'one' => 3, 'two' => 'dog' } }, + 'version' => '1.7.1') + end + + end + + context do + + let(:environment) do + { 'JBP_CONFIG_TEST' => '{version: 1.8.+}' } + end + + it 'overlays simple matching environment variable' do + expect(described_class.load('test')).to eq('foo' => { 'one' => '1', 'two' => 2 }, + 'bar' => { 'alpha' => { 'one' => 'cat', 'two' => 'dog' } }, + 'version' => '1.8.+') + end + + end + + context do + + let(:environment) do + { 'JBP_DEFAULT_TEST' => '{bar: {alpha: {one: 3, two: {one: 3}}, bravo: newValue}, foo: lion}' } + end + + it 'overlays operator default matching environment variables' do + + expect(described_class.load('test')).to eq('foo' => { 'one' => '1', 'two' => 2 }, + 'bar' => { 'alpha' => { 'one' => 3, 'two' => 'dog' } }, + 'version' => '1.7.1') + end + + end + + context do + + let(:environment) do + { 'JBP_DEFAULT_TEST' => '{version: 1.8.+}' } + end + + it 'overlays operator default config' do + expect(described_class.load('test')).to eq('foo' => { 'one' => '1', 'two' => 2 }, + 'bar' => { 'alpha' => { 'one' => 'cat', 'two' => 'dog' } }, + 'version' => '1.8.+') + end + + end + + context do + + let(:environment) do + { 'JBP_DEFAULT_TEST' => '{version: 11.+}', + 'JBP_CONFIG_TEST' => '{version: 17.+}' } + end + + it 'overlays operator default config and environment variable config' do + expect(described_class.load('test')).to eq('foo' => { 'one' => '1', 'two' => 2 }, + 'bar' => { 'alpha' => { 'one' => 'cat', 'two' => 'dog' } }, + 'version' => '17.+') + end + + end + + context do + + let(:environment) do + { 'JBP_DEFAULT_TEST' => '{bar: {alpha: {one: 3, two: {one: 3}}, bravo: newValue}}', + 'JBP_CONFIG_TEST' => '{bar: {alpha: {one: 9, two: {one: 3}}, bravo: newValue}}' } + end + + it 'overlays operator default matching and environment variables' do + + expect(described_class.load('test')).to eq('foo' => { 'one' => '1', 'two' => 2 }, + 'bar' => { 'alpha' => { 'one' => 9, 'two' => 'dog' } }, + 'version' => '1.7.1') + end + + end + + context do + + let(:environment) do + { 'JBP_CONFIG_TEST' => 'Not an array or a hash' } + end + + it 'raises an exception when invalid override value is specified' do + expect { described_class.load('test') } + .to raise_error(/User configuration value in environment variable JBP_CONFIG_TEST is not valid/) + end + + end + + context do + + let(:environment) do + { 'JBP_CONFIG_TEST' => '{version: 1.8.+' } + end + + it 'diagnoses invalid YAML syntax' do + expect { described_class.load('test') } + .to raise_error(/User configuration value in environment variable JBP_CONFIG_TEST has invalid syntax/) + end + + end + + end + + private + + def file_contents(file) + header = [] + File.open(file, 'r') do |f| + f.each do |line| + break if line =~ /^---/ + + header << line + end + end + header + end + +end diff --git a/spec/java_buildpack/util/constantize_spec.rb b/spec/java_buildpack/util/constantize_spec.rb index 80b5b84a68..611416f653 100644 --- a/spec/java_buildpack/util/constantize_spec.rb +++ b/spec/java_buildpack/util/constantize_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -30,6 +31,8 @@ end module Test + # rubocop:disable Lint/EmptyClass class StubClass end + # rubocop:enable Lint/EmptyClass end diff --git a/spec/java_buildpack/util/filtering_pathname_spec.rb b/spec/java_buildpack/util/filtering_pathname_spec.rb index 4b8e4b4750..0fdf972073 100644 --- a/spec/java_buildpack/util/filtering_pathname_spec.rb +++ b/spec/java_buildpack/util/filtering_pathname_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,13 +16,14 @@ # limitations under the License. require 'application_helper' +require 'logging_helper' require 'fileutils' require 'java_buildpack/util/filtering_pathname' require 'set' require 'spec_helper' describe JavaBuildpack::Util::FilteringPathname do - include_context 'application_helper' + include_context 'with application help' let(:filter_none) { ->(_) { true } } let(:filter_all) { ->(_) { false } } @@ -95,8 +97,8 @@ end it 'filters the result of join' do - expect(filtering_target.join 'good').to exist - expect(filtering_target.join 'bad').not_to exist + expect(filtering_target.join('good')).to exist + expect(filtering_target.join('bad')).not_to exist end it 'filters the result of parent', :filter do @@ -119,11 +121,11 @@ end it 'compares to pathnames using ==' do - expect((filtering_target + 'good') == (app_dir + 'good')).to be - expect((filtering_target + 'bad') == (app_dir + 'bad')).to be + expect((filtering_target + 'good') == (app_dir + 'good')).to be_truthy + expect((filtering_target + 'bad') == (app_dir + 'bad')).to be_truthy end - # rubocop:disable Lint/UselessComparison, Style/CaseEquality + # rubocop:disable Style/CaseEquality, Lint/BinaryOperatorWithIdenticalOperands it 'compares to filtering pathnames using <=>' do expect((filtering_target + 'good') <=> (filtering_target + 'good')).to eq(0) expect((filtering_target + 'good') <=> (filtering_target + 'bad')).to eq(1) @@ -133,25 +135,25 @@ end it 'compares to filtering pathnames using ==' do - expect((filtering_target + 'good') == (filtering_target + 'good')).to be - expect((filtering_target + 'bad') == (filtering_target + 'bad')).to be + expect((filtering_target + 'good') == (filtering_target + 'good')).to be_truthy + expect((filtering_target + 'bad') == (filtering_target + 'bad')).to be_truthy end it 'compares to pathnames using ===' do - expect((filtering_target + 'good') === (app_dir + 'good')).to be - expect((filtering_target + 'bad') === (app_dir + 'bad')).to be + expect((filtering_target + 'good') === (app_dir + 'good')).to be_truthy + expect((filtering_target + 'bad') === (app_dir + 'bad')).to be_truthy end it 'compares to filtering pathnames using ===' do - expect((filtering_target + 'good') === (filtering_target + 'good')).to be - expect((filtering_target + 'bad') === (filtering_target + 'bad')).to be + expect((filtering_target + 'good') === (filtering_target + 'good')).to be_truthy + expect((filtering_target + 'bad') === (filtering_target + 'bad')).to be_truthy end - # rubocop:enable Lint/UselessComparison, Style/CaseEquality + # rubocop:enable Style/CaseEquality, Lint/BinaryOperatorWithIdenticalOperands it 'delegates relative_path_from' do target = filtering_target + 'test1' underlying_pathname = target.send :pathname - expect(underlying_pathname).to receive(:relative_path_from) { Pathname.new('test1') } + allow(underlying_pathname).to receive(:relative_path_from) { Pathname.new('test1') } relative_path = target.relative_path_from(Pathname.new(app_dir)) expect(relative_path).to eq(Pathname.new('test1')) end @@ -175,7 +177,7 @@ it 'delegates each_line when the file is filtered in' do target = filtering_target + 'good' underlying_pathname = target.send :pathname - expect(underlying_pathname).to receive(:each_line).and_yield('test-line') + allow(underlying_pathname).to receive(:each_line).and_yield('test-line') expect { |b| target.each_line(&b) }.to yield_successive_args('test-line') end @@ -188,7 +190,7 @@ end it 'delegates opendir when the directory is filtered in' do - expect(app_dir).to receive(:opendir).and_yield('test-dir') + allow(app_dir).to receive(:opendir).and_yield('test-dir') expect { |b| filtering_target.opendir(&b) }.to yield_successive_args('test-dir') end @@ -199,7 +201,7 @@ it 'delegates sysopen when the file is filtered in' do target = filtering_target + 'good' underlying_pathname = target.send :pathname - expect(underlying_pathname).to receive(:sysopen).and_yield(999) + allow(underlying_pathname).to receive(:sysopen).and_yield(999) expect { |b| target.sysopen(&b) }.to yield_successive_args(999) end @@ -216,7 +218,8 @@ end it 'yields each child as a filtered pathname from each_child' do - expect { |b| filtering_target.each_child(&b) }.to yield_successive_args(app_dir + 'good') + expect { |b| filtering_target.each_child(&b) } + .to yield_successive_args(described_class.new(app_dir + 'good', filter_none, true)) end it 'yields each child as a pathname from each_child(false)' do @@ -228,22 +231,26 @@ end it 'yields each element of the path from descend' do - expect { |b| filename_target.descend(&b) }.to yield_successive_args(Pathname.new('/'), Pathname.new('/a'), - Pathname.new('/a/b')) + expect { |b| filename_target.descend(&b) } + .to yield_successive_args(described_class.new(Pathname.new('/'), filter_none, true), + described_class.new(Pathname.new('/a'), filter_none, true), + described_class.new(Pathname.new('/a/b'), filter_none, true)) end it 'yields each element of the path from ascend' do - expect { |b| filename_target.ascend(&b) }.to yield_successive_args(Pathname.new('/a/b'), Pathname.new('/a'), - Pathname.new('/')) + expect { |b| filename_target.ascend(&b) } + .to yield_successive_args(described_class.new(Pathname.new('/a/b'), filter_none, true), + described_class.new(Pathname.new('/a'), filter_none, true), + described_class.new(Pathname.new('/'), filter_none, true)) end it 'raises error if chmod is called on an immutable instance' do - expect { immutable_target.chmod(0644) }.to raise_error(/FilteringPathname is immutable/) + expect { immutable_target.chmod(0o644) }.to raise_error(/FilteringPathname is immutable/) end it 'delegates if chmod is called on a mutable instance' do - expect(app_dir).to receive(:chmod) - mutable_target.chmod(0644) + allow(app_dir).to receive(:chmod) + mutable_target.chmod(0o644) end it 'raises error if chown is called on an immutable instance' do @@ -251,7 +258,7 @@ end it 'delegates if chown is called on a mutable instance' do - expect(app_dir).to receive(:chown) + allow(app_dir).to receive(:chown) mutable_target.chown('test-user', 100) end @@ -260,17 +267,17 @@ end it 'delegates if delete is called on a mutable instance' do - expect(app_dir).to receive(:delete) + allow(app_dir).to receive(:delete) mutable_target.delete end it 'raises error if lchmod is called on an immutable instance' do - expect { immutable_target.lchmod(0644) }.to raise_error(/FilteringPathname is immutable/) + expect { immutable_target.lchmod(0o644) }.to raise_error(/FilteringPathname is immutable/) end it 'delegates if lchmod is called on a mutable instance' do - expect(app_dir).to receive(:lchmod) - mutable_target.lchmod(0644) + allow(app_dir).to receive(:lchmod) + mutable_target.lchmod(0o644) end it 'raises error if lchown is called on an immutable instance' do @@ -278,7 +285,7 @@ end it 'delegates if lchown is called on a mutable instance' do - expect(app_dir).to receive(:lchown) + allow(app_dir).to receive(:lchown) mutable_target.lchown('test-user', 100) end @@ -287,7 +294,7 @@ end it 'delegates if make_link is called on a mutable instance' do - expect(app_dir).to receive(:make_link) + allow(app_dir).to receive(:make_link) mutable_target.make_link('test') end @@ -296,7 +303,7 @@ end it 'delegates if make_symlink is called on a mutable instance' do - expect(app_dir).to receive(:make_symlink) + allow(app_dir).to receive(:make_symlink) mutable_target.make_symlink('test') end @@ -305,7 +312,7 @@ end it 'delegates if mkdir is called on a mutable instance' do - expect(app_dir).to receive(:mkdir) + allow(app_dir).to receive(:mkdir) mutable_target.mkdir('test') end @@ -317,34 +324,32 @@ end it 'delegates if open is called on an immutable instance with a non-mutating mode' do - expect(app_dir).to receive(:open) + allow(app_dir).to receive(:open) immutable_target.open('r') { |_| } end it 'delegates if open is called on a mutable instance' do - expect(app_dir).to receive(:open) + allow(app_dir).to receive(:open) mutable_target.open('w') { |_| } end it 'delegates if open is called on a mutable instance with permissions' do - expect(app_dir).to receive(:open).with('w', 0755) - mutable_target.open('w', 0755) { |_| } + allow(app_dir).to receive(:open).with('w', 0o755, {}) + mutable_target.open('w', 0o755, {}) { |_| } end it 'delegates if open is called on a mutable instance with permissions and options' do - expect(app_dir).to receive(:open).with('w', 0755, external_encoding: 'UTF-8') - mutable_target.open('w', 0755, external_encoding: 'UTF-8') { |_| } + allow(app_dir).to receive(:open).with('w', 0o755, external_encoding: 'UTF-8') + mutable_target.open('w', 0o755, external_encoding: 'UTF-8') { |_| } end it 'delegates if open is called on a mutable instance with options but no permissions' do - expect(app_dir).to receive(:open).with('w', external_encoding: 'UTF-8') + allow(app_dir).to receive(:open).with('w', external_encoding: 'UTF-8') mutable_target.open('w', external_encoding: 'UTF-8') { |_| } end it 'copes with options on open' do - content = (immutable_target + 'good').open('r', external_encoding: 'UTF-8') do |file| - file.read - end + content = (immutable_target + 'good').open('r', external_encoding: 'UTF-8', &:read) expect(content).to eq('good') end @@ -353,7 +358,7 @@ end it 'delegates if rename is called on a mutable instance' do - expect(app_dir).to receive(:rename) + allow(app_dir).to receive(:rename) mutable_target.rename('test') end @@ -362,7 +367,7 @@ end it 'delegates if rmdir is called on a mutable instance' do - expect(app_dir).to receive(:rmdir) + allow(app_dir).to receive(:rmdir) mutable_target.rmdir end @@ -371,34 +376,16 @@ end it 'delegates if unlink is called on a mutable instance' do - expect(app_dir).to receive(:unlink) + allow(app_dir).to receive(:unlink) mutable_target.unlink end - it 'raises error if untaint is called on an immutable instance' do - expect { immutable_target.untaint }.to raise_error(/FilteringPathname is immutable/) - end - - it 'delegates if untaint is called on a mutable instance' do - expect(app_dir).to receive(:untaint) - mutable_target.untaint - end - - it 'raises error if taint is called on an immutable instance' do - expect { immutable_target.taint }.to raise_error(/FilteringPathname is immutable/) - end - - it 'delegates if taint is called on a mutable instance' do - expect(app_dir).to receive(:taint) - mutable_target.taint - end - it 'raises error if mkpath is called on an immutable instance' do expect { immutable_target.mkpath }.to raise_error(/FilteringPathname is immutable/) end it 'delegates if mkpath is called on a mutable instance' do - expect(app_dir).to receive(:mkpath) + allow(app_dir).to receive(:mkpath) mutable_target.mkpath end @@ -407,7 +394,7 @@ end it 'delegates if rmtree is called on a mutable instance' do - expect(app_dir).to receive(:rmtree) + allow(app_dir).to receive(:rmtree) mutable_target.rmtree end @@ -418,19 +405,19 @@ it 'globs and yields the result' do g = strict_filtering_target + '*' - expect { |b| g.glob(&b) }.to yield_successive_args(app_dir + 'good') + expect { |b| g.glob(&b) }.to yield_successive_args(described_class.new(app_dir + 'good', filter_none, true)) end it 'raises error if getwd is used' do - expect { described_class.getwd }.to raise_error(/undefined method `getwd'/) + expect { described_class.getwd }.to raise_error(NoMethodError, /undefined method [`']getwd'/) end it 'raises error if glob is used' do - expect { described_class.glob '' }.to raise_error(/undefined method `glob'/) + expect { described_class.glob '' }.to raise_error(NoMethodError, /undefined method [`']glob'/) end it 'raises error if pwd is used' do - expect { described_class.pwd }.to raise_error(/undefined method `pwd'/) + expect { described_class.pwd }.to raise_error(NoMethodError, /undefined method [`']pwd'/) end def create_file(filename) diff --git a/spec/java_buildpack/util/format_duration_spec.rb b/spec/java_buildpack/util/format_duration_spec.rb index 84de30265b..bf37aab45d 100644 --- a/spec/java_buildpack/util/format_duration_spec.rb +++ b/spec/java_buildpack/util/format_duration_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,41 +20,35 @@ describe 'duration' do # rubocop:disable RSpec/DescribeClass + let(:millisecond) { 0.001 } + let(:tenth) { 100 * millisecond } + let(:second) { 10 * tenth } + let(:minute) { 60 * second } + let(:hour) { 60 * minute } + it 'displays seconds' do - expect_time_string '0.0s', MILLISECOND - expect_time_string '0.1s', TENTH - expect_time_string '1.0s', SECOND - expect_time_string '1.1s', SECOND + TENTH - expect_time_string '1.1s', SECOND + TENTH + MILLISECOND + expect_time_string '0.0s', millisecond + expect_time_string '0.1s', tenth + expect_time_string '1.0s', second + expect_time_string '1.1s', second + tenth + expect_time_string '1.1s', second + tenth + millisecond end it 'displays minutes' do - expect_time_string '1m 0s', MINUTE - expect_time_string '1m 1s', MINUTE + SECOND - expect_time_string '1m 1s', MINUTE + SECOND + TENTH - expect_time_string '1m 1s', MINUTE + SECOND + TENTH + MILLISECOND + expect_time_string '1m 0s', minute + expect_time_string '1m 1s', minute + second + expect_time_string '1m 1s', minute + second + tenth + expect_time_string '1m 1s', minute + second + tenth + millisecond end it 'displays hours' do - expect_time_string '1h 0m', HOUR - expect_time_string '1h 1m', HOUR + MINUTE - expect_time_string '1h 1m', HOUR + MINUTE + SECOND - expect_time_string '1h 1m', HOUR + MINUTE + SECOND + TENTH - expect_time_string '1h 1m', HOUR + MINUTE + SECOND + TENTH + MILLISECOND + expect_time_string '1h 0m', hour + expect_time_string '1h 1m', hour + minute + expect_time_string '1h 1m', hour + minute + second + expect_time_string '1h 1m', hour + minute + second + tenth + expect_time_string '1h 1m', hour + minute + second + tenth + millisecond end - private - - MILLISECOND = 0.001.freeze - - TENTH = (100 * MILLISECOND).freeze - - SECOND = (10 * TENTH).freeze - - MINUTE = (60 * SECOND).freeze - - HOUR = (60 * MINUTE).freeze - def expect_time_string(expected, time) expect(time.duration).to eq(expected) end diff --git a/spec/java_buildpack/util/java_main_utils_spec.rb b/spec/java_buildpack/util/java_main_utils_spec.rb index 4fc5ada333..f67038f20c 100644 --- a/spec/java_buildpack/util/java_main_utils_spec.rb +++ b/spec/java_buildpack/util/java_main_utils_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,14 +22,14 @@ require 'java_buildpack/util/java_main_utils' describe JavaBuildpack::Util::JavaMainUtils do - include_context 'application_helper' - include_context 'logging_helper' + include_context 'with application help' + include_context 'with logging help' let(:test_class_name) { 'test-java-main-class' } it 'uses a main class configuration in a configuration file' do allow(JavaBuildpack::Util::ConfigurationUtils).to receive(:load).with('java_main') - .and_return('java_main_class' => test_class_name) + .and_return('java_main_class' => test_class_name) expect(described_class.main_class(application)).to eq(test_class_name) end diff --git a/spec/java_buildpack/util/play/base_spec.rb b/spec/java_buildpack/util/play/base_spec.rb index 1d7e605992..b0d5dafe62 100644 --- a/spec/java_buildpack/util/play/base_spec.rb +++ b/spec/java_buildpack/util/play/base_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,20 +21,20 @@ require 'java_buildpack/util/play/base' describe JavaBuildpack::Util::Play::Base do - include_context 'droplet_helper' + include_context 'with droplet help' let(:play) { described_class.new(droplet) } it 'does not support with no start script' do allow(play).to receive(:start_script).and_return nil - expect(play.supports?).not_to be + expect(play).not_to be_supports end it 'does not support with a non-existent start script' do allow(play).to receive(:start_script).and_return(droplet.root + 'bin/start') - expect(play.supports?).not_to be + expect(play).not_to be_supports end it 'does not support with no play JAR' do @@ -43,7 +44,7 @@ FileUtils.mkdir_p app_dir + 'bin' FileUtils.touch app_dir + 'bin/start' - expect(play.supports?).not_to be + expect(play).not_to be_supports end it 'raises error if augment_classpath method is unimplemented' do @@ -77,7 +78,7 @@ end it 'supports application' do - expect(play.supports?).to be + expect(play).to be_supports end it 'returns a version' do @@ -93,9 +94,9 @@ end it 'determines whether or not certain JARs are present in the lib directory' do - expect(play.jar?(/so.*st.jar/)).to be - expect(play.jar?(/some.test.jar/)).to be - expect(play.jar?(/nosuch.jar/)).not_to be + expect(play).to be_jar(/so.*st.jar/) + expect(play).to be_jar(/some.test.jar/) + expect(play).not_to be_jar(/nosuch.jar/) end it 'replaces the bootstrap class' do diff --git a/spec/java_buildpack/util/play/factory_spec.rb b/spec/java_buildpack/util/play/factory_spec.rb index 57220ca44e..ad5915a5b2 100644 --- a/spec/java_buildpack/util/play/factory_spec.rb +++ b/spec/java_buildpack/util/play/factory_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,8 +21,8 @@ require 'java_buildpack/util/play/factory' describe JavaBuildpack::Util::Play::Factory do - include_context 'console_helper' - include_context 'droplet_helper' + include_context 'with console help' + include_context 'with droplet help' let(:trigger) { described_class.create(droplet) } diff --git a/spec/java_buildpack/util/play/post22_dist_spec.rb b/spec/java_buildpack/util/play/post22_dist_spec.rb index dad091c40c..6ab439a979 100644 --- a/spec/java_buildpack/util/play/post22_dist_spec.rb +++ b/spec/java_buildpack/util/play/post22_dist_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,62 +20,62 @@ require 'java_buildpack/util/play/post22_dist' describe JavaBuildpack::Util::Play::Post22Dist do - include_context 'component_helper' + include_context 'with component help' context do let(:trigger) { described_class.new(droplet).supports? } it 'does not recognize non-applications' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize Play 2.0 applications', app_fixture: 'container_play_2.0_dist' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize Play 2.1 dist applications', app_fixture: 'container_play_2.1_dist' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize Play 2.1 staged applications', app_fixture: 'container_play_2.1_staged' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize a Ratpack application', app_fixture: 'container_ratpack_dist' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize a Spring Boot application', app_fixture: 'container_spring_boot_dist' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize a distZip application', app_fixture: 'container_dist_zip' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'recognizes Play 2.2 dist applications', app_fixture: 'container_play_2.2_dist' do - expect(trigger).to be + expect(trigger).to be_truthy end it 'does not recognize Play 2.2 staged applications', app_fixture: 'container_play_2.2_staged' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end end diff --git a/spec/java_buildpack/util/play/post22_spec.rb b/spec/java_buildpack/util/play/post22_spec.rb index 094a1d1d7f..8ab4343678 100644 --- a/spec/java_buildpack/util/play/post22_spec.rb +++ b/spec/java_buildpack/util/play/post22_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,7 +21,7 @@ require 'java_buildpack/util/play/post22' describe JavaBuildpack::Util::Play::Post22 do - include_context 'component_helper' + include_context 'with component help' let(:play_app) { described_class.new(droplet) } @@ -43,33 +44,27 @@ expect((app_dir + 'bin/play-application').read) .to match 'declare -r app_classpath="\$app_home/../.additional_libs/test-jar-1.jar:' \ - '\$app_home/../.additional_libs/test-jar-2.jar:' + '\$app_home/../.additional_libs/test-jar-2.jar:' end it 'returns command' do - expect(play_app.release).to eq("PATH=#{java_home.root}/bin:$PATH #{java_home.as_env_var} " \ - '$PWD/bin/play-application -Jtest-opt-2 -Jtest-opt-1 -J-Dhttp.port=$PORT') - end - - context do - let(:java_opts) { super() << '-Xmx30m -Xms30m' } - - it 'does not allow multiple options in a single JAVA_OPTS array entry' do - expect { play_app.release }.to raise_error(/Invalid Java option contains more than one option/) - end + # rubocop:disable Layout/LineLength + expect(play_app.release).to eq('test-var-2 test-var-1 PATH=$PWD/.test-java-home/bin:$PATH ' \ + "#{java_home.as_env_var} exec $PWD/bin/play-application $(for I in $JAVA_OPTS ; do echo \"-J$I\" ; done)") + # rubocop:enable Layout/LineLength end context do let(:java_opts) do super() << '-Dappdynamics.agent.nodeName=$(expr "$VCAP_APPLICATION" : \'.' \ - '*instance_id[": ]*"\([a-z0-9]\+\)".*\')' + '*instance_id[": ]*"\([a-z0-9]\+\)".*\')' end it 'allows options with expressions' do play_app.release expect(java_opts).to include('-Dappdynamics.agent.nodeName=$(expr "$VCAP_APPLICATION" : \'.' \ - '*instance_id[": ]*"\([a-z0-9]\+\)".*\')') + '*instance_id[": ]*"\([a-z0-9]\+\)".*\')') end end diff --git a/spec/java_buildpack/util/play/post22_staged_spec.rb b/spec/java_buildpack/util/play/post22_staged_spec.rb index 2c69aa65ca..0f68840463 100644 --- a/spec/java_buildpack/util/play/post22_staged_spec.rb +++ b/spec/java_buildpack/util/play/post22_staged_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,74 +20,74 @@ require 'java_buildpack/util/play/post22_staged' describe JavaBuildpack::Util::Play::Post22Staged do - include_context 'component_helper' + include_context 'with component help' context do let(:trigger) { described_class.new(droplet).supports? } it 'does not recognize non-applications' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize Play 2.0 applications', app_fixture: 'container_play_2.0_dist' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize Play 2.1 dist applications', app_fixture: 'container_play_2.1_dist' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize Play 2.1 staged applications', app_fixture: 'container_play_2.1_staged' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize Play 2.2 dist applications', app_fixture: 'container_play_2.2_dist' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize a Ratpack application', app_fixture: 'container_ratpack_dist' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize a Spring Boot application', app_fixture: 'container_spring_boot_dist' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize a distZip application', app_fixture: 'container_dist_zip' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'recognizes Play 2.2 staged applications', app_fixture: 'container_play_2.2_staged' do - expect(trigger).to be + expect(trigger).to be_truthy end it 'recognizes a Play 2.2 application with a missing .bat file if there is precisely one start script', app_fixture: 'container_play_2.2_minus_bat_file' do - expect(trigger).to be + expect(trigger).to be_truthy end it 'does not recognize a Play 2.2 application with a missing .bat file and more than one start script', app_fixture: 'container_play_2.2_ambiguous_start_script' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end end diff --git a/spec/java_buildpack/util/play/pre22_dist_spec.rb b/spec/java_buildpack/util/play/pre22_dist_spec.rb index b086d6025e..ddd05675f7 100644 --- a/spec/java_buildpack/util/play/pre22_dist_spec.rb +++ b/spec/java_buildpack/util/play/pre22_dist_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,62 +20,62 @@ require 'java_buildpack/util/play/pre22_dist' describe JavaBuildpack::Util::Play::Pre22Dist do - include_context 'component_helper' + include_context 'with component help' context do let(:trigger) { described_class.new(droplet).supports? } it 'does not recognize non-applications' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'recognizes Play 2.0 dist applications', app_fixture: 'container_play_2.0_dist' do - expect(trigger).to be + expect(trigger).to be_truthy end it 'recognizes Play 2.1 dist applications', app_fixture: 'container_play_2.1_dist' do - expect(trigger).to be + expect(trigger).to be_truthy end it 'does not recognize Play 2.1 staged (or equivalently 2.0 staged) applications', app_fixture: 'container_play_2.1_staged' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize Play 2.2 dist applications', app_fixture: 'container_play_2.2_dist' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize Play 2.2 staged applications', app_fixture: 'container_play_2.2_staged' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize a Ratpack application', app_fixture: 'container_ratpack_dist' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize a Spring Boot application', app_fixture: 'container_spring_boot_dist' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize a distZip application', app_fixture: 'container_dist_zip' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end end @@ -89,22 +90,22 @@ it 'adds additional libraries to lib directory of a Play 2.0 dist application' do play_app.compile - lib_dir = app_dir + 'application-root/lib' - test_jar_1 = lib_dir + 'test-jar-1.jar' - test_jar_2 = lib_dir + 'test-jar-2.jar' + lib_dir = app_dir + 'application-root/lib' + test_jar1 = lib_dir + 'test-jar-1.jar' + test_jar2 = lib_dir + 'test-jar-2.jar' - expect(test_jar_1).to exist - expect(test_jar_1).to be_symlink - expect(test_jar_1.readlink).to eq((additional_libs_directory + 'test-jar-1.jar').relative_path_from(lib_dir)) + expect(test_jar1).to exist + expect(test_jar1).to be_symlink + expect(test_jar1.readlink).to eq((additional_libs_directory + 'test-jar-1.jar').relative_path_from(lib_dir)) - expect(test_jar_2).to exist - expect(test_jar_2).to be_symlink - expect(test_jar_2.readlink).to eq((additional_libs_directory + 'test-jar-2.jar').relative_path_from(lib_dir)) + expect(test_jar2).to exist + expect(test_jar2).to be_symlink + expect(test_jar2.readlink).to eq((additional_libs_directory + 'test-jar-2.jar').relative_path_from(lib_dir)) end it 'returns command' do - expect(play_app.release).to eq("PATH=#{java_home.root}/bin:$PATH #{java_home.as_env_var} " \ - '$PWD/application-root/start test-opt-2 test-opt-1 -Dhttp.port=$PORT') + expect(play_app.release).to eq('test-var-2 test-var-1 PATH=$PWD/.test-java-home/bin:$PATH ' \ + "#{java_home.as_env_var} exec $PWD/application-root/start $JAVA_OPTS") end end @@ -119,13 +120,15 @@ it 'extends the classpath of a Play 2.1 dist application' do play_app.compile + # rubocop:disable Layout/LineLength expect((app_dir + 'application-root/start').read).to match 'classpath="\$scriptdir/../.additional_libs/' \ - 'test-jar-1.jar:\$scriptdir/../.additional_libs/test-jar-2.jar:' + 'test-jar-1.jar:\$scriptdir/../.additional_libs/test-jar-2.jar:' + # rubocop:enable Layout/LineLength end it 'returns command' do - expect(play_app.release).to eq("PATH=#{java_home.root}/bin:$PATH #{java_home.as_env_var} " \ - '$PWD/application-root/start test-opt-2 test-opt-1 -Dhttp.port=$PORT') + expect(play_app.release).to eq('test-var-2 test-var-1 PATH=$PWD/.test-java-home/bin:$PATH ' \ + "#{java_home.as_env_var} exec $PWD/application-root/start $JAVA_OPTS") end end diff --git a/spec/java_buildpack/util/play/pre22_spec.rb b/spec/java_buildpack/util/play/pre22_spec.rb index 1c0564a5f9..f76bebf1bb 100644 --- a/spec/java_buildpack/util/play/pre22_spec.rb +++ b/spec/java_buildpack/util/play/pre22_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,8 +21,8 @@ require 'java_buildpack/util/play/pre22' describe JavaBuildpack::Util::Play::Pre22 do - include_context 'application_helper' - include_context 'droplet_helper' + include_context 'with application help' + include_context 'with droplet help' let(:play_app) { described_class.new(droplet) } diff --git a/spec/java_buildpack/util/play/pre22_staged_spec.rb b/spec/java_buildpack/util/play/pre22_staged_spec.rb index 93cbee12b3..a4e2fc3d25 100644 --- a/spec/java_buildpack/util/play/pre22_staged_spec.rb +++ b/spec/java_buildpack/util/play/pre22_staged_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,62 +20,62 @@ require 'java_buildpack/util/play/pre22_staged' describe JavaBuildpack::Util::Play::Pre22Staged do - include_context 'component_helper' + include_context 'with component help' context do let(:trigger) { described_class.new(droplet).supports? } it 'does not recognize non-applications' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize Play 2.0 dist applications', app_fixture: 'container_play_2.0_dist' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize Play 2.1 dist applications', app_fixture: 'container_play_2.1_dist' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'recognizes Play 2.1 staged (or equivalently 2.0 staged) applications', app_fixture: 'container_play_2.1_staged' do - expect(trigger).to be + expect(trigger).to be_truthy end it 'does not recognize Play 2.2 dist applications', app_fixture: 'container_play_2.2_dist' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize Play 2.2 staged applications', app_fixture: 'container_play_2.2_staged' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize a Ratpack application', app_fixture: 'container_ratpack_dist' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize a Spring Boot application', app_fixture: 'container_spring_boot_dist' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end it 'does not recognize a distZip application', app_fixture: 'container_dist_zip' do - expect(trigger).not_to be + expect(trigger).not_to be_truthy end end @@ -90,21 +91,21 @@ play_app.compile staged_dir = app_dir + 'staged' - test_jar_1 = staged_dir + 'test-jar-1.jar' - test_jar_2 = staged_dir + 'test-jar-2.jar' + test_jar1 = staged_dir + 'test-jar-1.jar' + test_jar2 = staged_dir + 'test-jar-2.jar' - expect(test_jar_1).to exist - expect(test_jar_1).to be_symlink - expect(test_jar_1.readlink).to eq((additional_libs_directory + 'test-jar-1.jar').relative_path_from(staged_dir)) + expect(test_jar1).to exist + expect(test_jar1).to be_symlink + expect(test_jar1.readlink).to eq((additional_libs_directory + 'test-jar-1.jar').relative_path_from(staged_dir)) - expect(test_jar_2).to exist - expect(test_jar_2).to be_symlink - expect(test_jar_2.readlink).to eq((additional_libs_directory + 'test-jar-2.jar').relative_path_from(staged_dir)) + expect(test_jar2).to exist + expect(test_jar2).to be_symlink + expect(test_jar2.readlink).to eq((additional_libs_directory + 'test-jar-2.jar').relative_path_from(staged_dir)) end it 'returns command' do - expect(play_app.release).to eq("PATH=#{java_home.root}/bin:$PATH #{java_home.as_env_var} $PWD/start " \ - 'test-opt-2 test-opt-1 -Dhttp.port=$PORT') + expect(play_app.release).to eq('test-var-2 test-var-1 PATH=$PWD/.test-java-home/bin:$PATH ' \ + "#{java_home.as_env_var} exec $PWD/start $JAVA_OPTS") end end diff --git a/spec/java_buildpack/util/properties_spec.rb b/spec/java_buildpack/util/properties_spec.rb index c3497f5640..75b4a593ae 100644 --- a/spec/java_buildpack/util/properties_spec.rb +++ b/spec/java_buildpack/util/properties_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/spec/java_buildpack/util/ratpack_utils_spec.rb b/spec/java_buildpack/util/ratpack_utils_spec.rb index 542e298681..4f398827f3 100644 --- a/spec/java_buildpack/util/ratpack_utils_spec.rb +++ b/spec/java_buildpack/util/ratpack_utils_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,26 +20,26 @@ require 'java_buildpack/util/ratpack_utils' describe JavaBuildpack::Util::RatpackUtils do - include_context 'application_helper' + include_context 'with application help' let(:utils) { described_class.new } it 'detects a dist Ratpack application', app_fixture: 'container_ratpack_dist' do - expect(utils.is?(application)).to be + expect(utils).to be_is(application) end it 'detects a staged Ratpack application', app_fixture: 'container_ratpack_staged' do - expect(utils.is?(application)).to be + expect(utils).to be_is(application) end it 'does not detect a non-Ratpack application', app_fixture: 'container_main' do - expect(utils.is?(application)).not_to be + expect(utils).not_to be_is(application) end it 'determines the version a dist Ratpack application', diff --git a/spec/java_buildpack/util/sanitize_spec.rb b/spec/java_buildpack/util/sanitize_spec.rb index fa43af155f..f7c929a48f 100644 --- a/spec/java_buildpack/util/sanitize_spec.rb +++ b/spec/java_buildpack/util/sanitize_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2014 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,10 +20,26 @@ require 'java_buildpack/util/sanitizer' describe 'sanitize_uri' do # rubocop:disable RSpec/DescribeClass - include_context 'application_helper' + include_context 'with application help' it 'sanitizes uri with credentials in' do - expect('https://myuser:mypass@myhost/path/to/file'.sanitize_uri).to eq('https://myhost/path/to/file') + expect('https://myuser:mypass@myhost/path/to/file'\ + '?authentication=verysecret'\ + '&cred=verysecret'\ + '&password=verysecret'\ + '&include=java'\ + '&bitness=64'\ + '&Api-Token=dt0c01.H67ALCXCXK7PWAAOQLENSRET.PRIVATEPART'\ + '&secret-token=verysecret'\ + '&token=123456789'.sanitize_uri).to eq('https://myhost/path/to/file'\ + '?authentication=***'\ + '&cred=***'\ + '&password=***'\ + '&include=java'\ + '&bitness=64'\ + '&Api-Token=***'\ + '&secret-token=***'\ + '&token=***') end it 'does not sanatize uri with no credentials in' do diff --git a/spec/java_buildpack/util/shell_spec.rb b/spec/java_buildpack/util/shell_spec.rb index 850e23a861..209868e93a 100644 --- a/spec/java_buildpack/util/shell_spec.rb +++ b/spec/java_buildpack/util/shell_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,17 +18,22 @@ require 'spec_helper' require 'console_helper' require 'java_buildpack/util/shell' +require 'timeout' describe JavaBuildpack::Util::Shell do include described_class - include_context 'console_helper' + include_context 'with console help' it 'returns if command returns a zero exit code' do shell 'true' end it 'raises an error if command returns a non-zero exit code' do - expect { shell 'false' }.to raise_error + expect { shell 'false' }.to raise_error RuntimeError + end + + it 'handles a large amount of output' do + Timeout.timeout(2) { shell "cat /dev/urandom | env LC_CTYPE=C tr -dc 'a-zA-Z0-9' | fold -w 1000000 | head -n 1" } end end diff --git a/spec/java_buildpack/util/spring_boot_utils_spec.rb b/spec/java_buildpack/util/spring_boot_utils_spec.rb index b3a71afb55..b7213179ec 100644 --- a/spec/java_buildpack/util/spring_boot_utils_spec.rb +++ b/spec/java_buildpack/util/spring_boot_utils_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright (c) 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,42 +16,101 @@ # limitations under the License. require 'spec_helper' -require 'application_helper' +require 'droplet_helper' require 'java_buildpack/util/spring_boot_utils' describe JavaBuildpack::Util::SpringBootUtils do - include_context 'application_helper' + include_context 'with droplet help' let(:utils) { described_class.new } it 'detects a dist Spring Boot application', app_fixture: 'container_spring_boot_dist' do - expect(utils.is?(application)).to be + expect(utils).to be_is(application) end it 'detects a staged Spring Boot application', app_fixture: 'container_spring_boot_staged' do - expect(utils.is?(application)).to be + expect(utils).to be_is(application) + end + + it 'detects a JAR Spring Boot application', + app_fixture: 'container_main_spring_boot_jar_launcher' do + + expect(utils).to be_is(application) end it 'does not detect a non-Spring Boot application', app_fixture: 'container_main' do - expect(utils.is?(application)).not_to be + expect(utils).not_to be_is(application) end - it 'determines the version a dist Spring Boot application', + it 'determines if an application is a thin application', + app_fixture: 'container_main_spring_boot_thin_launcher' do + + expect(utils).to be_thin(application) + end + + it 'determines the version of a dist Spring Boot application', app_fixture: 'container_spring_boot_dist' do expect(utils.version(application)).to match(/1.0.0.RELEASE/) end - it 'determines the version a staged Spring Boot application', + it 'determines the version of a staged Spring Boot application', app_fixture: 'container_spring_boot_staged' do expect(utils.version(application)).to match(/1.0.0.RELEASE/) end + it 'determines the version of a JAR Spring Boot application', + app_fixture: 'container_main_spring_boot_jar_launcher' do + + expect(utils.version(application)).to match(/1.2.5.RELEASE/) + end + + it 'returns BOOT-INF/lib as lib directory' do + FileUtils.mkdir_p(app_dir + 'BOOT-INF/lib') + + expect(utils.lib(droplet)).to eq(droplet.root + 'BOOT-INF/lib') + end + + it 'returns WEB-INF/lib as lib directory' do + FileUtils.mkdir_p(app_dir + 'WEB-INF/lib') + + expect(utils.lib(droplet)).to eq(droplet.root + 'WEB-INF/lib') + end + + it 'returns lib as lib directory' do + FileUtils.mkdir_p(app_dir + 'lib') + + expect(utils.lib(droplet)).to eq(droplet.root + 'lib') + end + + it 'returns manifest value as lib directory', + app_fixture: 'container_main_spring_boot_jar_launcher' do + + FileUtils.mkdir_p(app_dir + 'manifest-lib-value') + + expect(utils.lib(droplet)).to eq(droplet.root + 'manifest-lib-value/') + end + + it 'fails if there are no lib directories' do + expect { utils.lib(droplet) }.to raise_error RuntimeError + end + + it 'caches thin dependencies' do + allow(utils).to receive(:shell) + + utils.cache_thin_dependencies java_home.root, 'test-application-root', 'test-thin-root' + + # rubocop:disable Layout/LineLength + expect(utils).to have_received(:shell).with("#{java_home.root + 'bin/java'} -Dthin.dryrun " \ + '-Dthin.root=test-thin-root -cp test-application-root org.springframework.boot.loader.wrapper.ThinJarWrapper') + # rubocop:enable Layout/LineLength + end + end diff --git a/spec/java_buildpack/util/tokenized_version_spec.rb b/spec/java_buildpack/util/tokenized_version_spec.rb index 7b2ceb62fd..60c6ea046d 100644 --- a/spec/java_buildpack/util/tokenized_version_spec.rb +++ b/spec/java_buildpack/util/tokenized_version_spec.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -62,6 +63,14 @@ expect { described_class.new('1.6_26') }.to raise_error(/Invalid/) end + it 'accepts wildcards when legal' do + described_class.new('+') + described_class.new('1.+') + described_class.new('1.1.+') + described_class.new('1.1.1_+') + described_class.new('1.1.1_1+') + end + it 'raises an exception when micro version is missing' do expect { described_class.new('1.6') }.to raise_error(/Invalid/) end @@ -78,10 +87,6 @@ expect { described_class.new('1.6.0+') }.to raise_error(/Invalid/) end - it 'raises an exception when qualifier version is not legal' do - expect { described_class.new('1.6.0_05+') }.to raise_error(/Invalid/) - end - it 'raises an exception when the qualifier is not letter, number, or hyphen' do expect { described_class.new('1.6.0_?') }.to raise_error(/Invalid/) expect { described_class.new('1.6.0__5') }.to raise_error(/Invalid/) @@ -114,6 +119,7 @@ expect { described_class.new('1.+', false) }.to raise_error(/Invalid/) expect { described_class.new('1.1.+', false) }.to raise_error(/Invalid/) expect { described_class.new('1.1.1_+', false) }.to raise_error(/Invalid/) + expect { described_class.new('1.1.1_1+', false) }.to raise_error(/Invalid/) end it 'raises an exception when a version ends with a component separator' do diff --git a/spec/logging_helper.rb b/spec/logging_helper.rb index e62f7dffe4..0a914a3294 100644 --- a/spec/logging_helper.rb +++ b/spec/logging_helper.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,12 +20,14 @@ require 'console_helper' require 'fileutils' require 'java_buildpack/logging/logger_factory' +require 'yaml' -shared_context 'logging_helper' do - include_context 'console_helper' - include_context 'application_helper' +shared_context 'with logging help' do + include_context 'with console help' + include_context 'with application help' - previous_log_level = ENV['JBP_LOG_LEVEL'] + previous_log_config = ENV.fetch('JBP_CONFIG_LOGGING', nil) + previous_log_level = ENV.fetch('JBP_LOG_LEVEL', nil) previous_debug_level = $DEBUG previous_verbose_level = $VERBOSE @@ -34,6 +37,9 @@ log_level = example.metadata[:log_level] ENV['JBP_LOG_LEVEL'] = log_level if log_level + enable_log_file = example.metadata[:enable_log_file] + ENV['JBP_CONFIG_LOGGING'] = { 'enable_log_file' => true }.to_yaml if enable_log_file + $DEBUG = example.metadata[:debug] $VERBOSE = example.metadata[:verbose] @@ -43,9 +49,10 @@ after do JavaBuildpack::Logging::LoggerFactory.instance.reset - ENV['JBP_LOG_LEVEL'] = previous_log_level - $VERBOSE = previous_verbose_level - $DEBUG = previous_debug_level + ENV['JBP_CONFIG_LOGGING'] = previous_log_config + ENV['JBP_LOG_LEVEL'] = previous_log_level + $VERBOSE = previous_verbose_level + $DEBUG = previous_debug_level end end diff --git a/spec/memory_limit_helper.rb b/spec/memory_limit_helper.rb index 18c27d99dc..cd206fd819 100644 --- a/spec/memory_limit_helper.rb +++ b/spec/memory_limit_helper.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,9 +17,9 @@ require 'spec_helper' -shared_context 'memory_limit_helper' do +shared_context 'with memory limit help' do - previous_memory_limit = ENV['MEMORY_LIMIT'] + previous_memory_limit = ENV.fetch('MEMORY_LIMIT', nil) before do |example| memory_limit = example.metadata[:memory_limit] diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ffdb9be9d4..6012db5df9 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,7 @@ -# Encoding: utf-8 +# frozen_string_literal: true + # Cloud Foundry Java Buildpack -# Copyright 2013 the original author or authors. +# Copyright 2013-2020 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,14 +15,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'simplecov' -SimpleCov.start do - add_filter 'spec' -end - -require 'codeclimate-test-reporter' -CodeClimate::TestReporter.start - +require 'tmpdir' require 'webmock/rspec' WebMock.disable_net_connect!(allow: 'codeclimate.com')