diff --git a/lib/liberty_buildpack/container/liberty.rb b/lib/liberty_buildpack/container/liberty.rb index 6be4cd704..a0540f318 100644 --- a/lib/liberty_buildpack/container/liberty.rb +++ b/lib/liberty_buildpack/container/liberty.rb @@ -550,7 +550,8 @@ def download_and_install_liberty # read opt-out of service bindings information from env (manifest.yml), and initialise # services manager, which will be used to list dependencies for any bound services. - @services_manager = ServicesManager.new(@vcap_services, runtime_vars_dir(root), @environment['services_autoconfig_excludes']) + context = { app_dir: @app_dir, java_home: @java_home } + @services_manager = ServicesManager.new(@vcap_services, runtime_vars_dir(root), @environment['services_autoconfig_excludes'], context) # if the liberty feature manager and repository are not being used to install server # features, download the required files from the various configured locations. If the diff --git a/lib/liberty_buildpack/container/services_manager.rb b/lib/liberty_buildpack/container/services_manager.rb index 7cb1f05c8..78da964db 100644 --- a/lib/liberty_buildpack/container/services_manager.rb +++ b/lib/liberty_buildpack/container/services_manager.rb @@ -32,10 +32,11 @@ module LibertyBuildpack::Container # The class that encapsulate access to services and services information. class ServicesManager - def initialize(vcap_services, server_dir, opt_out_string) + def initialize(vcap_services, server_dir, opt_out_string, context = nil) @logger = LibertyBuildpack::Diagnostics::LoggerFactory.get_logger @logger.debug("init: server dir is #{server_dir}, vcap_services is #{LibertyBuildpack::Util.safe_vcap_services(vcap_services)} and opt_out is #{opt_out_string}") @opt_out = parse_opt_out(opt_out_string) + @context = context FileUtils.mkdir_p(server_dir) # The collection of service instances that require full autoconfig @services_full_autoconfig = [] @@ -411,7 +412,11 @@ def create_instance(element, type, config, instance_data) # require the file filename = File.join(File.expand_path('..', File.dirname(__FILE__)), 'services', file) require filename - instance = class_name.constantize.new(type, config) + if class_name.constantize.instance_method(:initialize).parameters.map(&:last).map(&:to_s).include? 'context' + instance = class_name.constantize.new(type, config, @context) + else + instance = class_name.constantize.new(type, config) + end instance.parse_vcap_services(element, instance_data) instance end diff --git a/lib/liberty_buildpack/services/compose_postgresql.rb b/lib/liberty_buildpack/services/compose_postgresql.rb new file mode 100644 index 000000000..b7c90d434 --- /dev/null +++ b/lib/liberty_buildpack/services/compose_postgresql.rb @@ -0,0 +1,96 @@ +# Encoding: utf-8 +# IBM WebSphere Application Server Liberty Buildpack +# Copyright 2014 the original author 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 'rexml/document' +require 'liberty_buildpack/services/client_jar_utils' +require 'liberty_buildpack/services/postgresql' + +module LibertyBuildpack::Services + + #------------------------------------------------------------------------------------ + # The ComposePostgreSQL class is the class for Compose for PostgreSQL relational database resources. + #------------------------------------------------------------------------------------ + class ComposePostgreSQL < PostgreSQL + + #------------------------------------------------------------------------------------ + # Initialize + # + # @param type - the vcap_services type + # @param config - a hash containing the configuration data from the yml file. + #------------------------------------------------------------------------------------ + def initialize(type, config, context) + super(type, config) + @config_type = 'compose-postgresql' + @app_dir = context[:app_dir] + end + + #----------------------------------------------------------------------------------------- + # parse the vcap services and create cloud properties + # + # @param element - the root element of the REXML document for runtime-vars.xml + # @param instance - the hash containing the vcap_services data for this instance + #------------------------------------------------------------------------------------------ + def parse_vcap_services(element, instance) + super + + credentials = instance['credentials'] || {} + + # fix the database name as the relational_db uses 'name' from vcap_services + # which does not match the actual database name in compose + service_uri = credentials['uri'] + uri = URI.parse(service_uri) + db_var_name = "cloud.services.#{@service_name}.connection.db" + new_element = REXML::Element.new('variable', element) + new_element.add_attribute('name', db_var_name) + new_element.add_attribute('value', uri.path[1..-1]) + @db_name = "${#{db_var_name}}" + + @service_cert = credentials['ca_certificate_base64'] + raise "Resource #{@service_name} does not contain a #{conn_prefix}uri property" if @service_cert.nil? + end + + protected + + #------------------------------------------------------------------------------------ + # Method to customize properties - called on create or update. + # + # @param properties_element - the properties element + #------------------------------------------------------------------------------------ + def modify_properties(properties_element) + save_cert + + properties_element.add_attribute('ssl', 'true') + properties_element.add_attribute('sslMode', 'verify-ca') + properties_element.add_attribute('sslRootCert', "/home/vcap/app/#{CRT_DIRECTORY}/#{CRT_FILE}") + end + + private + + CRT_DIRECTORY = '.compose_postgresql'.freeze + + CRT_FILE = 'cacert.pem'.freeze + + def save_cert + cert_dir = File.join(@app_dir, CRT_DIRECTORY) + FileUtils.mkdir_p(cert_dir) + cert_file = File.join(cert_dir, CRT_FILE) + File.open(cert_file, 'w+') { |f| f.write(Base64.decode64(@service_cert)) } + end + + end + +end diff --git a/lib/liberty_buildpack/services/config/compose_postgresql.yml b/lib/liberty_buildpack/services/config/compose_postgresql.yml new file mode 100644 index 000000000..46a7c868c --- /dev/null +++ b/lib/liberty_buildpack/services/config/compose_postgresql.yml @@ -0,0 +1,34 @@ +# IBM WebSphere Application Server Liberty Buildpack +# Copyright 2014 the original author 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. + +# Service configuration +--- +# attributes required by all service plugins +class_name : LibertyBuildpack::Services::ComposePostgreSQL +class_file : compose_postgresql.rb +server_xml_stanza : dataSource +service_filter : 'compose-for-postgresql' + +# plugin specific attributes +features: + if: ['jdbc-4.0'] + then: [] + else: ['jdbc-4.1'] + +client_jars : 'postgresql-.*.jar' + +driver: + version: 9.4.+ + repository_root: "https://download.run.pivotal.io/postgresql-jdbc" diff --git a/lib/liberty_buildpack/services/config/postgresql.yml b/lib/liberty_buildpack/services/config/postgresql.yml index f05e109ab..04fec4a4a 100644 --- a/lib/liberty_buildpack/services/config/postgresql.yml +++ b/lib/liberty_buildpack/services/config/postgresql.yml @@ -19,7 +19,7 @@ class_name : LibertyBuildpack::Services::PostgreSQL class_file : postgresql.rb server_xml_stanza : dataSource -service_filter : 'postgresql|elephantsql' +service_filter : '(?' + expected_vars << '' + expected_vars << '' + expected_vars << '' + expected_vars << '' + expected_vars << '' + expected_vars << '' + + runtime_vars = File.join(root, 'runtime-vars.xml') + validate_xml(runtime_vars, expected_vars) + end + + def check_server_xml_create(root, sm) + server_xml = File.join(root, 'server.xml') + server_xml_doc = REXML::Document.new('') + + sm.update_configuration(server_xml_doc, true, root) + + File.open(server_xml, 'w') { |file| server_xml_doc.write(file) } + + expected_config = [] + expected_config << 'jdbc-4.1' + t1 = "" + ssl_opts = "ssl='true' sslMode='verify-ca' sslRootCert='/home/vcap/app/.compose_postgresql/cacert.pem'" + t2 = "" + t3 = '' + expected_config << t1 + t2 + t3 + driver_info = "javax.sql.ConnectionPoolDataSource='org.postgresql.ds.PGConnectionPoolDataSource' javax.sql.XADataSource='org.postgresql.xa.PGXADataSource'" + expected_config << "" + expected_config << "" + validate_xml(server_xml, expected_config) + end + + def check_server_xml_update(root, sm) # rubocop:disable MethodLength + driver_info = "javax.sql.ConnectionPoolDataSource='org.postgresql.ds.PGConnectionPoolDataSource' javax.sql.XADataSource='org.postgresql.xa.PGXADataSource'" + + contents = [] + contents << '' + contents << 'jsp-2.2' + t1 = "" + t2 = "" + t3 = '' + contents << t1 + t2 + t3 + contents << "" + contents << "" + contents << '' + + server_xml_doc = REXML::Document.new(contents.join) + server_xml = File.join(root, 'server.xml') + sm.update_configuration(server_xml_doc, false, root) + + File.open(server_xml, 'w') { |file| server_xml_doc.write(file) } + + # check if dataSource properties and library were updated. Other elements should not be updated. + expected_config = [] + expected_config << 'jsp-2.2' + expected_config << 'jdbc-4.1' + t1 = "" + ssl_opts = "ssl='true' sslMode='verify-ca' sslRootCert='/home/vcap/app/.compose_postgresql/cacert.pem'" + t2 = "" + t3 = '' + expected_config << t1 + t2 + t3 + expected_config << "" + expected_config << "" + validate_xml(server_xml, expected_config) + end + + def run_test(vcap_services) + Dir.mktmpdir do |root| + context = { app_dir: root } + sm = LibertyBuildpack::Container::ServicesManager.new(vcap_services, root, nil, context) + check_variables(root, sm) + check_server_xml_create(root, sm) + check_server_xml_update(root, sm) + end + end + + it 'on Bluemix (compose for postgresql)' do + vcap_services = {} + postgresql = {} + postgresql['name'] = 'myDatabase' + postgresql['label'] = 'compose-for-postgresql' + postgresql_credentials = {} + postgresql_credentials['name'] = 'foobar' + postgresql_credentials['uri'] = 'postgres://myUser:myPassword@myHost.com:5432/myDb' + postgresql_credentials['ca_certificate_base64'] = 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNDVENDQVkrZ0F3SUJBZ0lRYUVwWWNJQnI4SThDK3ZiZTZMQ1FrREFLQmdncWhrak9QUVFEQXpCR01Rc3dDUVlEVlFRR0V3SkQKVGpFYU1CZ0dBMVVFQ2hNUlYyOVRhV2R1SUVOQklFeHBiV2wwWldReEd6QVpCZ05WQkFNVEVrTkJJRmR2VTJsbmJpQkZRME1nVW05dgpkREFlRncweE5ERXhNRGd3TURVNE5UaGFGdzAwTkRFeE1EZ3dNRFU0TlRoYU1FWXhDekFKQmdOVkJBWVRBa05PTVJvd0dBWURWUVFLCkV4RlhiMU5wWjI0Z1EwRWdUR2x0YVhSbFpERWJNQmtHQTFVRUF4TVNRMEVnVjI5VGFXZHVJRVZEUXlCU2IyOTBNSFl3RUFZSEtvWkkKemowQ0FRWUZLNEVFQUNJRFlnQUU0ZjJPdUVNa3E1WjdoY0s2QzYyTjREcmpKTG5Tc2I2SU9zcS9Tcmo1N3l3dnIxRlFQRWQxYlBpVQp0NXY4S0I3RlZNeGpuUlpMVThIbklLdk5yQ1hTZjQvQ3dWcUNYakNMZWxUT0E3V1JmNnFVME5HS1NNeUNCU2FoMVZFUzFuczJvMEl3ClFEQU9CZ05WSFE4QkFmOEVCQU1DQVFZd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXF2M1ZXcVAyaDRzeWhmM1IKTWx1QVJaUHpBN2d3Q2dZSUtvWkl6ajBFQXdNRGFBQXdaUUl4QU9Ta2hMQ0IxVDJ3ZEt5VXBPZ09QUUIwVEtHWGEva05VVHloMlR2MApEYXVwbjc1T2NzcUYxTm5zdFRKRkdHK3JyUUl3ZmNmM2FXTXZvZUdZN3hNUTBYay8wZjdxTzMvZVZ2U1FzUlVSMkxJaUZkQXZ3eVl1CmEvR1JzcEJsOUpybWtPNUsKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=' + postgresql['credentials'] = postgresql_credentials + vcap_services['compose-for-postgresql'] = [postgresql] + + run_test(vcap_services) + end + + end + + end + +end diff --git a/spec/liberty_buildpack/services/mysql_spec.rb b/spec/liberty_buildpack/services/mysql_spec.rb index 764108611..cedb75f3f 100644 --- a/spec/liberty_buildpack/services/mysql_spec.rb +++ b/spec/liberty_buildpack/services/mysql_spec.rb @@ -127,7 +127,7 @@ def check_server_xml_create(root, sm) end def check_server_xml_update(root, sm) - driver_info = "javax.sql.ConnectionPoolDataSource='org.mariadb.jdbc.MySQLDataSource'" + driver_info = "javax.sql.ConnectionPoolDataSource='org.mariadb.jdbc.MySQLDataSource' javax.sql.XADataSource='org.mariadb.jdbc.MySQLDataSource'" contents = [] contents << '' diff --git a/spec/liberty_buildpack/services/postgresql_spec.rb b/spec/liberty_buildpack/services/postgresql_spec.rb index d4786bd46..241b06602 100644 --- a/spec/liberty_buildpack/services/postgresql_spec.rb +++ b/spec/liberty_buildpack/services/postgresql_spec.rb @@ -129,7 +129,7 @@ def check_server_xml_create(root, sm) end def check_server_xml_update(root, sm) - driver_info = "javax.sql.ConnectionPoolDataSource='org.postgresql.ds.PGConnectionPoolDataSource'" + driver_info = "javax.sql.ConnectionPoolDataSource='org.postgresql.ds.PGConnectionPoolDataSource' javax.sql.XADataSource='org.postgresql.xa.PGXADataSource'" contents = [] contents << ''