diff options
author | Robert Speicher <robert@gitlab.com> | 2016-07-20 17:03:04 +0000 |
---|---|---|
committer | Robert Speicher <robert@gitlab.com> | 2016-07-20 17:03:04 +0000 |
commit | cf6de7dae95570c95e8e0590770d62d32a371bf6 (patch) | |
tree | 6e9ab9c2b419baca51eb1dfdb4918be05aa9d734 /lib | |
parent | 6f8156cea587f08e11ad578bec012af103cc82e4 (diff) | |
parent | a8bfe20d0dbc79616ad69b0e9c1c985ba1887407 (diff) | |
download | gitlab-ce-cf6de7dae95570c95e8e0590770d62d32a371bf6.tar.gz |
Merge branch 'migration-downtime-tags' into 'master'
Added checks for migration downtime
This adds a set of checks that check/list which migrations require downtime (or not). It also comes with a CI task that fails should a migration not be tagged properly.
Fixes #14545
See merge request !4911
Diffstat (limited to 'lib')
-rw-r--r-- | lib/gitlab/downtime_check.rb | 71 | ||||
-rw-r--r-- | lib/gitlab/downtime_check/message.rb | 28 | ||||
-rw-r--r-- | lib/tasks/downtime_check.rake | 26 | ||||
-rw-r--r-- | lib/tasks/gitlab/db.rake | 15 |
4 files changed, 140 insertions, 0 deletions
diff --git a/lib/gitlab/downtime_check.rb b/lib/gitlab/downtime_check.rb new file mode 100644 index 00000000000..ab9537ed7d7 --- /dev/null +++ b/lib/gitlab/downtime_check.rb @@ -0,0 +1,71 @@ +module Gitlab + # Checks if a set of migrations requires downtime or not. + class DowntimeCheck + # The constant containing the boolean that indicates if downtime is needed + # or not. + DOWNTIME_CONST = :DOWNTIME + + # The constant that specifies the reason for the migration requiring + # downtime. + DOWNTIME_REASON_CONST = :DOWNTIME_REASON + + # Checks the given migration paths and returns an Array of + # `Gitlab::DowntimeCheck::Message` instances. + # + # migrations - The migration file paths to check. + def check(migrations) + migrations.map do |path| + require(path) + + migration_class = class_for_migration_file(path) + + unless migration_class.const_defined?(DOWNTIME_CONST) + raise "The migration in #{path} does not specify if it requires " \ + "downtime or not" + end + + if online?(migration_class) + Message.new(path) + else + reason = downtime_reason(migration_class) + + unless reason + raise "The migration in #{path} requires downtime but no reason " \ + "was given" + end + + Message.new(path, true, reason) + end + end + end + + # Checks the given migrations and prints the results to STDOUT/STDERR. + # + # migrations - The migration file paths to check. + def check_and_print(migrations) + check(migrations).each do |message| + puts message.to_s # rubocop: disable Rails/Output + end + end + + # Returns the class for the given migration file path. + def class_for_migration_file(path) + File.basename(path, File.extname(path)).split('_', 2).last.camelize. + constantize + end + + # Returns true if the given migration can be performed without downtime. + def online?(migration) + migration.const_get(DOWNTIME_CONST) == false + end + + # Returns the downtime reason, or nil if none was defined. + def downtime_reason(migration) + if migration.const_defined?(DOWNTIME_REASON_CONST) + migration.const_get(DOWNTIME_REASON_CONST) + else + nil + end + end + end +end diff --git a/lib/gitlab/downtime_check/message.rb b/lib/gitlab/downtime_check/message.rb new file mode 100644 index 00000000000..4446e921e0d --- /dev/null +++ b/lib/gitlab/downtime_check/message.rb @@ -0,0 +1,28 @@ +module Gitlab + class DowntimeCheck + class Message + attr_reader :path, :offline, :reason + + OFFLINE = "\e[32moffline\e[0m" + ONLINE = "\e[31monline\e[0m" + + # path - The file path of the migration. + # offline - When set to `true` the migration will require downtime. + # reason - The reason as to why the migration requires downtime. + def initialize(path, offline = false, reason = nil) + @path = path + @offline = offline + @reason = reason + end + + def to_s + label = offline ? OFFLINE : ONLINE + + message = "[#{label}]: #{path}" + message += ": #{reason}" if reason + + message + end + end + end +end diff --git a/lib/tasks/downtime_check.rake b/lib/tasks/downtime_check.rake new file mode 100644 index 00000000000..30a2e9be5ce --- /dev/null +++ b/lib/tasks/downtime_check.rake @@ -0,0 +1,26 @@ +desc 'Checks if migrations in a branch require downtime' +task downtime_check: :environment do + # First we'll want to make sure we're comparing with the right upstream + # repository/branch. + current_branch = `git rev-parse --abbrev-ref HEAD`.strip + + # Either the developer ran this task directly on the master branch, or they're + # making changes directly on the master branch. + if current_branch == 'master' + if defined?(Gitlab::License) + repo = 'gitlab-ee' + else + repo = 'gitlab-ce' + end + + `git fetch https://gitlab.com/gitlab-org/#{repo}.git --depth 1` + + compare_with = 'FETCH_HEAD' + # The developer is working on a different branch, in this case we can just + # compare with the master branch. + else + compare_with = 'master' + end + + Rake::Task['gitlab:db:downtime_check'].invoke(compare_with) +end diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake index 7230b9485be..0ec19e1a625 100644 --- a/lib/tasks/gitlab/db.rake +++ b/lib/tasks/gitlab/db.rake @@ -46,5 +46,20 @@ namespace :gitlab do Rake::Task['db:seed_fu'].invoke end end + + desc 'Checks if migrations require downtime or not' + task :downtime_check, [:ref] => :environment do |_, args| + abort 'You must specify a Git reference to compare with' unless args[:ref] + + require 'shellwords' + + ref = Shellwords.escape(args[:ref]) + + migrations = `git diff #{ref}.. --name-only -- db/migrate`.lines. + map { |file| Rails.root.join(file.strip).to_s }. + select { |file| File.file?(file) } + + Gitlab::DowntimeCheck.new.check_and_print(migrations) + end end end |