summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-07-29 21:08:53 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-07-29 21:08:53 +0000
commita5c89610f3a29118fd7cb1de80ad1f8f0be5363e (patch)
tree20befdc0eb6370b726faa83f91e8d52d14dfe099
parenta16398e10f87edd229a12be2f1fc87cd4a76f902 (diff)
downloadgitlab-ce-a5c89610f3a29118fd7cb1de80ad1f8f0be5363e.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.rubocop_todo/layout/space_inside_block_braces.yml61
-rw-r--r--app/assets/stylesheets/framework/secondary_navigation_elements.scss19
-rw-r--r--app/assets/stylesheets/framework/variables.scss1
-rw-r--r--app/assets/stylesheets/pages/note_form.scss9
-rw-r--r--app/models/ci/build.rb7
-rw-r--r--app/models/deployment.rb2
-rw-r--r--app/models/environment.rb1
-rw-r--r--app/services/deployments/update_environment_service.rb2
-rw-r--r--app/services/google_cloud/base_service.rb2
-rw-r--r--app/services/google_cloud/create_cloudsql_instance_service.rb74
-rw-r--r--app/services/google_cloud/enable_cloudsql_service.rb23
-rw-r--r--app/services/google_cloud/get_cloudsql_instances_service.rb18
-rw-r--r--app/services/google_cloud/setup_cloudsql_instance_service.rb80
-rw-r--r--app/views/projects/buttons/_remove_tag.html.haml2
-rw-r--r--app/views/projects/tags/_edit_release_button.html.haml4
-rw-r--r--app/views/projects/tags/_tag.html.haml2
-rw-r--r--app/views/projects/tags/show.html.haml10
-rw-r--r--doc/.vale/gitlab/MultiLineLinks.yml14
-rw-r--r--doc/architecture/blueprints/ci_scale/index.md3
-rw-r--r--doc/architecture/blueprints/graphql_api/index.md3
-rw-r--r--doc/development/database/avoiding_downtime_in_migrations.md18
-rw-r--r--doc/development/database/loose_foreign_keys.md3
-rw-r--r--doc/development/documentation/styleguide/index.md4
-rw-r--r--lib/gitlab/ci/jwt.rb2
-rw-r--r--lib/gitlab/ci/pipeline/seed/environment.rb2
-rw-r--r--lib/google_api/cloud_platform/client.rb40
-rw-r--r--qa/qa/service/praefect_manager.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/license_detecton_spec.rb8
-rw-r--r--qa/qa/specs/features/sanity/feature_flags_spec.rb4
-rw-r--r--spec/features/admin/admin_mode/login_spec.rb2
-rw-r--r--spec/features/admin/users/users_spec.rb4
-rw-r--r--spec/features/boards/board_filters_spec.rb4
-rw-r--r--spec/features/boards/reload_boards_on_browser_back_spec.rb6
-rw-r--r--spec/features/dashboard/archived_projects_spec.rb2
-rw-r--r--spec/features/error_tracking/user_filters_errors_by_status_spec.rb4
-rw-r--r--spec/features/groups/issues_spec.rb6
-rw-r--r--spec/features/groups_spec.rb2
-rw-r--r--spec/features/issuables/user_sees_sidebar_spec.rb2
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb2
-rw-r--r--spec/features/issues/todo_spec.rb2
-rw-r--r--spec/features/issues/user_bulk_edits_issues_spec.rb2
-rw-r--r--spec/features/issues/user_interacts_with_awards_spec.rb2
-rw-r--r--spec/features/issues/user_uses_quick_actions_spec.rb2
-rw-r--r--spec/features/merge_request/user_approves_spec.rb2
-rw-r--r--spec/features/merge_request/user_customizes_merge_commit_message_spec.rb4
-rw-r--r--spec/features/merge_request/user_edits_assignees_sidebar_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_closing_issues_message_spec.rb4
-rw-r--r--spec/features/merge_request/user_sees_deployment_widget_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_diff_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_versions_spec.rb2
-rw-r--r--spec/features/merge_request/user_uses_quick_actions_spec.rb2
-rw-r--r--spec/features/profiles/user_edit_profile_spec.rb4
-rw-r--r--spec/features/projects/cluster_agents_spec.rb2
-rw-r--r--spec/features/projects/commits/user_browses_commits_spec.rb2
-rw-r--r--spec/features/projects/environments/environment_spec.rb8
-rw-r--r--spec/features/projects/files/user_browses_files_spec.rb4
-rw-r--r--spec/features/projects/members/manage_groups_spec.rb2
-rw-r--r--spec/features/projects/pipelines/legacy_pipelines_spec.rb2
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb2
-rw-r--r--spec/features/projects/settings/service_desk_setting_spec.rb2
-rw-r--r--spec/features/projects/tree/tree_show_spec.rb2
-rw-r--r--spec/features/users/login_spec.rb2
-rw-r--r--spec/lib/google_api/cloud_platform/client_spec.rb108
-rw-r--r--spec/models/ci/build_spec.rb40
-rw-r--r--spec/services/google_cloud/create_cloudsql_instance_service_spec.rb90
-rw-r--r--spec/services/google_cloud/enable_cloudsql_service_spec.rb39
-rw-r--r--spec/services/google_cloud/get_cloudsql_instances_service_spec.rb62
-rw-r--r--spec/services/google_cloud/setup_cloudsql_instance_service_spec.rb68
71 files changed, 725 insertions, 202 deletions
diff --git a/.rubocop_todo/layout/space_inside_block_braces.yml b/.rubocop_todo/layout/space_inside_block_braces.yml
index c084147eb58..f6128bda7f0 100644
--- a/.rubocop_todo/layout/space_inside_block_braces.yml
+++ b/.rubocop_todo/layout/space_inside_block_braces.yml
@@ -60,26 +60,6 @@ Layout/SpaceInsideBlockBraces:
- 'ee/spec/elastic_integration/global_search_spec.rb'
- 'ee/spec/factories/dast/profiles_pipelines.rb'
- 'ee/spec/factories/licenses.rb'
- - 'ee/spec/features/billings/billing_plans_spec.rb'
- - 'ee/spec/features/boards/board_filters_spec.rb'
- - 'ee/spec/features/boards/scoped_issue_board_spec.rb'
- - 'ee/spec/features/boards/swimlanes/epics_swimlanes_filtering_spec.rb'
- - 'ee/spec/features/epics/todo_spec.rb'
- - 'ee/spec/features/google_analytics_datalayer_spec.rb'
- - 'ee/spec/features/groups/issues_spec.rb'
- - 'ee/spec/features/issues/filtered_search/filter_issues_by_iteration_spec.rb'
- - 'ee/spec/features/issues/form_spec.rb'
- - 'ee/spec/features/issues/issue_sidebar_spec.rb'
- - 'ee/spec/features/issues/user_edits_issue_spec.rb'
- - 'ee/spec/features/merge_request/user_edits_multiple_reviewers_mr_spec.rb'
- - 'ee/spec/features/merge_request/user_sees_closing_issues_message_spec.rb'
- - 'ee/spec/features/merge_requests/user_resets_approvers_spec.rb'
- - 'ee/spec/features/merge_requests/user_views_all_merge_requests_spec.rb'
- - 'ee/spec/features/projects/integrations/user_activates_github_spec.rb'
- - 'ee/spec/features/projects/push_rules_spec.rb'
- - 'ee/spec/features/projects/security/dast_scanner_profiles_spec.rb'
- - 'ee/spec/features/projects/security/dast_site_profiles_spec.rb'
- - 'ee/spec/features/projects/settings/ee/service_desk_setting_spec.rb'
- 'ee/spec/finders/billed_users_finder_spec.rb'
- 'ee/spec/finders/clusters/environments_finder_spec.rb'
- 'ee/spec/finders/dast/profiles_finder_spec.rb'
@@ -274,15 +254,6 @@ Layout/SpaceInsideBlockBraces:
- 'lib/tasks/gitlab/praefect.rake'
- 'lib/tasks/gitlab/shell.rake'
- 'lib/tasks/gitlab/tw/codeowners.rake'
- - 'qa/qa/service/praefect_manager.rb'
- - 'qa/qa/specs/features/browser_ui/1_manage/project/project_access_token_spec.rb'
- - 'qa/qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/license/cloud_activation_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/license/license_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/12_geo/geo_replication_npm_registry_spec.rb'
- - 'qa/spec/scenario/test/integration/mattermost_spec.rb'
- - 'qa/spec/support/page_error_checker_spec.rb'
- 'rubocop/cop/migration/add_limit_to_text_columns.rb'
- 'spec/config/settings_spec.rb'
- 'spec/controllers/admin/application_settings_controller_spec.rb'
@@ -315,38 +286,6 @@ Layout/SpaceInsideBlockBraces:
- 'spec/factories/packages/packages.rb'
- 'spec/factories/prometheus_alert.rb'
- 'spec/factories/prometheus_metrics.rb'
- - 'spec/features/admin/admin_mode/login_spec.rb'
- - 'spec/features/admin/users/users_spec.rb'
- - 'spec/features/boards/board_filters_spec.rb'
- - 'spec/features/boards/reload_boards_on_browser_back_spec.rb'
- - 'spec/features/dashboard/archived_projects_spec.rb'
- - 'spec/features/error_tracking/user_filters_errors_by_status_spec.rb'
- - 'spec/features/groups/issues_spec.rb'
- - 'spec/features/groups_spec.rb'
- - 'spec/features/issuables/user_sees_sidebar_spec.rb'
- - 'spec/features/issues/gfm_autocomplete_spec.rb'
- - 'spec/features/issues/todo_spec.rb'
- - 'spec/features/issues/user_bulk_edits_issues_spec.rb'
- - 'spec/features/issues/user_interacts_with_awards_spec.rb'
- - 'spec/features/issues/user_uses_quick_actions_spec.rb'
- - 'spec/features/merge_request/user_approves_spec.rb'
- - 'spec/features/merge_request/user_customizes_merge_commit_message_spec.rb'
- - 'spec/features/merge_request/user_edits_assignees_sidebar_spec.rb'
- - 'spec/features/merge_request/user_sees_closing_issues_message_spec.rb'
- - 'spec/features/merge_request/user_sees_deployment_widget_spec.rb'
- - 'spec/features/merge_request/user_sees_diff_spec.rb'
- - 'spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb'
- - 'spec/features/merge_request/user_sees_versions_spec.rb'
- - 'spec/features/merge_request/user_uses_quick_actions_spec.rb'
- - 'spec/features/profiles/user_edit_profile_spec.rb'
- - 'spec/features/projects/cluster_agents_spec.rb'
- - 'spec/features/projects/commits/user_browses_commits_spec.rb'
- - 'spec/features/projects/environments/environment_spec.rb'
- - 'spec/features/projects/files/user_browses_files_spec.rb'
- - 'spec/features/projects/pipelines/pipelines_spec.rb'
- - 'spec/features/projects/settings/service_desk_setting_spec.rb'
- - 'spec/features/projects/tree/tree_show_spec.rb'
- - 'spec/features/users/login_spec.rb'
- 'spec/finders/ci/jobs_finder_spec.rb'
- 'spec/finders/ci/runners_finder_spec.rb'
- 'spec/finders/concerns/packages/finder_helper_spec.rb'
diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
index 74aed1bd984..92ca8654287 100644
--- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss
+++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
@@ -135,27 +135,8 @@
}
@include media-breakpoint-down(md) {
- $controls-margin: $btn-margin-5 - 2px;
flex: 0 0 100%;
margin-top: $gl-padding-8;
-
- .controls-item,
- .controls-item-full,
- .controls-item:last-child {
- flex: 1 1 35%;
- display: block;
- width: 100%;
- margin: $controls-margin;
-
- .btn,
- .dropdown {
- margin: 0;
- }
- }
-
- .controls-item-full {
- flex: 1 1 100%;
- }
}
@include media-breakpoint-down(sm) {
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 03dfd0397b0..68fdfae6713 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -454,7 +454,6 @@ $default-icon-size: 16px;
$layout-link-gray: #7e7c7c;
$btn-side-margin: $grid-size;
$btn-sm-side-margin: 7px;
-$btn-margin-5: 5px;
$count-arrow-border: #dce0e5;
$general-hover-transition-duration: 100ms;
$general-hover-transition-curve: linear;
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 645f145328b..9692becef4f 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -121,15 +121,6 @@
border-radius: $label-border-radius;
padding-top: $gl-vert-padding;
padding-bottom: $gl-vert-padding;
-
- .icon svg {
- position: relative;
- top: 2px;
- margin-right: $btn-margin-5;
- width: $gl-font-size;
- height: $gl-font-size;
- fill: $orange-600;
- }
}
}
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index fb38ba4f6c5..fd830536535 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -523,10 +523,14 @@ module Ci
self.options.fetch(:environment, {}).fetch(:action, 'start') if self.options
end
- def environment_deployment_tier
+ def environment_tier_from_options
self.options.dig(:environment, :deployment_tier) if self.options
end
+ def environment_tier
+ environment_tier_from_options || persisted_environment.try(:tier)
+ end
+
def triggered_by?(current_user)
user == current_user
end
@@ -581,6 +585,7 @@ module Ci
variables.concat(persisted_environment.predefined_variables)
variables.append(key: 'CI_ENVIRONMENT_ACTION', value: environment_action)
+ variables.append(key: 'CI_ENVIRONMENT_TIER', value: environment_tier)
# Here we're passing unexpanded environment_url for runner to expand,
# and we need to make sure that CI_ENVIRONMENT_NAME and
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index c25ba6f9268..68aadf577a0 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -438,7 +438,7 @@ class Deployment < ApplicationRecord
def tier_in_yaml
return unless deployable
- deployable.environment_deployment_tier
+ deployable.environment_tier_from_options
end
private
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 855fb281d81..02bdbd86cd1 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -253,7 +253,6 @@ class Environment < ApplicationRecord
Gitlab::Ci::Variables::Collection.new
.append(key: 'CI_ENVIRONMENT_NAME', value: name)
.append(key: 'CI_ENVIRONMENT_SLUG', value: slug)
- .append(key: 'CI_ENVIRONMENT_TIER', value: tier)
end
def recently_updated_on_branch?(ref)
diff --git a/app/services/deployments/update_environment_service.rb b/app/services/deployments/update_environment_service.rb
index 9f2c9596fb4..3cacedc7d6e 100644
--- a/app/services/deployments/update_environment_service.rb
+++ b/app/services/deployments/update_environment_service.rb
@@ -84,7 +84,7 @@ module Deployments
def renew_deployment_tier
return unless deployable
- if (tier = deployable.environment_deployment_tier)
+ if (tier = deployable.environment_tier_from_options)
environment.tier = tier
end
end
diff --git a/app/services/google_cloud/base_service.rb b/app/services/google_cloud/base_service.rb
index 016ab15408f..01aee2231c9 100644
--- a/app/services/google_cloud/base_service.rb
+++ b/app/services/google_cloud/base_service.rb
@@ -22,7 +22,7 @@ module GoogleCloud
def unique_gcp_project_ids
filter_params = { key: 'GCP_PROJECT_ID' }
- ::Ci::VariablesFinder.new(project, filter_params).execute.map(&:value).uniq
+ @unique_gcp_project_ids ||= ::Ci::VariablesFinder.new(project, filter_params).execute.map(&:value).uniq
end
def group_vars_by_environment(keys)
diff --git a/app/services/google_cloud/create_cloudsql_instance_service.rb b/app/services/google_cloud/create_cloudsql_instance_service.rb
new file mode 100644
index 00000000000..f7fca277c52
--- /dev/null
+++ b/app/services/google_cloud/create_cloudsql_instance_service.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+module GoogleCloud
+ DEFAULT_REGION = 'us-east1'
+
+ class CreateCloudsqlInstanceService < ::GoogleCloud::BaseService
+ WORKER_INTERVAL = 30.seconds
+
+ def execute
+ create_cloud_instance
+ trigger_instance_setup_worker
+ success
+ rescue Google::Apis::Error => err
+ error(err.to_json)
+ end
+
+ private
+
+ def create_cloud_instance
+ google_api_client.create_cloudsql_instance(gcp_project_id,
+ instance_name,
+ root_password,
+ database_version,
+ region,
+ tier)
+ end
+
+ def trigger_instance_setup_worker
+ GoogleCloud::CreateCloudsqlInstanceWorker.perform_in(WORKER_INTERVAL,
+ current_user.id,
+ project.id,
+ {
+ 'google_oauth2_token': google_oauth2_token,
+ 'gcp_project_id': gcp_project_id,
+ 'instance_name': instance_name,
+ 'database_version': database_version,
+ 'environment_name': environment_name,
+ 'is_protected': protected?
+ })
+ end
+
+ def protected?
+ project.protected_for?(environment_name)
+ end
+
+ def instance_name
+ # Generates an `instance_name` for the to-be-created Cloud SQL instance
+ # Example: `gitlab-34647-postgres-14-staging`
+ environment_alias = environment_name == '*' ? 'ALL' : environment_name
+ name = "gitlab-#{project.id}-#{database_version}-#{environment_alias}"
+ name.tr("_", "-").downcase
+ end
+
+ def root_password
+ SecureRandom.hex(16)
+ end
+
+ def database_version
+ params[:database_version]
+ end
+
+ def region
+ region = ::Ci::VariablesFinder
+ .new(project, { key: Projects::GoogleCloud::GcpRegionsController::GCP_REGION_CI_VAR_KEY,
+ environment_scope: environment_name })
+ .execute.first
+ region&.value || DEFAULT_REGION
+ end
+
+ def tier
+ params[:tier]
+ end
+ end
+end
diff --git a/app/services/google_cloud/enable_cloudsql_service.rb b/app/services/google_cloud/enable_cloudsql_service.rb
new file mode 100644
index 00000000000..a466b2f3696
--- /dev/null
+++ b/app/services/google_cloud/enable_cloudsql_service.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module GoogleCloud
+ class EnableCloudsqlService < ::GoogleCloud::BaseService
+ def execute
+ return no_projects_error if unique_gcp_project_ids.empty?
+
+ unique_gcp_project_ids.each do |gcp_project_id|
+ google_api_client.enable_cloud_sql_admin(gcp_project_id)
+ google_api_client.enable_compute(gcp_project_id)
+ google_api_client.enable_service_networking(gcp_project_id)
+ end
+
+ success({ gcp_project_ids: unique_gcp_project_ids })
+ end
+
+ private
+
+ def no_projects_error
+ error("No GCP projects found. Configure a service account or GCP_PROJECT_ID CI variable.")
+ end
+ end
+end
diff --git a/app/services/google_cloud/get_cloudsql_instances_service.rb b/app/services/google_cloud/get_cloudsql_instances_service.rb
new file mode 100644
index 00000000000..701e83d556d
--- /dev/null
+++ b/app/services/google_cloud/get_cloudsql_instances_service.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module GoogleCloud
+ class GetCloudsqlInstancesService < ::GoogleCloud::BaseService
+ CLOUDSQL_KEYS = %w[GCP_PROJECT_ID GCP_CLOUDSQL_INSTANCE_NAME GCP_CLOUDSQL_VERSION].freeze
+
+ def execute
+ group_vars_by_environment(CLOUDSQL_KEYS).map do |environment_scope, value|
+ {
+ ref: environment_scope,
+ gcp_project: value['GCP_PROJECT_ID'],
+ instance_name: value['GCP_CLOUDSQL_INSTANCE_NAME'],
+ version: value['GCP_CLOUDSQL_VERSION']
+ }
+ end
+ end
+ end
+end
diff --git a/app/services/google_cloud/setup_cloudsql_instance_service.rb b/app/services/google_cloud/setup_cloudsql_instance_service.rb
index 73650ee752f..10237f83b37 100644
--- a/app/services/google_cloud/setup_cloudsql_instance_service.rb
+++ b/app/services/google_cloud/setup_cloudsql_instance_service.rb
@@ -16,29 +16,29 @@ module GoogleCloud
return error("CloudSQL instance not RUNNABLE: #{get_instance_response.to_json}")
end
- database_response = google_api_client.create_cloudsql_database(gcp_project_id, instance_name, database_name)
+ save_instance_ci_vars(get_instance_response)
- if database_response.status != OPERATION_STATE_DONE
- return error("Database creation failed: #{database_response.to_json}")
- end
+ list_database_response = google_api_client.list_cloudsql_databases(gcp_project_id, instance_name)
+ list_user_response = google_api_client.list_cloudsql_users(gcp_project_id, instance_name)
- user_response = google_api_client.create_cloudsql_user(gcp_project_id, instance_name, username, password)
+ existing_database = list_database_response.items.find { |database| database.name == database_name }
+ existing_user = list_user_response.items.find { |user| user.name == username }
- if user_response.status != OPERATION_STATE_DONE
- return error("User creation failed: #{user_response.to_json}")
+ if existing_database && existing_user
+ save_database_ci_vars
+ save_user_ci_vars(existing_user)
+ return success
end
- primary_ip_address = get_instance_response.ip_addresses.first.ip_address
- connection_name = get_instance_response.connection_name
+ database_response = execute_database_setup(existing_database)
+ return database_response if database_response[:status] == :error
- save_ci_var('GCP_PROJECT_ID', gcp_project_id)
- save_ci_var('GCP_CLOUDSQL_INSTANCE_NAME', instance_name)
- save_ci_var('GCP_CLOUDSQL_CONNECTION_NAME', connection_name)
- save_ci_var('GCP_CLOUDSQL_PRIMARY_IP_ADDRESS', primary_ip_address)
- save_ci_var('GCP_CLOUDSQL_VERSION', database_version)
- save_ci_var('GCP_CLOUDSQL_DATABASE_NAME', database_name)
- save_ci_var('GCP_CLOUDSQL_DATABASE_USER', username)
- save_ci_var('GCP_CLOUDSQL_DATABASE_PASS', password, true)
+ save_database_ci_vars
+
+ user_response = execute_user_setup(existing_user)
+ return user_response if user_response[:status] == :error
+
+ save_user_ci_vars(existing_user)
success
rescue Google::Apis::Error => err
@@ -64,11 +64,55 @@ module GoogleCloud
end
def password
- SecureRandom.hex(16)
+ @password ||= SecureRandom.hex(16)
end
def save_ci_var(key, value, is_masked = false)
create_or_replace_project_vars(environment_name, key, value, @params[:is_protected], is_masked)
end
+
+ def save_instance_ci_vars(cloudsql_instance)
+ primary_ip_address = cloudsql_instance.ip_addresses.first.ip_address
+ connection_name = cloudsql_instance.connection_name
+
+ save_ci_var('GCP_PROJECT_ID', gcp_project_id)
+ save_ci_var('GCP_CLOUDSQL_INSTANCE_NAME', instance_name)
+ save_ci_var('GCP_CLOUDSQL_CONNECTION_NAME', connection_name)
+ save_ci_var('GCP_CLOUDSQL_PRIMARY_IP_ADDRESS', primary_ip_address)
+ save_ci_var('GCP_CLOUDSQL_VERSION', database_version)
+ end
+
+ def save_database_ci_vars
+ save_ci_var('GCP_CLOUDSQL_DATABASE_NAME', database_name)
+ end
+
+ def save_user_ci_vars(user_exists)
+ save_ci_var('GCP_CLOUDSQL_DATABASE_USER', username)
+ save_ci_var('GCP_CLOUDSQL_DATABASE_PASS', user_exists ? user_exists.password : password, true)
+ end
+
+ def execute_database_setup(database_exists)
+ return success if database_exists
+
+ database_response = google_api_client.create_cloudsql_database(gcp_project_id, instance_name, database_name)
+
+ if database_response.status != OPERATION_STATE_DONE
+ return error("Database creation failed: #{database_response.to_json}")
+ end
+
+ success
+ end
+
+ def execute_user_setup(existing_user)
+ return success if existing_user
+
+ user_response = google_api_client.create_cloudsql_user(gcp_project_id, instance_name, username, password)
+
+ if user_response.status != OPERATION_STATE_DONE
+ return error("User creation failed: #{user_response.to_json}")
+ end
+
+ success
+ end
end
end
diff --git a/app/views/projects/buttons/_remove_tag.html.haml b/app/views/projects/buttons/_remove_tag.html.haml
index 060a854d4e4..dfa643a87bb 100644
--- a/app/views/projects/buttons/_remove_tag.html.haml
+++ b/app/views/projects/buttons/_remove_tag.html.haml
@@ -8,4 +8,4 @@
- title = s_('TagsPage|Only a project maintainer or owner can delete a protected tag')
- disabled = true
-= render Pajamas::ButtonComponent.new(variant: :default, icon: 'remove', button_options: { class: "js-delete-tag-button gl-ml-3\!", 'aria-label': s_('TagsPage|Delete tag'), title: title, disabled: disabled, data: { toggle: 'tooltip', container: 'body', path: project_tag_path(@project, tag.name), tag_name: tag.name, is_protected: protected_tag?(project, tag).to_s } })
+= render Pajamas::ButtonComponent.new(variant: :default, icon: 'remove', button_options: { class: "js-delete-tag-button", 'aria-label': s_('TagsPage|Delete tag'), title: title, disabled: disabled, data: { toggle: 'tooltip', container: 'body', path: project_tag_path(@project, tag.name), tag_name: tag.name, is_protected: protected_tag?(project, tag).to_s } })
diff --git a/app/views/projects/tags/_edit_release_button.html.haml b/app/views/projects/tags/_edit_release_button.html.haml
index 05e0d0f0fa8..1c2626e5612 100644
--- a/app/views/projects/tags/_edit_release_button.html.haml
+++ b/app/views/projects/tags/_edit_release_button.html.haml
@@ -1,7 +1,9 @@
- release_btn_text = s_('TagsPage|Create release')
- release_btn_path = new_project_release_path(project, tag_name: tag.name)
+- option_css_classes = local_assigns.fetch(:option_css_classes, '')
+- css_classes = "btn gl-button btn-default btn-icon btn-edit has-tooltip #{option_css_classes}"
- if release
- release_btn_text = s_('TagsPage|Edit release')
- release_btn_path = edit_project_release_path(project, release)
-= link_to release_btn_path, class: 'btn gl-button btn-default btn-icon btn-edit has-tooltip', title: release_btn_text, data: { container: "body" } do
+= link_to release_btn_path, class: css_classes, title: release_btn_text, data: { container: "body" } do
= sprite_icon('pencil', css_class: 'gl-icon')
diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml
index 258f662420b..f9605b814fa 100644
--- a/app/views/projects/tags/_tag.html.haml
+++ b/app/views/projects/tags/_tag.html.haml
@@ -40,5 +40,5 @@
= render 'projects/buttons/download', project: @project, ref: tag.name, pipeline: @tags_pipelines[tag.name]
- if can?(current_user, :admin_tag, @project)
- = render 'edit_release_button', tag: tag, project: @project, release: release
+ = render 'edit_release_button', tag: tag, project: @project, release: release, option_css_classes: 'gl-mr-3!'
= render 'projects/buttons/remove_tag', project: @project, tag: tag
diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml
index 24da8e2db87..0a060310ae7 100644
--- a/app/views/projects/tags/show.html.haml
+++ b/app/views/projects/tags/show.html.haml
@@ -42,15 +42,13 @@
= render partial: 'projects/commit/signature', object: @tag.signature
- if can?(current_user, :admin_tag, @project)
= render 'edit_release_button', tag: @tag, project: @project, release: @release
- = link_to project_tree_path(@project, @tag.name), class: 'btn btn-icon gl-button btn-default controls-item has-tooltip', title: s_('TagsPage|Browse files') do
+ = link_to project_tree_path(@project, @tag.name), class: 'btn btn-icon gl-button btn-default has-tooltip', title: s_('TagsPage|Browse files') do
= sprite_icon('folder-open', css_class: 'gl-icon')
- = link_to project_commits_path(@project, @tag.name), class: 'btn btn-icon gl-button btn-default controls-item has-tooltip', title: s_('TagsPage|Browse commits') do
+ = link_to project_commits_path(@project, @tag.name), class: 'btn btn-icon gl-button btn-default has-tooltip', title: s_('TagsPage|Browse commits') do
= sprite_icon('history', css_class: 'gl-icon')
- .controls-item
- = render 'projects/buttons/download', project: @project, ref: @tag.name
+ = render 'projects/buttons/download', project: @project, ref: @tag.name
- if can?(current_user, :admin_tag, @project)
- .btn-container.controls-item-full
- = render 'projects/buttons/remove_tag', project: @project, tag: @tag
+ = render 'projects/buttons/remove_tag', project: @project, tag: @tag
- if @tag.message.present?
%pre.wrap{ data: { qa_selector: 'tag_message_content' } }
diff --git a/doc/.vale/gitlab/MultiLineLinks.yml b/doc/.vale/gitlab/MultiLineLinks.yml
new file mode 100644
index 00000000000..bb0efc3a2de
--- /dev/null
+++ b/doc/.vale/gitlab/MultiLineLinks.yml
@@ -0,0 +1,14 @@
+---
+# Error: gitlab.MultiLineLinks
+#
+# Checks that links are all on a single line.
+#
+# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
+extends: existence
+message: 'Link "%s" must be on a single line, even if very long.'
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#basic-link-criteria
+level: warning
+scope: raw
+raw:
+ - '\[[^\]]*?\n[^\]]*?\]\([^\)]*?\)|'
+ - '\[[^\]]*?\]\([^\)]*?\n[^\)]*\)'
diff --git a/doc/architecture/blueprints/ci_scale/index.md b/doc/architecture/blueprints/ci_scale/index.md
index 1c3aee2f860..40318f19286 100644
--- a/doc/architecture/blueprints/ci_scale/index.md
+++ b/doc/architecture/blueprints/ci_scale/index.md
@@ -135,8 +135,7 @@ stores more than 600 gigabytes of data, and `ci_builds.yaml_variables` more
than 300 gigabytes (as of February 2021).
It is a lot of data that needs to be reliably moved to a different place.
-Unfortunately, right now, our [background
-migrations](https://docs.gitlab.com/ee/development/background_migrations.html)
+Unfortunately, right now, our [background migrations](../../../development/database/background_migrations.md)
are not reliable enough to migrate this amount of data at scale. We need to
build mechanisms that will give us confidence in moving this data between
columns, tables, partitions or database shards.
diff --git a/doc/architecture/blueprints/graphql_api/index.md b/doc/architecture/blueprints/graphql_api/index.md
index b3ba1ad1960..2dfd83dc6e4 100644
--- a/doc/architecture/blueprints/graphql_api/index.md
+++ b/doc/architecture/blueprints/graphql_api/index.md
@@ -20,8 +20,7 @@ GraphQL development and helped to surface the need of improving tooling we use
to extend the new API.
This document describes the work that is needed to build a stable foundation that
-will support our development efforts and a large-scale usage of the [GraphQL
-API](https://docs.gitlab.com/ee/api/graphql/index.html).
+will support our development efforts and a large-scale usage of the [GraphQL API](../../../api/graphql/index.md).
## Summary
diff --git a/doc/development/database/avoiding_downtime_in_migrations.md b/doc/development/database/avoiding_downtime_in_migrations.md
index 2d079656e23..bc264177324 100644
--- a/doc/development/database/avoiding_downtime_in_migrations.md
+++ b/doc/development/database/avoiding_downtime_in_migrations.md
@@ -93,9 +93,8 @@ class RemoveUsersUpdatedAtColumn < Gitlab::Database::Migration[2.0]
end
```
-You can consider [enabling lock retries](
-https://docs.gitlab.com/ee/development/migration_style_guide.html#usage-with-transactional-migrations
-) when you run a migration on big tables, because it might take some time to
+You can consider [enabling lock retries](../migration_style_guide.md#usage-with-transactional-migrations)
+when you run a migration on big tables, because it might take some time to
acquire a lock on this table.
#### B. The removed column has an index or constraint that belongs to it
@@ -126,13 +125,11 @@ end
In the `down` method, we check to see if the column already exists before adding it again.
We do this because the migration is non-transactional and might have failed while it was running.
-The [`disable_ddl_transaction!`](
-https://docs.gitlab.com/ee/development/migration_style_guide.html#usage-with-non-transactional-migrations-disable_ddl_transaction
-) is used to disable the transaction that wraps the whole migration.
+The [`disable_ddl_transaction!`](../migration_style_guide.md#usage-with-non-transactional-migrations-disable_ddl_transaction)
+is used to disable the transaction that wraps the whole migration.
-You can refer to the page [Migration Style Guide](
-https://docs.gitlab.com/ee/development/migration_style_guide.html
-) for more information about database migrations.
+You can refer to the page [Migration Style Guide](../migration_style_guide.md)
+for more information about database migrations.
### Step 3: Removing the ignore rule (release M+2)
@@ -295,8 +292,7 @@ when migrating a column in a large table (for example, `issues`). Background
migrations spread the work / load over a longer time period, without slowing
down deployments.
-For more information, see [the documentation on cleaning up background
-migrations](background_migrations.md#cleaning-up).
+For more information, see [the documentation on cleaning up background migrations](background_migrations.md#cleaning-up).
## Adding Indexes
diff --git a/doc/development/database/loose_foreign_keys.md b/doc/development/database/loose_foreign_keys.md
index 52dc9c476c9..a390576c4e5 100644
--- a/doc/development/database/loose_foreign_keys.md
+++ b/doc/development/database/loose_foreign_keys.md
@@ -395,8 +395,7 @@ We considered using these Rails features as an alternative to foreign keys but t
For non-trivial objects that need to clean up data outside the
database (for example, object storage) where you might wish to use `dependent: :destroy`,
see alternatives in
-[Avoid `dependent: :nullify` and `dependent: :destroy` across
-databases](./multiple_databases.md#avoid-dependent-nullify-and-dependent-destroy-across-databases).
+[Avoid `dependent: :nullify` and `dependent: :destroy` across databases](multiple_databases.md#avoid-dependent-nullify-and-dependent-destroy-across-databases).
## Risks of loose foreign keys and possible mitigations
diff --git a/doc/development/documentation/styleguide/index.md b/doc/development/documentation/styleguide/index.md
index 48678dd32fa..72d489304c7 100644
--- a/doc/development/documentation/styleguide/index.md
+++ b/doc/development/documentation/styleguide/index.md
@@ -722,10 +722,12 @@ We include guidance for links in these categories:
- Use inline link Markdown markup `[Text](https://example.com)`.
It's easier to read, review, and maintain. Do not use `[Text][identifier]` reference-style links.
-
- Use meaningful anchor text.
For example, instead of writing something like `Read more about merge requests [here](LINK)`,
write `Read more about [merge requests](LINK)`.
+- Put the entire link on a single line. Some of our [linters](../testing.md) do not
+ validate links when split over multiple lines, and incorrect or broken links could
+ slip through.
### Links to internal documentation
diff --git a/lib/gitlab/ci/jwt.rb b/lib/gitlab/ci/jwt.rb
index c294291e538..d3e7210b820 100644
--- a/lib/gitlab/ci/jwt.rb
+++ b/lib/gitlab/ci/jwt.rb
@@ -65,7 +65,7 @@ module Gitlab
fields.merge!(
environment: environment.name,
environment_protected: environment_protected?.to_s,
- deployment_tier: build.environment_deployment_tier || environment.tier
+ deployment_tier: build.environment_tier
)
end
diff --git a/lib/gitlab/ci/pipeline/seed/environment.rb b/lib/gitlab/ci/pipeline/seed/environment.rb
index c8795840e5f..6bcc71a808b 100644
--- a/lib/gitlab/ci/pipeline/seed/environment.rb
+++ b/lib/gitlab/ci/pipeline/seed/environment.rb
@@ -30,7 +30,7 @@ module Gitlab
end
def deployment_tier
- job.environment_deployment_tier
+ job.environment_tier_from_options
end
def expanded_environment_name
diff --git a/lib/google_api/cloud_platform/client.rb b/lib/google_api/cloud_platform/client.rb
index b1a69a5e61f..fb1b6657012 100644
--- a/lib/google_api/cloud_platform/client.rb
+++ b/lib/google_api/cloud_platform/client.rb
@@ -148,16 +148,42 @@ module GoogleApi
enable_service(gcp_project_id, 'cloudbuild.googleapis.com')
end
+ def enable_cloud_sql_admin(gcp_project_id)
+ enable_service(gcp_project_id, 'sqladmin.googleapis.com')
+ end
+
+ def enable_compute(gcp_project_id)
+ enable_service(gcp_project_id, 'compute.googleapis.com')
+ end
+
+ def enable_service_networking(gcp_project_id)
+ enable_service(gcp_project_id, 'servicenetworking.googleapis.com')
+ end
+
def revoke_authorizations
uri = URI(REVOKE_URL)
Gitlab::HTTP.post(uri, body: { 'token' => access_token })
end
+ def list_cloudsql_databases(gcp_project_id, instance_name)
+ service = Google::Apis::SqladminV1beta4::SQLAdminService.new
+ service.authorization = access_token
+
+ service.list_databases(gcp_project_id, instance_name, options: user_agent_header)
+ end
+
def create_cloudsql_database(gcp_project_id, instance_name, database_name)
database = Google::Apis::SqladminV1beta4::Database.new(name: database_name)
sql_admin_service.insert_database(gcp_project_id, instance_name, database)
end
+ def list_cloudsql_users(gcp_project_id, instance_name)
+ service = Google::Apis::SqladminV1beta4::SQLAdminService.new
+ service.authorization = access_token
+
+ service.list_users(gcp_project_id, instance_name, options: user_agent_header)
+ end
+
def create_cloudsql_user(gcp_project_id, instance_name, username, password)
user = Google::Apis::SqladminV1beta4::User.new
user.name = username
@@ -169,6 +195,20 @@ module GoogleApi
sql_admin_service.get_instance(gcp_project_id, instance_name)
end
+ def create_cloudsql_instance(gcp_project_id, instance_name, root_password, database_version, region, tier)
+ database_instance = Google::Apis::SqladminV1beta4::DatabaseInstance.new(
+ name: instance_name,
+ root_password: root_password,
+ database_version: database_version,
+ region: region,
+ settings: Google::Apis::SqladminV1beta4::Settings.new(tier: tier)
+ )
+
+ service = Google::Apis::SqladminV1beta4::SQLAdminService.new
+ service.authorization = access_token
+ service.insert_instance(gcp_project_id, database_instance)
+ end
+
private
def enable_service(gcp_project_id, service_name)
diff --git a/qa/qa/service/praefect_manager.rb b/qa/qa/service/praefect_manager.rb
index eab41572cda..e500745c4f1 100644
--- a/qa/qa/service/praefect_manager.rb
+++ b/qa/qa/service/praefect_manager.rb
@@ -425,7 +425,7 @@ module QA
end
def value_for_node(data, node)
- data.find(-> {{ value: 0 }}) { |item| item[:node] == node }[:value]
+ data.find(-> { { value: 0 } }) { |item| item[:node] == node }[:value]
end
def wait_for_replication(project_id)
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb
index 36b7378ee2a..206e6b8a456 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb
@@ -3,7 +3,7 @@
module QA
RSpec.describe 'Plan', :reliable do
describe 'Custom issue templates' do
- let(:template_name) { 'custom_issue_template'}
+ let(:template_name) { 'custom_issue_template' }
let(:template_content) { 'This is a custom issue template test' }
let(:template_project) do
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb
index 3373f4f4233..6ce4217f8ac 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb
@@ -3,7 +3,7 @@
module QA
RSpec.describe 'Create', :reliable do
describe 'Merge request custom templates' do
- let(:template_name) { 'custom_merge_request_template'}
+ let(:template_name) { 'custom_merge_request_template' }
let(:template_content) { 'This is a custom merge request template test' }
let(:template_project) do
Resource::Project.fabricate_via_api! do |project|
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/license_detecton_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/license_detecton_spec.rb
index 8074e1fa992..3db8128bc6d 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/license_detecton_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/license_detecton_spec.rb
@@ -28,16 +28,16 @@ module QA
context 'on a project with a commonly used LICENSE',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/366842' do
it_behaves_like 'project license detection' do
- let(:license_file_name) {'bsd-3-clause'}
- let(:rendered_license_name) {'BSD 3-Clause "New" or "Revised" License'}
+ let(:license_file_name) { 'bsd-3-clause' }
+ let(:rendered_license_name) { 'BSD 3-Clause "New" or "Revised" License' }
end
end
context 'on a project with a less commonly used LICENSE',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/366843' do
it_behaves_like 'project license detection' do
- let(:license_file_name) {'GFDL-1.2-only'}
- let(:rendered_license_name) {'Other'}
+ let(:license_file_name) { 'GFDL-1.2-only' }
+ let(:rendered_license_name) { 'Other' }
end
end
end
diff --git a/qa/qa/specs/features/sanity/feature_flags_spec.rb b/qa/qa/specs/features/sanity/feature_flags_spec.rb
index f771978802e..acb9528fe6a 100644
--- a/qa/qa/specs/features/sanity/feature_flags_spec.rb
+++ b/qa/qa/specs/features/sanity/feature_flags_spec.rb
@@ -26,7 +26,7 @@ module QA
end
let(:flag) { Pathname.new(file.path).basename('.yml').to_s }
- let(:root) { '..'}
+ let(:root) { '..' }
before do
definition = <<~YAML
@@ -78,7 +78,7 @@ module QA
end
context 'with an EE feature flag' do
- let(:root) { '../ee'}
+ let(:root) { '../ee' }
include_examples 'gets flag value'
end
diff --git a/spec/features/admin/admin_mode/login_spec.rb b/spec/features/admin/admin_mode/login_spec.rb
index 659f66a67d2..a16adaf6076 100644
--- a/spec/features/admin/admin_mode/login_spec.rb
+++ b/spec/features/admin/admin_mode/login_spec.rb
@@ -121,7 +121,7 @@ RSpec.describe 'Admin Mode Login' do
end
context 'when logging in via omniauth' do
- let(:user) { create(:omniauth_user, :admin, :two_factor, extern_uid: 'my-uid', provider: 'saml', password_automatically_set: false)}
+ let(:user) { create(:omniauth_user, :admin, :two_factor, extern_uid: 'my-uid', provider: 'saml', password_automatically_set: false) }
let(:mock_saml_response) do
File.read('spec/fixtures/authentication/saml_response.xml')
end
diff --git a/spec/features/admin/users/users_spec.rb b/spec/features/admin/users/users_spec.rb
index e5df6cc0fd3..236327ea687 100644
--- a/spec/features/admin/users/users_spec.rb
+++ b/spec/features/admin/users/users_spec.rb
@@ -357,7 +357,7 @@ RSpec.describe 'Admin::Users' do
end
it 'creates new user' do
- expect { click_button 'Create user' }.to change {User.count}.by(1)
+ expect { click_button 'Create user' }.to change { User.count }.by(1)
end
it 'applies defaults to user' do
@@ -400,7 +400,7 @@ RSpec.describe 'Admin::Users' do
let_it_be(:user_username) { 'Bing bang' }
it "doesn't create the user and shows an error message" do
- expect { click_button 'Create user' }.to change {User.count}.by(0)
+ expect { click_button 'Create user' }.to change { User.count }.by(0)
expect(page).to have_content('The form contains the following error')
expect(page).to have_content('Username can contain only letters, digits')
diff --git a/spec/features/boards/board_filters_spec.rb b/spec/features/boards/board_filters_spec.rb
index 537b677cbd0..2e4dc4a29fc 100644
--- a/spec/features/boards/board_filters_spec.rb
+++ b/spec/features/boards/board_filters_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe 'Issue board filters', :js do
let_it_be(:award_emoji1) { create(:award_emoji, name: 'thumbsup', user: user, awardable: issue_1) }
let(:filtered_search) { find('[data-testid="issue-board-filtered-search"]') }
- let(:filter_input) { find('.gl-filtered-search-term-input')}
+ let(:filter_input) { find('.gl-filtered-search-term-input') }
let(:filter_dropdown) { find('.gl-filtered-search-suggestion-list') }
let(:filter_first_suggestion) { find('.gl-filtered-search-suggestion-list').first('.gl-filtered-search-suggestion') }
let(:filter_submit) { find('.gl-search-box-by-click-search-button') }
@@ -164,7 +164,7 @@ RSpec.describe 'Issue board filters', :js do
end
describe 'filters by type' do
- let_it_be(:incident) { create(:incident, project: project)}
+ let_it_be(:incident) { create(:incident, project: project) }
before do
set_filter('type')
diff --git a/spec/features/boards/reload_boards_on_browser_back_spec.rb b/spec/features/boards/reload_boards_on_browser_back_spec.rb
index 6a09e3c9506..7fa440befc1 100644
--- a/spec/features/boards/reload_boards_on_browser_back_spec.rb
+++ b/spec/features/boards/reload_boards_on_browser_back_spec.rb
@@ -3,9 +3,9 @@
require 'spec_helper'
RSpec.describe 'Ensure Boards do not show stale data on browser back', :js do
- let(:project) {create(:project, :public)}
- let(:board) {create(:board, project: project)}
- let(:user) {create(:user)}
+ let(:project) { create(:project, :public) }
+ let(:board) { create(:board, project: project) }
+ let(:user) { create(:user) }
context 'authorized user' do
before do
diff --git a/spec/features/dashboard/archived_projects_spec.rb b/spec/features/dashboard/archived_projects_spec.rb
index 1b349fa2276..d157d44bab7 100644
--- a/spec/features/dashboard/archived_projects_spec.rb
+++ b/spec/features/dashboard/archived_projects_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe 'Dashboard Archived Project' do
let(:user) { create :user }
- let(:project) { create :project}
+ let(:project) { create :project }
let(:archived_project) { create(:project, :archived) }
before do
diff --git a/spec/features/error_tracking/user_filters_errors_by_status_spec.rb b/spec/features/error_tracking/user_filters_errors_by_status_spec.rb
index d5dbe259159..2ac43f67f64 100644
--- a/spec/features/error_tracking/user_filters_errors_by_status_spec.rb
+++ b/spec/features/error_tracking/user_filters_errors_by_status_spec.rb
@@ -10,8 +10,8 @@ RSpec.describe 'When a user filters Sentry errors by status', :js, :use_clean_ra
let(:issues_api_url) { "#{sentry_api_urls.issues_url}?limit=20&query=is:unresolved" }
let(:issues_api_url_filter) { "#{sentry_api_urls.issues_url}?limit=20&query=is:ignored" }
- let(:auth_token) {{ 'Authorization' => 'Bearer access_token_123' }}
- let(:return_header) {{ 'Content-Type' => 'application/json' }}
+ let(:auth_token) { { 'Authorization' => 'Bearer access_token_123' } }
+ let(:return_header) { { 'Content-Type' => 'application/json' } }
before do
stub_request(:get, issues_api_url).with(headers: auth_token)
diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb
index c86705832b1..eec07c84cde 100644
--- a/spec/features/groups/issues_spec.rb
+++ b/spec/features/groups/issues_spec.rb
@@ -7,12 +7,12 @@ RSpec.describe 'Group issues page' do
include DragTo
let(:group) { create(:group) }
- let(:project) { create(:project, :public, group: group)}
+ let(:project) { create(:project, :public, group: group) }
let(:project_with_issues_disabled) { create(:project, :issues_disabled, group: group) }
let(:path) { issues_group_path(group) }
context 'with shared examples', :js do
- let(:issuable) { create(:issue, project: project, title: "this is my created issuable")}
+ let(:issuable) { create(:issue, project: project, title: "this is my created issuable") }
include_examples 'project features apply to issuables', Issue
@@ -68,7 +68,7 @@ RSpec.describe 'Group issues page' do
context 'issues list', :js do
let(:subgroup) { create(:group, parent: group) }
- let(:subgroup_project) { create(:project, :public, group: subgroup)}
+ let(:subgroup_project) { create(:project, :public, group: subgroup) }
let(:user_in_group) { create(:group_member, :maintainer, user: create(:user), group: group ).user }
let!(:issue) { create(:issue, project: project, title: 'root group issue') }
let!(:subgroup_issue) { create(:issue, project: subgroup_project, title: 'subgroup issue') }
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index 70279e5067a..c93ed01b873 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -378,7 +378,7 @@ RSpec.describe 'Group' do
end
it 'removes group', :sidekiq_might_not_need_inline do
- expect { remove_with_confirm('Remove group', group.path) }.to change {Group.count}.by(-1)
+ expect { remove_with_confirm('Remove group', group.path) }.to change { Group.count }.by(-1)
expect(group.members.all.count).to be_zero
expect(page).to have_content "scheduled for deletion"
end
diff --git a/spec/features/issuables/user_sees_sidebar_spec.rb b/spec/features/issuables/user_sees_sidebar_spec.rb
index 04bf704b6a4..66ed6044de6 100644
--- a/spec/features/issuables/user_sees_sidebar_spec.rb
+++ b/spec/features/issuables/user_sees_sidebar_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'Issue Sidebar on Mobile' do
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
let(:issue) { create(:issue, project: project) }
- let!(:user) { create(:user)}
+ let!(:user) { create(:user) }
before do
sign_in(user)
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index 8732e2ecff2..fa4ce6fe1c1 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -414,7 +414,7 @@ RSpec.describe 'GFM autocomplete', :js do
it 'shows all contacts' do
page.within(find_autocomplete_menu) do
- expected_data = contacts.map { |c| "#{c.first_name} #{c.last_name} #{c.email}"}
+ expected_data = contacts.map { |c| "#{c.first_name} #{c.last_name} #{c.email}" }
expect(page.all('li').map(&:text)).to match_array(expected_data)
end
diff --git a/spec/features/issues/todo_spec.rb b/spec/features/issues/todo_spec.rb
index d63d21353e5..6a53c12eda3 100644
--- a/spec/features/issues/todo_spec.rb
+++ b/spec/features/issues/todo_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe 'Manually create a todo item from issue', :js do
let!(:project) { create(:project) }
let!(:issue) { create(:issue, project: project) }
- let!(:user) { create(:user)}
+ let!(:user) { create(:user) }
before do
project.add_maintainer(user)
diff --git a/spec/features/issues/user_bulk_edits_issues_spec.rb b/spec/features/issues/user_bulk_edits_issues_spec.rb
index 0533f1688e2..1ef2918adec 100644
--- a/spec/features/issues/user_bulk_edits_issues_spec.rb
+++ b/spec/features/issues/user_bulk_edits_issues_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe 'Multiple issue updating from issues#index', :js do
let!(:project) { create(:project) }
let!(:issue) { create(:issue, project: project) }
- let!(:user) { create(:user)}
+ let!(:user) { create(:user) }
before do
project.add_maintainer(user)
diff --git a/spec/features/issues/user_interacts_with_awards_spec.rb b/spec/features/issues/user_interacts_with_awards_spec.rb
index 892b57bac5c..c86a2c32e2d 100644
--- a/spec/features/issues/user_interacts_with_awards_spec.rb
+++ b/spec/features/issues/user_interacts_with_awards_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe 'User interacts with awards' do
let(:user) { create(:user) }
describe 'User interacts with awards in an issue', :js do
- let(:issue) { create(:issue, project: project)}
+ let(:issue) { create(:issue, project: project) }
let(:project) { create(:project) }
before do
diff --git a/spec/features/issues/user_uses_quick_actions_spec.rb b/spec/features/issues/user_uses_quick_actions_spec.rb
index c6d743ed38f..d458c991668 100644
--- a/spec/features/issues/user_uses_quick_actions_spec.rb
+++ b/spec/features/issues/user_uses_quick_actions_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe 'Issues > User uses quick actions', :js do
let!(:label_feature) { create(:label, project: project, title: 'feature') }
let!(:milestone) { create(:milestone, project: project, title: 'ASAP') }
let(:issuable) { create(:issue, project: project) }
- let(:source_issuable) { create(:issue, project: project, milestone: milestone, labels: [label_bug, label_feature])}
+ let(:source_issuable) { create(:issue, project: project, milestone: milestone, labels: [label_bug, label_feature]) }
it_behaves_like 'close quick action', :issue
it_behaves_like 'issuable time tracker', :issue
diff --git a/spec/features/merge_request/user_approves_spec.rb b/spec/features/merge_request/user_approves_spec.rb
index 4f7bcb58551..9670012803e 100644
--- a/spec/features/merge_request/user_approves_spec.rb
+++ b/spec/features/merge_request/user_approves_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe 'Merge request > User approves', :js do
def verify_approvals_count_on_index!
visit(project_merge_requests_path(project, state: :all))
- expect(page.all('li').any? { |item| item["title"] == "1 approver (you've approved)"}).to be true
+ expect(page.all('li').any? { |item| item["title"] == "1 approver (you've approved)" }).to be true
visit project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb b/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb
index 059e1eb89c5..f0c0142a6cc 100644
--- a/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb
+++ b/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb
@@ -5,8 +5,8 @@ require 'spec_helper'
RSpec.describe 'Merge request < User customizes merge commit message', :js do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
- let(:issue_1) { create(:issue, project: project)}
- let(:issue_2) { create(:issue, project: project)}
+ let(:issue_1) { create(:issue, project: project) }
+ let(:issue_2) { create(:issue, project: project) }
let(:source_branch) { 'csv' }
let(:target_branch) { 'master' }
let(:squash) { false }
diff --git a/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb b/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb
index 92b9b785148..0dd87ac3e24 100644
--- a/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb
+++ b/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb
@@ -89,7 +89,7 @@ RSpec.describe 'Merge request > User edits assignees sidebar', :js do
context 'when GraphQL assignees widget feature flag is enabled' do
let(:sidebar_assignee_dropdown_item) { sidebar_assignee_block.find(".dropdown-item", text: assignee.username ) }
- let(:sidebar_assignee_dropdown_tooltip) { sidebar_assignee_dropdown_item['title']}
+ let(:sidebar_assignee_dropdown_tooltip) { sidebar_assignee_dropdown_item['title'] }
context 'when user is an owner' do
before do
diff --git a/spec/features/merge_request/user_sees_closing_issues_message_spec.rb b/spec/features/merge_request/user_sees_closing_issues_message_spec.rb
index 7b7fff5c936..f56db3d3dbe 100644
--- a/spec/features/merge_request/user_sees_closing_issues_message_spec.rb
+++ b/spec/features/merge_request/user_sees_closing_issues_message_spec.rb
@@ -5,8 +5,8 @@ require 'spec_helper'
RSpec.describe 'Merge request > User sees closing issues message', :js do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
- let(:issue_1) { create(:issue, project: project)}
- let(:issue_2) { create(:issue, project: project)}
+ let(:issue_1) { create(:issue, project: project) }
+ let(:issue_2) { create(:issue, project: project) }
let(:merge_request) do
create(
:merge_request,
diff --git a/spec/features/merge_request/user_sees_deployment_widget_spec.rb b/spec/features/merge_request/user_sees_deployment_widget_spec.rb
index e045f11c0d8..c02149eed87 100644
--- a/spec/features/merge_request/user_sees_deployment_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_deployment_widget_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe 'Merge request > User sees deployment widget', :js do
let(:ref) { merge_request.target_branch }
let(:sha) { project.commit(ref).id }
let(:pipeline) { create(:ci_pipeline, sha: sha, project: project, ref: ref) }
- let!(:manual) { }
+ let!(:manual) {}
let(:build) { create(:ci_build, :with_deployment, environment: environment.name, pipeline: pipeline) }
let!(:deployment) { build.deployment }
diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb
index 50f4cce5c23..2e65183d26f 100644
--- a/spec/features/merge_request/user_sees_diff_spec.rb
+++ b/spec/features/merge_request/user_sees_diff_spec.rb
@@ -86,7 +86,7 @@ RSpec.describe 'Merge request > User sees diff', :js do
context 'when file contains html' do
let(:current_user) { project.first_owner }
- let(:branch_name) {"test_branch"}
+ let(:branch_name) { "test_branch" }
it 'escapes any HTML special characters in the diff chunk header' do
file_content =
diff --git a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
index 09c6b6bce3b..2a1b9ea6009 100644
--- a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
@@ -25,7 +25,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
}
end
- let(:expected_detached_mr_tag) {'merge request'}
+ let(:expected_detached_mr_tag) { 'merge request' }
before do
stub_application_setting(auto_devops_enabled: false)
diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb
index 2c2a2dfd4a8..0e86e970f46 100644
--- a/spec/features/merge_request/user_sees_versions_spec.rb
+++ b/spec/features/merge_request/user_sees_versions_spec.rb
@@ -232,7 +232,7 @@ RSpec.describe 'Merge request > User sees versions', :js do
end
it 'only shows diffs from the commit' do
- diff_commit_ids = find_all('.diff-file [data-commit-id]').map {|diff| diff['data-commit-id']}
+ diff_commit_ids = find_all('.diff-file [data-commit-id]').map { |diff| diff['data-commit-id'] }
expect(diff_commit_ids).not_to be_empty
expect(diff_commit_ids).to all(eq(params[:commit_id]))
diff --git a/spec/features/merge_request/user_uses_quick_actions_spec.rb b/spec/features/merge_request/user_uses_quick_actions_spec.rb
index b48659353ec..563120fc8b7 100644
--- a/spec/features/merge_request/user_uses_quick_actions_spec.rb
+++ b/spec/features/merge_request/user_uses_quick_actions_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe 'Merge request > User uses quick actions', :js do
let!(:label_feature) { create(:label, project: project, title: 'feature') }
let!(:milestone) { create(:milestone, project: project, title: 'ASAP') }
let(:issuable) { create(:merge_request, source_project: project) }
- let(:source_issuable) { create(:issue, project: project, milestone: milestone, labels: [label_bug, label_feature])}
+ let(:source_issuable) { create(:issue, project: project, milestone: milestone, labels: [label_bug, label_feature]) }
it_behaves_like 'close quick action', :merge_request
it_behaves_like 'issuable time tracker', :merge_request
diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb
index 4b6ed458c68..2f7b722f553 100644
--- a/spec/features/profiles/user_edit_profile_spec.rb
+++ b/spec/features/profiles/user_edit_profile_spec.rb
@@ -294,7 +294,7 @@ RSpec.describe 'User edit profile' do
end
context 'user menu' do
- let(:issue) { create(:issue, project: project)}
+ let(:issue) { create(:issue, project: project) }
let(:project) { create(:project) }
def open_modal(button_text)
@@ -536,7 +536,7 @@ RSpec.describe 'User edit profile' do
end
context 'User time preferences', :js do
- let(:issue) { create(:issue, project: project)}
+ let(:issue) { create(:issue, project: project) }
let(:project) { create(:project) }
before do
diff --git a/spec/features/projects/cluster_agents_spec.rb b/spec/features/projects/cluster_agents_spec.rb
index 5d931afe4a7..8c557a9c37a 100644
--- a/spec/features/projects/cluster_agents_spec.rb
+++ b/spec/features/projects/cluster_agents_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe 'ClusterAgents', :js do
- let_it_be(:token) { create(:cluster_agent_token, description: 'feature test token')}
+ let_it_be(:token) { create(:cluster_agent_token, description: 'feature test token') }
let(:agent) { token.agent }
let(:project) { agent.project }
diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb
index 863fdbdadaa..2719316c5dc 100644
--- a/spec/features/projects/commits/user_browses_commits_spec.rb
+++ b/spec/features/projects/commits/user_browses_commits_spec.rb
@@ -150,7 +150,7 @@ RSpec.describe 'User browses commits' do
let(:ref) { project.repository.root_ref }
let(:newrev) { project.repository.commit('master').sha }
let(:short_newrev) { project.repository.commit('master').short_id }
- let(:message) { 'Glob characters'}
+ let(:message) { 'Glob characters' }
before do
create_file_in_repo(project, ref, ref, filename, 'Test file', commit_message: message)
diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb
index 951b24eafac..a53e8beb555 100644
--- a/spec/features/projects/environments/environment_spec.rb
+++ b/spec/features/projects/environments/environment_spec.rb
@@ -18,10 +18,10 @@ RSpec.describe 'Environment' do
describe 'environment details page' do
let!(:environment) { create(:environment, project: project) }
- let!(:permissions) { }
- let!(:deployment) { }
- let!(:action) { }
- let!(:cluster) { }
+ let!(:permissions) {}
+ let!(:deployment) {}
+ let!(:action) {}
+ let!(:cluster) {}
context 'with auto-stop' do
let!(:environment) { create(:environment, :will_auto_stop, name: 'staging', project: project) }
diff --git a/spec/features/projects/files/user_browses_files_spec.rb b/spec/features/projects/files/user_browses_files_spec.rb
index 53fdd5a15dd..0f3ce5a2bad 100644
--- a/spec/features/projects/files/user_browses_files_spec.rb
+++ b/spec/features/projects/files/user_browses_files_spec.rb
@@ -348,7 +348,7 @@ RSpec.describe "User browses files", :js do
end
it "shows raw file content in a new tab" do
- new_tab = window_opened_by {click_link 'Open raw'}
+ new_tab = window_opened_by { click_link 'Open raw' }
within_window new_tab do
expect(page).to have_content("Test file")
@@ -366,7 +366,7 @@ RSpec.describe "User browses files", :js do
end
it "shows raw file content in a new tab" do
- new_tab = window_opened_by {click_link 'Open raw'}
+ new_tab = window_opened_by { click_link 'Open raw' }
within_window new_tab do
expect(page).to have_content("*.rbc")
diff --git a/spec/features/projects/members/manage_groups_spec.rb b/spec/features/projects/members/manage_groups_spec.rb
index 006fa3b6eff..e86affbbca1 100644
--- a/spec/features/projects/members/manage_groups_spec.rb
+++ b/spec/features/projects/members/manage_groups_spec.rb
@@ -162,7 +162,7 @@ RSpec.describe 'Project > Members > Manage groups', :js do
let_it_be(:user) { maintainer }
let_it_be(:group) { parent_group }
let_it_be(:group_within_hierarchy) { create(:group, parent: group) }
- let_it_be(:project_within_hierarchy) { create(:project, group: group_within_hierarchy)}
+ let_it_be(:project_within_hierarchy) { create(:project, group: group_within_hierarchy) }
let_it_be(:members_page_path) { project_project_members_path(project) }
let_it_be(:members_page_path_within_hierarchy) { project_project_members_path(project_within_hierarchy) }
end
diff --git a/spec/features/projects/pipelines/legacy_pipelines_spec.rb b/spec/features/projects/pipelines/legacy_pipelines_spec.rb
index 15d889933bf..ca1610d5d35 100644
--- a/spec/features/projects/pipelines/legacy_pipelines_spec.rb
+++ b/spec/features/projects/pipelines/legacy_pipelines_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe 'Pipelines', :js do
include Spec::Support::Helpers::ModalHelpers
let(:project) { create(:project) }
- let(:expected_detached_mr_tag) {'merge request'}
+ let(:expected_detached_mr_tag) { 'merge request' }
context 'when user is logged in' do
let(:user) { create(:user) }
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 785edc69623..d8de5e87a16 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe 'Pipelines', :js do
include Spec::Support::Helpers::ModalHelpers
let(:project) { create(:project) }
- let(:expected_detached_mr_tag) {'merge request'}
+ let(:expected_detached_mr_tag) { 'merge request' }
context 'when user is logged in' do
let(:user) { create(:user) }
diff --git a/spec/features/projects/settings/service_desk_setting_spec.rb b/spec/features/projects/settings/service_desk_setting_spec.rb
index 0df4bd3f0d9..86c5c3d2d8c 100644
--- a/spec/features/projects/settings/service_desk_setting_spec.rb
+++ b/spec/features/projects/settings/service_desk_setting_spec.rb
@@ -81,7 +81,7 @@ RSpec.describe 'Service Desk Setting', :js, :clean_gitlab_redis_cache do
}
end
- let_it_be_with_reload(:group) { create(:group)}
+ let_it_be_with_reload(:group) { create(:group) }
let_it_be_with_reload(:project) { create(:project, :custom_repo, group: group, files: issuable_project_template_files) }
let_it_be(:group_template_repo) { create(:project, :custom_repo, group: group, files: issuable_group_template_files) }
diff --git a/spec/features/projects/tree/tree_show_spec.rb b/spec/features/projects/tree/tree_show_spec.rb
index 53e89cd2959..163e347d03d 100644
--- a/spec/features/projects/tree/tree_show_spec.rb
+++ b/spec/features/projects/tree/tree_show_spec.rb
@@ -54,7 +54,7 @@ RSpec.describe 'Projects tree', :js do
let(:filename) { File.join(path, 'test.txt') }
let(:newrev) { project.repository.commit('master').sha }
let(:short_newrev) { project.repository.commit('master').short_id }
- let(:message) { 'Glob characters'}
+ let(:message) { 'Glob characters' }
before do
create_file_in_repo(project, 'master', 'master', filename, 'Test file', commit_message: message)
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index 259a82498b9..22fba3af1c9 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -365,7 +365,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
end
context 'when logging in via OAuth' do
- let(:user) { create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: 'saml')}
+ let(:user) { create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: 'saml') }
let(:mock_saml_response) do
File.read('spec/fixtures/authentication/saml_response.xml')
end
diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb
index d8818d46836..aeca7b09a88 100644
--- a/spec/lib/google_api/cloud_platform/client_spec.rb
+++ b/spec/lib/google_api/cloud_platform/client_spec.rb
@@ -10,6 +10,25 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
let(:gcp_project_id) { String('gcp_proj_id') }
let(:operation) { true }
let(:database_instance) { Google::Apis::SqladminV1beta4::DatabaseInstance.new(state: 'RUNNABLE') }
+ let(:instance_name) { 'mock-instance-name' }
+ let(:root_password) { 'mock-root-password' }
+ let(:database_version) { 'mock-database-version' }
+ let(:region) { 'mock-region' }
+ let(:tier) { 'mock-tier' }
+
+ let(:database_list) do
+ Google::Apis::SqladminV1beta4::ListDatabasesResponse.new(items: [
+ Google::Apis::SqladminV1beta4::Database.new(name: 'db_01', instance: database_instance),
+ Google::Apis::SqladminV1beta4::Database.new(name: 'db_02', instance: database_instance)
+ ])
+ end
+
+ let(:user_list) do
+ Google::Apis::SqladminV1beta4::ListUsersResponse.new(items: [
+ Google::Apis::SqladminV1beta4::User.new(name: 'user_01', instance: database_instance),
+ Google::Apis::SqladminV1beta4::User.new(name: 'user_02', instance: database_instance)
+ ])
+ end
describe '.session_key_for_redirect_uri' do
let(:state) { 'random_string' }
@@ -342,6 +361,42 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
end
end
+ describe '#enable_cloud_sql_admin' do
+ subject { client.enable_cloud_sql_admin(gcp_project_id) }
+
+ it 'calls Google Api ServiceUsageService' do
+ expect_any_instance_of(Google::Apis::ServiceusageV1::ServiceUsageService)
+ .to receive(:enable_service)
+ .with("projects/#{gcp_project_id}/services/sqladmin.googleapis.com")
+ .and_return(operation)
+ is_expected.to eq(operation)
+ end
+ end
+
+ describe '#enable_compute' do
+ subject { client.enable_compute(gcp_project_id) }
+
+ it 'calls Google Api ServiceUsageService' do
+ expect_any_instance_of(Google::Apis::ServiceusageV1::ServiceUsageService)
+ .to receive(:enable_service)
+ .with("projects/#{gcp_project_id}/services/compute.googleapis.com")
+ .and_return(operation)
+ is_expected.to eq(operation)
+ end
+ end
+
+ describe '#enable_service_networking' do
+ subject { client.enable_service_networking(gcp_project_id) }
+
+ it 'calls Google Api ServiceUsageService' do
+ expect_any_instance_of(Google::Apis::ServiceusageV1::ServiceUsageService)
+ .to receive(:enable_service)
+ .with("projects/#{gcp_project_id}/services/servicenetworking.googleapis.com")
+ .and_return(operation)
+ is_expected.to eq(operation)
+ end
+ end
+
describe '#revoke_authorizations' do
subject { client.revoke_authorizations }
@@ -393,4 +448,57 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
is_expected.to eq(database_instance)
end
end
+
+ describe '#list_cloudsql_databases' do
+ subject { client.list_cloudsql_databases(:gcp_project_id, :instance_name) }
+
+ it 'calls Google Api SQLAdminService#list_databases' do
+ expect_any_instance_of(Google::Apis::SqladminV1beta4::SQLAdminService)
+ .to receive(:list_databases)
+ .with(any_args)
+ .and_return(database_list)
+ is_expected.to eq(database_list)
+ end
+ end
+
+ describe '#list_cloudsql_users' do
+ subject { client.list_cloudsql_users(:gcp_project_id, :instance_name) }
+
+ it 'calls Google Api SQLAdminService#list_users' do
+ expect_any_instance_of(Google::Apis::SqladminV1beta4::SQLAdminService)
+ .to receive(:list_users)
+ .with(any_args)
+ .and_return(user_list)
+ is_expected.to eq(user_list)
+ end
+ end
+
+ describe '#create_cloudsql_instance' do
+ subject do
+ client.create_cloudsql_instance(
+ gcp_project_id,
+ instance_name,
+ root_password,
+ database_version,
+ region,
+ tier
+ )
+ end
+
+ it 'calls Google Api SQLAdminService#insert_instance' do
+ expect_any_instance_of(Google::Apis::SqladminV1beta4::SQLAdminService)
+ .to receive(:insert_instance)
+ .with(gcp_project_id,
+ having_attributes(
+ class: ::Google::Apis::SqladminV1beta4::DatabaseInstance,
+ name: instance_name,
+ root_password: root_password,
+ database_version: database_version,
+ region: region,
+ settings: instance_of(Google::Apis::SqladminV1beta4::Settings)
+ ))
+ .and_return(operation)
+ is_expected.to eq(operation)
+ end
+ end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index b317fd8833b..c1a740b103d 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1518,8 +1518,8 @@ RSpec.describe Ci::Build do
end
end
- describe '#environment_deployment_tier' do
- subject { build.environment_deployment_tier }
+ describe '#environment_tier_from_options' do
+ subject { build.environment_tier_from_options }
let(:build) { described_class.new(options: options) }
let(:options) { { environment: { deployment_tier: 'production' } } }
@@ -1533,6 +1533,30 @@ RSpec.describe Ci::Build do
end
end
+ describe '#environment_tier' do
+ subject { build.environment_tier }
+
+ let(:options) { { environment: { deployment_tier: 'production' } } }
+ let!(:environment) { create(:environment, name: 'production', tier: 'development', project: project) }
+ let(:build) { described_class.new(options: options, environment: 'production', project: project) }
+
+ it { is_expected.to eq('production') }
+
+ context 'when options does not include deployment_tier' do
+ let(:options) { { environment: { name: 'production' } } }
+
+ it 'uses tier from environment' do
+ is_expected.to eq('development')
+ end
+
+ context 'when persisted environment is absent' do
+ let(:environment) { nil }
+
+ it { is_expected.to be_nil }
+ end
+ end
+ end
+
describe 'environment' do
describe '#has_environment?' do
subject { build.has_environment? }
@@ -2921,7 +2945,7 @@ RSpec.describe Ci::Build do
let(:expected_variables) do
predefined_variables.map { |variable| variable.fetch(:key) } +
%w[YAML_VARIABLE CI_ENVIRONMENT_NAME CI_ENVIRONMENT_SLUG
- CI_ENVIRONMENT_TIER CI_ENVIRONMENT_ACTION CI_ENVIRONMENT_URL]
+ CI_ENVIRONMENT_ACTION CI_ENVIRONMENT_TIER CI_ENVIRONMENT_URL]
end
before do
@@ -3088,6 +3112,16 @@ RSpec.describe Ci::Build do
end
end
+ context 'when environment_tier is updated in options' do
+ before do
+ build.update!(options: { environment: { name: 'production', deployment_tier: 'development' } })
+ end
+
+ it 'uses tier from options' do
+ is_expected.to include({ key: 'CI_ENVIRONMENT_TIER', value: 'development', public: true, masked: false })
+ end
+ end
+
context 'when project has an environment specific variable' do
let(:environment_specific_variable) do
{ key: 'MY_STAGING_ONLY_VARIABLE', value: 'environment_specific_variable', public: false, masked: false }
diff --git a/spec/services/google_cloud/create_cloudsql_instance_service_spec.rb b/spec/services/google_cloud/create_cloudsql_instance_service_spec.rb
new file mode 100644
index 00000000000..cd0dd75e576
--- /dev/null
+++ b/spec/services/google_cloud/create_cloudsql_instance_service_spec.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GoogleCloud::CreateCloudsqlInstanceService do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:gcp_project_id) { 'gcp_project_120' }
+ let(:environment_name) { 'test_env_42' }
+ let(:database_version) { 'POSTGRES_8000' }
+ let(:tier) { 'REIT_TIER' }
+ let(:service) do
+ described_class.new(project, user, {
+ gcp_project_id: gcp_project_id,
+ environment_name: environment_name,
+ database_version: database_version,
+ tier: tier
+ })
+ end
+
+ describe '#execute' do
+ before do
+ allow_next_instance_of(::Ci::VariablesFinder) do |variable_finder|
+ allow(variable_finder).to receive(:execute).and_return([])
+ end
+ end
+
+ it 'triggers creation of a cloudsql instance' do
+ expect_next_instance_of(GoogleApi::CloudPlatform::Client) do |client|
+ expected_instance_name = "gitlab-#{project.id}-postgres-8000-test-env-42"
+ expect(client).to receive(:create_cloudsql_instance)
+ .with(gcp_project_id,
+ expected_instance_name,
+ String,
+ database_version,
+ 'us-east1',
+ tier)
+ end
+
+ result = service.execute
+ expect(result[:status]).to be(:success)
+ end
+
+ it 'triggers worker to manage cloudsql instance creation operation results' do
+ expect_next_instance_of(GoogleApi::CloudPlatform::Client) do |client|
+ expect(client).to receive(:create_cloudsql_instance)
+ end
+
+ expect(GoogleCloud::CreateCloudsqlInstanceWorker).to receive(:perform_in)
+
+ result = service.execute
+ expect(result[:status]).to be(:success)
+ end
+
+ context 'when google APIs fail' do
+ it 'returns error' do
+ expect_next_instance_of(GoogleApi::CloudPlatform::Client) do |client|
+ expect(client).to receive(:create_cloudsql_instance).and_raise(Google::Apis::Error.new('mock-error'))
+ end
+
+ result = service.execute
+ expect(result[:status]).to eq(:error)
+ end
+ end
+
+ context 'when project has GCP_REGION defined' do
+ let(:gcp_region) { instance_double(::Ci::Variable, key: 'GCP_REGION', value: 'user-defined-region') }
+
+ before do
+ allow_next_instance_of(::Ci::VariablesFinder) do |variable_finder|
+ allow(variable_finder).to receive(:execute).and_return([gcp_region])
+ end
+ end
+
+ it 'uses defined region' do
+ expect_next_instance_of(GoogleApi::CloudPlatform::Client) do |client|
+ expect(client).to receive(:create_cloudsql_instance)
+ .with(gcp_project_id,
+ String,
+ String,
+ database_version,
+ 'user-defined-region',
+ tier)
+ end
+
+ service.execute
+ end
+ end
+ end
+end
diff --git a/spec/services/google_cloud/enable_cloudsql_service_spec.rb b/spec/services/google_cloud/enable_cloudsql_service_spec.rb
new file mode 100644
index 00000000000..e54e5a8d446
--- /dev/null
+++ b/spec/services/google_cloud/enable_cloudsql_service_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GoogleCloud::EnableCloudsqlService do
+ let_it_be(:project) { create(:project) }
+
+ subject(:result) { described_class.new(project).execute }
+
+ context 'when a project does not have any GCP_PROJECT_IDs configured' do
+ it 'returns error' do
+ message = 'No GCP projects found. Configure a service account or GCP_PROJECT_ID CI variable.'
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:message]).to eq(message)
+ end
+ end
+
+ context 'when a project has GCP_PROJECT_IDs configured' do
+ before do
+ project.variables.build(environment_scope: 'production', key: 'GCP_PROJECT_ID', value: 'prj-prod')
+ project.variables.build(environment_scope: 'staging', key: 'GCP_PROJECT_ID', value: 'prj-staging')
+ project.save!
+ end
+
+ it 'enables cloudsql, compute and service networking Google APIs', :aggregate_failures do
+ expect_next_instance_of(GoogleApi::CloudPlatform::Client) do |instance|
+ expect(instance).to receive(:enable_cloud_sql_admin).with('prj-prod')
+ expect(instance).to receive(:enable_compute).with('prj-prod')
+ expect(instance).to receive(:enable_service_networking).with('prj-prod')
+ expect(instance).to receive(:enable_cloud_sql_admin).with('prj-staging')
+ expect(instance).to receive(:enable_compute).with('prj-staging')
+ expect(instance).to receive(:enable_service_networking).with('prj-staging')
+ end
+
+ expect(result[:status]).to eq(:success)
+ end
+ end
+end
diff --git a/spec/services/google_cloud/get_cloudsql_instances_service_spec.rb b/spec/services/google_cloud/get_cloudsql_instances_service_spec.rb
new file mode 100644
index 00000000000..4587a5077c0
--- /dev/null
+++ b/spec/services/google_cloud/get_cloudsql_instances_service_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GoogleCloud::GetCloudsqlInstancesService do
+ let(:service) { described_class.new(project) }
+ let(:project) { create(:project) }
+
+ context 'when project has no registered cloud sql instances' do
+ it 'result is empty' do
+ expect(service.execute.length).to eq(0)
+ end
+ end
+
+ context 'when project has registered cloud sql instance' do
+ before do
+ keys = %w[
+ GCP_PROJECT_ID
+ GCP_CLOUDSQL_INSTANCE_NAME
+ GCP_CLOUDSQL_CONNECTION_NAME
+ GCP_CLOUDSQL_PRIMARY_IP_ADDRESS
+ GCP_CLOUDSQL_VERSION
+ GCP_CLOUDSQL_DATABASE_NAME
+ GCP_CLOUDSQL_DATABASE_USER
+ GCP_CLOUDSQL_DATABASE_PASS
+ ]
+
+ envs = %w[
+ *
+ STG
+ PRD
+ ]
+
+ keys.each do |key|
+ envs.each do |env|
+ project.variables.build(protected: false, environment_scope: env, key: key, value: "value-#{key}-#{env}")
+ end
+ end
+ end
+
+ it 'result is grouped by environment', :aggregate_failures do
+ expect(service.execute).to contain_exactly({
+ ref: '*',
+ gcp_project: 'value-GCP_PROJECT_ID-*',
+ instance_name: 'value-GCP_CLOUDSQL_INSTANCE_NAME-*',
+ version: 'value-GCP_CLOUDSQL_VERSION-*'
+ },
+ {
+ ref: 'STG',
+ gcp_project: 'value-GCP_PROJECT_ID-STG',
+ instance_name: 'value-GCP_CLOUDSQL_INSTANCE_NAME-STG',
+ version: 'value-GCP_CLOUDSQL_VERSION-STG'
+ },
+ {
+ ref: 'PRD',
+ gcp_project: 'value-GCP_PROJECT_ID-PRD',
+ instance_name: 'value-GCP_CLOUDSQL_INSTANCE_NAME-PRD',
+ version: 'value-GCP_CLOUDSQL_VERSION-PRD'
+ })
+ end
+ end
+end
diff --git a/spec/services/google_cloud/setup_cloudsql_instance_service_spec.rb b/spec/services/google_cloud/setup_cloudsql_instance_service_spec.rb
index 55553097423..e0a622bfa4a 100644
--- a/spec/services/google_cloud/setup_cloudsql_instance_service_spec.rb
+++ b/spec/services/google_cloud/setup_cloudsql_instance_service_spec.rb
@@ -5,6 +5,21 @@ require 'spec_helper'
RSpec.describe GoogleCloud::SetupCloudsqlInstanceService do
let(:random_user) { create(:user) }
let(:project) { create(:project) }
+ let(:list_databases_empty) { Google::Apis::SqladminV1beta4::ListDatabasesResponse.new(items: []) }
+ let(:list_users_empty) { Google::Apis::SqladminV1beta4::ListUsersResponse.new(items: []) }
+ let(:list_databases) do
+ Google::Apis::SqladminV1beta4::ListDatabasesResponse.new(items: [
+ Google::Apis::SqladminV1beta4::Database.new(name: 'postgres'),
+ Google::Apis::SqladminV1beta4::Database.new(name: 'main_db')
+ ])
+ end
+
+ let(:list_users) do
+ Google::Apis::SqladminV1beta4::ListUsersResponse.new(items: [
+ Google::Apis::SqladminV1beta4::User.new(name: 'postgres'),
+ Google::Apis::SqladminV1beta4::User.new(name: 'main_user')
+ ])
+ end
context 'when unauthorized user triggers worker' do
subject do
@@ -76,6 +91,8 @@ RSpec.describe GoogleCloud::SetupCloudsqlInstanceService do
allow_next_instance_of(GoogleApi::CloudPlatform::Client) do |google_api_client|
expect(google_api_client).to receive(:get_cloudsql_instance).and_return(get_instance_response_runnable)
expect(google_api_client).to receive(:create_cloudsql_database).and_return(operation_fail)
+ expect(google_api_client).to receive(:list_cloudsql_databases).and_return(list_databases_empty)
+ expect(google_api_client).to receive(:list_cloudsql_users).and_return(list_users_empty)
end
message = subject[:message]
@@ -92,6 +109,8 @@ RSpec.describe GoogleCloud::SetupCloudsqlInstanceService do
expect(google_api_client).to receive(:get_cloudsql_instance).and_return(get_instance_response_runnable)
expect(google_api_client).to receive(:create_cloudsql_database).and_return(operation_done)
expect(google_api_client).to receive(:create_cloudsql_user).and_return(operation_fail)
+ expect(google_api_client).to receive(:list_cloudsql_databases).and_return(list_databases_empty)
+ expect(google_api_client).to receive(:list_cloudsql_users).and_return(list_users_empty)
end
message = subject[:message]
@@ -102,12 +121,59 @@ RSpec.describe GoogleCloud::SetupCloudsqlInstanceService do
end
end
+ context 'when database and user already exist' do
+ it 'does not try to create a database or user' do
+ allow_next_instance_of(GoogleApi::CloudPlatform::Client) do |google_api_client|
+ expect(google_api_client).to receive(:get_cloudsql_instance).and_return(get_instance_response_runnable)
+ expect(google_api_client).not_to receive(:create_cloudsql_database)
+ expect(google_api_client).not_to receive(:create_cloudsql_user)
+ expect(google_api_client).to receive(:list_cloudsql_databases).and_return(list_databases)
+ expect(google_api_client).to receive(:list_cloudsql_users).and_return(list_users)
+ end
+
+ status = subject[:status]
+ expect(status).to eq(:success)
+ end
+ end
+
+ context 'when database already exists' do
+ it 'does not try to create a database' do
+ allow_next_instance_of(GoogleApi::CloudPlatform::Client) do |google_api_client|
+ expect(google_api_client).to receive(:get_cloudsql_instance).and_return(get_instance_response_runnable)
+ expect(google_api_client).not_to receive(:create_cloudsql_database)
+ expect(google_api_client).to receive(:create_cloudsql_user).and_return(operation_done)
+ expect(google_api_client).to receive(:list_cloudsql_databases).and_return(list_databases)
+ expect(google_api_client).to receive(:list_cloudsql_users).and_return(list_users_empty)
+ end
+
+ status = subject[:status]
+ expect(status).to eq(:success)
+ end
+ end
+
+ context 'when user already exists' do
+ it 'does not try to create a user' do
+ allow_next_instance_of(GoogleApi::CloudPlatform::Client) do |google_api_client|
+ expect(google_api_client).to receive(:get_cloudsql_instance).and_return(get_instance_response_runnable)
+ expect(google_api_client).to receive(:create_cloudsql_database).and_return(operation_done)
+ expect(google_api_client).not_to receive(:create_cloudsql_user)
+ expect(google_api_client).to receive(:list_cloudsql_databases).and_return(list_databases_empty)
+ expect(google_api_client).to receive(:list_cloudsql_users).and_return(list_users)
+ end
+
+ status = subject[:status]
+ expect(status).to eq(:success)
+ end
+ end
+
context 'when database and user creation succeeds' do
it 'stores project CI vars' do
allow_next_instance_of(GoogleApi::CloudPlatform::Client) do |google_api_client|
expect(google_api_client).to receive(:get_cloudsql_instance).and_return(get_instance_response_runnable)
expect(google_api_client).to receive(:create_cloudsql_database).and_return(operation_done)
expect(google_api_client).to receive(:create_cloudsql_user).and_return(operation_done)
+ expect(google_api_client).to receive(:list_cloudsql_databases).and_return(list_databases_empty)
+ expect(google_api_client).to receive(:list_cloudsql_users).and_return(list_users_empty)
end
subject
@@ -143,6 +209,8 @@ RSpec.describe GoogleCloud::SetupCloudsqlInstanceService do
expect(google_api_client).to receive(:get_cloudsql_instance).and_return(get_instance_response_runnable)
expect(google_api_client).to receive(:create_cloudsql_database).and_return(operation_done)
expect(google_api_client).to receive(:create_cloudsql_user).and_return(operation_done)
+ expect(google_api_client).to receive(:list_cloudsql_databases).and_return(list_databases_empty)
+ expect(google_api_client).to receive(:list_cloudsql_users).and_return(list_users_empty)
end
subject