diff --git a/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignees.vue b/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignees.vue index 2e91f1f2d72..16278cd1a84 100644 --- a/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignees.vue +++ b/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignees.vue @@ -11,7 +11,7 @@ import { GlSprintf, } from '@gitlab/ui'; import axios from '~/lib/utils/axios_utils'; -import { s__ } from '~/locale'; +import { s__, __ } from '~/locale'; import alertSetAssignees from '../../graphql/mutations/alert_set_assignees.graphql'; import SidebarAssignee from './sidebar_assignee.vue'; import { debounce } from 'lodash'; @@ -19,15 +19,18 @@ import { debounce } from 'lodash'; const DATA_REFETCH_DELAY = 250; export default { - FETCH_USERS_ERROR: s__( - 'AlertManagement|There was an error while updating the assignee(s) list. Please try again.', - ), - UPDATE_ALERT_ASSIGNEES_ERROR: s__( - 'AlertManagement|There was an error while updating the assignee(s) of the alert. Please try again.', - ), - UPDATE_ALERT_ASSIGNEES_GRAPHQL_ERROR: s__( - 'AlertManagement|This assignee cannot be assigned to this alert.', - ), + i18n: { + FETCH_USERS_ERROR: s__( + 'AlertManagement|There was an error while updating the assignee(s) list. Please try again.', + ), + UPDATE_ALERT_ASSIGNEES_ERROR: s__( + 'AlertManagement|There was an error while updating the assignee(s) of the alert. Please try again.', + ), + UPDATE_ALERT_ASSIGNEES_GRAPHQL_ERROR: s__( + 'AlertManagement|This assignee cannot be assigned to this alert.', + ), + ASSIGNEES_BLOCK: s__('AlertManagement|Alert assignee(s): %{assignees}'), + }, components: { GlIcon, GlDropdown, @@ -80,7 +83,7 @@ export default { return this.alert?.assignees?.nodes[0]?.username; }, assignedUser() { - return this.userName || s__('AlertManagement|None'); + return this.userName || __('None'); }, sortedUsers() { return this.users @@ -142,7 +145,7 @@ export default { this.users = data; }) .catch(() => { - this.$emit('alert-error', this.$options.FETCH_USERS_ERROR); + this.$emit('alert-error', this.$options.i18n.FETCH_USERS_ERROR); }) .finally(() => { this.isDropdownSearching = false; @@ -165,14 +168,14 @@ export default { if (errors[0]) { return this.$emit( 'alert-sidebar-error', - `${this.$options.UPDATE_ALERT_ASSIGNEES_GRAPHQL_ERROR} ${errors[0]}.`, + `${this.$options.i18n.UPDATE_ALERT_ASSIGNEES_GRAPHQL_ERROR} ${errors[0]}.`, ); } return this.$emit('alert-refresh'); }) .catch(() => { - this.$emit('alert-error', this.$options.UPDATE_ALERT_ASSIGNEES_ERROR); + this.$emit('alert-error', this.$options.i18n.UPDATE_ALERT_ASSIGNEES_ERROR); }) .finally(() => { this.isUpdating = false; @@ -189,7 +192,7 @@ export default { - + @@ -198,7 +201,7 @@ export default {

- {{ s__('AlertManagement|Assignee') }} + {{ __('Assignee') }} - {{ s__('AlertManagement|Edit') }} + {{ __('Edit') }}

@@ -222,7 +225,7 @@ export default { @hide="hideDropdown" > @@ -276,7 +279,7 @@ export default { assignedUser }} - {{ s__('AlertManagement|None -') }} + {{ __('None') }} - - {{ s__('AlertManagement| assign yourself') }} + {{ __('assign yourself') }}

diff --git a/app/controllers/admin/jobs_controller.rb b/app/controllers/admin/jobs_controller.rb index a3a18a115e9..7b50a45a9cd 100644 --- a/app/controllers/admin/jobs_controller.rb +++ b/app/controllers/admin/jobs_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::JobsController < Admin::ApplicationController + BUILDS_PER_PAGE = 30 + def index # We need all builds for tabs counters @all_builds = Ci::JobsFinder.new(current_user: current_user).execute @@ -8,7 +10,7 @@ class Admin::JobsController < Admin::ApplicationController @scope = params[:scope] @builds = Ci::JobsFinder.new(current_user: current_user, params: params).execute @builds = @builds.eager_load_everything - @builds = @builds.page(params[:page]).per(30) + @builds = @builds.page(params[:page]).per(BUILDS_PER_PAGE).without_count end def cancel_all diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb index 64c5fae7d96..772a5f79a4d 100644 --- a/app/helpers/dropdowns_helper.rb +++ b/app/helpers/dropdowns_helper.rb @@ -15,7 +15,10 @@ module DropdownsHelper dropdown_output = dropdown_toggle_link(toggle_text, data_attr, options) end - dropdown_output << content_tag(:div, class: "dropdown-menu dropdown-select #{options[:dropdown_class] if options.key?(:dropdown_class)}") do + content_tag_options = { class: "dropdown-menu dropdown-select #{options[:dropdown_class] if options.key?(:dropdown_class)}" } + content_tag_options[:data] = { qa_selector: "#{options[:dropdown_qa_selector]}" } if options[:dropdown_qa_selector] + + dropdown_output << content_tag(:div, content_tag_options) do output = [] if options.key?(:title) diff --git a/app/uploaders/object_storage.rb b/app/uploaders/object_storage.rb index 5297112eef8..63b6197a04d 100644 --- a/app/uploaders/object_storage.rb +++ b/app/uploaders/object_storage.rb @@ -169,6 +169,10 @@ module ObjectStorage object_store_options.connection.to_hash.deep_symbolize_keys end + def consolidated_settings? + object_store_options.fetch('consolidated_settings', false) + end + def remote_store_path object_store_options.remote_directory end @@ -196,7 +200,7 @@ module ObjectStorage id = [CarrierWave.generate_cache_id, SecureRandom.hex].join('-') upload_path = File.join(TMP_UPLOAD_PATH, id) direct_upload = ObjectStorage::DirectUpload.new(self.object_store_credentials, remote_store_path, upload_path, - has_length: has_length, maximum_size: maximum_size) + has_length: has_length, maximum_size: maximum_size, consolidated_settings: consolidated_settings?) direct_upload.to_hash.merge(ID: id) end diff --git a/app/views/projects/protected_tags/_create_protected_tag.html.haml b/app/views/projects/protected_tags/_create_protected_tag.html.haml index 7a7390efe51..d19a6401fc8 100644 --- a/app/views/projects/protected_tags/_create_protected_tag.html.haml +++ b/app/views/projects/protected_tags/_create_protected_tag.html.haml @@ -1,8 +1,9 @@ - content_for :create_access_levels do - .create_access_levels-container{ data: { qa_selector: 'access_levels_content' } } + .create_access_levels-container = dropdown_tag('Select', options: { toggle_class: 'js-allowed-to-create wide', dropdown_class: 'dropdown-menu-selectable capitalize-header', + dropdown_qa_selector: 'access_levels_content', data: { field_name: 'protected_tag[create_access_levels_attributes][0][access_level]', input_id: 'create_access_levels_attributes', qa_selector: 'access_levels_dropdown' }}) = render 'projects/protected_tags/shared/create_protected_tag' diff --git a/changelogs/unreleased/221184-rolling-28-day-deployments-metrics.yml b/changelogs/unreleased/221184-rolling-28-day-deployments-metrics.yml new file mode 100644 index 00000000000..772b40854a3 --- /dev/null +++ b/changelogs/unreleased/221184-rolling-28-day-deployments-metrics.yml @@ -0,0 +1,5 @@ +--- +title: Rolling 28 day time period counter for deployments +merge_request: 35493 +author: +type: added diff --git a/changelogs/unreleased/lm-resolve-timeout.yml b/changelogs/unreleased/lm-resolve-timeout.yml new file mode 100644 index 00000000000..d4a34db9ea9 --- /dev/null +++ b/changelogs/unreleased/lm-resolve-timeout.yml @@ -0,0 +1,5 @@ +--- +title: Resolve timeout in admin/jobs +merge_request: 35385 +author: +type: fixed diff --git a/changelogs/unreleased/sh-enable-workhorse-s3-client-consolidated.yml b/changelogs/unreleased/sh-enable-workhorse-s3-client-consolidated.yml new file mode 100644 index 00000000000..3289469af5f --- /dev/null +++ b/changelogs/unreleased/sh-enable-workhorse-s3-client-consolidated.yml @@ -0,0 +1,5 @@ +--- +title: Enable S3 Workhorse client if consolidated object settings used +merge_request: 35480 +author: +type: added diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index c6b4bcc36d8..dbbc946eaa7 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -210,7 +210,6 @@ production: &base ## within the types (e.g. artifacts, lfs, etc.). # object_store: # enabled: false - # remote_directory: artifacts # The bucket name # proxy_download: false # Passthrough all downloads via GitLab instead of using Redirects to Object Storage # connection: # provider: AWS # Only AWS supported at the moment diff --git a/config/object_store_settings.rb b/config/object_store_settings.rb index 0cd8fc98fb2..d8e1939a346 100644 --- a/config/object_store_settings.rb +++ b/config/object_store_settings.rb @@ -109,6 +109,7 @@ class ObjectStoreSettings # Map bucket (external name) -> remote_directory (internal representation) target_config['remote_directory'] = target_config.delete('bucket') + target_config['consolidated_settings'] = true section['object_store'] = target_config end end @@ -120,7 +121,7 @@ class ObjectStoreSettings # 2. The legacy settings are not defined def use_consolidated_settings? return false unless settings.dig('object_store', 'enabled') - return false unless settings.dig('object_store', 'connection') + return false unless settings.dig('object_store', 'connection').present? SUPPORTED_TYPES.each do |store| # to_h is needed because something strange happens to @@ -135,7 +136,8 @@ class ObjectStoreSettings next unless section return false if section.dig('object_store', 'enabled') - return false if section.dig('object_store', 'connection') + # Omnibus defaults to an empty hash + return false if section.dig('object_store', 'connection').present? end true diff --git a/doc/development/telemetry/usage_ping.md b/doc/development/telemetry/usage_ping.md index a31a6ddb3db..c0bc986379c 100644 --- a/doc/development/telemetry/usage_ping.md +++ b/doc/development/telemetry/usage_ping.md @@ -422,9 +422,12 @@ appear to be associated to any of the services running, since they all appear to | `auto_devops_disabled` | `counts` | `configure` | | | Projects with Auto DevOps template disabled | | `deploy_keys` | `counts` | | | | | | `deployments` | `counts` | `release` | | | Total deployments | +| `deployments` | `counts_monthly` | `release` | | | Total deployments last 28 days | | `dast_jobs` | `counts` | | | | | | `successful_deployments` | `counts` | `release` | | | Total successful deployments | +| `successful_deployments` | `counts_monthly` | `release` | | | Total successful deployments last 28 days | | `failed_deployments` | `counts` | `release` | | | Total failed deployments | +| `failed_deployments` | `counts_monthly` | `release` | | | Total failed deployments last 28 days | | `environments` | `counts` | `release` | | | Total available and stopped environments | | `clusters` | `counts` | `configure` | | | Total GitLab Managed clusters both enabled and disabled | | `clusters_enabled` | `counts` | `configure` | | | Total GitLab Managed clusters currently enabled | diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb index b1953aab4fd..073592797f0 100644 --- a/lib/gitlab/usage_data.rb +++ b/lib/gitlab/usage_data.rb @@ -170,6 +170,9 @@ module Gitlab def system_usage_data_monthly { counts_monthly: { + deployments: count(Deployment.where(last_28_days_time_period)), + successful_deployments: count(Deployment.success.where(last_28_days_time_period)), + failed_deployments: count(Deployment.failed.where(last_28_days_time_period)), personal_snippets: count(PersonalSnippet.where(last_28_days_time_period)), project_snippets: count(ProjectSnippet.where(last_28_days_time_period)) }.tap do |data| diff --git a/lib/gitlab/user_access.rb b/lib/gitlab/user_access.rb index 5e0a4faeba8..1551548d9b4 100644 --- a/lib/gitlab/user_access.rb +++ b/lib/gitlab/user_access.rb @@ -92,12 +92,6 @@ module Gitlab end end - def can_read_project? - return false unless can_access_git? - - user.can?(:read_project, project) - end - private def permission_cache diff --git a/lib/object_storage/direct_upload.rb b/lib/object_storage/direct_upload.rb index f973114e035..76f92f62e9c 100644 --- a/lib/object_storage/direct_upload.rb +++ b/lib/object_storage/direct_upload.rb @@ -23,9 +23,9 @@ module ObjectStorage MINIMUM_MULTIPART_SIZE = 5.megabytes attr_reader :credentials, :bucket_name, :object_name - attr_reader :has_length, :maximum_size + attr_reader :has_length, :maximum_size, :consolidated_settings - def initialize(credentials, bucket_name, object_name, has_length:, maximum_size: nil) + def initialize(credentials, bucket_name, object_name, has_length:, maximum_size: nil, consolidated_settings: false) unless has_length raise ArgumentError, 'maximum_size has to be specified if length is unknown' unless maximum_size end @@ -35,6 +35,7 @@ module ObjectStorage @object_name = object_name @has_length = has_length @maximum_size = maximum_size + @consolidated_settings = consolidated_settings end def to_hash @@ -80,10 +81,12 @@ module ObjectStorage end def use_workhorse_s3_client? - Feature.enabled?(:use_workhorse_s3_client, default_enabled: true) && - credentials.fetch(:use_iam_profile, false) && - # The Golang AWS SDK does not support V2 signatures - credentials.fetch(:aws_signature_version, 4).to_i >= 4 + return false unless Feature.enabled?(:use_workhorse_s3_client, default_enabled: true) + return false unless credentials.fetch(:use_iam_profile, false) || consolidated_settings + # The Golang AWS SDK does not support V2 signatures + return false unless credentials.fetch(:aws_signature_version, 4).to_i >= 4 + + true end def provider diff --git a/locale/gitlab.pot b/locale/gitlab.pot index d8bbaa33b40..e2891e57cc5 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -1905,9 +1905,6 @@ msgid_plural "Alerts" msgstr[0] "" msgstr[1] "" -msgid "AlertManagement| assign yourself" -msgstr "" - msgid "AlertManagement|Acknowledged" msgstr "" @@ -1932,15 +1929,9 @@ msgstr "" msgid "AlertManagement|All alerts" msgstr "" -msgid "AlertManagement|Assign To" -msgstr "" - msgid "AlertManagement|Assign status" msgstr "" -msgid "AlertManagement|Assignee" -msgstr "" - msgid "AlertManagement|Assignees" msgstr "" @@ -1980,9 +1971,6 @@ msgstr "" msgid "AlertManagement|More information" msgstr "" -msgid "AlertManagement|No Matching Results" -msgstr "" - msgid "AlertManagement|No alert data to display." msgstr "" @@ -1995,9 +1983,6 @@ msgstr "" msgid "AlertManagement|None" msgstr "" -msgid "AlertManagement|None -" -msgstr "" - msgid "AlertManagement|Open" msgstr "" @@ -3077,6 +3062,9 @@ msgstr "" msgid "Assign Iteration" msgstr "" +msgid "Assign To" +msgstr "" + msgid "Assign custom color like #FF0000" msgstr "" @@ -15193,6 +15181,9 @@ msgstr "" msgid "No Epic" msgstr "" +msgid "No Matching Results" +msgstr "" + msgid "No Scopes" msgstr "" @@ -24548,6 +24539,9 @@ msgstr "" msgid "Unassign from commenting user" msgstr "" +msgid "Unassigned" +msgstr "" + msgid "Unblock" msgstr "" diff --git a/qa/qa/page/project/settings/protected_tags.rb b/qa/qa/page/project/settings/protected_tags.rb index 0b9765dd9ea..bf8f349cfd5 100644 --- a/qa/qa/page/project/settings/protected_tags.rb +++ b/qa/qa/page/project/settings/protected_tags.rb @@ -26,6 +26,8 @@ module QA end def choose_access_level_role(role) + return if find_element(:access_levels_dropdown).text == role + click_element :access_levels_dropdown within_element(:access_levels_content) do click_on role diff --git a/spec/controllers/admin/jobs_controller_spec.rb b/spec/controllers/admin/jobs_controller_spec.rb new file mode 100644 index 00000000000..2d1482f40d4 --- /dev/null +++ b/spec/controllers/admin/jobs_controller_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Admin::JobsController do + describe 'GET #index' do + context 'with an authenticated admin user' do + it 'paginates builds without a total count', :aggregate_failures do + stub_const("Admin::JobsController::BUILDS_PER_PAGE", 1) + + sign_in(create(:admin)) + create_list(:ci_build, 2) + + get :index + + expect(response).to have_gitlab_http_status(:ok) + expect(assigns(:builds)).to be_a(Kaminari::PaginatableWithoutCount) + expect(assigns(:builds).count).to be(1) + end + end + + context 'without admin access' do + it 'returns `not_found`' do + sign_in(create(:user)) + + get :index + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end +end diff --git a/spec/factories/deployments.rb b/spec/factories/deployments.rb index 42046464213..2aab9764560 100644 --- a/spec/factories/deployments.rb +++ b/spec/factories/deployments.rb @@ -7,7 +7,7 @@ FactoryBot.define do tag { false } user { nil } project { nil } - deployable { association :ci_build, environment: environment.name, project: environment.project } + deployable { association :ci_build, environment: environment.name, pipeline: association(:ci_pipeline, project: environment.project) } environment factory: :environment after(:build) do |deployment, evaluator| diff --git a/spec/factories/usage_data.rb b/spec/factories/usage_data.rb index dcc0b6acb0c..92d6e8d3062 100644 --- a/spec/factories/usage_data.rb +++ b/spec/factories/usage_data.rb @@ -5,7 +5,8 @@ FactoryBot.define do skip_create # non-model factories (i.e. without #save) initialize_with do - projects = create_list(:project, 4) + projects = create_list(:project, 3) + projects << create(:project, :repository) create(:board, project: projects[0]) create(:jira_service, project: projects[0]) create(:jira_service, :without_properties_callback, project: projects[1]) @@ -91,7 +92,11 @@ FactoryBot.define do ProjectFeature.first.update_attribute('repository_access_level', 0) # Create fresh & a month (28-days SMAU) old data + env = create(:environment, project: projects[3]) [2, 29].each do |n| + deployment_options = { created_at: n.days.ago, project: env.project, environment: env } + create(:deployment, :failed, deployment_options) + create(:deployment, :success, deployment_options) create_list(:project_snippet, 2, project: projects[0], created_at: n.days.ago) create(:personal_snippet, created_at: n.days.ago) end diff --git a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb index b81c0e49538..e7eb4bb41cc 100644 --- a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb +++ b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb @@ -155,7 +155,7 @@ RSpec.describe 'User comments on a diff', :js do end end - it 'can apply multiple suggestions as a batch' do + it 'can apply multiple suggestions as a batch', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/224100' do files.each_with_index do |file, index| page.within("[id='#{file[:hash]}']") do find("button[title='Show full file']").click diff --git a/spec/helpers/notify_helper_spec.rb b/spec/helpers/notify_helper_spec.rb index 5b2a06b11e9..9c9d745cb53 100644 --- a/spec/helpers/notify_helper_spec.rb +++ b/spec/helpers/notify_helper_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe NotifyHelper do +RSpec.describe NotifyHelper do include ActionView::Helpers::UrlHelper describe 'merge_request_reference_link' do diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 77a2c824fcb..149db85a9b2 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -130,7 +130,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do expect(subject[:counts_monthly]).to be_an(Hash) end - it 'gathers projects data correctly' do + it 'gathers usage counts correctly' do count_data = subject[:counts] expect(count_data[:projects]).to eq(4) @@ -188,6 +188,9 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do expect(count_data[:clusters_applications_jupyter]).to eq(1) expect(count_data[:clusters_management_project]).to eq(1) + expect(count_data[:deployments]).to eq(4) + expect(count_data[:successful_deployments]).to eq(2) + expect(count_data[:failed_deployments]).to eq(2) expect(count_data[:snippets]).to eq(6) expect(count_data[:personal_snippets]).to eq(2) expect(count_data[:project_snippets]).to eq(4) @@ -274,9 +277,12 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do subject { described_class.system_usage_data_monthly } - it 'gathers projects data correctly' do + it 'gathers monthly usage counts correctly' do counts_monthly = subject[:counts_monthly] + expect(counts_monthly[:deployments]).to eq(2) + expect(counts_monthly[:successful_deployments]).to eq(1) + expect(counts_monthly[:failed_deployments]).to eq(1) expect(counts_monthly[:snippets]).to eq(3) expect(counts_monthly[:personal_snippets]).to eq(1) expect(counts_monthly[:project_snippets]).to eq(2) diff --git a/spec/lib/object_storage/direct_upload_spec.rb b/spec/lib/object_storage/direct_upload_spec.rb index e7d8b1de34e..1c1455e2456 100644 --- a/spec/lib/object_storage/direct_upload_spec.rb +++ b/spec/lib/object_storage/direct_upload_spec.rb @@ -6,6 +6,7 @@ RSpec.describe ObjectStorage::DirectUpload do let(:region) { 'us-east-1' } let(:path_style) { false } let(:use_iam_profile) { false } + let(:consolidated_settings) { false } let(:credentials) do { provider: 'AWS', @@ -23,7 +24,7 @@ RSpec.describe ObjectStorage::DirectUpload do let(:object_name) { 'tmp/uploads/my-file' } let(:maximum_size) { 1.gigabyte } - let(:direct_upload) { described_class.new(credentials, bucket_name, object_name, has_length: has_length, maximum_size: maximum_size) } + let(:direct_upload) { described_class.new(credentials, bucket_name, object_name, has_length: has_length, maximum_size: maximum_size, consolidated_settings: consolidated_settings) } before do Fog.unmock! @@ -141,6 +142,14 @@ RSpec.describe ObjectStorage::DirectUpload do expect(subject[:UseWorkhorseClient]).to eq(use_iam_profile) end end + + context 'when consolidated settings are used' do + let(:consolidated_settings) { true } + + it 'enables the Workhorse client' do + expect(subject[:UseWorkhorseClient]).to be true + end + end end shared_examples 'a valid Google upload' do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index c0712755396..c2c8b98df25 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -214,7 +214,7 @@ RSpec.describe User do describe 'validations' do describe 'password' do - let!(:user) { create(:user) } + let!(:user) { build_stubbed(:user) } before do allow(Devise).to receive(:password_length).and_return(8..128)