Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
a16398e10f
commit
a5c89610f3
71 changed files with 725 additions and 202 deletions
|
@ -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'
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
23
app/services/google_cloud/enable_cloudsql_service.rb
Normal file
23
app/services/google_cloud/enable_cloudsql_service.rb
Normal file
|
@ -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
|
18
app/services/google_cloud/get_cloudsql_instances_service.rb
Normal file
18
app/services/google_cloud/get_cloudsql_instances_service.rb
Normal file
|
@ -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
|
|
@ -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}")
|
||||
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)
|
||||
|
||||
existing_database = list_database_response.items.find { |database| database.name == database_name }
|
||||
existing_user = list_user_response.items.find { |user| user.name == username }
|
||||
|
||||
if existing_database && existing_user
|
||||
save_database_ci_vars
|
||||
save_user_ci_vars(existing_user)
|
||||
return success
|
||||
end
|
||||
|
||||
user_response = google_api_client.create_cloudsql_user(gcp_project_id, instance_name, username, password)
|
||||
database_response = execute_database_setup(existing_database)
|
||||
return database_response if database_response[:status] == :error
|
||||
|
||||
if user_response.status != OPERATION_STATE_DONE
|
||||
return error("User creation failed: #{user_response.to_json}")
|
||||
end
|
||||
save_database_ci_vars
|
||||
|
||||
primary_ip_address = get_instance_response.ip_addresses.first.ip_address
|
||||
connection_name = get_instance_response.connection_name
|
||||
user_response = execute_user_setup(existing_user)
|
||||
return user_response if user_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_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
|
||||
|
|
|
@ -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 } })
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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' } }
|
||||
|
|
14
doc/.vale/gitlab/MultiLineLinks.yml
Normal file
14
doc/.vale/gitlab/MultiLineLinks.yml
Normal file
|
@ -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[^\)]*\)'
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def deployment_tier
|
||||
job.environment_deployment_tier
|
||||
job.environment_tier_from_options
|
||||
end
|
||||
|
||||
def expanded_environment_name
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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') }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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) }
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
39
spec/services/google_cloud/enable_cloudsql_service_spec.rb
Normal file
39
spec/services/google_cloud/enable_cloudsql_service_spec.rb
Normal file
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue