Skip to content

Commit

Permalink
feat: allow Pact Broker to run on Heroku
Browse files Browse the repository at this point in the history
* ignore .data directory

* add support for connecting to a database via URL

* remove old database connection fcn

* add url provider mechanism
  • Loading branch information
adamstrickland authored May 6, 2020
1 parent ba1f1e1 commit f4dc1f1
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 28 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ test.sh
.gitkeep
pact_broker/pact_broker.sqlite
tmp
.data
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ pact_broker/log
pact_broker/tmp
pact_broker/.bundle
tmp
.data
5 changes: 4 additions & 1 deletion pact_broker/config.ru
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ dc.pact_broker_environment_variables.each{ |key, value| $logger.info "#{key}=#{v

app = PactBroker::App.new do | config |
config.logger = $logger
config.database_connection = create_database_connection(config.logger)

db_config = dc.database_configuration
config.database_connection = create_database_connection_from_config(config.logger, db_config)

config.database_connection.timezone = :utc
config.base_url = dc.base_url
config.webhook_host_whitelist = dc.webhook_host_whitelist
Expand Down
21 changes: 1 addition & 20 deletions pact_broker/database_connection.rb
Original file line number Diff line number Diff line change
@@ -1,26 +1,7 @@
require 'sequel'
require_relative 'database_logger'

def create_database_connection(logger)
database_adapter = ENV.fetch('PACT_BROKER_DATABASE_ADAPTER','') != '' ? ENV['PACT_BROKER_DATABASE_ADAPTER'] : 'postgres'

config = {
adapter: database_adapter,
user: ENV['PACT_BROKER_DATABASE_USERNAME'],
password: ENV['PACT_BROKER_DATABASE_PASSWORD'],
host: ENV['PACT_BROKER_DATABASE_HOST'],
database: ENV['PACT_BROKER_DATABASE_NAME'],
encoding: 'utf8'
}

if ENV.fetch('PACT_BROKER_DATABASE_SSLMODE','') != ''
config[:sslmode] = ENV['PACT_BROKER_DATABASE_SSLMODE']
end

if ENV['PACT_BROKER_DATABASE_PORT'] =~ /^\d+$/
config[:port] = ENV['PACT_BROKER_DATABASE_PORT'].to_i
end

def create_database_connection_from_config(logger, config)
##
# Sequel by default does not test connections in its connection pool before
# handing them to a client. To enable connection testing you need to load the
Expand Down
59 changes: 58 additions & 1 deletion pact_broker/docker_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ def webhook_http_method_whitelist
space_delimited_string_list_or_default(:webhook_http_method_whitelist)
end

def database_configuration
if configure_database_via_url?
database_configuration_from_url
else
database_configuration_from_parts
end.merge(encoding: 'utf8', sslmode: env(:database_sslmode)).compact
end

def base_equality_only_on_content_that_affects_verification_results
if env_populated?(:base_equality_only_on_content_that_affects_verification_results)
env(:base_equality_only_on_content_that_affects_verification_results) == 'true'
Expand Down Expand Up @@ -98,5 +106,54 @@ def to_s
end.join(' ')
end
end

private

def configure_database_via_url?
database_url
end

def database_url
if env_populated?(:database_url)
env(:database_url)
elsif env_populated?(:database_url_provider)
@env[env(:database_url_provider)]
end
end

def database_configuration_from_parts
database_adapter = if env_populated?(:database_adapter)
env(:database_adapter)
else
'postgres'
end

config = {
adapter: database_adapter,
user: env(:database_username),
password: env(:database_password),
host: env(:database_host),
database: env(:database_name),
}

if env(:database_port) =~ /^\d+$/
config[:port] = env(:database_port).to_i
end

config
end

def database_configuration_from_url
uri = URI(database_url)

{
adapter: uri.scheme,
user: uri.user,
password: uri.password,
host: uri.host,
database: uri.path.sub(/^\//, ''),
port: (uri.port || 5432).to_i,
}.compact
end
end
end
end
150 changes: 144 additions & 6 deletions spec/docker_configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,152 @@
}
end

let(:expected_environment_variables) do
{
"PACT_BROKER_FOO" => "foo",
"PACT_BROKER_PASSWORD" => "*****"
}
context "when the environment variables contains an arbitrary key" do
let(:expected_environment_variables) do
{
"PACT_BROKER_FOO" => "foo",
"PACT_BROKER_PASSWORD" => "*****"
}
end

its(:pact_broker_environment_variables) { is_expected.to eq expected_environment_variables }
end

its(:pact_broker_environment_variables) { is_expected.to eq expected_environment_variables }
context "when the environment variables contain database keys" do
let(:db_env) do
{
"PACT_BROKER_DATABASE_HOST" => "localhost"
}
end
let(:env) do
super().merge(db_env)
end

let(:expected_environment_variables) do
{
"PACT_BROKER_FOO" => "foo",
"PACT_BROKER_PASSWORD" => "*****"
}
end

its(:pact_broker_environment_variables) { is_expected.to have_key "PACT_BROKER_FOO" }
its(:pact_broker_environment_variables) { is_expected.to have_key "PACT_BROKER_PASSWORD" }
its(:pact_broker_environment_variables) { is_expected.to have_key "PACT_BROKER_DATABASE_HOST" }
its(:pact_broker_environment_variables) { is_expected.not_to have_key "SOMETHING" }
it { expect(subject.pact_broker_environment_variables["PACT_BROKER_PASSWORD"]).to eq "*****" }
end

context "when the environment variables contain a database url key" do
let(:db_env) do
{
"PACT_BROKER_DATABASE_URL" => "postgresql://pactbrokeruser:TheUserPassword@localhost:5432/pactbroker"
}
end
let(:env) do
super().merge(db_env)
end

let(:expected_environment_variables) do
{
"PACT_BROKER_FOO" => "foo",
"PACT_BROKER_PASSWORD" => "*****"
}
end

its(:pact_broker_environment_variables) { is_expected.to have_key "PACT_BROKER_DATABASE_URL" }
its(:pact_broker_environment_variables) { is_expected.not_to have_key "SOMETHING" }
it { expect(subject.pact_broker_environment_variables["PACT_BROKER_PASSWORD"]).to eq "*****" }
end
end

describe "database_configuration" do
let(:env) { super().merge(db_env) }

context "when then configuration is provided as a URL" do
context "using the default env var" do
let(:db_env) do
{
"PACT_BROKER_DATABASE_URL" => "postgresql://pactbrokeruser:TheUserPassword@localhost:5432/pactbroker"
}
end

its(:database_configuration) { is_expected.to be_a Hash }
its("database_configuration.keys") { are_expected.to include :adapter, :user, :password, :host, :database, :encoding, :port }
it { expect(subject.database_configuration[:user]).to eq "pactbrokeruser" }
it { expect(subject.database_configuration[:password]).to eq "TheUserPassword" }
it { expect(subject.database_configuration[:host]).to eq "localhost" }
it { expect(subject.database_configuration[:database]).to eq "pactbroker" }
it { expect(subject.database_configuration[:encoding]).to eq "utf8" }
it { expect(subject.database_configuration[:port]).to eq 5432 }
end

context "using a configured provider and an arbitrary env var" do
let(:db_env) do
{
"PACT_BROKER_DATABASE_URL_PROVIDER" => "DATABASE_URL",
"DATABASE_URL" => "postgresql://pactbrokeruser:TheUserPassword@localhost:5432/pactbroker"
}
end

its(:database_configuration) { is_expected.to be_a Hash }
its("database_configuration.keys") { are_expected.to include :adapter, :user, :password, :host, :database, :encoding, :port }
it { expect(subject.database_configuration[:user]).to eq "pactbrokeruser" }
it { expect(subject.database_configuration[:password]).to eq "TheUserPassword" }
it { expect(subject.database_configuration[:host]).to eq "localhost" }
it { expect(subject.database_configuration[:database]).to eq "pactbroker" }
it { expect(subject.database_configuration[:encoding]).to eq "utf8" }
it { expect(subject.database_configuration[:port]).to eq 5432 }
end
end

context "when then configuration is provided in separate env vars" do
let(:db_env) do
{
"PACT_BROKER_DATABASE_USERNAME" => "pactbrokeruser",
"PACT_BROKER_DATABASE_PASSWORD" => "TheUserPassword",
"PACT_BROKER_DATABASE_HOST" => "localhost",
"PACT_BROKER_DATABASE_NAME" => "pactbroker",
}
end

its(:database_configuration) { is_expected.to be_a Hash }
its("database_configuration.keys") { are_expected.to include :adapter, :user, :password, :host, :database, :encoding }
it { expect(subject.database_configuration[:user]).to eq "pactbrokeruser" }
it { expect(subject.database_configuration[:password]).to eq "TheUserPassword" }
it { expect(subject.database_configuration[:host]).to eq "localhost" }
it { expect(subject.database_configuration[:database]).to eq "pactbroker" }
it { expect(subject.database_configuration[:encoding]).to eq "utf8" }

context "when an adapter is supplied" do
let(:db_env) { super().merge("PACT_BROKER_DATABASE_ADAPTER" => "mysql") }

it { expect(subject.database_configuration[:adapter]).to eq "mysql" }
end

context "when an adapter is not supplied" do
it { expect(subject.database_configuration[:adapter]).to eq "postgres" }
end

context "when a port is supplied" do
let(:db_env) { super().merge("PACT_BROKER_DATABASE_PORT" => port) }

context "and the value is numeric" do
let(:port) { "1234" }

it { expect(subject.database_configuration[:port]).to eq 1234 }
end

context "and the value is not numeric" do
let(:port) { "abc" }

its("database_configuration.keys") { are_expected.not_to include :port }
end
end

context "when a port is not supplied" do
its("database_configuration.keys") { are_expected.not_to include :port }
end
end
end

describe "order_versions_by_date" do
Expand Down

0 comments on commit f4dc1f1

Please sign in to comment.