diff options
31 files changed, 1843 insertions, 1182 deletions
@@ -425,7 +425,7 @@ gem 'gitlab-mail_room', '~> 0.0.3', require: 'mail_room' gem 'email_reply_trimmer', '~> 0.1' gem 'html2text' -gem 'ruby-prof', '~> 1.0.0' +gem 'ruby-prof', '~> 1.3.0' gem 'stackprof', '~> 0.2.15', require: false gem 'rbtrace', '~> 0.4', require: false gem 'memory_profiler', '~> 0.9', require: false diff --git a/Gemfile.lock b/Gemfile.lock index e96a38ccf22..bb64fb09649 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -951,7 +951,7 @@ GEM i18n ruby-fogbugz (0.2.1) crack (~> 0.4) - ruby-prof (1.0.0) + ruby-prof (1.3.1) ruby-progressbar (1.10.1) ruby-saml (1.7.2) nokogiri (>= 1.5.10) @@ -1358,7 +1358,7 @@ DEPENDENCIES rubocop-performance (~> 1.4.1) rubocop-rspec (~> 1.37.0) ruby-fogbugz (~> 0.2.1) - ruby-prof (~> 1.0.0) + ruby-prof (~> 1.3.0) ruby-progressbar ruby_parser (~> 3.8) rubyzip (~> 2.0.0) diff --git a/app/models/concerns/bulk_insert_safe.rb b/app/models/concerns/bulk_insert_safe.rb index a61db2dc148..f1a2d566e97 100644 --- a/app/models/concerns/bulk_insert_safe.rb +++ b/app/models/concerns/bulk_insert_safe.rb @@ -68,6 +68,9 @@ module BulkInsertSafe # @param [Boolean] validate Whether validations should run on [items] # @param [Integer] batch_size How many items should at most be inserted at once # @param [Boolean] skip_duplicates Marks duplicates as allowed, and skips inserting them + # @param [Symbol] returns Pass :ids to return an array with the primary key values + # for all inserted records or nil to omit the underlying + # RETURNING SQL clause entirely. # @param [Proc] handle_attributes Block that will receive each item attribute hash # prior to insertion for further processing # @@ -78,10 +81,11 @@ module BulkInsertSafe # # @return true if operation succeeded, throws otherwise. # - def bulk_insert!(items, validate: true, skip_duplicates: false, batch_size: DEFAULT_BATCH_SIZE, &handle_attributes) + def bulk_insert!(items, validate: true, skip_duplicates: false, returns: nil, batch_size: DEFAULT_BATCH_SIZE, &handle_attributes) _bulk_insert_all!(items, validate: validate, on_duplicate: skip_duplicates ? :skip : :raise, + returns: returns, unique_by: nil, batch_size: batch_size, &handle_attributes) @@ -94,6 +98,9 @@ module BulkInsertSafe # @param [Boolean] validate Whether validations should run on [items] # @param [Integer] batch_size How many items should at most be inserted at once # @param [Symbol/Array] unique_by Defines index or columns to use to consider item duplicate + # @param [Symbol] returns Pass :ids to return an array with the primary key values + # for all inserted or updated records or nil to omit the + # underlying RETURNING SQL clause entirely. # @param [Proc] handle_attributes Block that will receive each item attribute hash # prior to insertion for further processing # @@ -109,10 +116,11 @@ module BulkInsertSafe # # @return true if operation succeeded, throws otherwise. # - def bulk_upsert!(items, unique_by:, validate: true, batch_size: DEFAULT_BATCH_SIZE, &handle_attributes) + def bulk_upsert!(items, unique_by:, returns: nil, validate: true, batch_size: DEFAULT_BATCH_SIZE, &handle_attributes) _bulk_insert_all!(items, validate: validate, on_duplicate: :update, + returns: returns, unique_by: unique_by, batch_size: batch_size, &handle_attributes) @@ -120,21 +128,30 @@ module BulkInsertSafe private - def _bulk_insert_all!(items, on_duplicate:, unique_by:, validate:, batch_size:, &handle_attributes) - return true if items.empty? + def _bulk_insert_all!(items, on_duplicate:, returns:, unique_by:, validate:, batch_size:, &handle_attributes) + return [] if items.empty? + + returning = + case returns + when :ids + [primary_key] + when nil + false + else + raise ArgumentError, "returns needs to be :ids or nil" + end transaction do - items.each_slice(batch_size) do |item_batch| + items.each_slice(batch_size).flat_map do |item_batch| attributes = _bulk_insert_item_attributes( item_batch, validate, &handle_attributes) ActiveRecord::InsertAll - .new(self, attributes, on_duplicate: on_duplicate, unique_by: unique_by) - .execute + .new(self, attributes, on_duplicate: on_duplicate, returning: returning, unique_by: unique_by) + .execute + .pluck(primary_key) end end - - true end def _bulk_insert_item_attributes(items, validate_items) diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb index f4214410226..fa098e7417c 100644 --- a/app/services/projects/import_export/export_service.rb +++ b/app/services/projects/import_export/export_service.rb @@ -58,7 +58,7 @@ module Projects end def tree_saver_class - if ::Feature.enabled?(:streaming_serializer, project) + if ::Feature.enabled?(:streaming_serializer, project, default_enabled: true) Gitlab::ImportExport::Project::TreeSaver else # Once we remove :streaming_serializer feature flag, Project::LegacyTreeSaver should be removed as well diff --git a/app/services/projects/prometheus/alerts/notify_service.rb b/app/services/projects/prometheus/alerts/notify_service.rb new file mode 100644 index 00000000000..89791aecabb --- /dev/null +++ b/app/services/projects/prometheus/alerts/notify_service.rb @@ -0,0 +1,124 @@ +# frozen_string_literal: true + +module Projects + module Prometheus + module Alerts + class NotifyService < BaseService + include Gitlab::Utils::StrongMemoize + include IncidentManagement::Settings + + def execute(token) + return false unless valid_payload_size? + return false unless valid_version? + return false unless valid_alert_manager_token?(token) + + persist_events + send_alert_email if send_email? + process_incident_issues if process_issues? + + true + end + + private + + def valid_payload_size? + Gitlab::Utils::DeepSize.new(params).valid? + end + + def send_email? + incident_management_setting.send_email && firings.any? + end + + def firings + @firings ||= alerts_by_status('firing') + end + + def alerts_by_status(status) + alerts.select { |alert| alert['status'] == status } + end + + def alerts + params['alerts'] + end + + def valid_version? + params['version'] == '4' + end + + def valid_alert_manager_token?(token) + valid_for_manual?(token) || valid_for_managed?(token) + end + + def valid_for_manual?(token) + prometheus = project.find_or_initialize_service('prometheus') + return false unless prometheus.manual_configuration? + + if setting = project.alerting_setting + compare_token(token, setting.token) + else + token.nil? + end + end + + def valid_for_managed?(token) + prometheus_application = available_prometheus_application(project) + return false unless prometheus_application + + if token + compare_token(token, prometheus_application.alert_manager_token) + else + prometheus_application.alert_manager_token.nil? + end + end + + def available_prometheus_application(project) + alert_id = gitlab_alert_id + return unless alert_id + + alert = find_alert(project, alert_id) + return unless alert + + cluster = alert.environment.deployment_platform&.cluster + return unless cluster&.enabled? + return unless cluster.application_prometheus_available? + + cluster.application_prometheus + end + + def find_alert(project, metric) + Projects::Prometheus::AlertsFinder + .new(project: project, metric: metric) + .execute + .first + end + + def gitlab_alert_id + alerts&.first&.dig('labels', 'gitlab_alert_id') + end + + def compare_token(expected, actual) + return unless expected && actual + + ActiveSupport::SecurityUtils.secure_compare(expected, actual) + end + + def send_alert_email + notification_service + .async + .prometheus_alerts_fired(project, firings) + end + + def process_incident_issues + alerts.each do |alert| + IncidentManagement::ProcessPrometheusAlertWorker + .perform_async(project.id, alert.to_h) + end + end + + def persist_events + CreateEventsService.new(project, nil, params).execute + end + end + end + end +end diff --git a/changelogs/unreleased/211818-enable-streaming-serializer-ff.yml b/changelogs/unreleased/211818-enable-streaming-serializer-ff.yml new file mode 100644 index 00000000000..d0f420d0c26 --- /dev/null +++ b/changelogs/unreleased/211818-enable-streaming-serializer-ff.yml @@ -0,0 +1,5 @@ +--- +title: Enable streaming serializer feature flag by default. +merge_request: 27813 +author: +type: performance diff --git a/changelogs/unreleased/dblessing_okta_scim.yml b/changelogs/unreleased/dblessing_okta_scim.yml new file mode 100644 index 00000000000..16baae42004 --- /dev/null +++ b/changelogs/unreleased/dblessing_okta_scim.yml @@ -0,0 +1,5 @@ +--- +title: Add support for Okta as a SCIM provider +merge_request: 25649 +author: +type: added diff --git a/config/initializers/active_record_schema_versions.rb b/config/initializers/active_record_schema_versions.rb new file mode 100644 index 00000000000..a7c342e8053 --- /dev/null +++ b/config/initializers/active_record_schema_versions.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +# Patch to use COPY in db/structure.sql when populating schema_migrations table +# This is intended to reduce potential for merge conflicts in db/structure.sql +ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(Gitlab::Database::PostgresqlAdapter::SchemaVersionsCopyMixin) diff --git a/db/structure.sql b/db/structure.sql index 92671eb5ae4..6301ec6df80 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -11775,1023 +11775,1024 @@ ALTER TABLE ONLY public.timelogs ALTER TABLE ONLY public.timelogs ADD CONSTRAINT fk_timelogs_merge_requests_merge_request_id FOREIGN KEY (merge_request_id) REFERENCES public.merge_requests(id) ON DELETE CASCADE; -INSERT INTO "schema_migrations" (version) VALUES -('20171230123729'), -('20180101160629'), -('20180101160630'), -('20180102220145'), -('20180103123548'), -('20180104131052'), -('20180105212544'), -('20180109183319'), -('20180113220114'), -('20180115094742'), -('20180115113902'), -('20180115201419'), -('20180116193854'), -('20180119121225'), -('20180119135717'), -('20180119160751'), -('20180122154930'), -('20180122162010'), -('20180125214301'), -('20180129193323'), -('20180201102129'), -('20180201110056'), -('20180201145907'), -('20180204200836'), -('20180206200543'), -('20180208183958'), -('20180209115333'), -('20180209165249'), -('20180212030105'), -('20180212101828'), -('20180212101928'), -('20180212102028'), -('20180213131630'), -('20180214093516'), -('20180214155405'), -('20180215181245'), -('20180216120000'), -('20180216120010'), -('20180216120020'), -('20180216120030'), -('20180216120040'), -('20180216120050'), -('20180216121020'), -('20180216121030'), -('20180219153455'), -('20180220150310'), -('20180221151752'), -('20180222043024'), -('20180223120443'), -('20180223124427'), -('20180223144945'), -('20180226050030'), -('20180227182112'), -('20180228172924'), -('20180301010859'), -('20180301084653'), -('20180302152117'), -('20180305095250'), -('20180305100050'), -('20180305144721'), -('20180306074045'), -('20180306134842'), -('20180306164012'), -('20180307012445'), -('20180308052825'), -('20180308125206'), -('20180309121820'), -('20180309160427'), -('20180314100728'), -('20180314145917'), -('20180315160435'), -('20180319190020'), -('20180320182229'), -('20180323150945'), -('20180326202229'), -('20180327101207'), -('20180330121048'), -('20180403035759'), -('20180405101928'), -('20180405142733'), -('20180408143354'), -('20180408143355'), -('20180409170809'), -('20180413022611'), -('20180416155103'), -('20180417090132'), -('20180417101040'), -('20180417101940'), -('20180418053107'), -('20180420010016'), -('20180420010616'), -('20180420080616'), -('20180423204600'), -('20180424090541'), -('20180424134533'), -('20180424151928'), -('20180424160449'), -('20180425075446'), -('20180425131009'), -('20180425205249'), -('20180426102016'), -('20180430101916'), -('20180430143705'), -('20180502122856'), -('20180503131624'), -('20180503141722'), -('20180503150427'), -('20180503175053'), -('20180503175054'), -('20180503193542'), -('20180503193953'), -('20180503200320'), -('20180504195842'), -('20180507083701'), -('20180508055821'), -('20180508100222'), -('20180508102840'), -('20180508135515'), -('20180511090724'), -('20180511131058'), -('20180511174224'), -('20180512061621'), -('20180514161336'), -('20180515005612'), -('20180515121227'), -('20180517082340'), -('20180523042841'), -('20180523125103'), -('20180524132016'), -('20180529093006'), -('20180529152628'), -('20180530135500'), -('20180531185349'), -('20180531220618'), -('20180601213245'), -('20180603190921'), -('20180604123514'), -('20180607071808'), -('20180608091413'), -('20180608110058'), -('20180608201435'), -('20180612103626'), -('20180613081317'), -('20180625113853'), -('20180626125654'), -('20180628124813'), -('20180629153018'), -('20180629191052'), -('20180702120647'), -('20180702124358'), -('20180702134423'), -('20180704145007'), -('20180704204006'), -('20180705160945'), -('20180706223200'), -('20180710162338'), -('20180711103851'), -('20180711103922'), -('20180713092803'), -('20180717125853'), -('20180718005113'), -('20180720023512'), -('20180722103201'), -('20180723135214'), -('20180726172057'), -('20180807153545'), -('20180808162000'), -('20180809195358'), -('20180813101999'), -('20180813102000'), -('20180814153625'), -('20180815040323'), -('20180815160409'), -('20180815170510'), -('20180815175440'), -('20180816161409'), -('20180816193530'), -('20180824202952'), -('20180826111825'), -('20180831164905'), -('20180831164907'), -('20180831164908'), -('20180831164909'), -('20180831164910'), -('20180901171833'), -('20180901200537'), -('20180902070406'), -('20180906101639'), -('20180907015926'), -('20180910115836'), -('20180910153412'), -('20180910153413'), -('20180912111628'), -('20180913142237'), -('20180914162043'), -('20180914201132'), -('20180916011959'), -('20180917172041'), -('20180924141949'), -('20180924190739'), -('20180924201039'), -('20180925200829'), -('20180927073410'), -('20181002172433'), -('20181005110927'), -('20181005125926'), -('20181006004100'), -('20181008145341'), -('20181008145359'), -('20181008200441'), -('20181009190428'), -('20181010133639'), -('20181010235606'), -('20181013005024'), -('20181014203236'), -('20181015155839'), -('20181016141739'), -('20181016152238'), -('20181017001059'), -('20181019032400'), -('20181019032408'), -('20181019105553'), -('20181022135539'), -('20181022173835'), -('20181023104858'), -('20181023144439'), -('20181025115728'), -('20181026091631'), -('20181026143227'), -('20181027114222'), -('20181028120717'), -('20181030135124'), -('20181030154446'), -('20181031145139'), -('20181031190558'), -('20181031190559'), -('20181101091005'), -('20181101091124'), -('20181101144347'), -('20181101191341'), -('20181105201455'), -('20181106135939'), -('20181107054254'), -('20181108091549'), -('20181112103239'), -('20181115140140'), -('20181116050532'), -('20181116141415'), -('20181116141504'), -('20181119081539'), -('20181119132520'), -('20181120082911'), -('20181120091639'), -('20181120151656'), -('20181121101842'), -('20181121101843'), -('20181121111200'), -('20181122160027'), -('20181123042307'), -('20181123135036'), -('20181123144235'), -('20181126150622'), -('20181126153547'), -('20181128123704'), -('20181129104854'), -('20181129104944'), -('20181130102132'), -('20181203002526'), -('20181205171941'), -('20181211092510'), -('20181211092514'), -('20181212104941'), -('20181212171634'), -('20181219130552'), -('20181219145520'), -('20181219145521'), -('20181228175414'), -('20190102152410'), -('20190103140724'), -('20190104182041'), -('20190107151020'), -('20190108192941'), -('20190109153125'), -('20190114172110'), -('20190115054215'), -('20190115054216'), -('20190115092821'), -('20190116234221'), -('20190124200344'), -('20190130091630'), -('20190131122559'), -('20190204115450'), -('20190206193120'), -('20190211131150'), -('20190214112022'), -('20190215154930'), -('20190218134158'), -('20190218134209'), -('20190219201635'), -('20190220142344'), -('20190220150130'), -('20190222051615'), -('20190225152525'), -('20190225160300'), -('20190225160301'), -('20190228192410'), -('20190301081611'), -('20190301182457'), -('20190312071108'), -('20190312113229'), -('20190312113634'), -('20190313092516'), -('20190315191339'), -('20190320174702'), -('20190322132835'), -('20190322164830'), -('20190325080727'), -('20190325105715'), -('20190325111602'), -('20190325165127'), -('20190326164045'), -('20190327163904'), -('20190329085614'), -('20190402150158'), -('20190402224749'), -('20190403161806'), -('20190404143330'), -('20190404231137'), -('20190408163745'), -('20190409224933'), -('20190410173409'), -('20190412155659'), -('20190412183653'), -('20190414185432'), -('20190415030217'), -('20190415095825'), -('20190415172035'), -('20190416185130'), -('20190416213556'), -('20190416213615'), -('20190416213631'), -('20190418132125'), -('20190418132750'), -('20190418182545'), -('20190419121952'), -('20190419123057'), -('20190422082247'), -('20190423124640'), -('20190424134256'), -('20190426180107'), -('20190429082448'), -('20190430131225'), -('20190430142025'), -('20190506135337'), -('20190506135400'), -('20190511144331'), -('20190513174947'), -('20190514105711'), -('20190515125613'), -('20190516011213'), -('20190516151857'), -('20190516155724'), -('20190517153211'), -('20190520200123'), -('20190520201748'), -('20190521174505'), -('20190522143720'), -('20190523112344'), -('20190524062810'), -('20190524071727'), -('20190524073827'), -('20190527011309'), -('20190527194830'), -('20190527194900'), -('20190528173628'), -('20190528180441'), -('20190529142545'), -('20190530042141'), -('20190530154715'), -('20190531153110'), -('20190602014139'), -('20190603124955'), -('20190604091310'), -('20190604184643'), -('20190605104727'), -('20190605184422'), -('20190606014128'), -('20190606034427'), -('20190606054649'), -('20190606054742'), -('20190606054832'), -('20190606163724'), -('20190606175050'), -('20190606202100'), -('20190607085356'), -('20190607145325'), -('20190607190856'), -('20190607205656'), -('20190610142825'), -('20190611090827'), -('20190611100201'), -('20190611100202'), -('20190611161641'), -('20190611161642'), -('20190612111201'), -('20190612111404'), -('20190613030606'), -('20190613044655'), -('20190613073003'), -('20190613231640'), -('20190617123615'), -('20190618171120'), -('20190619175843'), -('20190620105427'), -('20190620112608'), -('20190621022810'), -('20190621151636'), -('20190623212503'), -('20190624123615'), -('20190625115224'), -('20190625184066'), -('20190626175626'), -('20190627051902'), -('20190627100221'), -('20190627122264'), -('20190628145246'), -('20190628185000'), -('20190628185004'), -('20190628191740'), -('20190702173936'), -('20190703043358'), -('20190703130053'), -('20190703171157'), -('20190703171555'), -('20190703185326'), -('20190709204413'), -('20190709220014'), -('20190709220143'), -('20190710151229'), -('20190711124721'), -('20190711200053'), -('20190711200508'), -('20190711201818'), -('20190712040400'), -('20190712040412'), -('20190712064021'), -('20190715042813'), -('20190715043944'), -('20190715043954'), -('20190715044501'), -('20190715114644'), -('20190715140740'), -('20190715142138'), -('20190715173819'), -('20190715193142'), -('20190715215532'), -('20190715215549'), -('20190716144222'), -('20190719122333'), -('20190719174505'), -('20190722104947'), -('20190722132830'), -('20190722144316'), -('20190723105753'), -('20190723153247'), -('20190724112147'), -('20190725012225'), -('20190725080128'), -('20190725183432'), -('20190726101050'), -('20190726101133'), -('20190729062536'), -('20190729090456'), -('20190729180447'), -('20190731084415'), -('20190801060809'), -('20190801114109'), -('20190801142441'), -('20190801193427'), -('20190802012622'), -('20190802091750'), -('20190802195602'), -('20190802235445'), -('20190805140353'), -('20190806071559'), -('20190807023052'), -('20190808152507'), -('20190809072552'), -('20190812070645'), -('20190814205640'), -('20190815093936'), -('20190815093949'), -('20190816151221'), -('20190819131155'), -('20190819231552'), -('20190820163320'), -('20190821040941'), -('20190822175441'), -('20190822181528'), -('20190822185441'), -('20190823055948'), -('20190826090628'), -('20190826100605'), -('20190827102026'), -('20190827222124'), -('20190828083843'), -('20190828110802'), -('20190828170945'), -('20190828172831'), -('20190829131130'), -('20190830075508'), -('20190830080123'), -('20190830080626'), -('20190830140240'), -('20190901174200'), -('20190902131045'), -('20190902152329'), -('20190902160015'), -('20190903150358'), -('20190903150435'), -('20190904173203'), -('20190904205212'), -('20190905022045'), -('20190905074652'), -('20190905091812'), -('20190905091831'), -('20190905140605'), -('20190905223800'), -('20190905223900'), -('20190906104555'), -('20190907184714'), -('20190909045845'), -('20190909141517'), -('20190910000130'), -('20190910103144'), -('20190910114843'), -('20190910125852'), -('20190910211526'), -('20190910212256'), -('20190911115056'), -('20190911115109'), -('20190911115207'), -('20190911115222'), -('20190911251732'), -('20190912061145'), -('20190912223232'), -('20190913174707'), -('20190913175827'), -('20190914223900'), -('20190917173107'), -('20190918025618'), -('20190918102042'), -('20190918104212'), -('20190918104222'), -('20190918104731'), -('20190918121135'), -('20190919040324'), -('20190919091300'), -('20190919104119'), -('20190919162036'), -('20190919183411'), -('20190920122420'), -('20190920194925'), -('20190920224341'), -('20190924124627'), -('20190924152703'), -('20190925055714'), -('20190925055902'), -('20190926041216'), -('20190926180443'), -('20190926225633'), -('20190927055500'), -('20190927055540'), -('20190927074328'), -('20190929180751'), -('20190929180813'), -('20190929180827'), -('20190930025655'), -('20190930063627'), -('20190930082942'), -('20190930153535'), -('20191001040549'), -('20191001170300'), -('20191002031332'), -('20191002123516'), -('20191003015155'), -('20191003060227'), -('20191003064615'), -('20191003130045'), -('20191003150045'), -('20191003161031'), -('20191003161032'), -('20191003195218'), -('20191003195620'), -('20191003200045'), -('20191003250045'), -('20191003300045'), -('20191003350045'), -('20191004080818'), -('20191004081520'), -('20191004133612'), -('20191004151428'), -('20191007163701'), -('20191007163736'), -('20191008013056'), -('20191008142331'), -('20191008143850'), -('20191008180203'), -('20191008200204'), -('20191009100244'), -('20191009110124'), -('20191009110757'), -('20191009222222'), -('20191010174846'), -('20191011084019'), -('20191013100213'), -('20191014025629'), -('20191014030134'), -('20191014030730'), -('20191014084150'), -('20191014123159'), -('20191014132931'), -('20191015154408'), -('20191016072826'), -('20191016133352'), -('20191016220135'), -('20191017001326'), -('20191017045817'), -('20191017094449'), -('20191017134513'), -('20191017180026'), -('20191017191341'), -('20191021101942'), -('20191022113635'), -('20191023093207'), -('20191023132005'), -('20191023152913'), -('20191024134020'), -('20191025092748'), -('20191026041447'), -('20191026120008'), -('20191026120112'), -('20191026124116'), -('20191028130054'), -('20191028162543'), -('20191028184740'), -('20191029095537'), -('20191029125305'), -('20191029191901'), -('20191030135044'), -('20191030152934'), -('20191030193050'), -('20191030223057'), -('20191031095636'), -('20191031112603'), -('20191101092917'), -('20191103202505'), -('20191104142124'), -('20191104205020'), -('20191105094558'), -('20191105094625'), -('20191105134413'), -('20191105140942'), -('20191105155113'), -('20191105193652'), -('20191106144901'), -('20191106150931'), -('20191107064946'), -('20191107173446'), -('20191107220314'), -('20191108031900'), -('20191108202723'), -('20191111115229'), -('20191111115431'), -('20191111121500'), -('20191111165017'), -('20191111175230'), -('20191112023159'), -('20191112090226'), -('20191112105448'), -('20191112115247'), -('20191112115317'), -('20191112214305'), -('20191112221821'), -('20191112232338'), -('20191114132259'), -('20191114173508'), -('20191114173602'), -('20191114173624'), -('20191114201118'), -('20191114204343'), -('20191115001123'), -('20191115001843'), -('20191115091425'), -('20191115114032'), -('20191115115043'), -('20191115115522'), -('20191118053631'), -('20191118155702'), -('20191118173522'), -('20191118182722'), -('20191118211629'), -('20191119023952'), -('20191119220425'), -('20191119221041'), -('20191119231621'), -('20191120084627'), -('20191120115530'), -('20191120200015'), -('20191121111621'), -('20191121121947'), -('20191121122856'), -('20191121161018'), -('20191121193110'), -('20191122135327'), -('20191122161519'), -('20191123062354'), -('20191123081456'), -('20191124150431'), -('20191125024005'), -('20191125114345'), -('20191125133353'), -('20191125140458'), -('20191126134210'), -('20191127030005'), -('20191127151619'), -('20191127151629'), -('20191127163053'), -('20191127221608'), -('20191128145231'), -('20191128145232'), -('20191128145233'), -('20191128162854'), -('20191129134844'), -('20191129144630'), -('20191129144631'), -('20191202031812'), -('20191202181924'), -('20191203121729'), -('20191204070713'), -('20191204093410'), -('20191204114127'), -('20191204192726'), -('20191205060723'), -('20191205084057'), -('20191205094702'), -('20191205145647'), -('20191205212923'), -('20191205212924'), -('20191206014412'), -('20191206022133'), -('20191206122926'), -('20191207104000'), -('20191208071111'), -('20191208071112'), -('20191208110214'), -('20191209143606'), -('20191209215316'), -('20191210211253'), -('20191212140117'), -('20191212162434'), -('20191213104838'), -('20191213120427'), -('20191213143656'), -('20191213184609'), -('20191214175727'), -('20191216074800'), -('20191216074802'), -('20191216074803'), -('20191216094119'), -('20191216183531'), -('20191216183532'), -('20191217165641'), -('20191217212348'), -('20191218084115'), -('20191218122457'), -('20191218124915'), -('20191218125015'), -('20191218190253'), -('20191218225624'), -('20191223124940'), -('20191225071320'), -('20191227140254'), -('20191229140154'), -('20200102140148'), -('20200102170221'), -('20200103190741'), -('20200103192859'), -('20200103192914'), -('20200103195205'), -('20200104113850'), -('20200106071113'), -('20200106085831'), -('20200107172020'), -('20200108100603'), -('20200108155731'), -('20200108233040'), -('20200109030418'), -('20200109085206'), -('20200109233938'), -('20200110089001'), -('20200110090153'), -('20200110121314'), -('20200110144316'), -('20200110203532'), -('20200113133352'), -('20200113151354'), -('20200114112932'), -('20200114113341'), -('20200114140305'), -('20200114204949'), -('20200115135132'), -('20200115135234'), -('20200116051619'), -('20200116175538'), -('20200117112554'), -('20200117194830'), -('20200117194840'), -('20200117194850'), -('20200117194900'), -('20200120083607'), -('20200121132641'), -('20200121192942'), -('20200121194000'), -('20200121194048'), -('20200121194154'), -('20200121200203'), -('20200122123016'), -('20200122144759'), -('20200122161638'), -('20200123040535'), -('20200123045415'), -('20200123090839'), -('20200123091422'), -('20200123091622'), -('20200123091734'), -('20200123091854'), -('20200123155929'), -('20200124053531'), -('20200124110831'), -('20200124143014'), -('20200127090233'), -('20200127111840'), -('20200128105731'), -('20200128132510'), -('20200128133510'), -('20200128134110'), -('20200128141125'), -('20200128184209'), -('20200128210353'), -('20200129034515'), -('20200129035446'), -('20200129035708'), -('20200129133716'), -('20200129172428'), -('20200130134335'), -('20200130145430'), -('20200130161817'), -('20200131140428'), -('20200131181354'), -('20200131191754'), -('20200202100932'), -('20200203015140'), -('20200203025400'), -('20200203025602'), -('20200203025619'), -('20200203025744'), -('20200203025801'), -('20200203025821'), -('20200203104214'), -('20200203173508'), -('20200203183508'), -('20200203232433'), -('20200204070729'), -('20200204113223'), -('20200204113224'), -('20200204131054'), -('20200204131831'), -('20200205143231'), -('20200206091544'), -('20200206112850'), -('20200206135203'), -('20200206141511'), -('20200207062728'), -('20200207090921'), -('20200207132752'), -('20200207151640'), -('20200207182131'), -('20200207184023'), -('20200207185149'), -('20200209131152'), -('20200210062432'), -('20200210092405'), -('20200210135504'), -('20200210184410'), -('20200210184420'), -('20200211152410'), -('20200211155000'), -('20200211155100'), -('20200211155539'), -('20200211174946'), -('20200212014653'), -('20200212052620'), -('20200212133945'), -('20200212134201'), -('20200213093702'), -('20200213155311'), -('20200213204737'), -('20200213220159'), -('20200213220211'), -('20200214025454'), -('20200214034836'), -('20200214085940'), -('20200214214934'), -('20200215222507'), -('20200215225103'), -('20200217223651'), -('20200217225719'), -('20200219105209'), -('20200219133859'), -('20200219135440'), -('20200219141307'), -('20200219142522'), -('20200219183456'), -('20200219184219'), -('20200219193058'), -('20200219193117'), -('20200220180944'), -('20200221023320'), -('20200221074028'), -('20200221100514'), -('20200221105436'), -('20200221142216'), -('20200221144534'), -('20200222055543'), -('20200224020219'), -('20200224163804'), -('20200224185814'), -('20200225111018'), -('20200225123228'), -('20200226100614'), -('20200226100624'), -('20200226100634'), -('20200226162156'), -('20200226162239'), -('20200226162634'), -('20200226162723'), -('20200227140242'), -('20200227164113'), -('20200227165129'), -('20200228160542'), -('20200302142052'), -('20200302152516'), -('20200303055348'), -('20200303074328'), -('20200304085423'), -('20200304090155'), -('20200304121828'), -('20200304121844'), -('20200304124406'), -('20200304160800'), -('20200304160801'), -('20200304160823'), -('20200304211738'), -('20200305121159'), -('20200305151736'), -('20200306095654'), -('20200306160521'), -('20200306170211'), -('20200306170321'), -('20200306170531'), -('20200306192548'), -('20200306193236'), -('20200309140540'), -('20200309162244'), -('20200309195209'), -('20200309195710'), -('20200310075115'), -('20200310123229'), -('20200310132654'), -('20200310133822'), -('20200310135818'), -('20200310135823'), -('20200310145304'), -('20200311074438'), -('20200311082301'), -('20200311084025'), -('20200311093210'), -('20200311094020'), -('20200311141053'), -('20200311141943'), -('20200311154110'), -('20200311165635'), -('20200311192351'), -('20200311214912'), -('20200312125121'), -('20200312160532'), -('20200312163407'), -('20200313101649'), -('20200313123934'), -('20200316111759'), -('20200316162648'), -('20200316173312'), -('20200317142110'), -('20200318140400'), -('20200318152134'), -('20200318162148'), -('20200318163148'), -('20200318164448'), -('20200318165448'), -('20200318175008'), -('20200319123041'), -('20200319203901'), -('20200320112455'), -('20200320123839'), -('20200323075043'), -('20200323122201'), -('20200324115359'); +COPY "schema_migrations" (version) FROM STDIN; +20171230123729 +20180101160629 +20180101160630 +20180102220145 +20180103123548 +20180104131052 +20180105212544 +20180109183319 +20180113220114 +20180115094742 +20180115113902 +20180115201419 +20180116193854 +20180119121225 +20180119135717 +20180119160751 +20180122154930 +20180122162010 +20180125214301 +20180129193323 +20180201102129 +20180201110056 +20180201145907 +20180204200836 +20180206200543 +20180208183958 +20180209115333 +20180209165249 +20180212030105 +20180212101828 +20180212101928 +20180212102028 +20180213131630 +20180214093516 +20180214155405 +20180215181245 +20180216120000 +20180216120010 +20180216120020 +20180216120030 +20180216120040 +20180216120050 +20180216121020 +20180216121030 +20180219153455 +20180220150310 +20180221151752 +20180222043024 +20180223120443 +20180223124427 +20180223144945 +20180226050030 +20180227182112 +20180228172924 +20180301010859 +20180301084653 +20180302152117 +20180305095250 +20180305100050 +20180305144721 +20180306074045 +20180306134842 +20180306164012 +20180307012445 +20180308052825 +20180308125206 +20180309121820 +20180309160427 +20180314100728 +20180314145917 +20180315160435 +20180319190020 +20180320182229 +20180323150945 +20180326202229 +20180327101207 +20180330121048 +20180403035759 +20180405101928 +20180405142733 +20180408143354 +20180408143355 +20180409170809 +20180413022611 +20180416155103 +20180417090132 +20180417101040 +20180417101940 +20180418053107 +20180420010016 +20180420010616 +20180420080616 +20180423204600 +20180424090541 +20180424134533 +20180424151928 +20180424160449 +20180425075446 +20180425131009 +20180425205249 +20180426102016 +20180430101916 +20180430143705 +20180502122856 +20180503131624 +20180503141722 +20180503150427 +20180503175053 +20180503175054 +20180503193542 +20180503193953 +20180503200320 +20180504195842 +20180507083701 +20180508055821 +20180508100222 +20180508102840 +20180508135515 +20180511090724 +20180511131058 +20180511174224 +20180512061621 +20180514161336 +20180515005612 +20180515121227 +20180517082340 +20180523042841 +20180523125103 +20180524132016 +20180529093006 +20180529152628 +20180530135500 +20180531185349 +20180531220618 +20180601213245 +20180603190921 +20180604123514 +20180607071808 +20180608091413 +20180608110058 +20180608201435 +20180612103626 +20180613081317 +20180625113853 +20180626125654 +20180628124813 +20180629153018 +20180629191052 +20180702120647 +20180702124358 +20180702134423 +20180704145007 +20180704204006 +20180705160945 +20180706223200 +20180710162338 +20180711103851 +20180711103922 +20180713092803 +20180717125853 +20180718005113 +20180720023512 +20180722103201 +20180723135214 +20180726172057 +20180807153545 +20180808162000 +20180809195358 +20180813101999 +20180813102000 +20180814153625 +20180815040323 +20180815160409 +20180815170510 +20180815175440 +20180816161409 +20180816193530 +20180824202952 +20180826111825 +20180831164905 +20180831164907 +20180831164908 +20180831164909 +20180831164910 +20180901171833 +20180901200537 +20180902070406 +20180906101639 +20180907015926 +20180910115836 +20180910153412 +20180910153413 +20180912111628 +20180913142237 +20180914162043 +20180914201132 +20180916011959 +20180917172041 +20180924141949 +20180924190739 +20180924201039 +20180925200829 +20180927073410 +20181002172433 +20181005110927 +20181005125926 +20181006004100 +20181008145341 +20181008145359 +20181008200441 +20181009190428 +20181010133639 +20181010235606 +20181013005024 +20181014203236 +20181015155839 +20181016141739 +20181016152238 +20181017001059 +20181019032400 +20181019032408 +20181019105553 +20181022135539 +20181022173835 +20181023104858 +20181023144439 +20181025115728 +20181026091631 +20181026143227 +20181027114222 +20181028120717 +20181030135124 +20181030154446 +20181031145139 +20181031190558 +20181031190559 +20181101091005 +20181101091124 +20181101144347 +20181101191341 +20181105201455 +20181106135939 +20181107054254 +20181108091549 +20181112103239 +20181115140140 +20181116050532 +20181116141415 +20181116141504 +20181119081539 +20181119132520 +20181120082911 +20181120091639 +20181120151656 +20181121101842 +20181121101843 +20181121111200 +20181122160027 +20181123042307 +20181123135036 +20181123144235 +20181126150622 +20181126153547 +20181128123704 +20181129104854 +20181129104944 +20181130102132 +20181203002526 +20181205171941 +20181211092510 +20181211092514 +20181212104941 +20181212171634 +20181219130552 +20181219145520 +20181219145521 +20181228175414 +20190102152410 +20190103140724 +20190104182041 +20190107151020 +20190108192941 +20190109153125 +20190114172110 +20190115054215 +20190115054216 +20190115092821 +20190116234221 +20190124200344 +20190130091630 +20190131122559 +20190204115450 +20190206193120 +20190211131150 +20190214112022 +20190215154930 +20190218134158 +20190218134209 +20190219201635 +20190220142344 +20190220150130 +20190222051615 +20190225152525 +20190225160300 +20190225160301 +20190228192410 +20190301081611 +20190301182457 +20190312071108 +20190312113229 +20190312113634 +20190313092516 +20190315191339 +20190320174702 +20190322132835 +20190322164830 +20190325080727 +20190325105715 +20190325111602 +20190325165127 +20190326164045 +20190327163904 +20190329085614 +20190402150158 +20190402224749 +20190403161806 +20190404143330 +20190404231137 +20190408163745 +20190409224933 +20190410173409 +20190412155659 +20190412183653 +20190414185432 +20190415030217 +20190415095825 +20190415172035 +20190416185130 +20190416213556 +20190416213615 +20190416213631 +20190418132125 +20190418132750 +20190418182545 +20190419121952 +20190419123057 +20190422082247 +20190423124640 +20190424134256 +20190426180107 +20190429082448 +20190430131225 +20190430142025 +20190506135337 +20190506135400 +20190511144331 +20190513174947 +20190514105711 +20190515125613 +20190516011213 +20190516151857 +20190516155724 +20190517153211 +20190520200123 +20190520201748 +20190521174505 +20190522143720 +20190523112344 +20190524062810 +20190524071727 +20190524073827 +20190527011309 +20190527194830 +20190527194900 +20190528173628 +20190528180441 +20190529142545 +20190530042141 +20190530154715 +20190531153110 +20190602014139 +20190603124955 +20190604091310 +20190604184643 +20190605104727 +20190605184422 +20190606014128 +20190606034427 +20190606054649 +20190606054742 +20190606054832 +20190606163724 +20190606175050 +20190606202100 +20190607085356 +20190607145325 +20190607190856 +20190607205656 +20190610142825 +20190611090827 +20190611100201 +20190611100202 +20190611161641 +20190611161642 +20190612111201 +20190612111404 +20190613030606 +20190613044655 +20190613073003 +20190613231640 +20190617123615 +20190618171120 +20190619175843 +20190620105427 +20190620112608 +20190621022810 +20190621151636 +20190623212503 +20190624123615 +20190625115224 +20190625184066 +20190626175626 +20190627051902 +20190627100221 +20190627122264 +20190628145246 +20190628185000 +20190628185004 +20190628191740 +20190702173936 +20190703043358 +20190703130053 +20190703171157 +20190703171555 +20190703185326 +20190709204413 +20190709220014 +20190709220143 +20190710151229 +20190711124721 +20190711200053 +20190711200508 +20190711201818 +20190712040400 +20190712040412 +20190712064021 +20190715042813 +20190715043944 +20190715043954 +20190715044501 +20190715114644 +20190715140740 +20190715142138 +20190715173819 +20190715193142 +20190715215532 +20190715215549 +20190716144222 +20190719122333 +20190719174505 +20190722104947 +20190722132830 +20190722144316 +20190723105753 +20190723153247 +20190724112147 +20190725012225 +20190725080128 +20190725183432 +20190726101050 +20190726101133 +20190729062536 +20190729090456 +20190729180447 +20190731084415 +20190801060809 +20190801114109 +20190801142441 +20190801193427 +20190802012622 +20190802091750 +20190802195602 +20190802235445 +20190805140353 +20190806071559 +20190807023052 +20190808152507 +20190809072552 +20190812070645 +20190814205640 +20190815093936 +20190815093949 +20190816151221 +20190819131155 +20190819231552 +20190820163320 +20190821040941 +20190822175441 +20190822181528 +20190822185441 +20190823055948 +20190826090628 +20190826100605 +20190827102026 +20190827222124 +20190828083843 +20190828110802 +20190828170945 +20190828172831 +20190829131130 +20190830075508 +20190830080123 +20190830080626 +20190830140240 +20190901174200 +20190902131045 +20190902152329 +20190902160015 +20190903150358 +20190903150435 +20190904173203 +20190904205212 +20190905022045 +20190905074652 +20190905091812 +20190905091831 +20190905140605 +20190905223800 +20190905223900 +20190906104555 +20190907184714 +20190909045845 +20190909141517 +20190910000130 +20190910103144 +20190910114843 +20190910125852 +20190910211526 +20190910212256 +20190911115056 +20190911115109 +20190911115207 +20190911115222 +20190911251732 +20190912061145 +20190912223232 +20190913174707 +20190913175827 +20190914223900 +20190917173107 +20190918025618 +20190918102042 +20190918104212 +20190918104222 +20190918104731 +20190918121135 +20190919040324 +20190919091300 +20190919104119 +20190919162036 +20190919183411 +20190920122420 +20190920194925 +20190920224341 +20190924124627 +20190924152703 +20190925055714 +20190925055902 +20190926041216 +20190926180443 +20190926225633 +20190927055500 +20190927055540 +20190927074328 +20190929180751 +20190929180813 +20190929180827 +20190930025655 +20190930063627 +20190930082942 +20190930153535 +20191001040549 +20191001170300 +20191002031332 +20191002123516 +20191003015155 +20191003060227 +20191003064615 +20191003130045 +20191003150045 +20191003161031 +20191003161032 +20191003195218 +20191003195620 +20191003200045 +20191003250045 +20191003300045 +20191003350045 +20191004080818 +20191004081520 +20191004133612 +20191004151428 +20191007163701 +20191007163736 +20191008013056 +20191008142331 +20191008143850 +20191008180203 +20191008200204 +20191009100244 +20191009110124 +20191009110757 +20191009222222 +20191010174846 +20191011084019 +20191013100213 +20191014025629 +20191014030134 +20191014030730 +20191014084150 +20191014123159 +20191014132931 +20191015154408 +20191016072826 +20191016133352 +20191016220135 +20191017001326 +20191017045817 +20191017094449 +20191017134513 +20191017180026 +20191017191341 +20191021101942 +20191022113635 +20191023093207 +20191023132005 +20191023152913 +20191024134020 +20191025092748 +20191026041447 +20191026120008 +20191026120112 +20191026124116 +20191028130054 +20191028162543 +20191028184740 +20191029095537 +20191029125305 +20191029191901 +20191030135044 +20191030152934 +20191030193050 +20191030223057 +20191031095636 +20191031112603 +20191101092917 +20191103202505 +20191104142124 +20191104205020 +20191105094558 +20191105094625 +20191105134413 +20191105140942 +20191105155113 +20191105193652 +20191106144901 +20191106150931 +20191107064946 +20191107173446 +20191107220314 +20191108031900 +20191108202723 +20191111115229 +20191111115431 +20191111121500 +20191111165017 +20191111175230 +20191112023159 +20191112090226 +20191112105448 +20191112115247 +20191112115317 +20191112214305 +20191112221821 +20191112232338 +20191114132259 +20191114173508 +20191114173602 +20191114173624 +20191114201118 +20191114204343 +20191115001123 +20191115001843 +20191115091425 +20191115114032 +20191115115043 +20191115115522 +20191118053631 +20191118155702 +20191118173522 +20191118182722 +20191118211629 +20191119023952 +20191119220425 +20191119221041 +20191119231621 +20191120084627 +20191120115530 +20191120200015 +20191121111621 +20191121121947 +20191121122856 +20191121161018 +20191121193110 +20191122135327 +20191122161519 +20191123062354 +20191123081456 +20191124150431 +20191125024005 +20191125114345 +20191125133353 +20191125140458 +20191126134210 +20191127030005 +20191127151619 +20191127151629 +20191127163053 +20191127221608 +20191128145231 +20191128145232 +20191128145233 +20191128162854 +20191129134844 +20191129144630 +20191129144631 +20191202031812 +20191202181924 +20191203121729 +20191204070713 +20191204093410 +20191204114127 +20191204192726 +20191205060723 +20191205084057 +20191205094702 +20191205145647 +20191205212923 +20191205212924 +20191206014412 +20191206022133 +20191206122926 +20191207104000 +20191208071111 +20191208071112 +20191208110214 +20191209143606 +20191209215316 +20191210211253 +20191212140117 +20191212162434 +20191213104838 +20191213120427 +20191213143656 +20191213184609 +20191214175727 +20191216074800 +20191216074802 +20191216074803 +20191216094119 +20191216183531 +20191216183532 +20191217165641 +20191217212348 +20191218084115 +20191218122457 +20191218124915 +20191218125015 +20191218190253 +20191218225624 +20191223124940 +20191225071320 +20191227140254 +20191229140154 +20200102140148 +20200102170221 +20200103190741 +20200103192859 +20200103192914 +20200103195205 +20200104113850 +20200106071113 +20200106085831 +20200107172020 +20200108100603 +20200108155731 +20200108233040 +20200109030418 +20200109085206 +20200109233938 +20200110089001 +20200110090153 +20200110121314 +20200110144316 +20200110203532 +20200113133352 +20200113151354 +20200114112932 +20200114113341 +20200114140305 +20200114204949 +20200115135132 +20200115135234 +20200116051619 +20200116175538 +20200117112554 +20200117194830 +20200117194840 +20200117194850 +20200117194900 +20200120083607 +20200121132641 +20200121192942 +20200121194000 +20200121194048 +20200121194154 +20200121200203 +20200122123016 +20200122144759 +20200122161638 +20200123040535 +20200123045415 +20200123090839 +20200123091422 +20200123091622 +20200123091734 +20200123091854 +20200123155929 +20200124053531 +20200124110831 +20200124143014 +20200127090233 +20200127111840 +20200128105731 +20200128132510 +20200128133510 +20200128134110 +20200128141125 +20200128184209 +20200128210353 +20200129034515 +20200129035446 +20200129035708 +20200129133716 +20200129172428 +20200130134335 +20200130145430 +20200130161817 +20200131140428 +20200131181354 +20200131191754 +20200202100932 +20200203015140 +20200203025400 +20200203025602 +20200203025619 +20200203025744 +20200203025801 +20200203025821 +20200203104214 +20200203173508 +20200203183508 +20200203232433 +20200204070729 +20200204113223 +20200204113224 +20200204131054 +20200204131831 +20200205143231 +20200206091544 +20200206112850 +20200206135203 +20200206141511 +20200207062728 +20200207090921 +20200207132752 +20200207151640 +20200207182131 +20200207184023 +20200207185149 +20200209131152 +20200210062432 +20200210092405 +20200210135504 +20200210184410 +20200210184420 +20200211152410 +20200211155000 +20200211155100 +20200211155539 +20200211174946 +20200212014653 +20200212052620 +20200212133945 +20200212134201 +20200213093702 +20200213155311 +20200213204737 +20200213220159 +20200213220211 +20200214025454 +20200214034836 +20200214085940 +20200214214934 +20200215222507 +20200215225103 +20200217223651 +20200217225719 +20200219105209 +20200219133859 +20200219135440 +20200219141307 +20200219142522 +20200219183456 +20200219184219 +20200219193058 +20200219193117 +20200220180944 +20200221023320 +20200221074028 +20200221100514 +20200221105436 +20200221142216 +20200221144534 +20200222055543 +20200224020219 +20200224163804 +20200224185814 +20200225111018 +20200225123228 +20200226100614 +20200226100624 +20200226100634 +20200226162156 +20200226162239 +20200226162634 +20200226162723 +20200227140242 +20200227164113 +20200227165129 +20200228160542 +20200302142052 +20200302152516 +20200303055348 +20200303074328 +20200304085423 +20200304090155 +20200304121828 +20200304121844 +20200304124406 +20200304160800 +20200304160801 +20200304160823 +20200304211738 +20200305121159 +20200305151736 +20200306095654 +20200306160521 +20200306170211 +20200306170321 +20200306170531 +20200306192548 +20200306193236 +20200309140540 +20200309162244 +20200309195209 +20200309195710 +20200310075115 +20200310123229 +20200310132654 +20200310133822 +20200310135818 +20200310135823 +20200310145304 +20200311074438 +20200311082301 +20200311084025 +20200311093210 +20200311094020 +20200311141053 +20200311141943 +20200311154110 +20200311165635 +20200311192351 +20200311214912 +20200312125121 +20200312160532 +20200312163407 +20200313101649 +20200313123934 +20200316111759 +20200316162648 +20200316173312 +20200317142110 +20200318140400 +20200318152134 +20200318162148 +20200318163148 +20200318164448 +20200318165448 +20200318175008 +20200319123041 +20200319203901 +20200320112455 +20200320123839 +20200323075043 +20200323122201 +20200324115359 +\. diff --git a/doc/development/chatops_on_gitlabcom.md b/doc/development/chatops_on_gitlabcom.md index f3687b8d697..60056c2f65c 100644 --- a/doc/development/chatops_on_gitlabcom.md +++ b/doc/development/chatops_on_gitlabcom.md @@ -14,7 +14,7 @@ tasks such as: To request access to Chatops on GitLab.com: 1. Log into <https://ops.gitlab.net/users/sign_in> **using the same username** as for GitLab.com (you may have to rename it). -1. Ask in the [#production](https://gitlab.slack.com/messages/production) channel to add you by running `/chatops run member add <username> gitlab-com/chatops --ops`. +1. Ask in the [#production](https://gitlab.slack.com/messages/production) channel for an existing member to add you to the `chatops` project in Ops. They can do it by running `/chatops run member add <username> gitlab-com/chatops --ops` command in that channel. NOTE: **Note:** If you had to change your username for GitLab.com on the first step, make sure [to reflect this information](https://gitlab.com/gitlab-com/www-gitlab-com#adding-yourself-to-the-team-page) on [the team page](https://about.gitlab.com/company/team/). diff --git a/lib/api/releases.rb b/lib/api/releases.rb index 1be263ac80d..95b3e90323c 100644 --- a/lib/api/releases.rb +++ b/lib/api/releases.rb @@ -150,6 +150,10 @@ module API authorize! :download_code, release end + def authorize_create_evidence! + # This is a separate method so that EE can extend its behaviour + end + def release @release ||= user_project.releases.find_by_tag(params[:tag]) end diff --git a/lib/gitlab/database/postgresql_adapter/schema_versions_copy_mixin.rb b/lib/gitlab/database/postgresql_adapter/schema_versions_copy_mixin.rb new file mode 100644 index 00000000000..d8f96643dcb --- /dev/null +++ b/lib/gitlab/database/postgresql_adapter/schema_versions_copy_mixin.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Gitlab + module Database + module PostgresqlAdapter + module SchemaVersionsCopyMixin + extend ActiveSupport::Concern + + def dump_schema_information # :nodoc: + versions = schema_migration.all_versions + copy_versions_sql(versions) if versions.any? + end + + private + + def copy_versions_sql(versions) + sm_table = quote_table_name(schema_migration.table_name) + + sql = +"COPY #{sm_table} (version) FROM STDIN;\n" + sql << versions.map { |v| Integer(v) }.sort.join("\n") + sql << "\n\\.\n" + + sql + end + end + end + end +end diff --git a/lib/gitlab/profiler.rb b/lib/gitlab/profiler.rb index e10cdf0d8fb..42f43f998c4 100644 --- a/lib/gitlab/profiler.rb +++ b/lib/gitlab/profiler.rb @@ -168,9 +168,9 @@ module Gitlab # rubocop: enable CodeReuse/ActiveRecord def self.print_by_total_time(result, options = {}) - default_options = { sort_method: :total_time } + default_options = { sort_method: :total_time, filter_by: :total_time } - Gitlab::Profiler::TotalTimeFlatPrinter.new(result).print(STDOUT, default_options.merge(options)) + RubyProf::FlatPrinter.new(result).print(STDOUT, default_options.merge(options)) end end end diff --git a/lib/gitlab/profiler/total_time_flat_printer.rb b/lib/gitlab/profiler/total_time_flat_printer.rb deleted file mode 100644 index 9846bad3c08..00000000000 --- a/lib/gitlab/profiler/total_time_flat_printer.rb +++ /dev/null @@ -1,42 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Profiler - class TotalTimeFlatPrinter < RubyProf::FlatPrinter - def max_percent - @options[:max_percent] || 100 - end - - # Copied from: - # <https://github.com/ruby-prof/ruby-prof/blob/master/lib/ruby-prof/printers/flat_printer.rb> - # - # The changes are just to filter by total time, not self time, and add a - # max_percent option as well. - def print_methods(thread) - total_time = thread.total_time - methods = thread.methods.sort_by(&sort_method).reverse - - sum = 0 - methods.each do |method| - total_percent = (method.total_time / total_time) * 100 - next if total_percent < min_percent - next if total_percent > max_percent - - sum += method.self_time - - @output << "%6.2f %9.3f %9.3f %9.3f %9.3f %8d %s%-30s %s\n" % [ - method.self_time / total_time * 100, # %self - method.total_time, # total - method.self_time, # self - method.wait_time, # wait - method.children_time, # children - method.called, # calls - method.recursive? ? "*" : " ", # cycle - method.full_name, # method_name - method_location(method) # location - ] - end - end - end - end -end diff --git a/lib/gitlab/setup_helper.rb b/lib/gitlab/setup_helper.rb index 99a7e617884..196dc0e3447 100644 --- a/lib/gitlab/setup_helper.rb +++ b/lib/gitlab/setup_helper.rb @@ -4,75 +4,110 @@ require 'toml-rb' module Gitlab module SetupHelper - class << self - # We cannot create config.toml files for all possible Gitaly configuations. - # For instance, if Gitaly is running on another machine then it makes no - # sense to write a config.toml file on the current machine. This method will - # only generate a configuration for the most common and simplest case: when - # we have exactly one Gitaly process and we are sure it is running locally - # because it uses a Unix socket. - # For development and testing purposes, an extra storage is added to gitaly, - # which is not known to Rails, but must be explicitly stubbed. - def gitaly_configuration_toml(gitaly_dir, storage_paths, gitaly_ruby: true) - storages = [] - address = nil - - Gitlab.config.repositories.storages.each do |key, val| - if address - if address != val['gitaly_address'] - raise ArgumentError, "Your gitlab.yml contains more than one gitaly_address." + def create_configuration(dir, storage_paths, force: false) + generate_configuration( + configuration_toml(dir, storage_paths), + get_config_path(dir), + force: force + ) + end + + # rubocop:disable Rails/Output + def generate_configuration(toml_data, config_path, force: false) + FileUtils.rm_f(config_path) if force + + File.open(config_path, File::WRONLY | File::CREAT | File::EXCL) do |f| + f.puts toml_data + end + rescue Errno::EEXIST + puts 'Skipping config.toml generation:' + puts 'A configuration file already exists.' + rescue ArgumentError => e + puts 'Skipping config.toml generation:' + puts e.message + end + # rubocop:enable Rails/Output + + module Gitaly + extend Gitlab::SetupHelper + class << self + # We cannot create config.toml files for all possible Gitaly configuations. + # For instance, if Gitaly is running on another machine then it makes no + # sense to write a config.toml file on the current machine. This method will + # only generate a configuration for the most common and simplest case: when + # we have exactly one Gitaly process and we are sure it is running locally + # because it uses a Unix socket. + # For development and testing purposes, an extra storage is added to gitaly, + # which is not known to Rails, but must be explicitly stubbed. + def configuration_toml(gitaly_dir, storage_paths, gitaly_ruby: true) + storages = [] + address = nil + + Gitlab.config.repositories.storages.each do |key, val| + if address + if address != val['gitaly_address'] + raise ArgumentError, "Your gitlab.yml contains more than one gitaly_address." + end + elsif URI(val['gitaly_address']).scheme != 'unix' + raise ArgumentError, "Automatic config.toml generation only supports 'unix:' addresses." + else + address = val['gitaly_address'] end - elsif URI(val['gitaly_address']).scheme != 'unix' - raise ArgumentError, "Automatic config.toml generation only supports 'unix:' addresses." - else - address = val['gitaly_address'] + + storages << { name: key, path: storage_paths[key] } end - storages << { name: key, path: storage_paths[key] } - end + config = { socket_path: address.sub(/\Aunix:/, '') } - if Rails.env.test? - storage_path = Rails.root.join('tmp', 'tests', 'second_storage').to_s - storages << { name: 'test_second_storage', path: storage_path } - end + if Rails.env.test? + storage_path = Rails.root.join('tmp', 'tests', 'second_storage').to_s + storages << { name: 'test_second_storage', path: storage_path } + + config[:auth] = { token: 'secret' } + # Compared to production, tests run in constrained environments. This + # number is meant to grow with the number of concurrent rails requests / + # sidekiq jobs, and concurrency will be low anyway in test. + config[:git] = { catfile_cache_size: 5 } + end - config = { socket_path: address.sub(/\Aunix:/, ''), storage: storages } - config[:auth] = { token: 'secret' } if Rails.env.test? + config[:storage] = storages - internal_socket_dir = File.join(gitaly_dir, 'internal_sockets') - FileUtils.mkdir(internal_socket_dir) unless File.exist?(internal_socket_dir) - config[:internal_socket_dir] = internal_socket_dir + internal_socket_dir = File.join(gitaly_dir, 'internal_sockets') + FileUtils.mkdir(internal_socket_dir) unless File.exist?(internal_socket_dir) + config[:internal_socket_dir] = internal_socket_dir - config[:'gitaly-ruby'] = { dir: File.join(gitaly_dir, 'ruby') } if gitaly_ruby - config[:'gitlab-shell'] = { dir: Gitlab.config.gitlab_shell.path } - config[:bin_dir] = Gitlab.config.gitaly.client_path + config[:'gitaly-ruby'] = { dir: File.join(gitaly_dir, 'ruby') } if gitaly_ruby + config[:'gitlab-shell'] = { dir: Gitlab.config.gitlab_shell.path } + config[:bin_dir] = Gitlab.config.gitaly.client_path - if Rails.env.test? - # Compared to production, tests run in constrained environments. This - # number is meant to grow with the number of concurrent rails requests / - # sidekiq jobs, and concurrency will be low anyway in test. - config[:git] = { catfile_cache_size: 5 } + TomlRB.dump(config) end - TomlRB.dump(config) + private + + def get_config_path(dir) + File.join(dir, 'config.toml') + end end + end + + module Praefect + extend Gitlab::SetupHelper + class << self + def configuration_toml(gitaly_dir, storage_paths) + nodes = [{ storage: 'default', address: "unix:#{gitaly_dir}/gitaly.socket", primary: true, token: 'secret' }] + config = { socket_path: "#{gitaly_dir}/praefect.socket", virtual_storage_name: 'default', token: 'secret', node: nodes } + config[:token] = 'secret' if Rails.env.test? + + TomlRB.dump(config) + end - # rubocop:disable Rails/Output - def create_gitaly_configuration(dir, storage_paths, force: false) - config_path = File.join(dir, 'config.toml') - FileUtils.rm_f(config_path) if force + private - File.open(config_path, File::WRONLY | File::CREAT | File::EXCL) do |f| - f.puts gitaly_configuration_toml(dir, storage_paths) + def get_config_path(dir) + File.join(dir, 'praefect.config.toml') end - rescue Errno::EEXIST - puts "Skipping config.toml generation:" - puts "A configuration file already exists." - rescue ArgumentError => e - puts "Skipping config.toml generation:" - puts e.message end - # rubocop:enable Rails/Output end end end diff --git a/lib/tasks/gitlab/gitaly.rake b/lib/tasks/gitlab/gitaly.rake index c63ddb62f2a..ee47f71af93 100644 --- a/lib/tasks/gitlab/gitaly.rake +++ b/lib/tasks/gitlab/gitaly.rake @@ -27,7 +27,7 @@ Usage: rake "gitlab:gitaly:install[/installation/dir,/storage/path]") end storage_paths = { 'default' => args.storage_path } - Gitlab::SetupHelper.create_gitaly_configuration(args.dir, storage_paths) + Gitlab::SetupHelper::Gitaly.create_configuration(args.dir, storage_paths) Dir.chdir(args.dir) do # In CI we run scripts/gitaly-test-build instead of this command unless ENV['CI'].present? diff --git a/lib/uploaded_file.rb b/lib/uploaded_file.rb index 424db653fb8..f8d596b5d14 100644 --- a/lib/uploaded_file.rb +++ b/lib/uploaded_file.rb @@ -48,7 +48,7 @@ class UploadedFile return if path.blank? && remote_id.blank? file_path = nil - if path + if path.present? file_path = File.realpath(path) paths = Array(upload_paths) << Dir.tmpdir diff --git a/package.json b/package.json index 5102e5730b3..225c8e7817b 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "@babel/preset-env": "^7.8.4", "@gitlab/at.js": "^1.5.5", "@gitlab/svgs": "^1.115.0", - "@gitlab/ui": "^10.0.0", + "@gitlab/ui": "^10.0.1", "@gitlab/visual-review-tools": "1.5.1", "@sentry/browser": "^5.10.2", "@sourcegraph/code-host-integration": "0.0.33", diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb index 1c78deae2bf..4e189faec6e 100644 --- a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - context 'Create', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/issues/36817', type: :bug } do + context 'Create', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/30226', type: :bug } do describe 'Merge request rebasing' do it 'user rebases source branch of merge request' do Flow::Login.sign_in diff --git a/scripts/gitaly-test-build b/scripts/gitaly-test-build index 374401caf89..fcf0049162b 100755 --- a/scripts/gitaly-test-build +++ b/scripts/gitaly-test-build @@ -17,13 +17,16 @@ class GitalyTestBuild check_gitaly_config! # Starting gitaly further validates its configuration - pid = start_gitaly - Process.kill('TERM', pid) + gitaly_pid = start_gitaly + praefect_pid = start_praefect + Process.kill('TERM', gitaly_pid) + Process.kill('TERM', praefect_pid) # Make the 'gitaly' executable look newer than 'GITALY_SERVER_VERSION'. # Without this a gitaly executable created in the setup-test-env job # will look stale compared to GITALY_SERVER_VERSION. FileUtils.touch(File.join(tmp_tests_gitaly_dir, 'gitaly'), mtime: Time.now + (1 << 24)) + FileUtils.touch(File.join(tmp_tests_gitaly_dir, 'praefect'), mtime: Time.now + (1 << 24)) end end diff --git a/scripts/gitaly-test-spawn b/scripts/gitaly-test-spawn index e9f91f75650..8e16b2bb656 100755 --- a/scripts/gitaly-test-spawn +++ b/scripts/gitaly-test-spawn @@ -13,10 +13,9 @@ class GitalyTestSpawn # # Uncomment line below to see all gitaly logs merged into CI trace # spawn('sleep 1; tail -f log/gitaly-test.log') - pid = start_gitaly - # In local development this pid file is used by rspec. - IO.write(File.expand_path('../tmp/tests/gitaly.pid', __dir__), pid) + IO.write(File.expand_path('../tmp/tests/gitaly.pid', __dir__), start_gitaly) + IO.write(File.expand_path('../tmp/tests/praefect.pid', __dir__), start_praefect) end end diff --git a/scripts/gitaly_test.rb b/scripts/gitaly_test.rb index 922dc17ed2e..8db47afdd4d 100644 --- a/scripts/gitaly_test.rb +++ b/scripts/gitaly_test.rb @@ -37,16 +37,31 @@ module GitalyTest env_hash end - def config_path - File.join(tmp_tests_gitaly_dir, 'config.toml') + def config_path(service) + case service + when :gitaly + File.join(tmp_tests_gitaly_dir, 'config.toml') + when :praefect + File.join(tmp_tests_gitaly_dir, 'praefect.config.toml') + end end def start_gitaly - args = %W[#{tmp_tests_gitaly_dir}/gitaly #{config_path}] - pid = spawn(env, *args, [:out, :err] => 'log/gitaly-test.log') + start(:gitaly) + end + + def start_praefect + start(:praefect) + end + + def start(service) + args = ["#{tmp_tests_gitaly_dir}/#{service}"] + args.push("-config") if service == :praefect + args.push(config_path(service)) + pid = spawn(env, *args, [:out, :err] => "log/#{service}-test.log") begin - try_connect! + try_connect!(service) rescue Process.kill('TERM', pid) raise @@ -68,11 +83,11 @@ module GitalyTest abort 'bundle check failed' unless system(env, 'bundle', 'check', chdir: File.dirname(gemfile)) end - def read_socket_path + def read_socket_path(service) # This code needs to work in an environment where we cannot use bundler, # so we cannot easily use the toml-rb gem. This ad-hoc parser should be # good enough. - config_text = IO.read(config_path) + config_text = IO.read(config_path(service)) config_text.lines.each do |line| match_data = line.match(/^\s*socket_path\s*=\s*"([^"]*)"$/) @@ -80,14 +95,14 @@ module GitalyTest return match_data[1] if match_data end - raise "failed to find socket_path in #{config_path}" + raise "failed to find socket_path in #{config_path(service)}" end - def try_connect! - print "Trying to connect to gitaly: " + def try_connect!(service) + print "Trying to connect to #{service}: " timeout = 20 delay = 0.1 - socket = read_socket_path + socket = read_socket_path(service) Integer(timeout / delay).times do UNIXSocket.new(socket) diff --git a/spec/lib/gitlab/database/postgresql_adapter/schema_versions_copy_mixin_spec.rb b/spec/lib/gitlab/database/postgresql_adapter/schema_versions_copy_mixin_spec.rb new file mode 100644 index 00000000000..968dfc1ea43 --- /dev/null +++ b/spec/lib/gitlab/database/postgresql_adapter/schema_versions_copy_mixin_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Database::PostgresqlAdapter::SchemaVersionsCopyMixin do + let(:schema_migration) { double('schem_migration', table_name: table_name, all_versions: versions) } + let(:versions) { %w(5 2 1000 200 4 93 2) } + let(:table_name) { "schema_migrations" } + + let(:instance) do + Object.new.extend(described_class) + end + + before do + allow(instance).to receive(:schema_migration).and_return(schema_migration) + allow(instance).to receive(:quote_table_name).with(table_name).and_return("\"#{table_name}\"") + end + + subject { instance.dump_schema_information } + + it 'uses COPY FROM STDIN' do + expect(subject.split("\n").first).to match(/COPY "schema_migrations" \(version\) FROM STDIN;/) + end + + it 'contains a sorted list of versions by their numeric value' do + version_lines = subject.split("\n")[1..-2].map(&:to_i) + + expect(version_lines).to eq(versions.map(&:to_i).sort) + end + + it 'contains a end-of-data marker' do + expect(subject).to end_with("\\.\n") + end + + context 'with non-Integer versions' do + let(:versions) { %w(5 2 4 abc) } + + it 'raises an error' do + expect { subject }.to raise_error(/invalid value for Integer/) + end + end +end diff --git a/spec/lib/uploaded_file_spec.rb b/spec/lib/uploaded_file_spec.rb index 2bbbd67b13c..25536c07dd9 100644 --- a/spec/lib/uploaded_file_spec.rb +++ b/spec/lib/uploaded_file_spec.rb @@ -59,6 +59,16 @@ describe UploadedFile do expect(subject.sha256).to eq('sha256') expect(subject.remote_id).to eq('remote_id') end + + it 'handles a blank path' do + params['file.path'] = '' + + # Not a real file, so can't determine size itself + params['file.size'] = 1.byte + + expect { described_class.from_params(params, :file, upload_path) } + .not_to raise_error + end end end diff --git a/spec/models/concerns/bulk_insert_safe_spec.rb b/spec/models/concerns/bulk_insert_safe_spec.rb index a8e56cb8bdd..5ed1d6b9967 100644 --- a/spec/models/concerns/bulk_insert_safe_spec.rb +++ b/spec/models/concerns/bulk_insert_safe_spec.rb @@ -129,10 +129,37 @@ describe BulkInsertSafe do end.not_to change { described_class.count } end - it 'does nothing and returns true when items are empty' do - expect(described_class.bulk_insert!([])).to be(true) + it 'does nothing and returns an empty array when items are empty' do + expect(described_class.bulk_insert!([])).to eq([]) expect(described_class.count).to eq(0) end + + context 'with returns option set' do + context 'when is set to :ids' do + it 'return an array with the primary key values for all inserted records' do + items = described_class.valid_list(1) + + expect(described_class.bulk_insert!(items, returns: :ids)).to contain_exactly(a_kind_of(Integer)) + end + end + + context 'when is set to nil' do + it 'returns an empty array' do + items = described_class.valid_list(1) + + expect(described_class.bulk_insert!(items, returns: nil)).to eq([]) + end + end + + context 'when is set to anything else' do + it 'raises an error' do + items = described_class.valid_list(1) + + expect { described_class.bulk_insert!([items], returns: [:id, :name]) } + .to raise_error(ArgumentError, "returns needs to be :ids or nil") + end + end + end end context 'when duplicate items are to be inserted' do diff --git a/spec/requests/api/releases_spec.rb b/spec/requests/api/releases_spec.rb index e66e999dc27..0589554bf44 100644 --- a/spec/requests/api/releases_spec.rb +++ b/spec/requests/api/releases_spec.rb @@ -6,6 +6,7 @@ describe API::Releases do let(:project) { create(:project, :repository, :private) } let(:maintainer) { create(:user) } let(:reporter) { create(:user) } + let(:developer) { create(:user) } let(:guest) { create(:user) } let(:non_project_member) { create(:user) } let(:commit) { create(:commit, project: project) } @@ -15,6 +16,7 @@ describe API::Releases do project.add_maintainer(maintainer) project.add_reporter(reporter) project.add_guest(guest) + project.add_developer(developer) project.repository.add_tag(maintainer, 'v0.1', commit.id) project.repository.add_tag(maintainer, 'v0.2', commit.id) @@ -248,6 +250,24 @@ describe API::Releases do .to match_array(release.sources.map(&:url)) end + context 'with evidence' do + let!(:evidence) { create(:evidence, release: release) } + + it 'returns the evidence' do + get api("/projects/#{project.id}/releases/v0.1", maintainer) + + expect(json_response['evidences'].count).to eq(1) + end + + it '#collected_at' do + Timecop.freeze(Time.now.round) do + get api("/projects/#{project.id}/releases/v0.1", maintainer) + + expect(json_response['evidences'].first['collected_at'].to_datetime.to_i).to be_within(1.minute).of(release.evidences.first.created_at.to_i) + end + end + end + context 'when release has link asset' do let!(:link) do create(:release_link, diff --git a/spec/services/projects/prometheus/alerts/notify_service_spec.rb b/spec/services/projects/prometheus/alerts/notify_service_spec.rb new file mode 100644 index 00000000000..ce850e65329 --- /dev/null +++ b/spec/services/projects/prometheus/alerts/notify_service_spec.rb @@ -0,0 +1,343 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Projects::Prometheus::Alerts::NotifyService do + let_it_be(:project, reload: true) { create(:project) } + + let(:service) { described_class.new(project, nil, payload) } + let(:token_input) { 'token' } + + let!(:setting) do + create(:project_incident_management_setting, project: project, send_email: true, create_issue: true) + end + + let(:subject) { service.execute(token_input) } + + before do + # We use `let_it_be(:project)` so we make sure to clear caches + project.clear_memoization(:licensed_feature_available) + end + + shared_examples 'sends notification email' do + let(:notification_service) { spy } + + it 'sends a notification for firing alerts only' do + expect(NotificationService) + .to receive(:new) + .and_return(notification_service) + + expect(notification_service) + .to receive_message_chain(:async, :prometheus_alerts_fired) + + expect(subject).to eq(true) + end + end + + shared_examples 'processes incident issues' do |amount| + let(:create_incident_service) { spy } + + it 'processes issues' do + expect(IncidentManagement::ProcessPrometheusAlertWorker) + .to receive(:perform_async) + .with(project.id, kind_of(Hash)) + .exactly(amount).times + + Sidekiq::Testing.inline! do + expect(subject).to eq(true) + end + end + end + + shared_examples 'does not process incident issues' do + it 'does not process issues' do + expect(IncidentManagement::ProcessPrometheusAlertWorker) + .not_to receive(:perform_async) + + expect(subject).to eq(true) + end + end + + shared_examples 'persists events' do + let(:create_events_service) { spy } + + it 'persists events' do + expect(Projects::Prometheus::Alerts::CreateEventsService) + .to receive(:new) + .and_return(create_events_service) + + expect(create_events_service) + .to receive(:execute) + + expect(subject).to eq(true) + end + end + + shared_examples 'notifies alerts' do + it_behaves_like 'sends notification email' + it_behaves_like 'persists events' + end + + shared_examples 'no notifications' do + let(:notification_service) { spy } + let(:create_events_service) { spy } + + it 'does not notify' do + expect(notification_service).not_to receive(:async) + expect(create_events_service).not_to receive(:execute) + + expect(subject).to eq(false) + end + end + + context 'with valid payload' do + let(:alert_firing) { create(:prometheus_alert, project: project) } + let(:alert_resolved) { create(:prometheus_alert, project: project) } + let(:payload_raw) { payload_for(firing: [alert_firing], resolved: [alert_resolved]) } + let(:payload) { ActionController::Parameters.new(payload_raw).permit! } + let(:payload_alert_firing) { payload_raw['alerts'].first } + let(:token) { 'token' } + + context 'with project specific cluster' do + using RSpec::Parameterized::TableSyntax + + where(:cluster_enabled, :status, :configured_token, :token_input, :result) do + true | :installed | token | token | :success + true | :installed | nil | nil | :success + true | :updated | token | token | :success + true | :updating | token | token | :failure + true | :installed | token | 'x' | :failure + true | :installed | nil | token | :failure + true | :installed | token | nil | :failure + true | nil | token | token | :failure + false | :installed | token | token | :failure + end + + with_them do + before do + cluster = create(:cluster, :provided_by_user, + projects: [project], + enabled: cluster_enabled) + + if status + create(:clusters_applications_prometheus, status, + cluster: cluster, + alert_manager_token: configured_token) + end + end + + case result = params[:result] + when :success + it_behaves_like 'notifies alerts' + when :failure + it_behaves_like 'no notifications' + else + raise "invalid result: #{result.inspect}" + end + end + end + + context 'without project specific cluster' do + let!(:cluster) { create(:cluster, enabled: true) } + + it_behaves_like 'no notifications' + end + + context 'with manual prometheus installation' do + using RSpec::Parameterized::TableSyntax + + where(:alerting_setting, :configured_token, :token_input, :result) do + true | token | token | :success + true | token | 'x' | :failure + true | token | nil | :failure + false | nil | nil | :success + false | nil | token | :failure + end + + with_them do + let(:alert_manager_token) { token_input } + + before do + create(:prometheus_service, project: project) + + if alerting_setting + create(:project_alerting_setting, + project: project, + token: configured_token) + end + end + + case result = params[:result] + when :success + it_behaves_like 'notifies alerts' + when :failure + it_behaves_like 'no notifications' + else + raise "invalid result: #{result.inspect}" + end + end + end + + context 'alert emails' do + before do + create(:prometheus_service, project: project) + create(:project_alerting_setting, project: project, token: token) + end + + context 'when incident_management_setting does not exist' do + let!(:setting) { nil } + + it_behaves_like 'persists events' + + it 'does not send notification email', :sidekiq_might_not_need_inline do + expect_any_instance_of(NotificationService) + .not_to receive(:async) + + expect(subject).to eq(true) + end + end + + context 'when incident_management_setting.send_email is true' do + it_behaves_like 'notifies alerts' + end + + context 'incident_management_setting.send_email is false' do + let!(:setting) do + create(:project_incident_management_setting, send_email: false, project: project) + end + + it_behaves_like 'persists events' + + it 'does not send notification' do + expect(NotificationService).not_to receive(:new) + + expect(subject).to eq(true) + end + end + end + + context 'process incident issues' do + before do + create(:prometheus_service, project: project) + create(:project_alerting_setting, project: project, token: token) + end + + context 'with create_issue setting enabled' do + before do + setting.update!(create_issue: true) + end + + it_behaves_like 'processes incident issues', 2 + + context 'multiple firing alerts' do + let(:payload_raw) do + payload_for(firing: [alert_firing, alert_firing], resolved: []) + end + + it_behaves_like 'processes incident issues', 2 + end + + context 'without firing alerts' do + let(:payload_raw) do + payload_for(firing: [], resolved: [alert_resolved]) + end + + it_behaves_like 'processes incident issues', 1 + end + end + + context 'with create_issue setting disabled' do + before do + setting.update!(create_issue: false) + end + + it_behaves_like 'does not process incident issues' + end + end + end + + context 'with invalid payload' do + context 'without version' do + let(:payload) { {} } + + it_behaves_like 'no notifications' + end + + context 'when version is not "4"' do + let(:payload) { { 'version' => '5' } } + + it_behaves_like 'no notifications' + end + + context 'with missing alerts' do + let(:payload) { { 'version' => '4' } } + + it_behaves_like 'no notifications' + end + + context 'when the payload is too big' do + let(:payload) { { 'the-payload-is-too-big' => true } } + let(:deep_size_object) { instance_double(Gitlab::Utils::DeepSize, valid?: false) } + + before do + allow(Gitlab::Utils::DeepSize).to receive(:new).and_return(deep_size_object) + end + + it_behaves_like 'no notifications' + + it 'does not process issues' do + expect(IncidentManagement::ProcessPrometheusAlertWorker) + .not_to receive(:perform_async) + + subject + end + end + end + + private + + def payload_for(firing: [], resolved: []) + status = firing.any? ? 'firing' : 'resolved' + alerts = firing + resolved + alert_name = alerts.first.title + prometheus_metric_id = alerts.first.prometheus_metric_id.to_s + + alerts_map = \ + firing.map { |alert| map_alert_payload('firing', alert) } + + resolved.map { |alert| map_alert_payload('resolved', alert) } + + # See https://prometheus.io/docs/alerting/configuration/#%3Cwebhook_config%3E + { + 'version' => '4', + 'receiver' => 'gitlab', + 'status' => status, + 'alerts' => alerts_map, + 'groupLabels' => { + 'alertname' => alert_name + }, + 'commonLabels' => { + 'alertname' => alert_name, + 'gitlab' => 'hook', + 'gitlab_alert_id' => prometheus_metric_id + }, + 'commonAnnotations' => {}, + 'externalURL' => '', + 'groupKey' => "{}:{alertname=\'#{alert_name}\'}" + } + end + + def map_alert_payload(status, alert) + { + 'status' => status, + 'labels' => { + 'alertname' => alert.title, + 'gitlab' => 'hook', + 'gitlab_alert_id' => alert.prometheus_metric_id.to_s + }, + 'annotations' => {}, + 'startsAt' => '2018-09-24T08:57:31.095725221Z', + 'endsAt' => '0001-01-01T00:00:00Z', + 'generatorURL' => 'http://prometheus-prometheus-server-URL' + } + end +end diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb index 66c2faac2dd..2e69c59c80a 100644 --- a/spec/support/helpers/test_env.rb +++ b/spec/support/helpers/test_env.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'rspec/mocks' -require 'toml-rb' module TestEnv extend ActiveSupport::Concern @@ -87,7 +86,7 @@ module TestEnv 'conflict-resolvable-fork' => '404fa3f' }.freeze - TMP_TEST_PATH = Rails.root.join('tmp', 'tests', '**') + TMP_TEST_PATH = Rails.root.join('tmp', 'tests').freeze REPOS_STORAGE = 'default'.freeze SECOND_STORAGE_PATH = Rails.root.join('tmp', 'tests', 'second_storage') @@ -140,7 +139,7 @@ module TestEnv # # Keeps gitlab-shell and gitlab-test def clean_test_path - Dir[TMP_TEST_PATH].each do |entry| + Dir[File.join(TMP_TEST_PATH, '**')].each do |entry| unless test_dirs.include?(File.basename(entry)) FileUtils.rm_rf(entry) end @@ -164,7 +163,8 @@ module TestEnv install_dir: gitaly_dir, version: Gitlab::GitalyClient.expected_server_version, task: "gitlab:gitaly:install[#{install_gitaly_args}]") do - Gitlab::SetupHelper.create_gitaly_configuration(gitaly_dir, { 'default' => repos_path }, force: true) + Gitlab::SetupHelper::Gitaly.create_configuration(gitaly_dir, { 'default' => repos_path }, force: true) + Gitlab::SetupHelper::Praefect.create_configuration(gitaly_dir, { 'praefect' => repos_path }, force: true) start_gitaly(gitaly_dir) end end @@ -192,17 +192,38 @@ module TestEnv end end - @gitaly_pid = Integer(File.read('tmp/tests/gitaly.pid')) + gitaly_pid = Integer(File.read(TMP_TEST_PATH.join('gitaly.pid'))) + praefect_pid = Integer(File.read(TMP_TEST_PATH.join('praefect.pid'))) - Kernel.at_exit { stop_gitaly } + Kernel.at_exit { stop(gitaly_pid) } + Kernel.at_exit { stop(praefect_pid) } - wait_gitaly + wait('gitaly') + wait('praefect') end - def wait_gitaly + def stop(pid) + Process.kill('KILL', pid) + rescue Errno::ESRCH + # The process can already be gone if the test run was INTerrupted. + end + + def gitaly_url + ENV.fetch('GITALY_REPO_URL', nil) + end + + def socket_path(service) + TMP_TEST_PATH.join('gitaly', "#{service}.socket").to_s + end + + def praefect_socket_path + "unix:" + socket_path(:praefect) + end + + def wait(service) sleep_time = 10 sleep_interval = 0.1 - socket = Gitlab::GitalyClient.address('default').sub('unix:', '') + socket = socket_path(service) Integer(sleep_time / sleep_interval).times do Socket.unix(socket) @@ -211,19 +232,7 @@ module TestEnv sleep sleep_interval end - raise "could not connect to gitaly at #{socket.inspect} after #{sleep_time} seconds" - end - - def stop_gitaly - return unless @gitaly_pid - - Process.kill('KILL', @gitaly_pid) - rescue Errno::ESRCH - # The process can already be gone if the test run was INTerrupted. - end - - def gitaly_url - ENV.fetch('GITALY_REPO_URL', nil) + raise "could not connect to #{service} at #{socket.inspect} after #{sleep_time} seconds" end def setup_workhorse diff --git a/spec/support/praefect.rb b/spec/support/praefect.rb new file mode 100644 index 00000000000..3218275c2aa --- /dev/null +++ b/spec/support/praefect.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require_relative 'helpers/test_env' + +RSpec.configure do |config| + config.before(:each, :praefect) do + allow(Gitlab.config.repositories.storages['default']).to receive(:[]).and_call_original + allow(Gitlab.config.repositories.storages['default']).to receive(:[]).with('gitaly_address') + .and_return(TestEnv.praefect_socket_path) + end +end diff --git a/spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb b/spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb index c6180a5a196..7bcd6191f1d 100644 --- a/spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb +++ b/spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb @@ -45,11 +45,11 @@ RSpec.shared_examples 'a BulkInsertSafe model' do |klass| expect { target_class.bulk_insert!(items) }.to change { target_class.count }.by(items.size) end - it 'returns true' do + it 'returns an empty array' do items = valid_items_for_bulk_insertion expect(items).not_to be_empty - expect(target_class.bulk_insert!(items)).to be true + expect(target_class.bulk_insert!(items)).to eq([]) end end @@ -69,7 +69,7 @@ RSpec.shared_examples 'a BulkInsertSafe model' do |klass| # it is not always possible to create invalid items if items.any? - expect(target_class.bulk_insert!(items, validate: false)).to be(true) + expect(target_class.bulk_insert!(items, validate: false)).to eq([]) expect(target_class.count).to eq(items.size) end end diff --git a/yarn.lock b/yarn.lock index f97529d9e4c..4886a3651d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -786,10 +786,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.115.0.tgz#2762ad045d5a2bd728f74fcb4c00caa9bd6dbc22" integrity sha512-jlmNGqCTpSiPFrNbLaW6GGXNbvIShLdrpeYTtSEz/yFJMClQfPjHc8Zm9bl/PqAM5d/yGQqk8e+rBc4LeAhEfg== -"@gitlab/ui@^10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-10.0.0.tgz#dced1119237f328367e8c4922cf4e1ae986fac54" - integrity sha512-+qsojtfE5mhryjJyReXBY9C3J4s4jlRpHfEcaCFuhcebtq5Uhd6xgLwgxT+E7fMvtLQpGATMo1DiD80yhLb2pQ== +"@gitlab/ui@^10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-10.0.1.tgz#65deee8ded3b8d003dfd74cd93c7eb0549e11b37" + integrity sha512-RMOJjpZjmWJnu0ebfGJsPOn6/ko+HlfHYbBXBImpTIk6Xsr5AaRjT4yCYEoefZ55jK/SJ2nxHytqrMe26wjfDA== dependencies: "@babel/standalone" "^7.0.0" "@gitlab/vue-toasted" "^1.3.0" |