From 1a3aa19befe08dfa52d1bf528f3e1bfec1e86868 Mon Sep 17 00:00:00 2001 From: Ursula Florianczyk Date: Thu, 11 Jan 2024 13:59:07 +0100 Subject: [PATCH] Download sealights agent from 'customAgentUrl' Added support for the new parameter both from app configuration and sealights user-provided service Searching for jar which name starts with 'sl-test-listener' in zip downloaded from customAgentUrl Overwrite sl.enableUpgrade to false when agent is downloaded from customAgentUrl 'enable_upgrade' parameter name fix in config yml file ('auto_upgrade' was not used in sealights framework) Modified adding system property sl.tag to contain buildpack version --- config/sealights_agent.yml | 5 +- docs/framework-sealights_agent.md | 14 +- .../framework/sealights_agent.rb | 68 +++++++- .../stub-sealights-custom-agent-invalid.zip | Bin 0 -> 416 bytes spec/fixtures/stub-sealights-custom-agent.zip | Bin 0 -> 810 bytes .../framework/sealights_agent_spec.rb | 160 +++++++++++++++++- 6 files changed, 231 insertions(+), 16 deletions(-) create mode 100644 spec/fixtures/stub-sealights-custom-agent-invalid.zip create mode 100644 spec/fixtures/stub-sealights-custom-agent.zip diff --git a/config/sealights_agent.yml b/config/sealights_agent.yml index a75881a853..c7f64c5fbe 100644 --- a/config/sealights_agent.yml +++ b/config/sealights_agent.yml @@ -1,5 +1,5 @@ # Cloud Foundry Java Buildpack -# Copyright 2013-2020 the original author or authors. +# Copyright 2013-2024 the original author 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,4 +20,5 @@ repository_root: https://agents.sealights.co/pcf build_session_id: lab_id: proxy: -auto_upgrade: false +enable_upgrade: false +customAgentUrl: \ No newline at end of file diff --git a/docs/framework-sealights_agent.md b/docs/framework-sealights_agent.md index 235254b8a9..7e29c29779 100644 --- a/docs/framework-sealights_agent.md +++ b/docs/framework-sealights_agent.md @@ -16,11 +16,12 @@ Tags are printed to standard output by the buildpack detect script 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][] +| 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][] +| `customAgentUrl` | Specify an url to download zip containing custom Sealights agent jar. If the custom agent is downloaded then the 'enable_upgrade' is forced to 'false' All fields above except the agent token may be also specified in the [Configuration Section](#configuration) below. @@ -34,8 +35,9 @@ The framework can be configured by modifying the [`config/sealights_agent.yml`][ | `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 +| `enable_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 +| `customAgentUrl` | Specify an url to download zip containing custom Sealights agent jar. If the custom agent is downloaded then the 'enable_upgrade' is forced to 'false' Configuration settings will take precedence over the ones specified in the [User-Provided Service](#user-provided-service), if those are defined. diff --git a/lib/java_buildpack/framework/sealights_agent.rb b/lib/java_buildpack/framework/sealights_agent.rb index 3e6331a7fe..82c3237887 100644 --- a/lib/java_buildpack/framework/sealights_agent.rb +++ b/lib/java_buildpack/framework/sealights_agent.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Cloud Foundry Java Buildpack -# Copyright 2013-2020 the original author or authors. +# Copyright 2013-2024 the original author 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 +16,7 @@ # limitations under the License. require 'java_buildpack/framework' +require 'java_buildpack/buildpack_version' require 'java_buildpack/component/versioned_dependency_component' require 'shellwords' require 'fileutils' @@ -34,7 +35,16 @@ def initialize(context) # (see JavaBuildpack::Component::BaseComponent#compile) def compile - download_zip(false) + custom_download_uri = get_value(CUSTOM_AGENT_URL) + if custom_download_uri.nil? + download_zip(false) + else + target_directory = @droplet.sandbox + name = @component_name + download('custom-agent', custom_download_uri, name) do |file| + expand(file, name, target_directory) + end + end end # (see JavaBuildpack::Component::BaseComponent#release) @@ -42,10 +52,16 @@ 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') + @droplet.java_opts.add_system_property('sl.tags', Shellwords.escape("sl-pcf-#{buildpack_version}")) # add sl.enableUpgrade system property - @droplet.java_opts.add_system_property('sl.enableUpgrade', @configuration[ENABLE_UPGRADE] ? 'true' : 'false') + enable_upgrade_value = @configuration[ENABLE_UPGRADE] ? 'true' : 'false' + custom_download_uri = get_from_cfg_or_svc(credentials, CUSTOM_AGENT_URL) + unless custom_download_uri.nil? || enable_upgrade_value != 'true' + @logger.info { 'Switching sl.enableUpgrade to false because agent downloaded from customAgentUrl' } + enable_upgrade_value = 'false' + end + @droplet.java_opts.add_system_property('sl.enableUpgrade', enable_upgrade_value) # 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 @@ -80,8 +96,48 @@ def supports? private + def buildpack_version + version_hash = BuildpackVersion.new.to_hash + if version_hash.key?('version') && version_hash.key?('offline') && version_hash['offline'] + version_hash['version'] + '(offline)' + elsif version_hash.key?('version') + version_hash['version'] + else + 'v-unknown' + end + end + + def expand(file, name, target_directory) + with_timing "Expanding #{name} to #{target_directory.relative_path_from(@droplet.root)}" do + FileUtils.mkdir_p target_directory + shell "unzip -qq #{file.path} -d #{target_directory} 2>&1" + end + end + def agent - @droplet.sandbox + "sl-test-listener-#{@version}.jar" + custom_download_uri = get_value(CUSTOM_AGENT_URL) + if custom_download_uri.nil? + agent_jar_name = "sl-test-listener-#{@version}.jar" + else + jars = Dir["#{@droplet.sandbox}/sl-test-listener*.jar"] + raise 'Failed to find jar which name starts with \'sl-test-listener\' in downloaded zip' if jars.empty? + + agent_jar_name = File.basename(jars[0]) + end + @droplet.sandbox + agent_jar_name + end + + def get_from_cfg_or_svc(svc, config_key) + if @configuration.key?(config_key) + @configuration[config_key] + elsif svc.key?(config_key) + svc[config_key] + end + end + + def get_value(config_key) + svc = @application.services.find_service(FILTER, TOKEN)['credentials'] + get_from_cfg_or_svc(svc, config_key) end # Configuration property names @@ -95,6 +151,8 @@ def agent PROXY = 'proxy' + CUSTOM_AGENT_URL = 'customAgentUrl' + FILTER = /sealights/.freeze private_constant :TOKEN, :ENABLE_UPGRADE, :BUILD_SESSION_ID, :LAB_ID, :PROXY, :FILTER diff --git a/spec/fixtures/stub-sealights-custom-agent-invalid.zip b/spec/fixtures/stub-sealights-custom-agent-invalid.zip new file mode 100644 index 0000000000000000000000000000000000000000..467568b588442dc902f504cb1e7036f70f6e50fc GIT binary patch literal 416 zcmWIWW@Zs#-~hrExpTuApdgfyfkBEvfuT4@H>or;Cq=h7IWaFUwMf@Q&p^*eFDtPq zG=!IdJ$8Rz>MP|Lsr;vArnE$q0#> zSZA41+rM|d{GXeYkdVglA?-PXr=jCimIqgKn2r>j-Pcz)FK^vJ#j1UI^J~w~`S$VJ z)!ipvuU%dG?#$1#j%?vQ@;17cK7YFU@yp(bU0tok$FffOB36n=175 ze!0YyN*%uP#@|)GTjF5fgZ&m?HyK~x65pUKG1EmNVD7<|C+r~&kCaQ+o;cREAY=2< zhw~Zo!^}EbeR#Gy{1S*TWeD(Q=h(-`rLqec7NBql@MdHZVMc@zvK%OkU|>rlh(&&g V1$eWvflOco!VDm-1hkuh0RY{Lk=por;Cq=h7IWaFUwMf@Q&p^*mFDtPq zG=!IdJ$8RzYOnH))D1vfTEWf0$nt`jfdNc#e@IIcOnmU*LBfgbx<^MESS%zXBx+)v zWlC-T-ud!>Zc;)*8pnsU=M0{Pj#F74T+v}VQgC)(U){XCbq5uz_T|m5JwNB$$7@%2 zpLo4?b?Lh^KhHX{h4;wY=wAB#>FUQXdn0yrwH6=CKB;_ZQk10qkqO5_{tl9SNU#3 zXUGpT>uB}i+3N61Ai|U(z?+?8A0L;>E?`)I!XbcA2$iH3m+0nX7MG;r4x!B99YTyu yBFu credentials) + uri = 'https://test-apiurl/getcustomagent/agent.zip' + p = Pathname.new('spec/fixtures/stub-sealights-custom-agent.zip') + allow(application_cache).to receive(:get).with(uri).and_yield(p.open, false) end it 'detects with sealights service' do @@ -48,10 +51,43 @@ end context do - it 'updates JAVA_OPTS sl.tags' do + it 'updates JAVA_OPTS sl.tags with buildpack version number' do + allow_any_instance_of(JavaBuildpack::BuildpackVersion) + .to receive(:to_hash).and_return({ 'version' => '1234', + 'offline' => false, + 'remote' => 'test-remote', + 'hash' => 'test-hash' }) component.release - expect(java_opts).to include('-Dsl.tags=pivotal_cloud_foundry') + expect(java_opts).to include('-Dsl.tags=sl-pcf-1234') + end + + it 'updates JAVA_OPTS sl.tags with buildpack version number and offline info' do + allow_any_instance_of(JavaBuildpack::BuildpackVersion) + .to receive(:to_hash).and_return({ 'version' => '1234', + 'offline' => true, + 'remote' => 'test-remote', + 'hash' => 'test-hash' }) + component.release + + expect(java_opts).to include('-Dsl.tags=sl-pcf-1234\(offline\)') + end + + it 'updates JAVA_OPTS sl.tags with version number' do + allow_any_instance_of(JavaBuildpack::BuildpackVersion) + .to receive(:to_hash).and_return({ 'version' => '1234', + 'remote' => 'test-remote', + 'hash' => 'test-hash' }) + component.release + + expect(java_opts).to include('-Dsl.tags=sl-pcf-1234') + end + + it 'updates JAVA_OPTS sl.tags with information about unknown version number' do + allow_any_instance_of(JavaBuildpack::BuildpackVersion).to receive(:to_hash).and_return({}) + component.release + + expect(java_opts).to include('-Dsl.tags=sl-pcf-v-unknown') end it 'updates JAVA_OPTS sl.buildSessionId' do @@ -151,6 +187,124 @@ end end + context do + let(:credentials) { { 'token' => 'my_token' } } + + let(:configuration) do + { 'build_session_id' => '1234', + 'lab_id' => 'lab1', + 'enable_upgrade' => true, + 'customAgentUrl' => 'https://foo.com/getcustomagent/sealights-custom-agent.zip' } + 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) + uri = 'https://foo.com/getcustomagent/sealights-custom-agent.zip' + p = Pathname.new('spec/fixtures/stub-sealights-custom-agent.zip') + allow(application_cache).to receive(:get).with(uri).and_yield(p.open, false) + allow(Net::HTTP).to receive(:start).with('foo.com', 443, use_ssl: true).and_call_original + stub_request(:get, uri) + .with( + headers: { + 'Accept' => '*/*', + 'User-Agent' => 'Ruby' + } + ).to_return(status: 200, body: '', headers: {}) + + end + + it 'downloads custom agent jar', + cache_fixture: 'stub-sealights-custom-agent.zip' do + component.compile + expect(sandbox + 'sl-test-listener-4.0.1.jar').to exist + end + + it 'customAgentUrl from app configuration overwrites enableUpgrade to false', + cache_fixture: 'stub-sealights-custom-agent.zip' do + component.compile + component.release + expect(java_opts).to include('-Dsl.enableUpgrade=false') + end + end + + context do + let(:credentials) do + { 'token' => 'my_token', + 'customAgentUrl' => 'https://foo.com/getcustomagent/sealights-custom-agent.zip' } + end + + let(:configuration) do + { 'build_session_id' => '1234', + '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) + uri = 'https://foo.com/getcustomagent/sealights-custom-agent.zip' + p = Pathname.new('spec/fixtures/stub-sealights-custom-agent.zip') + allow(application_cache).to receive(:get).with(uri).and_yield(p.open, false) + allow(Net::HTTP).to receive(:start).with('foo.com', 443, use_ssl: true).and_call_original + stub_request(:get, uri) + .with( + headers: { + 'Accept' => '*/*', + 'User-Agent' => 'Ruby' + } + ).to_return(status: 200, body: '', headers: {}) + + end + + it 'downloads custom agent jar based on service settings', + cache_fixture: 'stub-sealights-custom-agent.zip' do + component.compile + expect(sandbox + 'sl-test-listener-4.0.1.jar').to exist + end + + it 'customAgentUrl from service settings forces overwrites enableUpgrade to false', + cache_fixture: 'stub-sealights-custom-agent.zip' do + component.compile + component.release + expect(java_opts).to include('-Dsl.enableUpgrade=false') + end + end + + context do + let(:credentials) { { 'token' => 'my_token' } } + + let(:configuration) do + { 'build_session_id' => '1234', + 'lab_id' => 'lab1', + 'customAgentUrl' => 'https://foo.com/getcustomagent/sealights-custom-agent-invalid.zip' } + 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) + uri = 'https://foo.com/getcustomagent/sealights-custom-agent-invalid.zip' + p = Pathname.new('spec/fixtures/stub-sealights-custom-agent-invalid.zip') + allow(application_cache).to receive(:get).with(uri).and_yield(p.open, false) + stub_request(:get, 'https://foo.com/getcustomagent/sealights-custom-agent-invalid.zip') + .with( + headers: { + 'Accept' => '*/*', + 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', + 'User-Agent' => 'Ruby' + } + ).to_return(status: 200, body: '', headers: {}) + end + + it 'test listener agent jar not found in downloaded zip', + cache_fixture: 'stub-sealights-custom-agent-invalid.zip' do + component.compile + expect { component.release } + .to raise_error(RuntimeError, + /Failed to find jar which name starts with 'sl-test-listener' in downloaded zip/) + end + end + end end