From 951616a26a61e880860ad862c1d45a8e3762b4bc Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 25 Feb 2020 18:09:02 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../stylesheets/framework/dropdowns.scss | 19 +--- .../admin/serverless/domains_controller.rb | 16 +++- app/models/pages_domain.rb | 1 + .../unreleased/add-feature-flags-version.yml | 5 + .../initializers/0_eager_load_http_cookie.rb | 10 ++ config/routes/admin.rb | 2 +- ...3804_add_version_to_feature_flags_table.rb | 23 +++++ db/schema.rb | 3 +- lib/gitlab/git/blob.rb | 4 +- .../move_file_strategy.rb | 19 ++++ lib/gitlab/profiler.rb | 32 +++---- lib/gitlab/sidekiq_config/cli_methods.rb | 2 +- .../request_store_middleware.rb | 10 +- lib/gitlab/utils/measuring.rb | 74 +++++++++++++++ lib/gitlab/with_request_store.rb | 13 +++ lib/tasks/gitlab/import_export/export.rake | 94 +++++++++++++++++++ lib/tasks/gitlab/import_export/import.rake | 89 +++++------------- locale/gitlab.pot | 21 +++++ package.json | 2 +- .../serverless/domains_controller_spec.rb | 90 ++++++++++++++++-- .../remove_cluster_confirmation_spec.js.snap | 9 +- spec/lib/gitlab/git/blob_spec.rb | 34 +++++++ .../gitlab/sidekiq_config/cli_methods_spec.rb | 32 +++---- spec/models/pages_domain_spec.rb | 1 + ...mples.rb => measurable_shared_examples.rb} | 2 +- .../gitlab/import_export/export_rake_spec.rb | 37 ++++++++ .../gitlab/import_export/import_rake_spec.rb | 2 +- yarn.lock | 15 +-- 28 files changed, 511 insertions(+), 150 deletions(-) create mode 100644 changelogs/unreleased/add-feature-flags-version.yml create mode 100644 config/initializers/0_eager_load_http_cookie.rb create mode 100644 db/migrate/20200224163804_add_version_to_feature_flags_table.rb create mode 100644 lib/gitlab/import_export/after_export_strategies/move_file_strategy.rb create mode 100644 lib/gitlab/utils/measuring.rb create mode 100644 lib/gitlab/with_request_store.rb create mode 100644 lib/tasks/gitlab/import_export/export.rake rename spec/support/shared_examples/tasks/gitlab/import_export/{import_measurement_shared_examples.rb => measurable_shared_examples.rb} (95%) create mode 100644 spec/tasks/gitlab/import_export/export_rake_spec.rb diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 53a642d68ce..efcbd63626d 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -164,24 +164,11 @@ } } -// Temporary hack until `gitlab-ui` issue is fixed. -// https://gitlab.com/gitlab-org/gitlab-ui/issues/164 .gl-dropdown .dropdown-menu-toggle { - .gl-dropdown-caret { - position: absolute; - right: $gl-padding-8; - top: $gl-padding-8; - } + padding-right: $gl-padding-8; - // Add some child to the button so that the default height kicks in - // when there's no text (since the caret is now aboslute) - &::after { - border: 0; - content: ' '; - display: inline-block; - margin: 0; - padding: 0; - position: relative; + .gl-dropdown-toggle-text { + min-height: $gl-line-height-20; } } diff --git a/app/controllers/admin/serverless/domains_controller.rb b/app/controllers/admin/serverless/domains_controller.rb index c37aec13105..9741a0716f2 100644 --- a/app/controllers/admin/serverless/domains_controller.rb +++ b/app/controllers/admin/serverless/domains_controller.rb @@ -2,7 +2,7 @@ class Admin::Serverless::DomainsController < Admin::ApplicationController before_action :check_feature_flag - before_action :domain, only: [:update, :verify] + before_action :domain, only: [:update, :verify, :destroy] def index @domain = PagesDomain.instance_serverless.first_or_initialize @@ -30,6 +30,20 @@ class Admin::Serverless::DomainsController < Admin::ApplicationController end end + def destroy + if domain.serverless_domain_clusters.count > 0 + return redirect_to admin_serverless_domains_path, + status: :conflict, + notice: _('Domain cannot be deleted while associated to one or more clusters.') + end + + domain.destroy! + + redirect_to admin_serverless_domains_path, + status: :found, + notice: _('Domain was successfully deleted.') + end + def verify result = VerifyPagesDomainService.new(domain).execute diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb index 05cf427184c..814c970f745 100644 --- a/app/models/pages_domain.rb +++ b/app/models/pages_domain.rb @@ -11,6 +11,7 @@ class PagesDomain < ApplicationRecord belongs_to :project has_many :acme_orders, class_name: "PagesDomainAcmeOrder" + has_many :serverless_domain_clusters, class_name: 'Serverless::DomainCluster', inverse_of: :pages_domain validates :domain, hostname: { allow_numeric_hostname: true } validates :domain, uniqueness: { case_sensitive: false } diff --git a/changelogs/unreleased/add-feature-flags-version.yml b/changelogs/unreleased/add-feature-flags-version.yml new file mode 100644 index 00000000000..580bd8da7e1 --- /dev/null +++ b/changelogs/unreleased/add-feature-flags-version.yml @@ -0,0 +1,5 @@ +--- +title: Add version column to operations_feature_flags table +merge_request: 25552 +author: +type: added diff --git a/config/initializers/0_eager_load_http_cookie.rb b/config/initializers/0_eager_load_http_cookie.rb new file mode 100644 index 00000000000..ed633fdb079 --- /dev/null +++ b/config/initializers/0_eager_load_http_cookie.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# https://gitlab.com/gitlab-org/gitlab/issues/207937 +# http-cookie is not thread-safe while loading it the first time, see: +# https://github.com/sparklemotion/http-cookie/issues/6#issuecomment-543570876 +# If we're using it, we should eagerly load it. +# For now, we have an implicit dependency on it via: +# * http +# * rest-client +require 'http/cookie_jar/hash_store' if Gem.loaded_specs.key?('http-cookie') diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 5210b84c8ba..54df15137d6 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -33,7 +33,7 @@ namespace :admin do resources :gitaly_servers, only: [:index] namespace :serverless do - resources :domains, only: [:index, :create, :update] do + resources :domains, only: [:index, :create, :update, :destroy] do member do post '/verify', to: 'domains#verify' end diff --git a/db/migrate/20200224163804_add_version_to_feature_flags_table.rb b/db/migrate/20200224163804_add_version_to_feature_flags_table.rb new file mode 100644 index 00000000000..e2ccefd8955 --- /dev/null +++ b/db/migrate/20200224163804_add_version_to_feature_flags_table.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class AddVersionToFeatureFlagsTable < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + FEATURE_FLAG_LEGACY_VERSION = 1 + + def up + # The operations_feature_flags table is small enough that we can disable this cop. + # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25552#note_291202882 + # rubocop: disable Migration/AddColumnWithDefault + add_column_with_default(:operations_feature_flags, :version, :smallint, default: FEATURE_FLAG_LEGACY_VERSION, allow_null: false) + # rubocop: enable Migration/AddColumnWithDefault + end + + def down + remove_column(:operations_feature_flags, :version) + end +end diff --git a/db/schema.rb b/db/schema.rb index c3b1172c6dc..8052a93e841 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2020_02_21_144534) do +ActiveRecord::Schema.define(version: 2020_02_24_163804) do # These are extensions that must be enabled in order to support this database enable_extension "pg_trgm" @@ -2916,6 +2916,7 @@ ActiveRecord::Schema.define(version: 2020_02_21_144534) do t.string "name", null: false t.text "description" t.integer "iid", null: false + t.integer "version", limit: 2, default: 1, null: false t.index ["project_id", "iid"], name: "index_operations_feature_flags_on_project_id_and_iid", unique: true t.index ["project_id", "name"], name: "index_operations_feature_flags_on_project_id_and_name", unique: true end diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb index caa1314dd7f..5579449bf57 100644 --- a/lib/gitlab/git/blob.rb +++ b/lib/gitlab/git/blob.rb @@ -165,7 +165,9 @@ module Gitlab end def truncated? - size && (size > loaded_size) + return false unless size && loaded_size + + size > loaded_size end # Valid LFS object pointer is a text file consisting of diff --git a/lib/gitlab/import_export/after_export_strategies/move_file_strategy.rb b/lib/gitlab/import_export/after_export_strategies/move_file_strategy.rb new file mode 100644 index 00000000000..2e3136936f8 --- /dev/null +++ b/lib/gitlab/import_export/after_export_strategies/move_file_strategy.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Gitlab + module ImportExport + module AfterExportStrategies + class MoveFileStrategy < BaseAfterExportStrategy + def initialize(archive_path:) + @archive_path = archive_path + end + + private + + def strategy_execute + FileUtils.mv(project.export_file.path, @archive_path) + end + end + end + end +end diff --git a/lib/gitlab/profiler.rb b/lib/gitlab/profiler.rb index f47ccb8fed9..e10cdf0d8fb 100644 --- a/lib/gitlab/profiler.rb +++ b/lib/gitlab/profiler.rb @@ -2,6 +2,8 @@ module Gitlab module Profiler + extend WithRequestStore + FILTERED_STRING = '[FILTERED]' IGNORE_BACKTRACES = %w[ @@ -58,28 +60,26 @@ module Gitlab logger = create_custom_logger(logger, private_token: private_token) - RequestStore.begin! + result = with_request_store do + # Make an initial call for an asset path in development mode to avoid + # sprockets dominating the profiler output. + ActionController::Base.helpers.asset_path('katex.css') if Rails.env.development? - # Make an initial call for an asset path in development mode to avoid - # sprockets dominating the profiler output. - ActionController::Base.helpers.asset_path('katex.css') if Rails.env.development? + # Rails loads internationalization files lazily the first time a + # translation is needed. Running this prevents this overhead from showing + # up in profiles. + ::I18n.t('.')[:test_string] - # Rails loads internationalization files lazily the first time a - # translation is needed. Running this prevents this overhead from showing - # up in profiles. - ::I18n.t('.')[:test_string] + # Remove API route mounting from the profile. + app.get('/api/v4/users') - # Remove API route mounting from the profile. - app.get('/api/v4/users') - - result = with_custom_logger(logger) do - with_user(user) do - RubyProf.profile { app.public_send(verb, url, params: post_data, headers: headers) } # rubocop:disable GitlabSecurity/PublicSend + with_custom_logger(logger) do + with_user(user) do + RubyProf.profile { app.public_send(verb, url, params: post_data, headers: headers) } # rubocop:disable GitlabSecurity/PublicSend + end end end - RequestStore.end! - log_load_times_by_model(logger) result diff --git a/lib/gitlab/sidekiq_config/cli_methods.rb b/lib/gitlab/sidekiq_config/cli_methods.rb index 8f19b557d24..c95ba6faf1e 100644 --- a/lib/gitlab/sidekiq_config/cli_methods.rb +++ b/lib/gitlab/sidekiq_config/cli_methods.rb @@ -21,7 +21,7 @@ module Gitlab QUERY_OR_OPERATOR = '|' QUERY_AND_OPERATOR = '&' QUERY_CONCATENATE_OPERATOR = ',' - QUERY_TERM_REGEX = %r{^(\w+)(!?=)([\w#{QUERY_CONCATENATE_OPERATOR}]+)}.freeze + QUERY_TERM_REGEX = %r{^(\w+)(!?=)([\w:#{QUERY_CONCATENATE_OPERATOR}]+)}.freeze QUERY_PREDICATES = { feature_category: :to_sym, diff --git a/lib/gitlab/sidekiq_middleware/request_store_middleware.rb b/lib/gitlab/sidekiq_middleware/request_store_middleware.rb index 8824f81e8e3..f6142bd6ca5 100644 --- a/lib/gitlab/sidekiq_middleware/request_store_middleware.rb +++ b/lib/gitlab/sidekiq_middleware/request_store_middleware.rb @@ -3,12 +3,12 @@ module Gitlab module SidekiqMiddleware class RequestStoreMiddleware + include Gitlab::WithRequestStore + def call(worker, job, queue) - RequestStore.begin! - yield - ensure - RequestStore.end! - RequestStore.clear! + with_request_store do + yield + end end end end diff --git a/lib/gitlab/utils/measuring.rb b/lib/gitlab/utils/measuring.rb new file mode 100644 index 00000000000..20c57e777d8 --- /dev/null +++ b/lib/gitlab/utils/measuring.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require 'prometheus/pid_provider' + +module Gitlab + module Utils + class Measuring + def initialize(logger: Logger.new($stdout)) + @logger = logger + end + + def with_measuring + logger.info "Measuring enabled..." + with_gc_counter do + with_count_queries do + with_measure_time do + yield + end + end + end + + logger.info "Memory usage: #{Gitlab::Metrics::System.memory_usage.to_f / 1024 / 1024} MiB" + logger.info "Label: #{::Prometheus::PidProvider.worker_id}" + end + + private + + attr_reader :logger + + def with_count_queries(&block) + count = 0 + + counter_f = ->(_name, _started, _finished, _unique_id, payload) { + count += 1 unless payload[:name].in? %w[CACHE SCHEMA] + } + + ActiveSupport::Notifications.subscribed(counter_f, "sql.active_record", &block) + + logger.info "Number of sql calls: #{count}" + end + + def with_gc_counter + gc_counts_before = GC.stat.select { |k, _v| k =~ /count/ } + yield + gc_counts_after = GC.stat.select { |k, _v| k =~ /count/ } + stats = gc_counts_before.merge(gc_counts_after) { |_k, vb, va| va - vb } + + logger.info "Total GC count: #{stats[:count]}" + logger.info "Minor GC count: #{stats[:minor_gc_count]}" + logger.info "Major GC count: #{stats[:major_gc_count]}" + end + + def with_measure_time + timing = Benchmark.realtime do + yield + end + + logger.info "Time to finish: #{duration_in_numbers(timing)}" + end + + def duration_in_numbers(duration_in_seconds) + seconds = duration_in_seconds % 1.minute + minutes = (duration_in_seconds / 1.minute) % (1.hour / 1.minute) + hours = duration_in_seconds / 1.hour + + if hours == 0 + "%02d:%02d" % [minutes, seconds] + else + "%02d:%02d:%02d" % [hours, minutes, seconds] + end + end + end + end +end diff --git a/lib/gitlab/with_request_store.rb b/lib/gitlab/with_request_store.rb new file mode 100644 index 00000000000..d6c05e1e256 --- /dev/null +++ b/lib/gitlab/with_request_store.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Gitlab + module WithRequestStore + def with_request_store + RequestStore.begin! + yield + ensure + RequestStore.end! + RequestStore.clear! + end + end +end diff --git a/lib/tasks/gitlab/import_export/export.rake b/lib/tasks/gitlab/import_export/export.rake new file mode 100644 index 00000000000..6cedf4e8cf1 --- /dev/null +++ b/lib/tasks/gitlab/import_export/export.rake @@ -0,0 +1,94 @@ +# frozen_string_literal: true + +require 'gitlab/with_request_store' + +# Export project to archive +# +# @example +# bundle exec rake "gitlab:import_export:export[root, root, project_to_export, /path/to/file.tar.gz, true]" +# +namespace :gitlab do + namespace :import_export do + desc 'GitLab | Import/Export | EXPERIMENTAL | Export large project archives' + task :export, [:username, :namespace_path, :project_path, :archive_path, :measurement_enabled] => :gitlab_environment do |_t, args| + # Load it here to avoid polluting Rake tasks with Sidekiq test warnings + require 'sidekiq/testing' + + warn_user_is_not_gitlab + + if ENV['IMPORT_DEBUG'].present? + ActiveRecord::Base.logger = Logger.new(STDOUT) + Gitlab::Metrics::Exporter::SidekiqExporter.instance.start + end + + GitlabProjectExport.new( + namespace_path: args.namespace_path, + project_path: args.project_path, + username: args.username, + file_path: args.archive_path, + measurement_enabled: Gitlab::Utils.to_boolean(args.measurement_enabled) + ).export + end + end +end + +class GitlabProjectExport + include Gitlab::WithRequestStore + + def initialize(opts) + @project_path = opts.fetch(:project_path) + @file_path = opts.fetch(:file_path) + @current_user = User.find_by_username(opts.fetch(:username)) + namespace = Namespace.find_by_full_path(opts.fetch(:namespace_path)) + @project = namespace.projects.find_by_path(@project_path) + @measurement_enabled = opts.fetch(:measurement_enabled) + @measurable = Gitlab::Utils::Measuring.new if @measurement_enabled + end + + def export + validate_project + validate_file_path + + with_export do + ::Projects::ImportExport::ExportService.new(project, current_user) + .execute(Gitlab::ImportExport::AfterExportStrategies::MoveFileStrategy.new(archive_path: file_path)) + end + + puts 'Done!' + rescue StandardError => e + puts "Exception: #{e.message}" + puts e.backtrace + exit 1 + end + + private + + attr_reader :measurable, :project, :current_user, :file_path, :project_path + + def validate_project + unless project + puts "Error: Project with path: #{project_path} was not found. Please provide correct project path" + exit 1 + end + end + + def validate_file_path + directory = File.dirname(file_path) + unless Dir.exist?(directory) + puts "Error: Invalid file path: #{file_path}. Please provide correct file path" + exit 1 + end + end + + def with_export + with_request_store do + ::Gitlab::GitalyClient.allow_n_plus_1_calls do + measurement_enabled? ? measurable.with_measuring { yield } : yield + end + end + end + + def measurement_enabled? + @measurement_enabled + end +end diff --git a/lib/tasks/gitlab/import_export/import.rake b/lib/tasks/gitlab/import_export/import.rake index c832cba0287..4ed724a5c82 100644 --- a/lib/tasks/gitlab/import_export/import.rake +++ b/lib/tasks/gitlab/import_export/import.rake @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'gitlab/with_request_store' + # Import large project archives # # This task: @@ -27,19 +29,22 @@ namespace :gitlab do project_path: args.project_path, username: args.username, file_path: args.archive_path, - measurement_enabled: args.measurement_enabled == 'true' + measurement_enabled: Gitlab::Utils.to_boolean(args.measurement_enabled) ).import end end end class GitlabProjectImport + include Gitlab::WithRequestStore + def initialize(opts) @project_path = opts.fetch(:project_path) @file_path = opts.fetch(:file_path) @namespace = Namespace.find_by_full_path(opts.fetch(:namespace_path)) @current_user = User.find_by_username(opts.fetch(:username)) @measurement_enabled = opts.fetch(:measurement_enabled) + @measurement = Gitlab::Utils::Measuring.new if @measurement_enabled end def import @@ -49,11 +54,11 @@ class GitlabProjectImport show_import_failures_count - if @project&.import_state&.last_error - puts "ERROR: #{@project.import_state.last_error}" + if project&.import_state&.last_error + puts "ERROR: #{project.import_state.last_error}" exit 1 - elsif @project.errors.any? - puts "ERROR: #{@project.errors.full_messages.join(', ')}" + elsif project.errors.any? + puts "ERROR: #{project.errors.full_messages.join(', ')}" exit 1 else puts 'Done!' @@ -66,60 +71,10 @@ class GitlabProjectImport private - def with_request_store - RequestStore.begin! - yield - ensure - RequestStore.end! - RequestStore.clear! - end - - def with_count_queries(&block) - count = 0 - - counter_f = ->(name, started, finished, unique_id, payload) { - unless payload[:name].in? %w[CACHE SCHEMA] - count += 1 - end - } - - ActiveSupport::Notifications.subscribed(counter_f, "sql.active_record", &block) - - puts "Number of sql calls: #{count}" - end - - def with_gc_counter - gc_counts_before = GC.stat.select { |k, v| k =~ /count/ } - yield - gc_counts_after = GC.stat.select { |k, v| k =~ /count/ } - stats = gc_counts_before.merge(gc_counts_after) { |k, vb, va| va - vb } - puts "Total GC count: #{stats[:count]}" - puts "Minor GC count: #{stats[:minor_gc_count]}" - puts "Major GC count: #{stats[:major_gc_count]}" - end - - def with_measure_time - timing = Benchmark.realtime do - yield - end - - time = Time.at(timing).utc.strftime("%H:%M:%S") - puts "Time to finish: #{time}" - end - - def with_measuring - puts "Measuring enabled..." - with_gc_counter do - with_count_queries do - with_measure_time do - yield - end - end - end - end + attr_reader :measurement, :project, :namespace, :current_user, :file_path, :project_path def measurement_enabled? - @measurement_enabled != false + @measurement_enabled end # We want to ensure that all Sidekiq jobs are executed @@ -135,7 +90,7 @@ class GitlabProjectImport # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24475#note_283090635 # For development setups, this code-path will be excluded from n+1 detection. ::Gitlab::GitalyClient.allow_n_plus_1_calls do - measurement_enabled? ? with_measuring { yield } : yield + measurement_enabled? ? measurement.with_measuring { yield } : yield end end @@ -158,11 +113,11 @@ class GitlabProjectImport # 2. Download of archive before unpacking disable_upload_object_storage do service = Projects::GitlabProjectsImportService.new( - @current_user, + current_user, { - namespace_id: @namespace.id, - path: @project_path, - file: File.open(@file_path) + namespace_id: namespace.id, + path: project_path, + file: File.open(file_path) } ) @@ -193,18 +148,18 @@ class GitlabProjectImport end def full_path - "#{@namespace.full_path}/#{@project_path}" + "#{namespace.full_path}/#{project_path}" end def show_import_start_message - puts "Importing GitLab export: #{@file_path} into GitLab" \ + puts "Importing GitLab export: #{file_path} into GitLab" \ " #{full_path}" \ - " as #{@current_user.name}" + " as #{current_user.name}" end def show_import_failures_count - return unless @project.import_failures.exists? + return unless project.import_failures.exists? - puts "Total number of not imported relations: #{@project.import_failures.count}" + puts "Total number of not imported relations: #{project.import_failures.count}" end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 04fd7d3cc1a..ef30d868a01 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3480,6 +3480,9 @@ msgstr "" msgid "Checkout|Country" msgstr "" +msgid "Checkout|Create a new group" +msgstr "" + msgid "Checkout|Credit card form failed to load. Please try again." msgstr "" @@ -3507,6 +3510,9 @@ msgstr "" msgid "Checkout|Failed to register credit card. Please try again." msgstr "" +msgid "Checkout|GitLab group" +msgstr "" + msgid "Checkout|GitLab plan" msgstr "" @@ -3531,6 +3537,9 @@ msgstr "" msgid "Checkout|Please select a state" msgstr "" +msgid "Checkout|Select" +msgstr "" + msgid "Checkout|State" msgstr "" @@ -3555,9 +3564,15 @@ msgstr "" msgid "Checkout|Users" msgstr "" +msgid "Checkout|You'll create your new group after checkout" +msgstr "" + msgid "Checkout|Your organization" msgstr "" +msgid "Checkout|Your subscription will be applied to this group" +msgstr "" + msgid "Checkout|Zip code" msgstr "" @@ -6865,12 +6880,18 @@ msgstr "" msgid "Domain" msgstr "" +msgid "Domain cannot be deleted while associated to one or more clusters." +msgstr "" + msgid "Domain verification is an essential security measure for public GitLab sites. Users are required to demonstrate they control a domain before it is enabled" msgstr "" msgid "Domain was successfully created." msgstr "" +msgid "Domain was successfully deleted." +msgstr "" + msgid "Domain was successfully updated." msgstr "" diff --git a/package.json b/package.json index 4398ecc0bc4..ccd9c41cf8d 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "@babel/preset-env": "^7.6.2", "@gitlab/at.js": "^1.5.5", "@gitlab/svgs": "^1.99.0", - "@gitlab/ui": "^9.11.1", + "@gitlab/ui": "^9.11.2", "@gitlab/visual-review-tools": "1.5.1", "@sentry/browser": "^5.10.2", "@sourcegraph/code-host-integration": "0.0.30", diff --git a/spec/controllers/admin/serverless/domains_controller_spec.rb b/spec/controllers/admin/serverless/domains_controller_spec.rb index aed83e190be..43c3f0117bc 100644 --- a/spec/controllers/admin/serverless/domains_controller_spec.rb +++ b/spec/controllers/admin/serverless/domains_controller_spec.rb @@ -15,7 +15,7 @@ describe Admin::Serverless::DomainsController do it 'responds with 404' do get :index - expect(response.status).to eq(404) + expect(response).to have_gitlab_http_status(:not_found) end end @@ -33,7 +33,7 @@ describe Admin::Serverless::DomainsController do it 'responds with 404' do get :index - expect(response.status).to eq(404) + expect(response).to have_gitlab_http_status(:not_found) end end @@ -81,7 +81,7 @@ describe Admin::Serverless::DomainsController do it 'responds with 404' do post :create, params: { pages_domain: create_params } - expect(response.status).to eq(404) + expect(response).to have_gitlab_http_status(:not_found) end end @@ -98,7 +98,7 @@ describe Admin::Serverless::DomainsController do it 'responds with 404' do post :create, params: { pages_domain: create_params } - expect(response.status).to eq(404) + expect(response).to have_gitlab_http_status(:not_found) end end @@ -169,7 +169,7 @@ describe Admin::Serverless::DomainsController do it 'responds with 404' do put :update, params: { id: domain.id, pages_domain: update_params } - expect(response.status).to eq(404) + expect(response).to have_gitlab_http_status(:not_found) end end @@ -186,7 +186,7 @@ describe Admin::Serverless::DomainsController do it 'responds with 404' do put :update, params: { id: domain.id, pages_domain: update_params } - expect(response.status).to eq(404) + expect(response).to have_gitlab_http_status(:not_found) end end @@ -221,7 +221,7 @@ describe Admin::Serverless::DomainsController do it 'returns 404' do put :update, params: { id: 0, pages_domain: update_params } - expect(response.status).to eq(404) + expect(response).to have_gitlab_http_status(:not_found) end end @@ -247,7 +247,7 @@ describe Admin::Serverless::DomainsController do it 'responds with 404' do post :verify, params: { id: domain.id } - expect(response.status).to eq(404) + expect(response).to have_gitlab_http_status(:not_found) end end @@ -272,7 +272,7 @@ describe Admin::Serverless::DomainsController do it 'responds with 404' do post :verify, params: { id: domain.id } - expect(response.status).to eq(404) + expect(response).to have_gitlab_http_status(:not_found) end end @@ -295,4 +295,76 @@ describe Admin::Serverless::DomainsController do end end end + + describe '#destroy' do + let!(:domain) { create(:pages_domain, :instance_serverless) } + + context 'non-admin user' do + before do + sign_in(user) + end + + it 'responds with 404' do + delete :destroy, params: { id: domain.id } + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'admin user' do + before do + sign_in(admin) + end + + context 'with serverless_domain feature disabled' do + before do + stub_feature_flags(serverless_domain: false) + end + + it 'responds with 404' do + delete :destroy, params: { id: domain.id } + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when domain exists' do + context 'and is not associated to any clusters' do + it 'deletes the domain' do + expect { delete :destroy, params: { id: domain.id } } + .to change { PagesDomain.count }.from(1).to(0) + + expect(response).to have_gitlab_http_status(:found) + expect(flash[:notice]).to include('Domain was successfully deleted.') + end + end + + context 'and is associated to any clusters' do + before do + create(:serverless_domain_cluster, pages_domain: domain) + end + + it 'does not delete the domain' do + expect { delete :destroy, params: { id: domain.id } } + .not_to change { PagesDomain.count } + + expect(response).to have_gitlab_http_status(:conflict) + expect(flash[:notice]).to include('Domain cannot be deleted while associated to one or more clusters.') + end + end + end + + context 'when domain does not exist' do + before do + domain.destroy! + end + + it 'responds with 404' do + delete :destroy, params: { id: domain.id } + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end + end end diff --git a/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap b/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap index a35348d86ea..d4269bf14ba 100644 --- a/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap +++ b/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap @@ -9,9 +9,12 @@ exports[`Remove cluster confirmation modal renders splitbutton with modal includ class="btn btn-danger" type="button" > - - Remove integration and resources - + + Remove integration and resources + +