module PactBroker
TODO remove this class
Not required now we have the auto_detect_main_branch feature
Data migrations run every time the broker starts up, after the schema migrations. Their purpose is to ensure that data integrity is maintained during rolling migrations in architectures with multiple application instances running against the same database (eg. EC2 autoscaling group) where “old” data might be inserted by the application instance running the previous version of the code AFTER the schema migrations have been run on the first application instance with the new version of the code.
This class most accurately represents a PactPublication
The Wisper implementation of temporary listeners clears all listeners at the end of the block, rather the just the ones that were supplied in block. This implementation just clears the specified ones, allowing multiple temporary overlapping listeners.
A collection of matrix rows with the same pact publication id It's basically a normalised view of a denormalised view :( A pact publication may be the overall latest, and/or the latest for a tag
Represents the integration relationship between a consumer and a provider in the context of a matrix or can-i-deploy query. If the required flag is set, then one of the pacticipants (consumers) specified in the HTTP query requires the provider. It would not be required if a provider was specified, and it had an integration with a consumer.
The difference between `join_verifications_for` and `join_verifications` is that the left outer join is done on a pre-filtered dataset in `join_verifications_for`, so that we get a row with null verification fields for a pact that has been verified by a different version of the provider we're interested in, rather than being excluded from the dataset altogether.
A selector with the pacticipant id, name, version number, and version id set This is created from either specified or inferred data, based on the user's query eg. can-i-deploy –pacticipant Foo –version 1 (this is a specified selector)
--to prod (this is used to create inferred selectors)
TODO DELETE THIS!!!
A head pact is the pact for the latest consumer version with the specified tag (ignoring later versions that might have the specified tag but no pact)
All of these pacts have the same underlying pact_version_sha (content) No point verifying them multiple times, so squash all the relevant info into one “verifiable pact”
Splits all index_items up into groups of non-connecting index_items.
require 'pact_broker/tasks'
PactBroker::DB::DataMigrationTask.new
do | task |
require 'my_app/db' task.database_connection = MyApp::DB
end
require 'pact_broker/tasks'
PactBroker::DB::MigrationTask.new
do | task |
require 'my_app/db' task.database_connection = MyApp::DB
end
require 'pact_broker/tasks'
PactBroker::DB::VersionTask.new
do | task |
require 'my_app/db' task.database_connection = MyApp::DB
end
TODO handle 404 gracefully
The concept of “stale” (the pact used to be verified but then it changed and we haven't got a new verification result yet) only really make sense if we're trying to summarise the state of an integration or pseudo branch. Once we start showing multiple pacts for each integration (ie. the latest for each tag) then each pact version is either verified, or it's not verified.
Represents the relationship between a webhook and the event and object that caused it to be triggered. eg a pact publication
Constants
- CONSUMER_VERSION_HEADER
- DO_NOT_ROLLBACK
- INTEGRATIONS_TABLES
- PACT_PARSING_OPTIONS
- VERSION
Public Class Methods
rubocop: disable Metrics/MethodLength
# File lib/pact_broker/api.rb, line 27 def self.build_api(application_context = PactBroker::ApplicationContext.default_application_context) pact_api = Webmachine::Application.new do |app| app.routes do add(["trace", :*], Webmachine::Trace::TraceResource) unless ENV["RACK_ENV"] == "production" add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "versions"], Api::Resources::PactVersions, {resource_name: "pact_publications"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "versions", :consumer_version_number], Api::Resources::Pact, {resource_name: "pact_publication", deprecated: true} # Not the standard URL, but keep for backwards compatibility add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "tag", :tag], Api::Resources::TaggedPactVersions, {resource_name: "tagged_pact_publications"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "branch", :branch_name], Api::Resources::PactVersionsForBranch, {resource_name: "pact_publications_for_branch"} # Pacts add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "version", :consumer_version_number], Api::Resources::Pact, {resource_name: "pact_publication"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "pact-version", :pact_version_sha], Api::Resources::PactVersion, {resource_name: "pact_publication"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "pact-version", :pact_version_sha, "metadata", :metadata], Api::Resources::PactVersion, {resource_name: "pact_publication"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "version", :consumer_version_number, "previous-distinct"], Api::Resources::PreviousDistinctPactVersion, {resource_name: "previous_distinct_pact_version"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "version", :consumer_version_number, "diff", "previous-distinct"], Api::Resources::PactContentDiff, {resource_name: "previous_distinct_pact_version_diff"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "version", :consumer_version_number, "diff", "version", :comparison_consumer_version], Api::Resources::PactContentDiff, {resource_name: "pact_version_diff_by_consumer_version"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "pact-version", :pact_version_sha, "diff", "pact-version", :comparison_pact_version_sha], Api::Resources::PactContentDiff, {resource_name: "pact_version_diff_by_pact_version_sha"} # Verifications add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "pact-version", :pact_version_sha, "verification-results"], Api::Resources::Verifications, {resource_name: "verification_results"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "pact-version", :pact_version_sha, "metadata", :metadata, "verification-results"], Api::Resources::Verifications, {resource_name: "verification_results"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "version", :consumer_version_number, "verification-results", "latest"], Api::Resources::LatestVerificationForPact, {resource_name: "latest_verification_results_for_pact_publication"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "pact-version", :pact_version_sha, "verification-results", "latest"], Api::Resources::LatestVerificationForPact, {resource_name: "latest_verification_results_for_pact_version"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "pact-version", :pact_version_sha, "verification-results", :verification_number], Api::Resources::Verification, {resource_name: "verification_result"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "pact-version", :pact_version_sha, "metadata", :metadata, "verification-results", :verification_number], Api::Resources::Verification, {resource_name: "verification_result"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "pact-version", :pact_version_sha, "verification-results", :verification_number, "triggered-webhooks"], Api::Resources::VerificationTriggeredWebhooks, {resource_name: "verification_result_triggered_webhooks"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "latest", "verification-results","latest"], Api::Resources::LatestVerificationForLatestPact, {resource_name: "latest_verification_results_for_latest_pact_publication"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "latest", :tag, "verification-results","latest"], Api::Resources::LatestVerificationForLatestPact, {resource_name: "latest_verification_results_for_latest_tagged_pact_publication"} add ["verification-results", "consumer", :consumer_name, "version", :consumer_version_number,"latest"], Api::Resources::LatestVerificationsForConsumerVersion, {resource_name: "verification_results_for_consumer_version"} # Badges add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "latest", "badge"], Api::Resources::Badge, {resource_name: "latest_pact_badge"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "latest", :tag, "badge"], Api::Resources::Badge, {resource_name: "latest_tagged_pact_badge"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "latest-untagged", "badge"], Api::Resources::Badge, {resource_name: "latest_untagged_pact_badge", tag: :untagged} # Latest pacts add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "latest"], Api::Resources::LatestPact, {resource_name: "latest_pact_publication"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "latest", :tag], Api::Resources::LatestPact, {resource_name: "latest_tagged_pact_publication"} add ["pacts", "provider", :provider_name], Api::Resources::ProviderPacts, {resource_name: "provider_pact_publications"} add ["pacts", "provider", :provider_name, "tag", :tag], Api::Resources::ProviderPacts, {resource_name: "tagged_provider_pact_publications"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "latest-untagged"], Api::Resources::LatestPact, {resource_name: "latest_untagged_pact_publication", tag: :untagged} add ["pacts", "provider", :provider_name, "latest"], Api::Resources::LatestProviderPacts, {resource_name: "latest_provider_pact_publications"} add ["pacts", "provider", :provider_name, "latest", :tag], Api::Resources::LatestProviderPacts, {resource_name: "latest_tagged_provider_pact_publications"} add ["pacts", "latest"], Api::Resources::LatestPacts, {resource_name: "latest_pacts"} # Pacts for verification add ["pacts", "provider", :provider_name, "for-verification"], Api::Resources::ProviderPactsForVerification, {resource_name: "pacts_for_verification"} # Deprecated pact add ["pact", "provider", :provider_name, "consumer", :consumer_name, "version", :consumer_version_number], Api::Resources::Pact, {resource_name: "pact_publication", deprecated: "true"} # Deprecate, singular /pact add ["pact", "provider", :provider_name, "consumer", :consumer_name, "latest"], Api::Resources::LatestPact, {resource_name: "latest_pact_publications", deprecated: "true"} # Pacticipants add ["pacticipants"], Api::Resources::Pacticipants, {resource_name: "pacticipants"} add ["pacticipants", "label", :label_name], PactBroker::Api::Resources::PacticipantsForLabel, {resource_name: "pacticipants_for_label"} add ["pacticipants", :pacticipant_name], Api::Resources::Pacticipant, {resource_name: "pacticipant"} add ["pacticipants", :pacticipant_name, "versions"], Api::Resources::Versions, {resource_name: "pacticipant_versions"} add ["pacticipants", :pacticipant_name, "versions", :pacticipant_version_number], Api::Resources::Version, {resource_name: "pacticipant_version"} add ["pacticipants", :pacticipant_name, "latest-version", :tag], Api::Resources::LatestVersion, {resource_name: "latest_tagged_pacticipant_version"} add ["pacticipants", :pacticipant_name, "latest-version", :tag, "can-i-deploy", "to", :to], Api::Resources::CanIDeployPacticipantVersion, { resource_name: "can_i_deploy_latest_tagged_version" } add ["pacticipants", :pacticipant_name, "latest-version", :tag, "can-i-deploy", "to", :to, "badge"], Api::Resources::CanIDeployBadge, { resource_name: "can_i_deploy_badge" } add ["pacticipants", :pacticipant_name, "latest-version"], Api::Resources::LatestVersion, {resource_name: "latest_pacticipant_version"} add ["pacticipants", :pacticipant_name, "versions", :pacticipant_version_number, "tags", :tag_name], Api::Resources::Tag, {resource_name: "pacticipant_version_tag"} add ["pacticipants", :pacticipant_name, "labels", :label_name], Api::Resources::Label, {resource_name: "pacticipant_label"} add ["pacticipants", :pacticipant_name, "branches", :branch_name, "versions", :version_number], Api::Resources::BranchVersion, { resource_name: "branch_version" } # Webhooks add ["webhooks", "provider", :provider_name, "consumer", :consumer_name ], Api::Resources::PacticipantWebhooks, {resource_name: "pacticipant_webhooks"} add ["webhooks", "provider", :provider_name], Api::Resources::PacticipantWebhooks, {resource_name: "provider_webhooks"} add ["webhooks", "consumer", :consumer_name], Api::Resources::PacticipantWebhooks, {resource_name: "consumer_webhooks"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "webhooks"], Api::Resources::PactWebhooks, {resource_name: "pact_webhooks"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "webhooks", "status"], Api::Resources::PactWebhooksStatus, {resource_name: "pact_webhooks_status"} add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "version", :consumer_version_number, "triggered-webhooks"], Api::Resources::PactTriggeredWebhooks, {resource_name: "pact_triggered_webhooks"} add ["webhooks", "execute" ], Api::Resources::WebhookExecution, {resource_name: "execute_unsaved_webhook"} add ["webhooks", :uuid ], Api::Resources::Webhook, {resource_name: "webhook"} add ["triggered-webhooks", :uuid, "logs" ], Api::Resources::TriggeredWebhookLogs, { resource_name: "triggered_webhook_logs" } add ["webhooks", :uuid, "execute" ], Api::Resources::WebhookExecution, {resource_name: "execute_webhook"} add ["webhooks"], Api::Resources::AllWebhooks, {resource_name: "webhooks"} add ["relationships"], Api::Resources::Relationships, {resource_name: "relationships"} add ["groups", :pacticipant_name], Api::Resources::Group, {resource_name: "group"} # matrix add ["matrix", "provider", :provider_name, "consumer", :consumer_name], Api::Resources::MatrixForConsumerAndProvider, {resource_name: "matrix_consumer_provider"} add ["matrix", "provider", :provider_name, "latest", :provider_tag, "consumer", :consumer_name, "latest", :tag, "badge"], Api::Resources::MatrixBadge, {resource_name: "matrix_tag_badge"} add ["matrix"], Api::Resources::Matrix, {resource_name: "matrix"} add ["can-i-deploy"], Api::Resources::CanIDeploy, {resource_name: "can_i_deploy"} add ["dashboard"], Api::Resources::Dashboard, {resource_name: "dashboard"} add ["dashboard", "provider", :provider_name, "consumer", :consumer_name ], Api::Resources::Dashboard, {resource_name: "integration_dashboard"} add ["test","error"], Api::Resources::ErrorTest, {resource_name: "error_test"} add ["contracts", "publish"], Api::Resources::PublishContracts, { resource_name: "publish_contracts" } add ["environments"], Api::Resources::Environments, { resource_name: "environments" } add ["environments", :environment_uuid], Api::Resources::Environment, { resource_name: "environment" } add ["environments", :environment_uuid, "deployed-versions", "currently-deployed"], Api::Resources::CurrentlyDeployedVersionsForEnvironment, { resource_name: "environment_currently_deployed_deployed_versions" } add ["environments", :environment_uuid, "released-versions", "currently-supported"], Api::Resources::CurrentlySupportedVersionsForEnvironment, { resource_name: "environment_currently_supported_released_versions" } add ["pacticipants", :pacticipant_name, "versions", :pacticipant_version_number, "deployed-versions", "environment", :environment_uuid], Api::Resources::DeployedVersionsForVersionAndEnvironment, { resource_name: "deployed_versions_for_version_and_environment" } add ["pacticipants", :pacticipant_name, "versions", :pacticipant_version_number, "released-versions", "environment", :environment_uuid], Api::Resources::ReleasedVersionsForVersionAndEnvironment, { resource_name: "released_versions_for_version_and_environment" } add ["released-versions", :uuid], Api::Resources::ReleasedVersion, { resource_name: "released_version" } add ["deployed-versions", :uuid], Api::Resources::DeployedVersion, { resource_name: "deployed_version" } add ["integrations"], Api::Resources::Integrations, {resource_name: "integrations"} add ["integrations", "provider", :provider_name, "consumer", :consumer_name], Api::Resources::Integration, {resource_name: "integration"} add ["metrics"], Api::Resources::Metrics, {resource_name: "metrics"} add [], Api::Resources::Index, {resource_name: "index"} end end # naughty, but better than setting each route manually pact_api.routes.each do | route | route.instance_variable_get(:@bindings)[:application_context] = application_context end pact_api.configure do |config| config.adapter = :RackMapped end pact_api.adapter end
# File lib/pact_broker/configuration.rb, line 10 def self.configuration RequestStore.store[:pact_broker_configuration] ||= Configuration.default_configuration end
# File lib/pact_broker/initializers/database_connection.rb, line 6 def self.create_database_connection(config, logger = nil) logger&.info "Connecting to database:", payload: "#{config.merge(password: "*****")}" sequel_config = config.dup max_retries = sequel_config.delete(:connect_max_retries) || 0 connection_validation_timeout = config.delete(:connection_validation_timeout) configure_logger(sequel_config) create_sqlite_database_dir(config) connection = with_retries(max_retries, logger) do Sequel.connect(sequel_config) end logger&.info "Connected to database #{sequel_config[:database]}" configure_connection(connection, connection_validation_timeout) end
# File lib/pact_broker/feature_toggle.rb, line 22 def self.feature_enabled?(feature, ignore_env = false) FeatureToggle.enabled?(feature, ignore_env) end
# File lib/pact_broker/policies.rb, line 46 def self.policy!(*args) PactBroker.configuration.policy_builder.call(*args) end
# File lib/pact_broker/policies.rb, line 50 def self.policy_scope!(*args) PactBroker.configuration.policy_scope_builder.call(*args) end
# File lib/pact_broker/project_root.rb, line 4 def self.project_root @project_root ||= Pathname.new(File.expand_path("../../../",__FILE__)).freeze end
@private, for testing only
# File lib/pact_broker/configuration.rb, line 23 def self.reset_configuration RequestStore.store[:pact_broker_configuration] = Configuration.default_configuration end
# File lib/pact_broker/api.rb, line 156 def self.routes require "webmachine/describe_routes" @routes ||= Webmachine::DescribeRoutes.call([API.application]) end
# File lib/pact_broker/configuration.rb, line 14 def self.set_configuration(configuration) RequestStore.store[:pact_broker_configuration] = configuration end
# File lib/pact_broker/configuration.rb, line 18 def self.with_runtime_configuration_overrides(overrides, &block) self.configuration.with_runtime_configuration_overrides(overrides, &block) end
Private Class Methods
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 “connection_validator” extension like below. The connection validator extension is configurable, by default it only checks connections once per hour:
sequel.rubyforge.org/rdoc-plugins/files/lib/sequel/extensions/connection_validator_rb.html
A gotcha here is that it is not enough to enable the “connection_validator” extension, we also need to specify that we want to use the threaded connection pool, as noted in the documentation for the extension.
-1 means that connections will be validated every time, which avoids errors when databases are restarted and connections are killed. This has a performance penalty, so consider increasing this timeout if building a frequently accessed service.
# File lib/pact_broker/initializers/database_connection.rb, line 75 def self.configure_connection(connection, connection_validation_timeout) connection.extension(:connection_validator) connection.pool.connection_validation_timeout = connection_validation_timeout if connection_validation_timeout connection end
# File lib/pact_broker/initializers/database_connection.rb, line 49 def self.configure_logger(sequel_config) if sequel_config[:sql_log_level] == :none sequel_config.delete(:sql_log_level) elsif logger sequel_config[:logger] = PactBroker::DB::LogQuietener.new(logger) end end
# File lib/pact_broker/initializers/database_connection.rb, line 42 def self.create_sqlite_database_dir(config) if config[:adapter] == "sqlite" && config[:database] && !File.exist?(File.dirname(config[:database])) logger&.info "Creating directory #{File.expand_path(File.dirname(config[:database]))} for Sqlite database" FileUtils.mkdir_p(File.dirname(config[:database])) end end
# File lib/pact_broker/initializers/database_connection.rb, line 24 def self.with_retries(max_retries, logger) tries = 0 max_tries = max_retries + 1 wait = 3 begin yield rescue StandardError => e if (tries += 1) < max_tries logger&.info "Error connecting to database (#{e.class}). Waiting #{wait} seconds and trying again. #{max_tries-tries} tries to go." sleep wait retry else raise e end end end