From 2ab69f0d0c825f8546f189a61189246d6c90b7ff Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 5 Dec 2017 12:02:07 +0100 Subject: Schedule full build stage migration in the background For builds that are still missing `stage_id`. --- ...0171205101928_schedule_build_stage_migration.rb | 20 ++++++++++ .../schedule_build_stage_migration_spec.rb | 43 ++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 db/post_migrate/20171205101928_schedule_build_stage_migration.rb create mode 100644 spec/migrations/schedule_build_stage_migration_spec.rb diff --git a/db/post_migrate/20171205101928_schedule_build_stage_migration.rb b/db/post_migrate/20171205101928_schedule_build_stage_migration.rb new file mode 100644 index 00000000000..60293c60e8e --- /dev/null +++ b/db/post_migrate/20171205101928_schedule_build_stage_migration.rb @@ -0,0 +1,20 @@ +class ScheduleBuildStageMigration < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + MIGRATION = 'MigrateBuildStage'.freeze + BATCH = 10_000 + + class Build < ActiveRecord::Base + include EachBatch + self.table_name = 'ci_builds' + end + + def change + Build.where('stage_id IS NULL').each_batch(of: BATCH) do |builds, index| + builds.pluck(:id).map { |id| [MIGRATION, [id]] }.tap do |migrations| + BackgroundMigrationWorker.perform_bulk_in(index.minutes, migrations) + end + end + end +end diff --git a/spec/migrations/schedule_build_stage_migration_spec.rb b/spec/migrations/schedule_build_stage_migration_spec.rb new file mode 100644 index 00000000000..f80c0a94b32 --- /dev/null +++ b/spec/migrations/schedule_build_stage_migration_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20171205101928_schedule_build_stage_migration') + +describe ScheduleBuildStageMigration, :migration do + let(:projects) { table(:projects) } + let(:pipelines) { table(:ci_pipelines) } + let(:stages) { table(:ci_stages) } + let(:jobs) { table(:ci_builds) } + + before do + ## + # Dependencies + # + projects.create!(id: 123, name: 'gitlab', path: 'gitlab-ce') + pipelines.create!(id: 1, project_id: 123, ref: 'master', sha: 'adf43c3a') + stages.create!(id: 1, project_id: 123, pipeline_id: 1, name: 'test') + + ## + # CI/CD jobs + # + jobs.create!(id: 10, commit_id: 1, project_id: 123, stage_id: nil) + jobs.create!(id: 20, commit_id: 1, project_id: 123, stage_id: nil) + jobs.create!(id: 30, commit_id: 1, project_id: 123, stage_id: nil) + jobs.create!(id: 40, commit_id: 1, project_id: 123, stage_id: 1) + end + + before do + stub_const("#{described_class}::BATCH", 1) + end + + it 'schedules background migrations in batches in bulk' do + Sidekiq::Testing.fake! do + Timecop.freeze do + migrate! + + expect(described_class::MIGRATION).to be_scheduled_migration(1.minutes, 10) + expect(described_class::MIGRATION).to be_scheduled_migration(2.minutes, 20) + expect(described_class::MIGRATION).to be_scheduled_migration(3.minutes, 30) + expect(BackgroundMigrationWorker.jobs.size).to eq 3 + end + end + end +end -- cgit v1.2.1 From cd6d0dbd5fe9548ab7c0cd7b09e640a7b425daf1 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 5 Dec 2017 12:42:24 +0100 Subject: Migrate a build stage completely in a background migration --- .../background_migration/migrate_build_stage.rb | 67 ++++++++++++++++++++++ .../migrate_build_stage_spec.rb | 48 ++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 lib/gitlab/background_migration/migrate_build_stage.rb create mode 100644 spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb diff --git a/lib/gitlab/background_migration/migrate_build_stage.rb b/lib/gitlab/background_migration/migrate_build_stage.rb new file mode 100644 index 00000000000..05edaab29d2 --- /dev/null +++ b/lib/gitlab/background_migration/migrate_build_stage.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true +# rubocop:disable Metrics/AbcSize +# rubocop:disable Style/Documentation + +module Gitlab + module BackgroundMigration + class MigrateBuildStage + def perform(id) + Ci::Build.find_by(id: id).try do |build| + Stage.new(build).tap do |stage| + return if stage.exists? + + stage.ensure! + stage.migrate_reference! + stage.migrate_status! + end + end + end + + private + + class Ci::Stage < ActiveRecord::Base + self.table_name = 'ci_stages' + end + + class Ci::Build < ActiveRecord::Base + self.table_name = 'ci_builds' + end + + class Stage + def initialize(build) + @build = build + end + + def exists? + @build.reload.stage_id.present? + end + + def ensure! + find || create! + end + + def find + Ci::Stage.find_by(name: @build.stage, + pipeline_id: @build.commit_id, + project_id: @build.project_id) + end + + def create! + Ci::Stage.create!(name: @build.stage, + pipeline_id: @build.commit_id, + project_id: @build.project_id) + end + + def migrate_reference! + MigrateBuildStageIdReference.new.perform(@build.id, @build.id) + end + + def migrate_status! + raise ArgumentError unless exists? + + MigrateStageStatus.new.perform(@build.stage_id, @build.stage_id) + end + end + end + end +end diff --git a/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb b/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb new file mode 100644 index 00000000000..baa9c532c6d --- /dev/null +++ b/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +describe Gitlab::BackgroundMigration::MigrateBuildStage, :migration, schema: 20171205101928 do + let(:projects) { table(:projects) } + let(:pipelines) { table(:ci_pipelines) } + let(:stages) { table(:ci_stages) } + let(:jobs) { table(:ci_builds) } + + STATUSES = { created: 0, pending: 1, running: 2, success: 3, + failed: 4, canceled: 5, skipped: 6, manual: 7 }.freeze + + before do + ## + # Dependencies + # + projects.create!(id: 123, name: 'gitlab', path: 'gitlab-ce') + pipelines.create!(id: 1, project_id: 123, ref: 'master', sha: 'adf43c3a') + + ## + # CI/CD jobs + # + jobs.create!(id: 1, commit_id: 1, project_id: 123, + stage_idx: 2, stage: 'build', status: :success) + jobs.create!(id: 2, commit_id: 1, project_id: 123, + stage_idx: 2, stage: 'build', status: :success) + jobs.create!(id: 3, commit_id: 1, project_id: 123, + stage_idx: 1, stage: 'test', status: :failed) + jobs.create!(id: 4, commit_id: 1, project_id: 123, + stage_idx: 1, stage: 'test', status: :success) + jobs.create!(id: 5, commit_id: 1, project_id: 123, + stage_idx: 3, stage: 'deploy', status: :pending) + end + + it 'correctly migrates builds stages' do + expect(stages.count).to be_zero + + jobs.all.find_each do |job| + described_class.new.perform(job.id) + end + + expect(stages.count).to eq 3 + expect(stages.all.pluck(:name)).to match_array %w[test build deploy] + expect(jobs.where(stage_id: nil)).to be_empty + expect(stages.all.pluck(:status)).to match_array [STATUSES[:success], + STATUSES[:failed], + STATUSES[:pending]] + end +end -- cgit v1.2.1 From b50270a54efaa448e06018ad9a824ef4a512da31 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 5 Dec 2017 14:20:17 +0100 Subject: Fix Rubocop offenses in build status migrations :cop: --- lib/gitlab/background_migration/migrate_build_stage.rb | 4 +--- spec/migrations/schedule_build_stage_migration_spec.rb | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/gitlab/background_migration/migrate_build_stage.rb b/lib/gitlab/background_migration/migrate_build_stage.rb index 05edaab29d2..a870609e68d 100644 --- a/lib/gitlab/background_migration/migrate_build_stage.rb +++ b/lib/gitlab/background_migration/migrate_build_stage.rb @@ -8,7 +8,7 @@ module Gitlab def perform(id) Ci::Build.find_by(id: id).try do |build| Stage.new(build).tap do |stage| - return if stage.exists? + break if stage.exists? stage.ensure! stage.migrate_reference! @@ -17,8 +17,6 @@ module Gitlab end end - private - class Ci::Stage < ActiveRecord::Base self.table_name = 'ci_stages' end diff --git a/spec/migrations/schedule_build_stage_migration_spec.rb b/spec/migrations/schedule_build_stage_migration_spec.rb index f80c0a94b32..c840d0b77fc 100644 --- a/spec/migrations/schedule_build_stage_migration_spec.rb +++ b/spec/migrations/schedule_build_stage_migration_spec.rb @@ -33,7 +33,7 @@ describe ScheduleBuildStageMigration, :migration do Timecop.freeze do migrate! - expect(described_class::MIGRATION).to be_scheduled_migration(1.minutes, 10) + expect(described_class::MIGRATION).to be_scheduled_migration(1.minute, 10) expect(described_class::MIGRATION).to be_scheduled_migration(2.minutes, 20) expect(described_class::MIGRATION).to be_scheduled_migration(3.minutes, 30) expect(BackgroundMigrationWorker.jobs.size).to eq 3 -- cgit v1.2.1 From 1ab0ffe3830340c0c9c2def74f59c95a44bcf9f7 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 6 Jan 2018 14:21:45 +0100 Subject: Update background stages migration timestamp --- .../20171205101928_schedule_build_stage_migration.rb | 20 -------------------- .../20180105101928_schedule_build_stage_migration.rb | 20 ++++++++++++++++++++ db/schema.rb | 2 +- .../background_migration/migrate_build_stage_spec.rb | 2 +- .../schedule_build_stage_migration_spec.rb | 2 +- 5 files changed, 23 insertions(+), 23 deletions(-) delete mode 100644 db/post_migrate/20171205101928_schedule_build_stage_migration.rb create mode 100644 db/post_migrate/20180105101928_schedule_build_stage_migration.rb diff --git a/db/post_migrate/20171205101928_schedule_build_stage_migration.rb b/db/post_migrate/20171205101928_schedule_build_stage_migration.rb deleted file mode 100644 index 60293c60e8e..00000000000 --- a/db/post_migrate/20171205101928_schedule_build_stage_migration.rb +++ /dev/null @@ -1,20 +0,0 @@ -class ScheduleBuildStageMigration < ActiveRecord::Migration - include Gitlab::Database::MigrationHelpers - - DOWNTIME = false - MIGRATION = 'MigrateBuildStage'.freeze - BATCH = 10_000 - - class Build < ActiveRecord::Base - include EachBatch - self.table_name = 'ci_builds' - end - - def change - Build.where('stage_id IS NULL').each_batch(of: BATCH) do |builds, index| - builds.pluck(:id).map { |id| [MIGRATION, [id]] }.tap do |migrations| - BackgroundMigrationWorker.perform_bulk_in(index.minutes, migrations) - end - end - end -end diff --git a/db/post_migrate/20180105101928_schedule_build_stage_migration.rb b/db/post_migrate/20180105101928_schedule_build_stage_migration.rb new file mode 100644 index 00000000000..3d7f6278244 --- /dev/null +++ b/db/post_migrate/20180105101928_schedule_build_stage_migration.rb @@ -0,0 +1,20 @@ +class ScheduleBuildStageMigration < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + MIGRATION = 'MigrateBuildStage'.freeze + BATCH = 10_000 + + class Build < ActiveRecord::Base + include EachBatch + self.table_name = 'ci_builds' + end + + def change + Build.where('stage_id IS NULL').each_batch(of: BATCH) do |builds, index| + builds.pluck(:id).map { |id| [MIGRATION, [id]] }.tap do |migrations| + BackgroundMigrationWorker.bulk_perform_in(index.minutes, migrations) + end + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 740e80ccfd4..1eba30b28c8 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20171230123729) do +ActiveRecord::Schema.define(version: 20180105101928) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb b/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb index baa9c532c6d..b675db463af 100644 --- a/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb +++ b/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::BackgroundMigration::MigrateBuildStage, :migration, schema: 20171205101928 do +describe Gitlab::BackgroundMigration::MigrateBuildStage, :migration, schema: 20180105101928 do let(:projects) { table(:projects) } let(:pipelines) { table(:ci_pipelines) } let(:stages) { table(:ci_stages) } diff --git a/spec/migrations/schedule_build_stage_migration_spec.rb b/spec/migrations/schedule_build_stage_migration_spec.rb index c840d0b77fc..485ff84819d 100644 --- a/spec/migrations/schedule_build_stage_migration_spec.rb +++ b/spec/migrations/schedule_build_stage_migration_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -require Rails.root.join('db', 'post_migrate', '20171205101928_schedule_build_stage_migration') +require Rails.root.join('db', 'post_migrate', '20180105101928_schedule_build_stage_migration') describe ScheduleBuildStageMigration, :migration do let(:projects) { table(:projects) } -- cgit v1.2.1 From aaa678cbc69ad5768d076a8e1cc3f249b2adeb29 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 6 Jan 2018 14:27:07 +0100 Subject: Update specs for delayed background migration of stages --- spec/migrations/schedule_build_stage_migration_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/migrations/schedule_build_stage_migration_spec.rb b/spec/migrations/schedule_build_stage_migration_spec.rb index 485ff84819d..c96acceeeda 100644 --- a/spec/migrations/schedule_build_stage_migration_spec.rb +++ b/spec/migrations/schedule_build_stage_migration_spec.rb @@ -28,14 +28,14 @@ describe ScheduleBuildStageMigration, :migration do stub_const("#{described_class}::BATCH", 1) end - it 'schedules background migrations in batches in bulk' do + it 'schedules delayed background migrations in batches in bulk' do Sidekiq::Testing.fake! do Timecop.freeze do migrate! - expect(described_class::MIGRATION).to be_scheduled_migration(1.minute, 10) - expect(described_class::MIGRATION).to be_scheduled_migration(2.minutes, 20) - expect(described_class::MIGRATION).to be_scheduled_migration(3.minutes, 30) + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(1.minute, 10) + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(2.minutes, 20) + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(3.minutes, 30) expect(BackgroundMigrationWorker.jobs.size).to eq 3 end end -- cgit v1.2.1 From 3c89252e53147fed0d134988a6ec29aa2c02abe5 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 6 Jan 2018 14:29:35 +0100 Subject: Reduce batch size of stages we schedule for a migration --- db/post_migrate/20180105101928_schedule_build_stage_migration.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/post_migrate/20180105101928_schedule_build_stage_migration.rb b/db/post_migrate/20180105101928_schedule_build_stage_migration.rb index 3d7f6278244..b33127140bb 100644 --- a/db/post_migrate/20180105101928_schedule_build_stage_migration.rb +++ b/db/post_migrate/20180105101928_schedule_build_stage_migration.rb @@ -3,7 +3,7 @@ class ScheduleBuildStageMigration < ActiveRecord::Migration DOWNTIME = false MIGRATION = 'MigrateBuildStage'.freeze - BATCH = 10_000 + BATCH = 1000 class Build < ActiveRecord::Base include EachBatch -- cgit v1.2.1 From 2d2f9e51594e33f04bfda4012e3d4c7e48539fe3 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 6 Jan 2018 14:44:43 +0100 Subject: Do not attempt to migrate legacy pipeline stages --- .../background_migration/migrate_build_stage.rb | 33 ++++++++++++++-------- .../migrate_build_stage_spec.rb | 5 +++- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/lib/gitlab/background_migration/migrate_build_stage.rb b/lib/gitlab/background_migration/migrate_build_stage.rb index a870609e68d..7139ac03737 100644 --- a/lib/gitlab/background_migration/migrate_build_stage.rb +++ b/lib/gitlab/background_migration/migrate_build_stage.rb @@ -6,9 +6,9 @@ module Gitlab module BackgroundMigration class MigrateBuildStage def perform(id) - Ci::Build.find_by(id: id).try do |build| - Stage.new(build).tap do |stage| - break if stage.exists? + DatabaseBuild.find_by(id: id).try do |build| + MigratableStage.new(build).tap do |stage| + break if stage.exists? || stage.legacy? stage.ensure! stage.migrate_reference! @@ -17,15 +17,15 @@ module Gitlab end end - class Ci::Stage < ActiveRecord::Base + class DatabaseStage < ActiveRecord::Base self.table_name = 'ci_stages' end - class Ci::Build < ActiveRecord::Base + class DatabaseBuild < ActiveRecord::Base self.table_name = 'ci_builds' end - class Stage + class MigratableStage def initialize(build) @build = build end @@ -34,20 +34,29 @@ module Gitlab @build.reload.stage_id.present? end + ## + # We can have some very old stages that do not have `ci_builds.stage` set. + # + # In that case we just don't migrate such stage. + # + def legacy? + @build.stage.nil? + end + def ensure! find || create! end def find - Ci::Stage.find_by(name: @build.stage, - pipeline_id: @build.commit_id, - project_id: @build.project_id) + DatabaseStage.find_by(name: @build.stage, + pipeline_id: @build.commit_id, + project_id: @build.project_id) end def create! - Ci::Stage.create!(name: @build.stage, - pipeline_id: @build.commit_id, - project_id: @build.project_id) + DatabaseStage.create!(name: @build.stage, + pipeline_id: @build.commit_id, + project_id: @build.project_id) end def migrate_reference! diff --git a/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb b/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb index b675db463af..d20b5c29a71 100644 --- a/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb +++ b/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb @@ -29,6 +29,8 @@ describe Gitlab::BackgroundMigration::MigrateBuildStage, :migration, schema: 201 stage_idx: 1, stage: 'test', status: :success) jobs.create!(id: 5, commit_id: 1, project_id: 123, stage_idx: 3, stage: 'deploy', status: :pending) + jobs.create!(id: 6, commit_id: 1, project_id: 123, + stage_idx: 3, stage: nil, status: :pending) end it 'correctly migrates builds stages' do @@ -40,7 +42,8 @@ describe Gitlab::BackgroundMigration::MigrateBuildStage, :migration, schema: 201 expect(stages.count).to eq 3 expect(stages.all.pluck(:name)).to match_array %w[test build deploy] - expect(jobs.where(stage_id: nil)).to be_empty + expect(jobs.where(stage_id: nil)).to be_one + expect(jobs.find_by(stage_id: nil).id).to eq 6 expect(stages.all.pluck(:status)).to match_array [STATUSES[:success], STATUSES[:failed], STATUSES[:pending]] -- cgit v1.2.1 From fefb8ef74f2c269f1aecffc59fba32eb90c8ba73 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 6 Jan 2018 14:56:09 +0100 Subject: Randomize build ids in stages migration test data --- spec/migrations/schedule_build_stage_migration_spec.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/migrations/schedule_build_stage_migration_spec.rb b/spec/migrations/schedule_build_stage_migration_spec.rb index c96acceeeda..79f00fdbcf0 100644 --- a/spec/migrations/schedule_build_stage_migration_spec.rb +++ b/spec/migrations/schedule_build_stage_migration_spec.rb @@ -18,10 +18,10 @@ describe ScheduleBuildStageMigration, :migration do ## # CI/CD jobs # - jobs.create!(id: 10, commit_id: 1, project_id: 123, stage_id: nil) - jobs.create!(id: 20, commit_id: 1, project_id: 123, stage_id: nil) - jobs.create!(id: 30, commit_id: 1, project_id: 123, stage_id: nil) - jobs.create!(id: 40, commit_id: 1, project_id: 123, stage_id: 1) + jobs.create!(id: 11, commit_id: 1, project_id: 123, stage_id: nil) + jobs.create!(id: 206, commit_id: 1, project_id: 123, stage_id: nil) + jobs.create!(id: 3413, commit_id: 1, project_id: 123, stage_id: nil) + jobs.create!(id: 4109, commit_id: 1, project_id: 123, stage_id: 1) end before do @@ -33,9 +33,9 @@ describe ScheduleBuildStageMigration, :migration do Timecop.freeze do migrate! - expect(described_class::MIGRATION).to be_scheduled_delayed_migration(1.minute, 10) - expect(described_class::MIGRATION).to be_scheduled_delayed_migration(2.minutes, 20) - expect(described_class::MIGRATION).to be_scheduled_delayed_migration(3.minutes, 30) + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(1.minute, 11) + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(2.minutes, 206) + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(3.minutes, 3413) expect(BackgroundMigrationWorker.jobs.size).to eq 3 end end -- cgit v1.2.1 From c181683d99e4447c44a5963afe3c2680f0c82a9a Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 6 Jan 2018 16:07:55 +0100 Subject: Fix rubocop offense in build stage migration classes --- lib/gitlab/background_migration/migrate_build_stage.rb | 2 +- spec/migrations/schedule_build_stage_migration_spec.rb | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/gitlab/background_migration/migrate_build_stage.rb b/lib/gitlab/background_migration/migrate_build_stage.rb index 7139ac03737..6819810b145 100644 --- a/lib/gitlab/background_migration/migrate_build_stage.rb +++ b/lib/gitlab/background_migration/migrate_build_stage.rb @@ -40,7 +40,7 @@ module Gitlab # In that case we just don't migrate such stage. # def legacy? - @build.stage.nil? + @build.stage.nil? end def ensure! diff --git a/spec/migrations/schedule_build_stage_migration_spec.rb b/spec/migrations/schedule_build_stage_migration_spec.rb index 79f00fdbcf0..b475e1fe7de 100644 --- a/spec/migrations/schedule_build_stage_migration_spec.rb +++ b/spec/migrations/schedule_build_stage_migration_spec.rb @@ -8,6 +8,8 @@ describe ScheduleBuildStageMigration, :migration do let(:jobs) { table(:ci_builds) } before do + stub_const("#{described_class}::BATCH", 1) + ## # Dependencies # @@ -24,10 +26,6 @@ describe ScheduleBuildStageMigration, :migration do jobs.create!(id: 4109, commit_id: 1, project_id: 123, stage_id: 1) end - before do - stub_const("#{described_class}::BATCH", 1) - end - it 'schedules delayed background migrations in batches in bulk' do Sidekiq::Testing.fake! do Timecop.freeze do -- cgit v1.2.1 From d4d564c8e7d2cbc3e6742475a793ba0f630167e3 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 1 Feb 2018 18:48:32 +0800 Subject: Try not to hold env and release the controller after the request. This way, we could release the project referred from the controller, which potentially referred a repository which potentially allocated a lot of memories. Before this change, we could hold the last request data and cannot release the memory. After this change, the largest request data should be able to be collected from GC. This might not impact the instances having heavy load, as the last request should be changing all the time, and GC won't kick in for each request anyway. However it could still potentially allow us to free more memories for each GC runs, because now we could free one more request anyway. --- config.ru | 1 + lib/gitlab/middleware/read_only.rb | 2 +- lib/gitlab/middleware/release_controller.rb | 9 +++++++++ .../lib/gitlab/middleware/release_controller_spec.rb | 20 ++++++++++++++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 lib/gitlab/middleware/release_controller.rb create mode 100644 spec/lib/gitlab/middleware/release_controller_spec.rb diff --git a/config.ru b/config.ru index de0400f4f67..c4bef72308e 100644 --- a/config.ru +++ b/config.ru @@ -23,5 +23,6 @@ warmup do |app| end map ENV['RAILS_RELATIVE_URL_ROOT'] || "/" do + use Gitlab::ReleaseController run Gitlab::Application end diff --git a/lib/gitlab/middleware/read_only.rb b/lib/gitlab/middleware/read_only.rb index c26656704d7..a68c6c3d15c 100644 --- a/lib/gitlab/middleware/read_only.rb +++ b/lib/gitlab/middleware/read_only.rb @@ -28,7 +28,7 @@ module Gitlab end end - @app.call(env) + @app.call(env).tap { @env = nil } end private diff --git a/lib/gitlab/middleware/release_controller.rb b/lib/gitlab/middleware/release_controller.rb new file mode 100644 index 00000000000..a21d718d51c --- /dev/null +++ b/lib/gitlab/middleware/release_controller.rb @@ -0,0 +1,9 @@ +module Gitlab + module Middleware + ReleaseController = Struct.new(:app) do + def call(env) + app.call(env).tap { env.delete('action_controller.instance') } + end + end + end +end diff --git a/spec/lib/gitlab/middleware/release_controller_spec.rb b/spec/lib/gitlab/middleware/release_controller_spec.rb new file mode 100644 index 00000000000..854bac6e751 --- /dev/null +++ b/spec/lib/gitlab/middleware/release_controller_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Gitlab::Middleware::ReleaseController do + let(:inner_app) { double(:app) } + let(:app) { described_class.new(inner_app) } + let(:env) { { 'action_controller.instance' => 'something' } } + + before do + expect(inner_app).to receive(:call).with(env).and_return('yay') + end + + describe '#call' do + it 'calls the app and delete the controller' do + result = app.call(env) + + expect(result).to eq('yay') + expect(env).to be_empty + end + end +end -- cgit v1.2.1 From bbfce29ba8d75df5344dae34dc472dfb3b3acf4b Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 2 Feb 2018 22:12:28 +0800 Subject: Use a controller to hold request values So that we don't need to hold env after the request. This makes it much harder to test, especially Rails session is acting weirdly, so we need `dig('flash', 'flashes', 'alert')` to dig the actual flash value. --- lib/gitlab/middleware/read_only.rb | 125 ++++++++++++++------------- spec/lib/gitlab/middleware/read_only_spec.rb | 23 ++++- 2 files changed, 87 insertions(+), 61 deletions(-) diff --git a/lib/gitlab/middleware/read_only.rb b/lib/gitlab/middleware/read_only.rb index a68c6c3d15c..b7649ea01db 100644 --- a/lib/gitlab/middleware/read_only.rb +++ b/lib/gitlab/middleware/read_only.rb @@ -5,86 +5,95 @@ module Gitlab APPLICATION_JSON = 'application/json'.freeze API_VERSIONS = (3..4) - def initialize(app) - @app = app - @whitelisted = internal_routes - end - - def call(env) - @env = env - @route_hash = nil + class Controller + def initialize(app, env) + @app = app + @env = env + end - if disallowed_request? && Gitlab::Database.read_only? - Rails.logger.debug('GitLab ReadOnly: preventing possible non read-only operation') - error_message = 'You cannot do writing operations on a read-only GitLab instance' + def call + if disallowed_request? && Gitlab::Database.read_only? + Rails.logger.debug('GitLab ReadOnly: preventing possible non read-only operation') + error_message = 'You cannot do writing operations on a read-only GitLab instance' - if json_request? - return [403, { 'Content-Type' => 'application/json' }, [{ 'message' => error_message }.to_json]] - else - rack_flash.alert = error_message - rack_session['flash'] = rack_flash.to_session_value + if json_request? + return [403, { 'Content-Type' => 'application/json' }, [{ 'message' => error_message }.to_json]] + else + rack_flash.alert = error_message + rack_session['flash'] = rack_flash.to_session_value - return [301, { 'Location' => last_visited_url }, []] + return [301, { 'Location' => last_visited_url }, []] + end end + + @app.call(@env) end - @app.call(env).tap { @env = nil } - end + private - private + def disallowed_request? + DISALLOWED_METHODS.include?(@env['REQUEST_METHOD']) && + !whitelisted_routes + end - def internal_routes - API_VERSIONS.flat_map { |version| "api/v#{version}/internal" } - end + def json_request? + request.media_type == APPLICATION_JSON + end - def disallowed_request? - DISALLOWED_METHODS.include?(@env['REQUEST_METHOD']) && !whitelisted_routes - end + def rack_flash + @rack_flash ||= ActionDispatch::Flash::FlashHash.from_session_value(rack_session) + end - def json_request? - request.media_type == APPLICATION_JSON - end + def rack_session + @env['rack.session'] + end - def rack_flash - @rack_flash ||= ActionDispatch::Flash::FlashHash.from_session_value(rack_session) - end + def request + @env['rack.request'] ||= Rack::Request.new(@env) + end - def rack_session - @env['rack.session'] - end + def last_visited_url + @env['HTTP_REFERER'] || rack_session['user_return_to'] || Gitlab::Routing.url_helpers.root_url + end - def request - @env['rack.request'] ||= Rack::Request.new(@env) - end + def route_hash + @route_hash ||= Rails.application.routes.recognize_path(request.url, { method: request.request_method }) rescue {} + end - def last_visited_url - @env['HTTP_REFERER'] || rack_session['user_return_to'] || Gitlab::Routing.url_helpers.root_url - end + def whitelisted_routes + grack_route || ReadOnly.internal_routes.any? { |path| request.path.include?(path) } || lfs_route || sidekiq_route + end - def route_hash - @route_hash ||= Rails.application.routes.recognize_path(request.url, { method: request.request_method }) rescue {} - end + def sidekiq_route + request.path.start_with?('/admin/sidekiq') + end - def whitelisted_routes - grack_route || @whitelisted.any? { |path| request.path.include?(path) } || lfs_route || sidekiq_route - end + def grack_route + # Calling route_hash may be expensive. Only do it if we think there's a possible match + return false unless request.path.end_with?('.git/git-upload-pack') - def sidekiq_route - request.path.start_with?('/admin/sidekiq') - end + route_hash[:controller] == 'projects/git_http' && route_hash[:action] == 'git_upload_pack' + end + + def lfs_route + # Calling route_hash may be expensive. Only do it if we think there's a possible match + return false unless request.path.end_with?('/info/lfs/objects/batch') - def grack_route - # Calling route_hash may be expensive. Only do it if we think there's a possible match - return false unless request.path.end_with?('.git/git-upload-pack') + route_hash[:controller] == 'projects/lfs_api' && route_hash[:action] == 'batch' + end + end - route_hash[:controller] == 'projects/git_http' && route_hash[:action] == 'git_upload_pack' + def self.internal_routes + @internal_routes ||= + API_VERSIONS.map { |version| "api/v#{version}/internal" } end - def lfs_route - # Calling route_hash may be expensive. Only do it if we think there's a possible match - return false unless request.path.end_with?('/info/lfs/objects/batch') + def initialize(app) + @app = app + end - route_hash[:controller] == 'projects/lfs_api' && route_hash[:action] == 'batch' + def call(env) + Controller.new(@app, env).call end end end diff --git a/spec/lib/gitlab/middleware/read_only_spec.rb b/spec/lib/gitlab/middleware/read_only_spec.rb index 07ba11b93a3..b3c85142b82 100644 --- a/spec/lib/gitlab/middleware/read_only_spec.rb +++ b/spec/lib/gitlab/middleware/read_only_spec.rb @@ -11,8 +11,10 @@ describe Gitlab::Middleware::ReadOnly do RSpec::Matchers.define :disallow_request do match do |middleware| - flash = middleware.send(:rack_flash) - flash['alert'] && flash['alert'].include?('You cannot do writing operations') + alert = middleware.env['rack.session'].to_hash + .dig('flash', 'flashes', 'alert') + + alert&.include?('You cannot do writing operations') end end @@ -34,7 +36,22 @@ describe Gitlab::Middleware::ReadOnly do rack.to_app end - subject { described_class.new(fake_app) } + let(:observe_env) do + Module.new do + attr_reader :env + + def call(env) + @env = env + super + end + end + end + + subject do + app = described_class.new(fake_app) + app.extend(observe_env) + app + end let(:request) { Rack::MockRequest.new(rack_stack) } -- cgit v1.2.1 From 31f1ec59a7cf7517cd5935ef3af540aceba07bb3 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 7 Feb 2018 22:44:05 +0800 Subject: Release the entire env --- config.ru | 2 +- lib/gitlab/middleware/release_controller.rb | 9 --------- lib/gitlab/middleware/release_env.rb | 14 ++++++++++++++ .../lib/gitlab/middleware/release_controller_spec.rb | 20 -------------------- spec/lib/gitlab/middleware/release_env_spec.rb | 20 ++++++++++++++++++++ 5 files changed, 35 insertions(+), 30 deletions(-) delete mode 100644 lib/gitlab/middleware/release_controller.rb create mode 100644 lib/gitlab/middleware/release_env.rb delete mode 100644 spec/lib/gitlab/middleware/release_controller_spec.rb create mode 100644 spec/lib/gitlab/middleware/release_env_spec.rb diff --git a/config.ru b/config.ru index c4bef72308e..7b15939c6ff 100644 --- a/config.ru +++ b/config.ru @@ -23,6 +23,6 @@ warmup do |app| end map ENV['RAILS_RELATIVE_URL_ROOT'] || "/" do - use Gitlab::ReleaseController + use Gitlab::Middleware::ReleaseEnv run Gitlab::Application end diff --git a/lib/gitlab/middleware/release_controller.rb b/lib/gitlab/middleware/release_controller.rb deleted file mode 100644 index a21d718d51c..00000000000 --- a/lib/gitlab/middleware/release_controller.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Gitlab - module Middleware - ReleaseController = Struct.new(:app) do - def call(env) - app.call(env).tap { env.delete('action_controller.instance') } - end - end - end -end diff --git a/lib/gitlab/middleware/release_env.rb b/lib/gitlab/middleware/release_env.rb new file mode 100644 index 00000000000..f8d0a135965 --- /dev/null +++ b/lib/gitlab/middleware/release_env.rb @@ -0,0 +1,14 @@ +module Gitlab + module Middleware + # Some of middleware would hold env for no good reason even after the + # request had already been processed, and we could not garbage collect + # them due to this. Put this middleware as the first middleware so that + # it would clear the env after the request is done, allowing GC gets a + # chance to release memory for the last request. + ReleaseEnv = Struct.new(:app) do + def call(env) + app.call(env).tap { env.clear } + end + end + end +end diff --git a/spec/lib/gitlab/middleware/release_controller_spec.rb b/spec/lib/gitlab/middleware/release_controller_spec.rb deleted file mode 100644 index 854bac6e751..00000000000 --- a/spec/lib/gitlab/middleware/release_controller_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'spec_helper' - -describe Gitlab::Middleware::ReleaseController do - let(:inner_app) { double(:app) } - let(:app) { described_class.new(inner_app) } - let(:env) { { 'action_controller.instance' => 'something' } } - - before do - expect(inner_app).to receive(:call).with(env).and_return('yay') - end - - describe '#call' do - it 'calls the app and delete the controller' do - result = app.call(env) - - expect(result).to eq('yay') - expect(env).to be_empty - end - end -end diff --git a/spec/lib/gitlab/middleware/release_env_spec.rb b/spec/lib/gitlab/middleware/release_env_spec.rb new file mode 100644 index 00000000000..657b705502a --- /dev/null +++ b/spec/lib/gitlab/middleware/release_env_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Gitlab::Middleware::ReleaseEnv do + let(:inner_app) { double(:app) } + let(:app) { described_class.new(inner_app) } + let(:env) { { 'action_controller.instance' => 'something' } } + + before do + expect(inner_app).to receive(:call).with(env).and_return('yay') + end + + describe '#call' do + it 'calls the app and delete the controller' do + result = app.call(env) + + expect(result).to eq('yay') + expect(env).to be_empty + end + end +end -- cgit v1.2.1 From 5309d4457aea74729a8c6be9ec76d535f922bf8a Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 7 Feb 2018 22:44:18 +0800 Subject: Put controller in its separate file --- lib/gitlab/middleware/read_only.rb | 80 +------------------------- lib/gitlab/middleware/read_only/controller.rb | 83 +++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 79 deletions(-) create mode 100644 lib/gitlab/middleware/read_only/controller.rb diff --git a/lib/gitlab/middleware/read_only.rb b/lib/gitlab/middleware/read_only.rb index b7649ea01db..19b74c0c122 100644 --- a/lib/gitlab/middleware/read_only.rb +++ b/lib/gitlab/middleware/read_only.rb @@ -5,84 +5,6 @@ module Gitlab APPLICATION_JSON = 'application/json'.freeze API_VERSIONS = (3..4) - class Controller - def initialize(app, env) - @app = app - @env = env - end - - def call - if disallowed_request? && Gitlab::Database.read_only? - Rails.logger.debug('GitLab ReadOnly: preventing possible non read-only operation') - error_message = 'You cannot do writing operations on a read-only GitLab instance' - - if json_request? - return [403, { 'Content-Type' => 'application/json' }, [{ 'message' => error_message }.to_json]] - else - rack_flash.alert = error_message - rack_session['flash'] = rack_flash.to_session_value - - return [301, { 'Location' => last_visited_url }, []] - end - end - - @app.call(@env) - end - - private - - def disallowed_request? - DISALLOWED_METHODS.include?(@env['REQUEST_METHOD']) && - !whitelisted_routes - end - - def json_request? - request.media_type == APPLICATION_JSON - end - - def rack_flash - @rack_flash ||= ActionDispatch::Flash::FlashHash.from_session_value(rack_session) - end - - def rack_session - @env['rack.session'] - end - - def request - @env['rack.request'] ||= Rack::Request.new(@env) - end - - def last_visited_url - @env['HTTP_REFERER'] || rack_session['user_return_to'] || Gitlab::Routing.url_helpers.root_url - end - - def route_hash - @route_hash ||= Rails.application.routes.recognize_path(request.url, { method: request.request_method }) rescue {} - end - - def whitelisted_routes - grack_route || ReadOnly.internal_routes.any? { |path| request.path.include?(path) } || lfs_route || sidekiq_route - end - - def sidekiq_route - request.path.start_with?('/admin/sidekiq') - end - - def grack_route - # Calling route_hash may be expensive. Only do it if we think there's a possible match - return false unless request.path.end_with?('.git/git-upload-pack') - - route_hash[:controller] == 'projects/git_http' && route_hash[:action] == 'git_upload_pack' - end - - def lfs_route - # Calling route_hash may be expensive. Only do it if we think there's a possible match - return false unless request.path.end_with?('/info/lfs/objects/batch') - - route_hash[:controller] == 'projects/lfs_api' && route_hash[:action] == 'batch' - end - end - def self.internal_routes @internal_routes ||= API_VERSIONS.map { |version| "api/v#{version}/internal" } @@ -93,7 +15,7 @@ module Gitlab end def call(env) - Controller.new(@app, env).call + ReadOnly::Controller.new(@app, env).call end end end diff --git a/lib/gitlab/middleware/read_only/controller.rb b/lib/gitlab/middleware/read_only/controller.rb new file mode 100644 index 00000000000..053cb6f0a9f --- /dev/null +++ b/lib/gitlab/middleware/read_only/controller.rb @@ -0,0 +1,83 @@ +module Gitlab + module Middleware + class ReadOnly + class Controller + def initialize(app, env) + @app = app + @env = env + end + + def call + if disallowed_request? && Gitlab::Database.read_only? + Rails.logger.debug('GitLab ReadOnly: preventing possible non read-only operation') + error_message = 'You cannot do writing operations on a read-only GitLab instance' + + if json_request? + return [403, { 'Content-Type' => 'application/json' }, [{ 'message' => error_message }.to_json]] + else + rack_flash.alert = error_message + rack_session['flash'] = rack_flash.to_session_value + + return [301, { 'Location' => last_visited_url }, []] + end + end + + @app.call(@env) + end + + private + + def disallowed_request? + DISALLOWED_METHODS.include?(@env['REQUEST_METHOD']) && + !whitelisted_routes + end + + def json_request? + request.media_type == APPLICATION_JSON + end + + def rack_flash + @rack_flash ||= ActionDispatch::Flash::FlashHash.from_session_value(rack_session) + end + + def rack_session + @env['rack.session'] + end + + def request + @env['rack.request'] ||= Rack::Request.new(@env) + end + + def last_visited_url + @env['HTTP_REFERER'] || rack_session['user_return_to'] || Gitlab::Routing.url_helpers.root_url + end + + def route_hash + @route_hash ||= Rails.application.routes.recognize_path(request.url, { method: request.request_method }) rescue {} + end + + def whitelisted_routes + grack_route || ReadOnly.internal_routes.any? { |path| request.path.include?(path) } || lfs_route || sidekiq_route + end + + def sidekiq_route + request.path.start_with?('/admin/sidekiq') + end + + def grack_route + # Calling route_hash may be expensive. Only do it if we think there's a possible match + return false unless request.path.end_with?('.git/git-upload-pack') + + route_hash[:controller] == 'projects/git_http' && route_hash[:action] == 'git_upload_pack' + end + + def lfs_route + # Calling route_hash may be expensive. Only do it if we think there's a possible match + return false unless request.path.end_with?('/info/lfs/objects/batch') + + route_hash[:controller] == 'projects/lfs_api' && route_hash[:action] == 'batch' + end + end + end + end +end -- cgit v1.2.1 From 461ecbcf07f0785b5ea50c62b114bf8217ac5199 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 7 Feb 2018 22:55:50 +0800 Subject: Update peek-performance_bar which doesn't hold env --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index e78c3c5f794..546ba2f2a18 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -601,7 +601,7 @@ GEM atomic (>= 1.0.0) mysql2 peek - peek-performance_bar (1.3.0) + peek-performance_bar (1.3.1) peek (>= 0.1.0) peek-pg (1.3.0) concurrent-ruby -- cgit v1.2.1 From 89c652b8fe24dd1784acb7b9d475eec6775b6a04 Mon Sep 17 00:00:00 2001 From: George Tsiolis Date: Tue, 6 Feb 2018 16:44:38 +0200 Subject: Move BoardNewIssue vue component --- .../javascripts/boards/components/board_list.vue | 2 +- .../boards/components/board_new_issue.js | 100 ------------------ .../boards/components/board_new_issue.vue | 113 +++++++++++++++++++++ app/assets/javascripts/boards/models/issue.js | 2 + ...refactor-move-board-new-issue-vue-component.yml | 5 + spec/javascripts/boards/board_new_issue_spec.js | 2 +- 6 files changed, 122 insertions(+), 102 deletions(-) delete mode 100644 app/assets/javascripts/boards/components/board_new_issue.js create mode 100644 app/assets/javascripts/boards/components/board_new_issue.vue create mode 100644 changelogs/unreleased/refactor-move-board-new-issue-vue-component.yml diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue index 9a0442e2afe..6637904d87d 100644 --- a/app/assets/javascripts/boards/components/board_list.vue +++ b/app/assets/javascripts/boards/components/board_list.vue @@ -1,6 +1,6 @@ + + diff --git a/app/assets/javascripts/boards/models/issue.js b/app/assets/javascripts/boards/models/issue.js index 81edd95bf2b..3bfb6d39ad5 100644 --- a/app/assets/javascripts/boards/models/issue.js +++ b/app/assets/javascripts/boards/models/issue.js @@ -110,3 +110,5 @@ class ListIssue { } window.ListIssue = ListIssue; + +export default ListIssue; diff --git a/changelogs/unreleased/refactor-move-board-new-issue-vue-component.yml b/changelogs/unreleased/refactor-move-board-new-issue-vue-component.yml new file mode 100644 index 00000000000..20d05530513 --- /dev/null +++ b/changelogs/unreleased/refactor-move-board-new-issue-vue-component.yml @@ -0,0 +1,5 @@ +--- +title: Move BoardNewIssue vue component +merge_request: 16947 +author: George Tsiolis +type: performance diff --git a/spec/javascripts/boards/board_new_issue_spec.js b/spec/javascripts/boards/board_new_issue_spec.js index e204985f039..d5fbfdeaa91 100644 --- a/spec/javascripts/boards/board_new_issue_spec.js +++ b/spec/javascripts/boards/board_new_issue_spec.js @@ -4,7 +4,7 @@ import Vue from 'vue'; import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; -import boardNewIssue from '~/boards/components/board_new_issue'; +import boardNewIssue from '~/boards/components/board_new_issue.vue'; import '~/boards/models/list'; import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data'; -- cgit v1.2.1 From acc70f7da1c21071a11bb07b86486c8e61799be8 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 12 Feb 2018 13:23:03 +0100 Subject: Move pipeline stages background migration in time --- .../20180105101928_schedule_build_stage_migration.rb | 20 -------------------- .../20180212101928_schedule_build_stage_migration.rb | 20 ++++++++++++++++++++ db/schema.rb | 2 +- .../schedule_build_stage_migration_spec.rb | 2 +- 4 files changed, 22 insertions(+), 22 deletions(-) delete mode 100644 db/post_migrate/20180105101928_schedule_build_stage_migration.rb create mode 100644 db/post_migrate/20180212101928_schedule_build_stage_migration.rb diff --git a/db/post_migrate/20180105101928_schedule_build_stage_migration.rb b/db/post_migrate/20180105101928_schedule_build_stage_migration.rb deleted file mode 100644 index b33127140bb..00000000000 --- a/db/post_migrate/20180105101928_schedule_build_stage_migration.rb +++ /dev/null @@ -1,20 +0,0 @@ -class ScheduleBuildStageMigration < ActiveRecord::Migration - include Gitlab::Database::MigrationHelpers - - DOWNTIME = false - MIGRATION = 'MigrateBuildStage'.freeze - BATCH = 1000 - - class Build < ActiveRecord::Base - include EachBatch - self.table_name = 'ci_builds' - end - - def change - Build.where('stage_id IS NULL').each_batch(of: BATCH) do |builds, index| - builds.pluck(:id).map { |id| [MIGRATION, [id]] }.tap do |migrations| - BackgroundMigrationWorker.bulk_perform_in(index.minutes, migrations) - end - end - end -end diff --git a/db/post_migrate/20180212101928_schedule_build_stage_migration.rb b/db/post_migrate/20180212101928_schedule_build_stage_migration.rb new file mode 100644 index 00000000000..b33127140bb --- /dev/null +++ b/db/post_migrate/20180212101928_schedule_build_stage_migration.rb @@ -0,0 +1,20 @@ +class ScheduleBuildStageMigration < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + MIGRATION = 'MigrateBuildStage'.freeze + BATCH = 1000 + + class Build < ActiveRecord::Base + include EachBatch + self.table_name = 'ci_builds' + end + + def change + Build.where('stage_id IS NULL').each_batch(of: BATCH) do |builds, index| + builds.pluck(:id).map { |id| [MIGRATION, [id]] }.tap do |migrations| + BackgroundMigrationWorker.bulk_perform_in(index.minutes, migrations) + end + end + end +end diff --git a/db/schema.rb b/db/schema.rb index d07a4c31618..e3474a4995f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180206200543) do +ActiveRecord::Schema.define(version: 20180212101928) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/spec/migrations/schedule_build_stage_migration_spec.rb b/spec/migrations/schedule_build_stage_migration_spec.rb index b475e1fe7de..5e18d2e72f9 100644 --- a/spec/migrations/schedule_build_stage_migration_spec.rb +++ b/spec/migrations/schedule_build_stage_migration_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -require Rails.root.join('db', 'post_migrate', '20180105101928_schedule_build_stage_migration') +require Rails.root.join('db', 'post_migrate', '20180212101928_schedule_build_stage_migration') describe ScheduleBuildStageMigration, :migration do let(:projects) { table(:projects) } -- cgit v1.2.1 From 8488c98e70561c6d7dde025131c15df4692b20b1 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 13 Feb 2018 16:18:09 +0100 Subject: Schedule pipeline stages migration every 5 minutes [ci skip] --- .../20180212101928_schedule_build_stage_migration.rb | 18 +++++++++++++----- spec/migrations/schedule_build_stage_migration_spec.rb | 14 ++++---------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/db/post_migrate/20180212101928_schedule_build_stage_migration.rb b/db/post_migrate/20180212101928_schedule_build_stage_migration.rb index b33127140bb..f03a3f8d355 100644 --- a/db/post_migrate/20180212101928_schedule_build_stage_migration.rb +++ b/db/post_migrate/20180212101928_schedule_build_stage_migration.rb @@ -3,18 +3,26 @@ class ScheduleBuildStageMigration < ActiveRecord::Migration DOWNTIME = false MIGRATION = 'MigrateBuildStage'.freeze - BATCH = 1000 + BATCH_SIZE = 500 + + disable_ddl_transaction! class Build < ActiveRecord::Base include EachBatch self.table_name = 'ci_builds' end - def change - Build.where('stage_id IS NULL').each_batch(of: BATCH) do |builds, index| - builds.pluck(:id).map { |id| [MIGRATION, [id]] }.tap do |migrations| - BackgroundMigrationWorker.bulk_perform_in(index.minutes, migrations) + def up + disable_statement_timeout + + Build.where('stage_id IS NULL').each_batch(of: BATCH_SIZE) do |builds, index| + builds.pluck('MIN(id)', 'MAX(id)').first.tap do |range| + BackgroundMigrationWorker.perform_in(index * 5.minutes, MIGRATION, range) end end end + + def down + # noop + end end diff --git a/spec/migrations/schedule_build_stage_migration_spec.rb b/spec/migrations/schedule_build_stage_migration_spec.rb index 5e18d2e72f9..06657410173 100644 --- a/spec/migrations/schedule_build_stage_migration_spec.rb +++ b/spec/migrations/schedule_build_stage_migration_spec.rb @@ -8,18 +8,12 @@ describe ScheduleBuildStageMigration, :migration do let(:jobs) { table(:ci_builds) } before do - stub_const("#{described_class}::BATCH", 1) + stub_const("#{described_class}::BATCH_SIZE", 1) - ## - # Dependencies - # projects.create!(id: 123, name: 'gitlab', path: 'gitlab-ce') pipelines.create!(id: 1, project_id: 123, ref: 'master', sha: 'adf43c3a') stages.create!(id: 1, project_id: 123, pipeline_id: 1, name: 'test') - ## - # CI/CD jobs - # jobs.create!(id: 11, commit_id: 1, project_id: 123, stage_id: nil) jobs.create!(id: 206, commit_id: 1, project_id: 123, stage_id: nil) jobs.create!(id: 3413, commit_id: 1, project_id: 123, stage_id: nil) @@ -31,9 +25,9 @@ describe ScheduleBuildStageMigration, :migration do Timecop.freeze do migrate! - expect(described_class::MIGRATION).to be_scheduled_delayed_migration(1.minute, 11) - expect(described_class::MIGRATION).to be_scheduled_delayed_migration(2.minutes, 206) - expect(described_class::MIGRATION).to be_scheduled_delayed_migration(3.minutes, 3413) + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, 11, 11) + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, 206, 206) + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(15.minutes, 3413, 3413) expect(BackgroundMigrationWorker.jobs.size).to eq 3 end end -- cgit v1.2.1 From 378b2bad11b893fabebc083d000295c1ec499b23 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 14 Feb 2018 13:59:16 +0100 Subject: Migrate pipeline stages in batches instead of single row --- .../background_migration/migrate_build_stage.rb | 84 ++++++++-------------- .../migrate_build_stage_spec.rb | 12 +--- 2 files changed, 32 insertions(+), 64 deletions(-) diff --git a/lib/gitlab/background_migration/migrate_build_stage.rb b/lib/gitlab/background_migration/migrate_build_stage.rb index 6819810b145..3e297a90b19 100644 --- a/lib/gitlab/background_migration/migrate_build_stage.rb +++ b/lib/gitlab/background_migration/migrate_build_stage.rb @@ -5,69 +5,45 @@ module Gitlab module BackgroundMigration class MigrateBuildStage - def perform(id) - DatabaseBuild.find_by(id: id).try do |build| - MigratableStage.new(build).tap do |stage| - break if stage.exists? || stage.legacy? - - stage.ensure! - stage.migrate_reference! - stage.migrate_status! - end - end - end - - class DatabaseStage < ActiveRecord::Base - self.table_name = 'ci_stages' - end - - class DatabaseBuild < ActiveRecord::Base - self.table_name = 'ci_builds' - end - - class MigratableStage - def initialize(build) - @build = build - end - - def exists? - @build.reload.stage_id.present? + module Migratable + class Stage < ActiveRecord::Base + self.table_name = 'ci_stages' end - ## - # We can have some very old stages that do not have `ci_builds.stage` set. - # - # In that case we just don't migrate such stage. - # - def legacy? - @build.stage.nil? - end + class Build < ActiveRecord::Base + self.table_name = 'ci_builds' - def ensure! - find || create! - end + def ensure_stage! + find || create! + rescue ActiveRecord::RecordNotUnique + # TODO + end - def find - DatabaseStage.find_by(name: @build.stage, - pipeline_id: @build.commit_id, - project_id: @build.project_id) - end + def find + Stage.find_by(name: self.stage, + pipeline_id: self.commit_id, + project_id: self.project_id) + end - def create! - DatabaseStage.create!(name: @build.stage, - pipeline_id: @build.commit_id, - project_id: @build.project_id) + def create! + Stage.create!(name: self.stage || 'test', + pipeline_id: self.commit_id, + project_id: self.project_id) + end end + end - def migrate_reference! - MigrateBuildStageIdReference.new.perform(@build.id, @build.id) - end + def perform(start_id, stop_id) + # TODO, should we disable_statement_timeout? + # TODO, use plain SQL query? - def migrate_status! - raise ArgumentError unless exists? + stages = Migratable::Build.where('stage_id IS NULL') + .where("id BETWEEN #{start_id.to_i} AND #{stop_id.to_i}") + .map { |build| build.ensure_stage! } + .compact.map(&:id) - MigrateStageStatus.new.perform(@build.stage_id, @build.stage_id) - end + MigrateBuildStageIdReference.new.perform(start_id, stop_id) + MigrateStageStatus.new.perform(stages.min, stages.max) end end end diff --git a/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb b/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb index d20b5c29a71..27cb63848de 100644 --- a/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb +++ b/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::BackgroundMigration::MigrateBuildStage, :migration, schema: 20180105101928 do +describe Gitlab::BackgroundMigration::MigrateBuildStage, :migration, schema: 20180212101928 do let(:projects) { table(:projects) } let(:pipelines) { table(:ci_pipelines) } let(:stages) { table(:ci_stages) } @@ -10,15 +10,9 @@ describe Gitlab::BackgroundMigration::MigrateBuildStage, :migration, schema: 201 failed: 4, canceled: 5, skipped: 6, manual: 7 }.freeze before do - ## - # Dependencies - # projects.create!(id: 123, name: 'gitlab', path: 'gitlab-ce') pipelines.create!(id: 1, project_id: 123, ref: 'master', sha: 'adf43c3a') - ## - # CI/CD jobs - # jobs.create!(id: 1, commit_id: 1, project_id: 123, stage_idx: 2, stage: 'build', status: :success) jobs.create!(id: 2, commit_id: 1, project_id: 123, @@ -36,9 +30,7 @@ describe Gitlab::BackgroundMigration::MigrateBuildStage, :migration, schema: 201 it 'correctly migrates builds stages' do expect(stages.count).to be_zero - jobs.all.find_each do |job| - described_class.new.perform(job.id) - end + described_class.new.perform(1, 6) expect(stages.count).to eq 3 expect(stages.all.pluck(:name)).to match_array %w[test build deploy] -- cgit v1.2.1 From b3d8a1e29a44fdb7770929b466b24c286e271208 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 14 Feb 2018 16:09:56 +0200 Subject: update changelog --- changelogs/unreleased/4826-create-empty-wiki-when-it-s-enabled.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/4826-create-empty-wiki-when-it-s-enabled.yml diff --git a/changelogs/unreleased/4826-create-empty-wiki-when-it-s-enabled.yml b/changelogs/unreleased/4826-create-empty-wiki-when-it-s-enabled.yml new file mode 100644 index 00000000000..c0fa8e2e377 --- /dev/null +++ b/changelogs/unreleased/4826-create-empty-wiki-when-it-s-enabled.yml @@ -0,0 +1,5 @@ +--- +title: Make sure wiki exists when it's enabled +merge_request: +author: +type: fixed -- cgit v1.2.1 From c1828eaed56159998d1eaafdaa135f1b3480549b Mon Sep 17 00:00:00 2001 From: Dylan Griffith Date: Mon, 12 Feb 2018 14:22:15 +1100 Subject: Persist external IP of ingress controller created for GKE (#42643) --- app/models/clusters/applications/ingress.rb | 7 ++ app/models/clusters/concerns/application_core.rb | 4 ++ app/serializers/cluster_application_entity.rb | 1 + .../check_ingress_ip_address_service.rb | 37 ++++++++++ .../check_installation_progress_service.rb | 1 + app/workers/all_queues.yml | 1 + .../cluster_wait_for_ingress_ip_address_worker.rb | 14 ++++ ...external_ip_to_clusters_applications_ingress.rb | 31 +++++++++ db/schema.rb | 3 +- spec/fixtures/api/schemas/cluster_status.json | 3 +- spec/models/clusters/applications/ingress_spec.rb | 14 ++++ .../serializers/cluster_application_entity_spec.rb | 14 ++++ .../check_ingress_ip_address_service_spec.rb | 81 ++++++++++++++++++++++ ...ster_wait_for_ingress_ip_address_worker_spec.rb | 20 ++++++ 14 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 app/services/clusters/applications/check_ingress_ip_address_service.rb create mode 100644 app/workers/cluster_wait_for_ingress_ip_address_worker.rb create mode 100644 db/migrate/20180212030105_add_external_ip_to_clusters_applications_ingress.rb create mode 100644 spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb create mode 100644 spec/workers/cluster_wait_for_ingress_ip_address_worker_spec.rb diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb index aa5cf97756f..5e9086aecca 100644 --- a/app/models/clusters/applications/ingress.rb +++ b/app/models/clusters/applications/ingress.rb @@ -13,6 +13,8 @@ module Clusters nginx: 1 } + IP_ADDRESS_FETCH_RETRIES = 3 + def chart 'stable/nginx-ingress' end @@ -24,6 +26,11 @@ module Clusters def install_command Gitlab::Kubernetes::Helm::InstallCommand.new(name, chart: chart, chart_values_file: chart_values_file) end + + def post_install + ClusterWaitForIngressIpAddressWorker.perform_in( + ClusterWaitForIngressIpAddressWorker::INTERVAL, name, id, IP_ADDRESS_FETCH_RETRIES) + end end end end diff --git a/app/models/clusters/concerns/application_core.rb b/app/models/clusters/concerns/application_core.rb index a98fa85a5ff..b047fbce214 100644 --- a/app/models/clusters/concerns/application_core.rb +++ b/app/models/clusters/concerns/application_core.rb @@ -23,6 +23,10 @@ module Clusters def name self.class.application_name end + + def post_install + # Override for any extra work that needs to be done after install + end end end end diff --git a/app/serializers/cluster_application_entity.rb b/app/serializers/cluster_application_entity.rb index 3f9a275ad08..b22a0b666ef 100644 --- a/app/serializers/cluster_application_entity.rb +++ b/app/serializers/cluster_application_entity.rb @@ -2,4 +2,5 @@ class ClusterApplicationEntity < Grape::Entity expose :name expose :status_name, as: :status expose :status_reason + expose :external_ip, if: -> (e, _) { e.respond_to?(:external_ip) } end diff --git a/app/services/clusters/applications/check_ingress_ip_address_service.rb b/app/services/clusters/applications/check_ingress_ip_address_service.rb new file mode 100644 index 00000000000..cf132676aa6 --- /dev/null +++ b/app/services/clusters/applications/check_ingress_ip_address_service.rb @@ -0,0 +1,37 @@ +module Clusters + module Applications + class CheckIngressIpAddressService < BaseHelmService + def execute(retries_remaining) + return if app.external_ip + + service = get_service + + if service.status.loadBalancer.ingress + resolve_external_ip(service) + else + retry_if_necessary(retries_remaining) + end + + rescue KubeException + retry_if_necessary(retries_remaining) + end + + private + + def resolve_external_ip(service) + app.update!( external_ip: service.status.loadBalancer.ingress[0].ip) + end + + def get_service + kubeclient.get_service('ingress-nginx-ingress-controller', Gitlab::Kubernetes::Helm::NAMESPACE) + end + + def retry_if_necessary(retries_remaining) + if retries_remaining > 0 + ClusterWaitForIngressIpAddressWorker.perform_in( + ClusterWaitForIngressIpAddressWorker::INTERVAL, app.name, app.id, retries_remaining - 1) + end + end + end + end +end diff --git a/app/services/clusters/applications/check_installation_progress_service.rb b/app/services/clusters/applications/check_installation_progress_service.rb index bde090eaeec..7dcddc1c3f7 100644 --- a/app/services/clusters/applications/check_installation_progress_service.rb +++ b/app/services/clusters/applications/check_installation_progress_service.rb @@ -20,6 +20,7 @@ module Clusters def on_success app.make_installed! + app.post_install ensure remove_installation_pod end diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index f2c20114534..35ffa5d5fda 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -23,6 +23,7 @@ - gcp_cluster:cluster_wait_for_app_installation - gcp_cluster:wait_for_cluster_creation - gcp_cluster:check_gcp_project_billing +- gcp_cluster:cluster_wait_for_ingress_ip_address - github_import_advance_stage - github_importer:github_import_import_diff_note diff --git a/app/workers/cluster_wait_for_ingress_ip_address_worker.rb b/app/workers/cluster_wait_for_ingress_ip_address_worker.rb new file mode 100644 index 00000000000..829417484cf --- /dev/null +++ b/app/workers/cluster_wait_for_ingress_ip_address_worker.rb @@ -0,0 +1,14 @@ +class ClusterWaitForIngressIpAddressWorker + include ApplicationWorker + include ClusterQueue + include ClusterApplications + + INTERVAL = 10.seconds + TIMEOUT = 20.minutes + + def perform(app_name, app_id, retries_remaining) + find_application(app_name, app_id) do |app| + Clusters::Applications::CheckIngressIpAddressService.new(app).execute(retries_remaining) + end + end +end diff --git a/db/migrate/20180212030105_add_external_ip_to_clusters_applications_ingress.rb b/db/migrate/20180212030105_add_external_ip_to_clusters_applications_ingress.rb new file mode 100644 index 00000000000..c18d1d7a67b --- /dev/null +++ b/db/migrate/20180212030105_add_external_ip_to_clusters_applications_ingress.rb @@ -0,0 +1,31 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddExternalIpToClustersApplicationsIngress < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + # When a migration requires downtime you **must** uncomment the following + # constant and define a short and easy to understand explanation as to why the + # migration requires downtime. + # DOWNTIME_REASON = '' + + # When using the methods "add_concurrent_index", "remove_concurrent_index" or + # "add_column_with_default" you must disable the use of transactions + # as these methods can not run in an existing transaction. + # When using "add_concurrent_index" or "remove_concurrent_index" methods make sure + # that either of them is the _only_ method called in the migration, + # any other changes should go in a separate migration. + # This ensures that upon failure _only_ the index creation or removing fails + # and can be retried or reverted easily. + # + # To disable transactions uncomment the following line and remove these + # comments: + # disable_ddl_transaction! + + def change + add_column :clusters_applications_ingress, :external_ip, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 6b43fc8403c..e13415926ec 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180208183958) do +ActiveRecord::Schema.define(version: 20180212030105) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -568,6 +568,7 @@ ActiveRecord::Schema.define(version: 20180208183958) do t.string "version", null: false t.string "cluster_ip" t.text "status_reason" + t.string "external_ip" end create_table "clusters_applications_prometheus", force: :cascade do |t| diff --git a/spec/fixtures/api/schemas/cluster_status.json b/spec/fixtures/api/schemas/cluster_status.json index 489d563be2b..9617157ee37 100644 --- a/spec/fixtures/api/schemas/cluster_status.json +++ b/spec/fixtures/api/schemas/cluster_status.json @@ -30,7 +30,8 @@ ] } }, - "status_reason": { "type": ["string", "null"] } + "status_reason": { "type": ["string", "null"] }, + "external_ip": { "type": ["string", null] } }, "required" : [ "name", "status" ] } diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb index 619c088b0bf..da9535f9d16 100644 --- a/spec/models/clusters/applications/ingress_spec.rb +++ b/spec/models/clusters/applications/ingress_spec.rb @@ -5,4 +5,18 @@ describe Clusters::Applications::Ingress do it { is_expected.to validate_presence_of(:cluster) } include_examples 'cluster application specs', described_class + + describe '#post_install' do + let(:application) { create(:clusters_applications_ingress, :installed) } + + before do + allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in) + application.post_install + end + + it 'schedules a ClusterWaitForIngressIpAddressWorker' do + expect(ClusterWaitForIngressIpAddressWorker).to have_received(:perform_in) + .with(ClusterWaitForIngressIpAddressWorker::INTERVAL, 'ingress', application.id, 3) + end + end end diff --git a/spec/serializers/cluster_application_entity_spec.rb b/spec/serializers/cluster_application_entity_spec.rb index b5a55b4ef6e..70b274083b2 100644 --- a/spec/serializers/cluster_application_entity_spec.rb +++ b/spec/serializers/cluster_application_entity_spec.rb @@ -26,5 +26,19 @@ describe ClusterApplicationEntity do expect(subject[:status_reason]).to eq(application.status_reason) end end + + context 'for ingress application' do + let(:application) do + build( + :clusters_applications_ingress, + :installed, + external_ip: '111.222.111.222', + ) + end + + it 'includes external_ip' do + expect(subject[:external_ip]).to eq('111.222.111.222') + end + end end end diff --git a/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb b/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb new file mode 100644 index 00000000000..5d99196fbe6 --- /dev/null +++ b/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb @@ -0,0 +1,81 @@ +require 'spec_helper' + +describe Clusters::Applications::CheckIngressIpAddressService do + let(:application) { create(:clusters_applications_ingress, :installed) } + let(:service) { described_class.new(application) } + let(:kube_service) do + ::Kubeclient::Resource.new( + { + status: { + loadBalancer: { + ingress: ingress + } + } + } + ) + end + let(:kubeclient) { double(::Kubeclient::Client, get_service: kube_service) } + let(:ingress) { [{ ip: '111.222.111.222' }] } + + before do + allow(application.cluster).to receive(:kubeclient).and_return(kubeclient) + end + + describe '#execute' do + context 'when the ingress ip address is available' do + it 'updates the external_ip for the app and does not schedule another worker' do + expect(ClusterWaitForIngressIpAddressWorker).not_to receive(:perform_in) + + service.execute(1) + + expect(application.external_ip).to eq('111.222.111.222') + end + end + + context 'when the ingress ip address is not available' do + let(:ingress) { nil } + + it 'it schedules another worker with 1 less retry' do + expect(ClusterWaitForIngressIpAddressWorker) + .to receive(:perform_in) + .with(ClusterWaitForIngressIpAddressWorker::INTERVAL, 'ingress', application.id, 0) + + service.execute(1) + end + + context 'when no more retries remaining' do + it 'does not schedule another worker' do + expect(ClusterWaitForIngressIpAddressWorker).not_to receive(:perform_in) + + service.execute(0) + end + end + end + + context 'when there is already an external_ip' do + let(:application) { create(:clusters_applications_ingress, :installed, external_ip: '001.111.002.111') } + + it 'does nothing' do + expect(kubeclient).not_to receive(:get_service) + + service.execute(1) + + expect(application.external_ip).to eq('001.111.002.111') + end + end + + context 'when a kubernetes error occurs' do + before do + allow(kubeclient).to receive(:get_service).and_raise(KubeException.new(500, 'something blew up', nil)) + end + + it 'it schedules another worker with 1 less retry' do + expect(ClusterWaitForIngressIpAddressWorker) + .to receive(:perform_in) + .with(ClusterWaitForIngressIpAddressWorker::INTERVAL, 'ingress', application.id, 0) + + service.execute(1) + end + end + end +end diff --git a/spec/workers/cluster_wait_for_ingress_ip_address_worker_spec.rb b/spec/workers/cluster_wait_for_ingress_ip_address_worker_spec.rb new file mode 100644 index 00000000000..9f8bf35f604 --- /dev/null +++ b/spec/workers/cluster_wait_for_ingress_ip_address_worker_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe ClusterWaitForIngressIpAddressWorker do + describe '#perform' do + let(:service) { instance_double(Clusters::Applications::CheckIngressIpAddressService) } + let(:application) { instance_double(Clusters::Applications::Ingress) } + let(:worker) { described_class.new } + + it 'finds the application and calls CheckIngressIpAddressService#execute' do + expect(worker).to receive(:find_application).with('ingress', 117).and_yield(application) + expect(Clusters::Applications::CheckIngressIpAddressService) + .to receive(:new) + .with(application) + .and_return(service) + expect(service).to receive(:execute).with(2) + + worker.perform('ingress', 117, 2) + end + end +end -- cgit v1.2.1 From 5190ef57a3c5a4333020a5281904e56c00519e91 Mon Sep 17 00:00:00 2001 From: Dylan Griffith Date: Thu, 15 Feb 2018 17:24:59 +1100 Subject: Ensure CheckIngressIpAddressService obtains exclusive lease per ingress controller (#42643) --- .../applications/check_ingress_ip_address_service.rb | 9 +++++++++ .../check_ingress_ip_address_service_spec.rb | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/app/services/clusters/applications/check_ingress_ip_address_service.rb b/app/services/clusters/applications/check_ingress_ip_address_service.rb index cf132676aa6..3262aa59a90 100644 --- a/app/services/clusters/applications/check_ingress_ip_address_service.rb +++ b/app/services/clusters/applications/check_ingress_ip_address_service.rb @@ -1,8 +1,11 @@ module Clusters module Applications class CheckIngressIpAddressService < BaseHelmService + LEASE_TIMEOUT = 3.seconds.to_i + def execute(retries_remaining) return if app.external_ip + return unless try_obtain_lease service = get_service @@ -18,6 +21,12 @@ module Clusters private + def try_obtain_lease + Gitlab::ExclusiveLease + .new("check_ingress_ip_address_service:#{app.id}", timeout: LEASE_TIMEOUT) + .try_obtain + end + def resolve_external_ip(service) app.update!( external_ip: service.status.loadBalancer.ingress[0].ip) end diff --git a/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb b/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb index 5d99196fbe6..6c81acfcf84 100644 --- a/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb +++ b/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb @@ -16,9 +16,14 @@ describe Clusters::Applications::CheckIngressIpAddressService do end let(:kubeclient) { double(::Kubeclient::Client, get_service: kube_service) } let(:ingress) { [{ ip: '111.222.111.222' }] } + let(:exclusive_lease) { instance_double(Gitlab::ExclusiveLease, try_obtain: true) } before do allow(application.cluster).to receive(:kubeclient).and_return(kubeclient) + allow(Gitlab::ExclusiveLease) + .to receive(:new) + .with("check_ingress_ip_address_service:#{application.id}", timeout: 3.seconds.to_i) + .and_return(exclusive_lease) end describe '#execute' do @@ -52,6 +57,20 @@ describe Clusters::Applications::CheckIngressIpAddressService do end end + context 'when the exclusive lease cannot be obtained' do + before do + allow(exclusive_lease) + .to receive(:try_obtain) + .and_return(false) + end + + it 'does not call kubeclient' do + expect(kubeclient).not_to receive(:get_service) + + service.execute(1) + end + end + context 'when there is already an external_ip' do let(:application) { create(:clusters_applications_ingress, :installed, external_ip: '001.111.002.111') } -- cgit v1.2.1 From 6cb5b7c8729151c95d1610f0b2f7255fdac2bdec Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 15 Feb 2018 10:13:20 +0100 Subject: Recover from unique constraint violation in stages migration --- .../20180212101928_schedule_build_stage_migration.rb | 2 +- lib/gitlab/background_migration/migrate_build_stage.rb | 18 +++++++++--------- .../background_migration/migrate_build_stage_spec.rb | 11 +++++++++++ 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/db/post_migrate/20180212101928_schedule_build_stage_migration.rb b/db/post_migrate/20180212101928_schedule_build_stage_migration.rb index f03a3f8d355..ad65a6fb16e 100644 --- a/db/post_migrate/20180212101928_schedule_build_stage_migration.rb +++ b/db/post_migrate/20180212101928_schedule_build_stage_migration.rb @@ -3,7 +3,7 @@ class ScheduleBuildStageMigration < ActiveRecord::Migration DOWNTIME = false MIGRATION = 'MigrateBuildStage'.freeze - BATCH_SIZE = 500 + BATCH_SIZE = 800 disable_ddl_transaction! diff --git a/lib/gitlab/background_migration/migrate_build_stage.rb b/lib/gitlab/background_migration/migrate_build_stage.rb index 3e297a90b19..adca16751af 100644 --- a/lib/gitlab/background_migration/migrate_build_stage.rb +++ b/lib/gitlab/background_migration/migrate_build_stage.rb @@ -13,19 +13,20 @@ module Gitlab class Build < ActiveRecord::Base self.table_name = 'ci_builds' - def ensure_stage! - find || create! + def ensure_stage!(attempts: 2) + find_stage || create_stage! rescue ActiveRecord::RecordNotUnique - # TODO + retry if (attempts -= 1) > 0 + raise end - def find - Stage.find_by(name: self.stage, + def find_stage + Stage.find_by(name: self.stage || 'test', pipeline_id: self.commit_id, project_id: self.project_id) end - def create! + def create_stage! Stage.create!(name: self.stage || 'test', pipeline_id: self.commit_id, project_id: self.project_id) @@ -34,11 +35,10 @@ module Gitlab end def perform(start_id, stop_id) - # TODO, should we disable_statement_timeout? - # TODO, use plain SQL query? + # TODO, statement timeout? stages = Migratable::Build.where('stage_id IS NULL') - .where("id BETWEEN #{start_id.to_i} AND #{stop_id.to_i}") + .where('id BETWEEN ? AND ?', start_id, stop_id) .map { |build| build.ensure_stage! } .compact.map(&:id) diff --git a/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb b/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb index 27cb63848de..e112e9e9e3d 100644 --- a/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb +++ b/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb @@ -40,4 +40,15 @@ describe Gitlab::BackgroundMigration::MigrateBuildStage, :migration, schema: 201 STATUSES[:failed], STATUSES[:pending]] end + + it 'recovers from unique constraint violation only twice' do + allow(described_class::Migratable::Stage) + .to receive(:find_by).and_return(nil) + + expect(described_class::Migratable::Stage) + .to receive(:find_by).exactly(3).times + + expect { described_class.new.perform(1, 6) } + .to raise_error ActiveRecord::RecordNotUnique + end end -- cgit v1.2.1 From 0af391d2f45f23fb806584a5c80747145a07b7c0 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Thu, 15 Feb 2018 12:26:38 +0100 Subject: Refactored use of boards/boards_bundle.js, created a dipatcher import --- app/assets/javascripts/boards/boards_bundle.js | 239 --------------------- app/assets/javascripts/boards/index.js | 239 +++++++++++++++++++++ app/assets/javascripts/dispatcher.js | 5 + .../javascripts/pages/projects/boards/index.js | 6 +- app/views/shared/boards/_show.html.haml | 1 - config/webpack.config.js | 1 - spec/javascripts/test_bundle.js | 2 +- 7 files changed, 249 insertions(+), 244 deletions(-) delete mode 100644 app/assets/javascripts/boards/boards_bundle.js create mode 100644 app/assets/javascripts/boards/index.js diff --git a/app/assets/javascripts/boards/boards_bundle.js b/app/assets/javascripts/boards/boards_bundle.js deleted file mode 100644 index 90166b3d3d1..00000000000 --- a/app/assets/javascripts/boards/boards_bundle.js +++ /dev/null @@ -1,239 +0,0 @@ -/* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren */ - -import _ from 'underscore'; -import Vue from 'vue'; -import Flash from '../flash'; -import { __ } from '../locale'; -import FilteredSearchBoards from './filtered_search_boards'; -import eventHub from './eventhub'; -import sidebarEventHub from '../sidebar/event_hub'; -import './models/issue'; -import './models/label'; -import './models/list'; -import './models/milestone'; -import './models/assignee'; -import './stores/boards_store'; -import './stores/modal_store'; -import BoardService from './services/board_service'; -import './mixins/modal_mixins'; -import './mixins/sortable_default_options'; -import './filters/due_date_filters'; -import './components/board'; -import './components/board_sidebar'; -import './components/new_list_dropdown'; -import './components/modal/index'; -import '../vue_shared/vue_resource_interceptor'; - -$(() => { - const $boardApp = document.getElementById('board-app'); - const Store = gl.issueBoards.BoardsStore; - const ModalStore = gl.issueBoards.ModalStore; - - window.gl = window.gl || {}; - - if (gl.IssueBoardsApp) { - gl.IssueBoardsApp.$destroy(true); - } - - Store.create(); - - // hack to allow sidebar scripts like milestone_select manipulate the BoardsStore - gl.issueBoards.boardStoreIssueSet = (...args) => Vue.set(Store.detail.issue, ...args); - gl.issueBoards.boardStoreIssueDelete = (...args) => Vue.delete(Store.detail.issue, ...args); - - gl.IssueBoardsApp = new Vue({ - el: $boardApp, - components: { - 'board': gl.issueBoards.Board, - 'board-sidebar': gl.issueBoards.BoardSidebar, - 'board-add-issues-modal': gl.issueBoards.IssuesModal, - }, - data: { - state: Store.state, - loading: true, - boardsEndpoint: $boardApp.dataset.boardsEndpoint, - listsEndpoint: $boardApp.dataset.listsEndpoint, - boardId: $boardApp.dataset.boardId, - disabled: $boardApp.dataset.disabled === 'true', - issueLinkBase: $boardApp.dataset.issueLinkBase, - rootPath: $boardApp.dataset.rootPath, - bulkUpdatePath: $boardApp.dataset.bulkUpdatePath, - detailIssue: Store.detail, - defaultAvatar: $boardApp.dataset.defaultAvatar, - }, - computed: { - detailIssueVisible () { - return Object.keys(this.detailIssue.issue).length; - }, - }, - created () { - gl.boardService = new BoardService({ - boardsEndpoint: this.boardsEndpoint, - listsEndpoint: this.listsEndpoint, - bulkUpdatePath: this.bulkUpdatePath, - boardId: this.boardId, - }); - Store.rootPath = this.boardsEndpoint; - - eventHub.$on('updateTokens', this.updateTokens); - eventHub.$on('newDetailIssue', this.updateDetailIssue); - eventHub.$on('clearDetailIssue', this.clearDetailIssue); - sidebarEventHub.$on('toggleSubscription', this.toggleSubscription); - }, - beforeDestroy() { - eventHub.$off('updateTokens', this.updateTokens); - eventHub.$off('newDetailIssue', this.updateDetailIssue); - eventHub.$off('clearDetailIssue', this.clearDetailIssue); - sidebarEventHub.$off('toggleSubscription', this.toggleSubscription); - }, - mounted () { - this.filterManager = new FilteredSearchBoards(Store.filter, true); - this.filterManager.setup(); - - Store.disabled = this.disabled; - gl.boardService.all() - .then(res => res.data) - .then((data) => { - data.forEach((board) => { - const list = Store.addList(board, this.defaultAvatar); - - if (list.type === 'closed') { - list.position = Infinity; - } else if (list.type === 'backlog') { - list.position = -1; - } - }); - - this.state.lists = _.sortBy(this.state.lists, 'position'); - - Store.addBlankState(); - this.loading = false; - }) - .catch(() => { - Flash('An error occurred while fetching the board lists. Please try again.'); - }); - }, - methods: { - updateTokens() { - this.filterManager.updateTokens(); - }, - updateDetailIssue(newIssue) { - const sidebarInfoEndpoint = newIssue.sidebarInfoEndpoint; - if (sidebarInfoEndpoint && newIssue.subscribed === undefined) { - newIssue.setFetchingState('subscriptions', true); - BoardService.getIssueInfo(sidebarInfoEndpoint) - .then(res => res.data) - .then((data) => { - newIssue.setFetchingState('subscriptions', false); - newIssue.updateData({ - subscribed: data.subscribed, - }); - }) - .catch(() => { - newIssue.setFetchingState('subscriptions', false); - Flash(__('An error occurred while fetching sidebar data')); - }); - } - - Store.detail.issue = newIssue; - }, - clearDetailIssue() { - Store.detail.issue = {}; - }, - toggleSubscription(id) { - const issue = Store.detail.issue; - if (issue.id === id && issue.toggleSubscriptionEndpoint) { - issue.setFetchingState('subscriptions', true); - BoardService.toggleIssueSubscription(issue.toggleSubscriptionEndpoint) - .then(() => { - issue.setFetchingState('subscriptions', false); - issue.updateData({ - subscribed: !issue.subscribed, - }); - }) - .catch(() => { - issue.setFetchingState('subscriptions', false); - Flash(__('An error occurred when toggling the notification subscription')); - }); - } - } - }, - }); - - gl.IssueBoardsSearch = new Vue({ - el: document.getElementById('js-add-list'), - data: { - filters: Store.state.filters, - }, - mounted () { - gl.issueBoards.newListDropdownInit(); - }, - }); - - gl.IssueBoardsModalAddBtn = new Vue({ - el: document.getElementById('js-add-issues-btn'), - mixins: [gl.issueBoards.ModalMixins], - data() { - return { - modal: ModalStore.store, - store: Store.state, - }; - }, - computed: { - disabled() { - if (!this.store) { - return true; - } - return !this.store.lists.filter(list => !list.preset).length; - }, - tooltipTitle() { - if (this.disabled) { - return 'Please add a list to your board first'; - } - - return ''; - }, - }, - watch: { - disabled() { - this.updateTooltip(); - }, - }, - mounted() { - this.updateTooltip(); - }, - methods: { - updateTooltip() { - const $tooltip = $(this.$refs.addIssuesButton); - - this.$nextTick(() => { - if (this.disabled) { - $tooltip.tooltip(); - } else { - $tooltip.tooltip('destroy'); - } - }); - }, - openModal() { - if (!this.disabled) { - this.toggleModal(true); - } - }, - }, - template: ` -
- -
- `, - }); -}); diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js new file mode 100644 index 00000000000..7adbf442f00 --- /dev/null +++ b/app/assets/javascripts/boards/index.js @@ -0,0 +1,239 @@ +/* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren */ + +import _ from 'underscore'; +import Vue from 'vue'; +import Flash from '../flash'; +import { __ } from '../locale'; +import FilteredSearchBoards from './filtered_search_boards'; +import eventHub from './eventhub'; +import sidebarEventHub from '../sidebar/event_hub'; +import './models/issue'; +import './models/label'; +import './models/list'; +import './models/milestone'; +import './models/assignee'; +import './stores/boards_store'; +import './stores/modal_store'; +import BoardService from './services/board_service'; +import './mixins/modal_mixins'; +import './mixins/sortable_default_options'; +import './filters/due_date_filters'; +import './components/board'; +import './components/board_sidebar'; +import './components/new_list_dropdown'; +import './components/modal/index'; +import '../vue_shared/vue_resource_interceptor'; + +export default function initBoards() { + const $boardApp = document.getElementById('board-app'); + const Store = gl.issueBoards.BoardsStore; + const ModalStore = gl.issueBoards.ModalStore; + + window.gl = window.gl || {}; + + if (gl.IssueBoardsApp) { + gl.IssueBoardsApp.$destroy(true); + } + + Store.create(); + + // hack to allow sidebar scripts like milestone_select manipulate the BoardsStore + gl.issueBoards.boardStoreIssueSet = (...args) => Vue.set(Store.detail.issue, ...args); + gl.issueBoards.boardStoreIssueDelete = (...args) => Vue.delete(Store.detail.issue, ...args); + + gl.IssueBoardsApp = new Vue({ + el: $boardApp, + components: { + 'board': gl.issueBoards.Board, + 'board-sidebar': gl.issueBoards.BoardSidebar, + 'board-add-issues-modal': gl.issueBoards.IssuesModal, + }, + data: { + state: Store.state, + loading: true, + boardsEndpoint: $boardApp.dataset.boardsEndpoint, + listsEndpoint: $boardApp.dataset.listsEndpoint, + boardId: $boardApp.dataset.boardId, + disabled: $boardApp.dataset.disabled === 'true', + issueLinkBase: $boardApp.dataset.issueLinkBase, + rootPath: $boardApp.dataset.rootPath, + bulkUpdatePath: $boardApp.dataset.bulkUpdatePath, + detailIssue: Store.detail, + defaultAvatar: $boardApp.dataset.defaultAvatar, + }, + computed: { + detailIssueVisible () { + return Object.keys(this.detailIssue.issue).length; + }, + }, + created () { + gl.boardService = new BoardService({ + boardsEndpoint: this.boardsEndpoint, + listsEndpoint: this.listsEndpoint, + bulkUpdatePath: this.bulkUpdatePath, + boardId: this.boardId, + }); + Store.rootPath = this.boardsEndpoint; + + eventHub.$on('updateTokens', this.updateTokens); + eventHub.$on('newDetailIssue', this.updateDetailIssue); + eventHub.$on('clearDetailIssue', this.clearDetailIssue); + sidebarEventHub.$on('toggleSubscription', this.toggleSubscription); + }, + beforeDestroy() { + eventHub.$off('updateTokens', this.updateTokens); + eventHub.$off('newDetailIssue', this.updateDetailIssue); + eventHub.$off('clearDetailIssue', this.clearDetailIssue); + sidebarEventHub.$off('toggleSubscription', this.toggleSubscription); + }, + mounted () { + this.filterManager = new FilteredSearchBoards(Store.filter, true); + this.filterManager.setup(); + + Store.disabled = this.disabled; + gl.boardService.all() + .then(res => res.data) + .then((data) => { + data.forEach((board) => { + const list = Store.addList(board, this.defaultAvatar); + + if (list.type === 'closed') { + list.position = Infinity; + } else if (list.type === 'backlog') { + list.position = -1; + } + }); + + this.state.lists = _.sortBy(this.state.lists, 'position'); + + Store.addBlankState(); + this.loading = false; + }) + .catch(() => { + Flash('An error occurred while fetching the board lists. Please try again.'); + }); + }, + methods: { + updateTokens() { + this.filterManager.updateTokens(); + }, + updateDetailIssue(newIssue) { + const sidebarInfoEndpoint = newIssue.sidebarInfoEndpoint; + if (sidebarInfoEndpoint && newIssue.subscribed === undefined) { + newIssue.setFetchingState('subscriptions', true); + BoardService.getIssueInfo(sidebarInfoEndpoint) + .then(res => res.data) + .then((data) => { + newIssue.setFetchingState('subscriptions', false); + newIssue.updateData({ + subscribed: data.subscribed, + }); + }) + .catch(() => { + newIssue.setFetchingState('subscriptions', false); + Flash(__('An error occurred while fetching sidebar data')); + }); + } + + Store.detail.issue = newIssue; + }, + clearDetailIssue() { + Store.detail.issue = {}; + }, + toggleSubscription(id) { + const issue = Store.detail.issue; + if (issue.id === id && issue.toggleSubscriptionEndpoint) { + issue.setFetchingState('subscriptions', true); + BoardService.toggleIssueSubscription(issue.toggleSubscriptionEndpoint) + .then(() => { + issue.setFetchingState('subscriptions', false); + issue.updateData({ + subscribed: !issue.subscribed, + }); + }) + .catch(() => { + issue.setFetchingState('subscriptions', false); + Flash(__('An error occurred when toggling the notification subscription')); + }); + } + } + }, + }); + + gl.IssueBoardsSearch = new Vue({ + el: document.getElementById('js-add-list'), + data: { + filters: Store.state.filters, + }, + mounted () { + gl.issueBoards.newListDropdownInit(); + }, + }); + + gl.IssueBoardsModalAddBtn = new Vue({ + el: document.getElementById('js-add-issues-btn'), + mixins: [gl.issueBoards.ModalMixins], + data() { + return { + modal: ModalStore.store, + store: Store.state, + }; + }, + computed: { + disabled() { + if (!this.store) { + return true; + } + return !this.store.lists.filter(list => !list.preset).length; + }, + tooltipTitle() { + if (this.disabled) { + return 'Please add a list to your board first'; + } + + return ''; + }, + }, + watch: { + disabled() { + this.updateTooltip(); + }, + }, + mounted() { + this.updateTooltip(); + }, + methods: { + updateTooltip() { + const $tooltip = $(this.$refs.addIssuesButton); + + this.$nextTick(() => { + if (this.disabled) { + $tooltip.tooltip(); + } else { + $tooltip.tooltip('destroy'); + } + }); + }, + openModal() { + if (!this.disabled) { + this.toggleModal(true); + } + }, + }, + template: ` +
+ +
+ `, + }); +} diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index f8082c74943..872ddcbbbe6 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -180,6 +180,11 @@ var Dispatcher; .then(callDefault) .catch(fail); break; + case 'projects:boards:index': + import('./pages/projects/boards') + .then(callDefault) + .catch(fail); + break; case 'projects:issues:new': import('./pages/projects/issues/new') .then(callDefault) diff --git a/app/assets/javascripts/pages/projects/boards/index.js b/app/assets/javascripts/pages/projects/boards/index.js index 3aeeedbb45d..0bda9fcc421 100644 --- a/app/assets/javascripts/pages/projects/boards/index.js +++ b/app/assets/javascripts/pages/projects/boards/index.js @@ -1,7 +1,9 @@ import UsersSelect from '~/users_select'; import ShortcutsNavigation from '~/shortcuts_navigation'; +import initBoards from '~/boards'; -document.addEventListener('DOMContentLoaded', () => { +export default () => { new UsersSelect(); // eslint-disable-line no-new new ShortcutsNavigation(); // eslint-disable-line no-new -}); + initBoards(); +}; diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml index ee8ad8e3999..de470730f5e 100644 --- a/app/views/shared/boards/_show.html.haml +++ b/app/views/shared/boards/_show.html.haml @@ -7,7 +7,6 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' = webpack_bundle_tag 'filtered_search' - = webpack_bundle_tag 'boards' %script#js-board-template{ type: "text/x-template" }= render "shared/boards/components/board" %script#js-board-modal-filter{ type: "text/x-template" }= render "shared/issuable/search_bar", type: :boards_modal diff --git a/config/webpack.config.js b/config/webpack.config.js index a4e6c64fce5..ba7003f3673 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -51,7 +51,6 @@ var config = { account: './profile/account/index.js', balsamiq_viewer: './blob/balsamiq_viewer.js', blob: './blob_edit/blob_bundle.js', - boards: './boards/boards_bundle.js', common: './commons/index.js', common_vue: './vue_shared/vue_resource_interceptor.js', cycle_analytics: './cycle_analytics/cycle_analytics_bundle.js', diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index 9b2a5379855..96671041cd2 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -113,7 +113,7 @@ if (process.env.BABEL_ENV === 'coverage') { // exempt these files from the coverage report const troubleMakers = [ './blob_edit/blob_bundle.js', - './boards/boards_bundle.js', + './boards/index.js', './cycle_analytics/cycle_analytics_bundle.js', './cycle_analytics/components/stage_plan_component.js', './cycle_analytics/components/stage_staging_component.js', -- cgit v1.2.1 From 7282dbea094e5dffa3af29f00e183d063a3717b1 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Fri, 16 Feb 2018 01:17:23 +0100 Subject: Fixed failing test --- app/assets/javascripts/dispatcher.js | 1 + spec/javascripts/test_bundle.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 872ddcbbbe6..99ac424c433 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -180,6 +180,7 @@ var Dispatcher; .then(callDefault) .catch(fail); break; + case 'projects:boards:show': case 'projects:boards:index': import('./pages/projects/boards') .then(callDefault) diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index 96671041cd2..9d2dee990fd 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -113,7 +113,6 @@ if (process.env.BABEL_ENV === 'coverage') { // exempt these files from the coverage report const troubleMakers = [ './blob_edit/blob_bundle.js', - './boards/index.js', './cycle_analytics/cycle_analytics_bundle.js', './cycle_analytics/components/stage_plan_component.js', './cycle_analytics/components/stage_staging_component.js', -- cgit v1.2.1 From b284ce0428d855600899764a1c9bf0a7f3e88277 Mon Sep 17 00:00:00 2001 From: Dylan Griffith Date: Fri, 16 Feb 2018 11:47:42 +1100 Subject: Fix rubocop warning (#42643) --- spec/serializers/cluster_application_entity_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/serializers/cluster_application_entity_spec.rb b/spec/serializers/cluster_application_entity_spec.rb index 70b274083b2..852b6af9f7f 100644 --- a/spec/serializers/cluster_application_entity_spec.rb +++ b/spec/serializers/cluster_application_entity_spec.rb @@ -32,7 +32,7 @@ describe ClusterApplicationEntity do build( :clusters_applications_ingress, :installed, - external_ip: '111.222.111.222', + external_ip: '111.222.111.222' ) end -- cgit v1.2.1 From dfef5437a26320fc4a55fc857b6e59d5e92c13c3 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 16 Feb 2018 12:28:34 +0100 Subject: Use a helper to schedule pipeline stages migration --- .../20180212101928_schedule_build_stage_migration.rb | 11 ++++++----- lib/gitlab/background_migration/migrate_build_stage.rb | 2 -- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/db/post_migrate/20180212101928_schedule_build_stage_migration.rb b/db/post_migrate/20180212101928_schedule_build_stage_migration.rb index ad65a6fb16e..df15b2cd9d4 100644 --- a/db/post_migrate/20180212101928_schedule_build_stage_migration.rb +++ b/db/post_migrate/20180212101928_schedule_build_stage_migration.rb @@ -3,7 +3,7 @@ class ScheduleBuildStageMigration < ActiveRecord::Migration DOWNTIME = false MIGRATION = 'MigrateBuildStage'.freeze - BATCH_SIZE = 800 + BATCH_SIZE = 500 disable_ddl_transaction! @@ -15,10 +15,11 @@ class ScheduleBuildStageMigration < ActiveRecord::Migration def up disable_statement_timeout - Build.where('stage_id IS NULL').each_batch(of: BATCH_SIZE) do |builds, index| - builds.pluck('MIN(id)', 'MAX(id)').first.tap do |range| - BackgroundMigrationWorker.perform_in(index * 5.minutes, MIGRATION, range) - end + Build.where('stage_id IS NULL').tap do |relation| + queue_background_migration_jobs_by_range_at_intervals(relation, + MIGRATION, + 5.minutes, + batch_size: BATCH_SIZE) end end diff --git a/lib/gitlab/background_migration/migrate_build_stage.rb b/lib/gitlab/background_migration/migrate_build_stage.rb index adca16751af..8fe4f1a2289 100644 --- a/lib/gitlab/background_migration/migrate_build_stage.rb +++ b/lib/gitlab/background_migration/migrate_build_stage.rb @@ -35,8 +35,6 @@ module Gitlab end def perform(start_id, stop_id) - # TODO, statement timeout? - stages = Migratable::Build.where('stage_id IS NULL') .where('id BETWEEN ? AND ?', start_id, stop_id) .map { |build| build.ensure_stage! } -- cgit v1.2.1 From 4efcc0618ccf147db301f4afe50fb800fcf490c7 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 16 Feb 2018 13:13:23 +0100 Subject: Add pipeline expression statement class --- lib/gitlab/ci/pipeline/expression/statement.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 lib/gitlab/ci/pipeline/expression/statement.rb diff --git a/lib/gitlab/ci/pipeline/expression/statement.rb b/lib/gitlab/ci/pipeline/expression/statement.rb new file mode 100644 index 00000000000..adc36896fd3 --- /dev/null +++ b/lib/gitlab/ci/pipeline/expression/statement.rb @@ -0,0 +1,20 @@ +module Gitlab + module Ci + module Pipeline + module Expression + class Statement + def initialize(pipeline, statement) + @pipeline = pipeline + @statement = statement + end + + def errors + end + + def matches? + end + end + end + end + end +end -- cgit v1.2.1 From f0b27f9b406579a03e55fa16cbc7095009dc8c2b Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 19 Feb 2018 14:03:41 +0000 Subject: Adds support to render the IP address in the application ingress row Updates components to use a slot to allow to reuse the clipboard button Adds tests --- .../clusters/components/application_row.vue | 11 +- .../clusters/components/applications.vue | 123 +++++++++++++++++---- .../vue_shared/components/clipboard_button.vue | 7 +- ...rsist-external-ip-of-ingress-controller-gke.yml | 5 + .../clusters/components/applications_spec.js | 70 ++++++++++++ 5 files changed, 189 insertions(+), 27 deletions(-) create mode 100644 changelogs/unreleased/42643-persist-external-ip-of-ingress-controller-gke.yml diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue index 50e35bbbba5..428a762a9c8 100644 --- a/app/assets/javascripts/clusters/components/application_row.vue +++ b/app/assets/javascripts/clusters/components/application_row.vue @@ -36,10 +36,6 @@ type: String, required: false, }, - description: { - type: String, - required: true, - }, status: { type: String, required: false, @@ -148,11 +144,14 @@ class="table-section section-wrap" role="gridcell" > -
+