Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-06-23 21:09:26 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-06-23 21:09:26 +0300
commit35272ed523e03fd0f2a77e6951eb4017cdb224ff (patch)
tree0491e10e68a7932168c9ddadfb0de26780de4ad3
parentcd81aadde2782c9fbb0ad69d5471bad792345120 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/issue_templates/Broken Master - Flaky.md25
-rw-r--r--.gitlab/issue_templates/Broken Master - Non-flaky.md24
-rw-r--r--app/assets/javascripts/access_tokens/components/access_token_table_app.vue5
-rw-r--r--app/controllers/import/bitbucket_server_controller.rb7
-rw-r--r--app/models/ci/pipeline.rb4
-rw-r--r--app/models/ci/pipeline_artifact.rb17
-rw-r--r--app/services/ci/pipeline_artifacts/coverage_report_service.rb35
-rw-r--r--app/views/import/bitbucket_server/new.html.haml2
-rw-r--r--app/views/import/bitbucket_server/status.html.haml2
-rw-r--r--app/views/projects/_import_project_pane.html.haml2
-rw-r--r--app/workers/ci/pipeline_artifacts/coverage_report_worker.rb12
-rw-r--r--config/metrics/counts_all/20210916200931_clusters_integrations_elastic_stack.yml4
-rw-r--r--doc/development/deprecation_guidelines/img/deprecation_removal_process.pngbin53890 -> 27632 bytes
-rw-r--r--doc/user/analytics/img/time_to_restore_service_charts_v15_1.pngbin86137 -> 30457 bytes
-rw-r--r--doc/user/application_security/policies/scan-execution-policies.md10
-rw-r--r--doc/user/clusters/agent/troubleshooting.md22
-rw-r--r--doc/user/infrastructure/clusters/connect/img/variables_civo.pngbin74744 -> 22629 bytes
-rw-r--r--doc/user/project/img/time_tracking_report_v15_1.pngbin31669 -> 14862 bytes
-rw-r--r--doc/user/project/issues/img/turn_on_confidentiality_v15_1.pngbin37584 -> 16370 bytes
-rw-r--r--doc/user/search/img/basic_search_results_v15_1.pngbin52267 -> 17833 bytes
-rw-r--r--doc/user/search/img/code_search_git_blame_v15_1.pngbin20655 -> 7461 bytes
-rw-r--r--lib/gitlab/usage_data.rb1
-rw-r--r--spec/controllers/import/bitbucket_server_controller_spec.rb17
-rw-r--r--spec/factories/usage_data.rb1
-rw-r--r--spec/frontend/access_tokens/components/access_token_table_app_spec.js15
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb1
-rw-r--r--spec/models/ci/pipeline_artifact_spec.rb74
-rw-r--r--spec/models/ci/pipeline_spec.rb30
-rw-r--r--spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb52
-rw-r--r--spec/support/helpers/usage_data_helpers.rb1
-rw-r--r--spec/workers/ci/pipeline_artifacts/coverage_report_worker_spec.rb57
31 files changed, 358 insertions, 62 deletions
diff --git a/.gitlab/issue_templates/Broken Master - Flaky.md b/.gitlab/issue_templates/Broken Master - Flaky.md
new file mode 100644
index 00000000000..6b56845ba8c
--- /dev/null
+++ b/.gitlab/issue_templates/Broken Master - Flaky.md
@@ -0,0 +1,25 @@
+<!---
+This issue template is for a master pipeline is failing for a flaky reason that cannot be reliably reproduced.
+
+Please read the below documentations for a workflow of triaging and resolving broken master.
+
+- https://about.gitlab.com/handbook/engineering/workflow/#triage-broken-master
+- https://gitlab.com/gitlab-org/quality/engineering-productivity/team/-/blob/main/runbooks/master-broken.md
+- https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/development/testing_guide/flaky_tests.md
+--->
+
+### Summary
+
+<!-- Link to the failing master build and add the build failure output in the below code block section. -->
+
+### Steps to reproduce
+
+<!-- If the pipeline failure is reproducible, provide steps to recreate the issue locally. Please use an ordered list. -->
+
+### Proposed Resolution
+
+<!-- Describe the proposed change to restore master stability. -->
+
+Please refer to the [Resolution guidance](https://about.gitlab.com/handbook/engineering/workflow/#resolution-of-broken-master) to learn more about resolution of broken master.
+
+/label ~"failure::flaky-test" ~"Engineering Productivity" ~"priority::2" ~"severity::2"
diff --git a/.gitlab/issue_templates/Broken Master - Non-flaky.md b/.gitlab/issue_templates/Broken Master - Non-flaky.md
new file mode 100644
index 00000000000..97a34aa759d
--- /dev/null
+++ b/.gitlab/issue_templates/Broken Master - Non-flaky.md
@@ -0,0 +1,24 @@
+<!---
+This issue template is for a master pipeline is failing for a non-flaky reason.
+
+Please read the below documentations for a workflow of triaging and resolving broken master.
+
+- https://about.gitlab.com/handbook/engineering/workflow/#triage-broken-master
+- https://gitlab.com/gitlab-org/quality/engineering-productivity/team/-/blob/main/runbooks/master-broken.md
+--->
+
+### Summary
+
+<!-- Link to the failing master build and add the build failure output in the below code block section. -->
+
+### Steps to reproduce
+
+<!-- If the pipeline failure is reproducible, provide steps to recreate the issue locally. Please use an ordered list. -->
+
+### Proposed Resolution
+
+<!-- Describe the proposed change to restore master stability. -->
+
+Please refer to the [Resolution guidance](https://about.gitlab.com/handbook/engineering/workflow/#resolution-of-broken-master) to learn more about resolution of broken master.
+
+/label ~"master:broken" ~"Engineering Productivity" ~"priority::1" ~"severity::1"
diff --git a/app/assets/javascripts/access_tokens/components/access_token_table_app.vue b/app/assets/javascripts/access_tokens/components/access_token_table_app.vue
index 944a2ef7f64..59f0e0dd17d 100644
--- a/app/assets/javascripts/access_tokens/components/access_token_table_app.vue
+++ b/app/assets/javascripts/access_tokens/components/access_token_table_app.vue
@@ -138,10 +138,9 @@ export default {
}}</span>
</template>
- <template #cell(action)="{ item: { revokePath, expiresAt } }">
+ <template #cell(action)="{ item: { revokePath } }">
<gl-button
- variant="danger"
- :category="expiresAt ? 'primary' : 'secondary'"
+ category="tertiary"
:aria-label="$options.i18n.revokeButton"
:data-confirm="modalMessage"
data-confirm-btn-variant="danger"
diff --git a/app/controllers/import/bitbucket_server_controller.rb b/app/controllers/import/bitbucket_server_controller.rb
index 00f3f0b08b2..12147196749 100644
--- a/app/controllers/import/bitbucket_server_controller.rb
+++ b/app/controllers/import/bitbucket_server_controller.rb
@@ -49,7 +49,7 @@ class Import::BitbucketServerController < Import::BaseController
session[bitbucket_server_username_key] = params[:bitbucket_server_username]
session[bitbucket_server_url_key] = params[:bitbucket_server_url]
- redirect_to status_import_bitbucket_server_path
+ redirect_to status_import_bitbucket_server_path(namespace_id: params[:namespace_id])
end
# We need to re-expose controller's internal method 'status' as action.
@@ -115,7 +115,7 @@ class Import::BitbucketServerController < Import::BaseController
unless session[bitbucket_server_url_key].present? &&
session[bitbucket_server_username_key].present? &&
session[personal_access_token_key].present?
- redirect_to new_import_bitbucket_server_path
+ redirect_to new_import_bitbucket_server_path(namespace_id: params[:namespace_id])
end
end
@@ -170,9 +170,6 @@ class Import::BitbucketServerController < Import::BaseController
}
}, status: :unprocessable_entity
end
- format.html do
- redirect_to new_import_bitbucket_server_path
- end
end
end
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 5d316906bd3..d7889407849 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -1004,6 +1004,10 @@ module Ci
object_hierarchy(project_condition: :same).base_and_descendants
end
+ def self_and_descendants_complete?
+ self_and_descendants.all?(&:complete?)
+ end
+
# Follow the parent-child relationships and return the top-level parent
def root_ancestor
return self unless child?
diff --git a/app/models/ci/pipeline_artifact.rb b/app/models/ci/pipeline_artifact.rb
index 2284a05bcc9..cdc3d69f754 100644
--- a/app/models/ci/pipeline_artifact.rb
+++ b/app/models/ci/pipeline_artifact.rb
@@ -51,6 +51,23 @@ module Ci
def find_by_file_type(file_type)
find_by(file_type: file_type)
end
+
+ def create_or_replace_for_pipeline!(pipeline:, file_type:, file:, size:)
+ transaction do
+ pipeline.pipeline_artifacts.find_by_file_type(file_type)&.destroy!
+
+ pipeline.pipeline_artifacts.create!(
+ file_type: file_type,
+ project_id: pipeline.project_id,
+ size: size,
+ file: file,
+ file_format: REPORT_TYPES[file_type],
+ expire_at: EXPIRATION_DATE.from_now
+ )
+ end
+ rescue ActiveRecord::ActiveRecordError => err
+ Gitlab::ErrorTracking.track_and_raise_exception(err, { pipeline_id: pipeline.id, file_type: file_type })
+ end
end
def present
diff --git a/app/services/ci/pipeline_artifacts/coverage_report_service.rb b/app/services/ci/pipeline_artifacts/coverage_report_service.rb
index b0acb1d5a0b..c69cb721bb4 100644
--- a/app/services/ci/pipeline_artifacts/coverage_report_service.rb
+++ b/app/services/ci/pipeline_artifacts/coverage_report_service.rb
@@ -9,17 +9,16 @@ module Ci
end
def execute
- return if pipeline.has_coverage_reports?
+ unless Feature.enabled?(:ci_child_pipeline_coverage_reports, pipeline.project) ||
+ !pipeline.has_coverage_reports?
+ return
+ end
+
return if report.empty?
- pipeline.pipeline_artifacts.create!(
- project_id: pipeline.project_id,
- file_type: :code_coverage,
- file_format: Ci::PipelineArtifact::REPORT_TYPES.fetch(:code_coverage),
- size: carrierwave_file["tempfile"].size,
- file: carrierwave_file,
- expire_at: Ci::PipelineArtifact::EXPIRATION_DATE.from_now
- )
+ Ci::PipelineArtifact.create_or_replace_for_pipeline!(**pipeline_artifact_params).tap do |pipeline_artifact|
+ Gitlab::AppLogger.info(log_params(pipeline_artifact))
+ end
end
private
@@ -32,6 +31,15 @@ module Ci
end
end
+ def pipeline_artifact_params
+ {
+ pipeline: pipeline,
+ file_type: :code_coverage,
+ file: carrierwave_file,
+ size: carrierwave_file['tempfile'].size
+ }
+ end
+
def carrierwave_file
strong_memoize(:carrier_wave_file) do
CarrierWaveStringFile.new_file(
@@ -41,6 +49,15 @@ module Ci
)
end
end
+
+ def log_params(pipeline_artifact)
+ {
+ project_id: pipeline.project_id,
+ pipeline_id: pipeline.id,
+ pipeline_artifact_id: pipeline_artifact.id,
+ message: "Created code coverage for pipeline."
+ }
+ end
end
end
end
diff --git a/app/views/import/bitbucket_server/new.html.haml b/app/views/import/bitbucket_server/new.html.haml
index 0d87cf66814..292dd9d071c 100644
--- a/app/views/import/bitbucket_server/new.html.haml
+++ b/app/views/import/bitbucket_server/new.html.haml
@@ -10,7 +10,7 @@
%p
= _('Enter in your Bitbucket Server URL and personal access token below')
-= form_tag configure_import_bitbucket_server_path, method: :post do
+= form_tag configure_import_bitbucket_server_path(namespace_id: params[:namespace_id]), method: :post do
.form-group.row
= label_tag :bitbucket_server_url, 'Bitbucket Server URL', class: 'col-form-label col-md-2'
.col-md-4
diff --git a/app/views/import/bitbucket_server/status.html.haml b/app/views/import/bitbucket_server/status.html.haml
index 05b42767668..7e0c7b3dd74 100644
--- a/app/views/import/bitbucket_server/status.html.haml
+++ b/app/views/import/bitbucket_server/status.html.haml
@@ -5,4 +5,4 @@
= sprite_icon('bitbucket', css_class: 'gl-mr-2')
= _('Import projects from Bitbucket Server')
-= render 'import/githubish_status', provider: 'bitbucket_server', paginatable: true, extra_data: { reconfigure_path: configure_import_bitbucket_server_path }
+= render 'import/githubish_status', provider: 'bitbucket_server', paginatable: true, default_namespace: @namespace, extra_data: { reconfigure_path: configure_import_bitbucket_server_path }
diff --git a/app/views/projects/_import_project_pane.html.haml b/app/views/projects/_import_project_pane.html.haml
index 034c4a7501d..9f2c7dd2559 100644
--- a/app/views/projects/_import_project_pane.html.haml
+++ b/app/views/projects/_import_project_pane.html.haml
@@ -31,7 +31,7 @@
Bitbucket Cloud
- if bitbucket_server_import_enabled?
%div
- = link_to status_import_bitbucket_server_path, class: "gl-button btn-default btn import_bitbucket js-import-project-btn", data: { platform: 'bitbucket_server', **tracking_attrs_data(track_label, 'click_button', 'bitbucket_server') } do
+ = link_to status_import_bitbucket_server_path(namespace_id: namespace_id), class: "gl-button btn-default btn import_bitbucket js-import-project-btn", data: { platform: 'bitbucket_server', **tracking_attrs_data(track_label, 'click_button', 'bitbucket_server') } do
.gl-button-icon
= sprite_icon('bitbucket')
Bitbucket Server
diff --git a/app/workers/ci/pipeline_artifacts/coverage_report_worker.rb b/app/workers/ci/pipeline_artifacts/coverage_report_worker.rb
index 8ee518e3ae6..5be40f64930 100644
--- a/app/workers/ci/pipeline_artifacts/coverage_report_worker.rb
+++ b/app/workers/ci/pipeline_artifacts/coverage_report_worker.rb
@@ -15,7 +15,17 @@ module Ci
idempotent!
def perform(pipeline_id)
- Ci::Pipeline.find_by_id(pipeline_id).try do |pipeline|
+ pipeline = Ci::Pipeline.find_by_id(pipeline_id)
+
+ return unless pipeline
+
+ if Feature.enabled?(:ci_child_pipeline_coverage_reports, pipeline.project)
+ pipeline.root_ancestor.try do |root_ancestor_pipeline|
+ next unless root_ancestor_pipeline.self_and_descendants_complete?
+
+ Ci::PipelineArtifacts::CoverageReportService.new(root_ancestor_pipeline).execute
+ end
+ else
Ci::PipelineArtifacts::CoverageReportService.new(pipeline).execute
end
end
diff --git a/config/metrics/counts_all/20210916200931_clusters_integrations_elastic_stack.yml b/config/metrics/counts_all/20210916200931_clusters_integrations_elastic_stack.yml
index 54437cfbf36..bbaba8463a0 100644
--- a/config/metrics/counts_all/20210916200931_clusters_integrations_elastic_stack.yml
+++ b/config/metrics/counts_all/20210916200931_clusters_integrations_elastic_stack.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: all
data_source: database
distribution:
@@ -19,3 +19,5 @@ tier:
- ultimate
performance_indicator_type: []
milestone: "14.4"
+milestone_removed: "15.2"
+removed_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90699"
diff --git a/doc/development/deprecation_guidelines/img/deprecation_removal_process.png b/doc/development/deprecation_guidelines/img/deprecation_removal_process.png
index 99642ebbae0..594e15861b0 100644
--- a/doc/development/deprecation_guidelines/img/deprecation_removal_process.png
+++ b/doc/development/deprecation_guidelines/img/deprecation_removal_process.png
Binary files differ
diff --git a/doc/user/analytics/img/time_to_restore_service_charts_v15_1.png b/doc/user/analytics/img/time_to_restore_service_charts_v15_1.png
index 25aac385750..fbc59f72e72 100644
--- a/doc/user/analytics/img/time_to_restore_service_charts_v15_1.png
+++ b/doc/user/analytics/img/time_to_restore_service_charts_v15_1.png
Binary files differ
diff --git a/doc/user/application_security/policies/scan-execution-policies.md b/doc/user/application_security/policies/scan-execution-policies.md
index 6c1367b3fc9..37c1cff4ce6 100644
--- a/doc/user/application_security/policies/scan-execution-policies.md
+++ b/doc/user/application_security/policies/scan-execution-policies.md
@@ -209,16 +209,10 @@ scan_execution_policy:
rules:
- type: schedule
cadence: "15 3 * * *"
- clusters:
- production-cluster:
- containers:
- - database
- resources:
- - production-application
+ agents:
+ production-agent:
namespaces:
- production-namespace
- kinds:
- - deployment
actions:
- scan: cluster_image_scanning
```
diff --git a/doc/user/clusters/agent/troubleshooting.md b/doc/user/clusters/agent/troubleshooting.md
index cc2ef48c341..1eca2eaecd5 100644
--- a/doc/user/clusters/agent/troubleshooting.md
+++ b/doc/user/clusters/agent/troubleshooting.md
@@ -207,3 +207,25 @@ to the Helm command when [installing the agent](install/#install-the-agent-in-th
```shell
kubectl create serviceaccount gitlab-agent -n gitlab-agent
```
+
+## Failed to perform vulnerability scan on workload: jobs.batch already exists
+
+```json
+{
+ "level": "error",
+ "time": "2022-06-22T21:03:04.769Z",
+ "msg": "Failed to perform vulnerability scan on workload",
+ "mod_name": "starboard_vulnerability",
+ "error": "running scan job: creating job: jobs.batch \"scan-vulnerabilityreport-b8d497769\" already exists"
+}
+```
+
+The GitLab agent performs vulnerability scans by creating a job to scan each workload. If a scan
+is interrupted, these jobs may be left behind and will need to be cleaned up before more jobs can
+be run. You can clean up these jobs by running:
+
+```shell
+kubectl delete jobs -l app.kubernetes.io/managed-by=starboard -n gitlab-agent
+```
+
+[We're working on making the cleanup of these jobs more robust.](https://gitlab.com/gitlab-org/gitlab/-/issues/362016)
diff --git a/doc/user/infrastructure/clusters/connect/img/variables_civo.png b/doc/user/infrastructure/clusters/connect/img/variables_civo.png
index 5a20478b13c..a668c3dd53c 100644
--- a/doc/user/infrastructure/clusters/connect/img/variables_civo.png
+++ b/doc/user/infrastructure/clusters/connect/img/variables_civo.png
Binary files differ
diff --git a/doc/user/project/img/time_tracking_report_v15_1.png b/doc/user/project/img/time_tracking_report_v15_1.png
index a9ddefebb2f..4eeccf8a684 100644
--- a/doc/user/project/img/time_tracking_report_v15_1.png
+++ b/doc/user/project/img/time_tracking_report_v15_1.png
Binary files differ
diff --git a/doc/user/project/issues/img/turn_on_confidentiality_v15_1.png b/doc/user/project/issues/img/turn_on_confidentiality_v15_1.png
index 24a7ad554f8..c81ac85ab13 100644
--- a/doc/user/project/issues/img/turn_on_confidentiality_v15_1.png
+++ b/doc/user/project/issues/img/turn_on_confidentiality_v15_1.png
Binary files differ
diff --git a/doc/user/search/img/basic_search_results_v15_1.png b/doc/user/search/img/basic_search_results_v15_1.png
index b85627c9b95..0de0b976d7d 100644
--- a/doc/user/search/img/basic_search_results_v15_1.png
+++ b/doc/user/search/img/basic_search_results_v15_1.png
Binary files differ
diff --git a/doc/user/search/img/code_search_git_blame_v15_1.png b/doc/user/search/img/code_search_git_blame_v15_1.png
index e61ee5993c2..426f829b186 100644
--- a/doc/user/search/img/code_search_git_blame_v15_1.png
+++ b/doc/user/search/img/code_search_git_blame_v15_1.png
Binary files differ
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index cb4cbc81b33..12dae79c60e 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -105,7 +105,6 @@ module Gitlab
clusters_platforms_gke: count(::Clusters::Cluster.gcp_installed.enabled),
clusters_platforms_user: count(::Clusters::Cluster.user_provided.enabled),
clusters_management_project: count(::Clusters::Cluster.with_management_project),
- clusters_integrations_elastic_stack: count(::Clusters::Integrations::ElasticStack.enabled),
clusters_integrations_prometheus: count(::Clusters::Integrations::Prometheus.enabled),
kubernetes_agents: count(::Clusters::Agent),
kubernetes_agents_with_token: distinct_count(::Clusters::AgentToken, :agent_id),
diff --git a/spec/controllers/import/bitbucket_server_controller_spec.rb b/spec/controllers/import/bitbucket_server_controller_spec.rb
index d5f94be65b6..ac56d3af54f 100644
--- a/spec/controllers/import/bitbucket_server_controller_spec.rb
+++ b/spec/controllers/import/bitbucket_server_controller_spec.rb
@@ -134,6 +134,15 @@ RSpec.describe Import::BitbucketServerController do
expect(response).to have_gitlab_http_status(:found)
expect(response).to redirect_to(status_import_bitbucket_server_path)
end
+
+ it 'passes namespace_id to status page if provided' do
+ namespace_id = 5
+ allow(controller).to receive(:allow_local_requests?).and_return(true)
+
+ post :configure, params: { personal_access_token: token, bitbucket_server_username: username, bitbucket_server_url: url, namespace_id: namespace_id }
+
+ expect(response).to redirect_to(status_import_bitbucket_server_path(namespace_id: namespace_id))
+ end
end
describe 'GET status' do
@@ -160,6 +169,14 @@ RSpec.describe Import::BitbucketServerController do
expect(json_response.dig("provider_repos", 0, "id")).to eq(@repo.full_name)
end
+ it 'redirects to connection form if session is missing auth data' do
+ session[:bitbucket_server_url] = nil
+
+ get :status, format: :html
+
+ expect(response).to redirect_to(new_import_bitbucket_server_path)
+ end
+
it_behaves_like 'import controller status' do
let(:repo) { @repo }
let(:repo_id) { "#{@repo.project_key}/#{@repo.slug}" }
diff --git a/spec/factories/usage_data.rb b/spec/factories/usage_data.rb
index 8a43ea64390..0e944b90d0c 100644
--- a/spec/factories/usage_data.rb
+++ b/spec/factories/usage_data.rb
@@ -83,7 +83,6 @@ FactoryBot.define do
# Cluster Integrations
create(:clusters_integrations_prometheus, cluster: gcp_cluster)
- create(:clusters_integrations_elastic_stack, cluster: gcp_cluster)
create(:grafana_integration, project: projects[0], enabled: true)
create(:grafana_integration, project: projects[1], enabled: true)
diff --git a/spec/frontend/access_tokens/components/access_token_table_app_spec.js b/spec/frontend/access_tokens/components/access_token_table_app_spec.js
index b45abe418e4..6013fa3ec39 100644
--- a/spec/frontend/access_tokens/components/access_token_table_app_spec.js
+++ b/spec/frontend/access_tokens/components/access_token_table_app_spec.js
@@ -1,4 +1,4 @@
-import { GlPagination, GlTable } from '@gitlab/ui';
+import { GlButton, GlPagination, GlTable } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import AccessTokenTableApp from '~/access_tokens/components/access_token_table_app.vue';
@@ -164,8 +164,8 @@ describe('~/access_tokens/components/access_token_table_app', () => {
expect(cells.at(3).text()).toBe(__('Never'));
expect(cells.at(4).text()).toBe(__('Never'));
expect(cells.at(5).text()).toBe('Maintainer');
- let anchor = cells.at(6).find('a');
- expect(anchor.attributes()).toMatchObject({
+ let button = cells.at(6).findComponent(GlButton);
+ expect(button.attributes()).toMatchObject({
'aria-label': __('Revoke'),
'data-qa-selector': __('revoke_button'),
href: '/-/profile/personal_access_tokens/1/revoke',
@@ -176,8 +176,7 @@ describe('~/access_tokens/components/access_token_table_app', () => {
{ accessTokenType },
),
});
-
- expect(anchor.classes()).toContain('btn-danger-secondary');
+ expect(button.props('category')).toBe('tertiary');
// Second row
expect(cells.at(7).text()).toBe('b');
@@ -186,9 +185,9 @@ describe('~/access_tokens/components/access_token_table_app', () => {
expect(cells.at(10).text()).not.toBe(__('Never'));
expect(cells.at(11).text()).toBe(__('Expired'));
expect(cells.at(12).text()).toBe('Maintainer');
- anchor = cells.at(13).find('a');
- expect(anchor.attributes('href')).toBe('/-/profile/personal_access_tokens/2/revoke');
- expect(anchor.classes()).toEqual(['btn', 'btn-danger', 'btn-md', 'gl-button', 'btn-icon']);
+ button = cells.at(13).findComponent(GlButton);
+ expect(button.attributes('href')).toBe('/-/profile/personal_access_tokens/2/revoke');
+ expect(button.props('category')).toBe('tertiary');
});
it('sorts rows alphabetically', async () => {
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 3a223dfd491..77e112edaca 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -560,7 +560,6 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
expect(count_data[:clusters_platforms_eks]).to eq(1)
expect(count_data[:clusters_platforms_gke]).to eq(1)
expect(count_data[:clusters_platforms_user]).to eq(1)
- expect(count_data[:clusters_integrations_elastic_stack]).to eq(1)
expect(count_data[:clusters_integrations_prometheus]).to eq(1)
expect(count_data[:grafana_integrated_projects]).to eq(2)
expect(count_data[:clusters_management_project]).to eq(1)
diff --git a/spec/models/ci/pipeline_artifact_spec.rb b/spec/models/ci/pipeline_artifact_spec.rb
index 801505f0231..b051f646bd4 100644
--- a/spec/models/ci/pipeline_artifact_spec.rb
+++ b/spec/models/ci/pipeline_artifact_spec.rb
@@ -196,6 +196,80 @@ RSpec.describe Ci::PipelineArtifact, type: :model do
end
end
+ describe '.create_or_replace_for_pipeline!' do
+ let_it_be(:pipeline) { create(:ci_empty_pipeline) }
+
+ let(:file_type) { :code_coverage }
+ let(:file) { CarrierWaveStringFile.new_file(file_content: 'content', filename: 'file.json', content_type: 'json') }
+ let(:size) { file['tempfile'].size }
+
+ subject do
+ Ci::PipelineArtifact.create_or_replace_for_pipeline!(
+ pipeline: pipeline,
+ file_type: file_type,
+ file: file,
+ size: size
+ )
+ end
+
+ around do |example|
+ freeze_time { example.run }
+ end
+
+ context 'when there is no existing record' do
+ it 'creates a new pipeline artifact for the given parameters' do
+ expect { subject }.to change { Ci::PipelineArtifact.count }.from(0).to(1)
+
+ expect(subject.code_coverage?).to be(true)
+ expect(subject.pipeline).to eq(pipeline)
+ expect(subject.project_id).to eq(pipeline.project_id)
+ expect(subject.file.filename).to eq(file['filename'])
+ expect(subject.size).to eq(size)
+ expect(subject.file_format).to eq(Ci::PipelineArtifact::REPORT_TYPES[file_type].to_s)
+ expect(subject.expire_at).to eq(Ci::PipelineArtifact::EXPIRATION_DATE.from_now)
+ end
+ end
+
+ context 'when there are existing records with different types' do
+ let!(:existing_artifact) do
+ create(:ci_pipeline_artifact, pipeline: pipeline, file_type: file_type, expire_at: 1.day.from_now)
+ end
+
+ let!(:other_artifact) { create(:ci_pipeline_artifact, pipeline: pipeline, file_type: :code_quality_mr_diff) }
+
+ it 'replaces the existing pipeline artifact record with the given file type' do
+ expect { subject }.not_to change { Ci::PipelineArtifact.count }
+
+ expect(subject.id).not_to eq(existing_artifact.id)
+
+ expect(subject.code_coverage?).to be(true)
+ expect(subject.pipeline).to eq(pipeline)
+ expect(subject.project_id).to eq(pipeline.project_id)
+ expect(subject.file.filename).to eq(file['filename'])
+ expect(subject.size).to eq(size)
+ expect(subject.file_format).to eq(Ci::PipelineArtifact::REPORT_TYPES[file_type].to_s)
+ expect(subject.expire_at).to eq(Ci::PipelineArtifact::EXPIRATION_DATE.from_now)
+ end
+ end
+
+ context 'when ActiveRecordError is raised' do
+ let(:pipeline) { instance_double(Ci::Pipeline, id: 1) }
+ let(:file_type) { :code_coverage }
+ let(:error) { ActiveRecord::ActiveRecordError.new('something went wrong') }
+
+ before do
+ allow(pipeline).to receive(:pipeline_artifacts).and_raise(error)
+ end
+
+ it 'tracks and raise the exception' do
+ expect(Gitlab::ErrorTracking).to receive(:track_and_raise_exception)
+ .with(error, { pipeline_id: pipeline.id, file_type: file_type }).and_call_original
+
+ expect { subject }.to raise_error(ActiveRecord::ActiveRecordError, 'something went wrong')
+ end
+ end
+ end
+
describe '#present' do
subject(:presenter) { report.present }
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 31752f300f4..b719adcdace 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -106,6 +106,18 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ describe 'state machine transitions' do
+ context 'from failed to success' do
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, :failed) }
+
+ it 'schedules CoverageReportWorker' do
+ expect(Ci::PipelineArtifacts::CoverageReportWorker).to receive(:perform_async).with(pipeline.id)
+
+ pipeline.succeed!
+ end
+ end
+ end
+
describe '#set_status' do
let(:pipeline) { build(:ci_empty_pipeline, :created) }
@@ -3760,6 +3772,24 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ describe '#self_and_descendants_complete?' do
+ let_it_be(:pipeline) { create(:ci_pipeline, :success) }
+ let_it_be(:child_pipeline) { create(:ci_pipeline, :success, child_of: pipeline) }
+ let_it_be_with_reload(:grandchild_pipeline) { create(:ci_pipeline, :success, child_of: child_pipeline) }
+
+ context 'when all pipelines in the hierarchy is complete' do
+ it { expect(pipeline.self_and_descendants_complete?).to be(true) }
+ end
+
+ context 'when a pipeline in the hierarchy is not complete' do
+ before do
+ grandchild_pipeline.update!(status: :running)
+ end
+
+ it { expect(pipeline.self_and_descendants_complete?).to be(false) }
+ end
+ end
+
describe '#builds_in_self_and_descendants' do
subject(:builds) { pipeline.builds_in_self_and_descendants }
diff --git a/spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb b/spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb
index 403afde5da3..4b85c52ebce 100644
--- a/spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb
+++ b/spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb
@@ -2,16 +2,18 @@
require 'spec_helper'
-RSpec.describe ::Ci::PipelineArtifacts::CoverageReportService do
+RSpec.describe Ci::PipelineArtifacts::CoverageReportService do
describe '#execute' do
let_it_be(:project) { create(:project, :repository) }
subject { described_class.new(pipeline).execute }
- shared_examples 'creating a pipeline coverage report' do
+ shared_examples 'creating or updating a pipeline coverage report' do
context 'when pipeline is finished' do
- it 'creates a pipeline artifact' do
- expect { subject }.to change { Ci::PipelineArtifact.count }.from(0).to(1)
+ it 'creates or updates a pipeline artifact' do
+ subject
+
+ expect(pipeline.reload.pipeline_artifacts.count).to eq(1)
end
it 'persists the default file name' do
@@ -22,7 +24,7 @@ RSpec.describe ::Ci::PipelineArtifacts::CoverageReportService do
expect(file.filename).to eq('code_coverage.json')
end
- it 'sets expire_at to 1 week' do
+ it 'sets expire_at to 1 week from now' do
freeze_time do
subject
@@ -31,13 +33,16 @@ RSpec.describe ::Ci::PipelineArtifacts::CoverageReportService do
expect(pipeline_artifact.expire_at).to eq(1.week.from_now)
end
end
- end
- context 'when pipeline artifact has already been created' do
- it 'does not raise an error and does not persist the same artifact twice' do
- expect { 2.times { described_class.new(pipeline).execute } }.not_to raise_error
+ it 'logs relevant information' do
+ expect(Gitlab::AppLogger).to receive(:info).with({
+ project_id: project.id,
+ pipeline_id: pipeline.id,
+ pipeline_artifact_id: kind_of(Numeric),
+ message: kind_of(String)
+ })
- expect(Ci::PipelineArtifact.count).to eq(1)
+ subject
end
end
end
@@ -45,21 +50,42 @@ RSpec.describe ::Ci::PipelineArtifacts::CoverageReportService do
context 'when pipeline has coverage report' do
let!(:pipeline) { create(:ci_pipeline, :with_coverage_reports, project: project) }
- it_behaves_like 'creating a pipeline coverage report'
+ it_behaves_like 'creating or updating a pipeline coverage report'
end
context 'when pipeline has coverage report from child pipeline' do
let!(:pipeline) { create(:ci_pipeline, :success, project: project) }
let!(:child_pipeline) { create(:ci_pipeline, :with_coverage_reports, project: project, child_of: pipeline) }
- it_behaves_like 'creating a pipeline coverage report'
+ it_behaves_like 'creating or updating a pipeline coverage report'
+ end
+
+ context 'when pipeline has existing pipeline artifact for coverage report' do
+ let!(:pipeline) { create(:ci_pipeline, :with_coverage_reports, project: project) }
+ let!(:child_pipeline) { create(:ci_pipeline, :with_coverage_reports, project: project, child_of: pipeline) }
+
+ let!(:pipeline_artifact) do
+ create(:ci_pipeline_artifact, :with_coverage_report, pipeline: pipeline, expire_at: 1.day.from_now)
+ end
+
+ it_behaves_like 'creating or updating a pipeline coverage report'
+
+ context 'when the feature flag is disabled' do
+ before do
+ stub_feature_flags(ci_child_pipeline_coverage_reports: false)
+ end
+
+ it 'does not change existing pipeline artifact' do
+ expect { subject }.not_to change { pipeline_artifact.reload.updated_at }
+ end
+ end
end
context 'when pipeline is running and coverage report does not exist' do
let(:pipeline) { create(:ci_pipeline, :running) }
it 'does not persist data' do
- expect { subject }.not_to change { Ci::PipelineArtifact.count }
+ expect { subject }.not_to change { Ci::PipelineArtifact.count }.from(0)
end
end
end
diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb
index 1041350129e..2a9144614d0 100644
--- a/spec/support/helpers/usage_data_helpers.rb
+++ b/spec/support/helpers/usage_data_helpers.rb
@@ -53,7 +53,6 @@ module UsageDataHelpers
clusters_platforms_eks
clusters_platforms_gke
clusters_platforms_user
- clusters_integrations_elastic_stack
clusters_integrations_prometheus
clusters_management_project
in_review_folder
diff --git a/spec/workers/ci/pipeline_artifacts/coverage_report_worker_spec.rb b/spec/workers/ci/pipeline_artifacts/coverage_report_worker_spec.rb
index 000eda055af..f51e66aa948 100644
--- a/spec/workers/ci/pipeline_artifacts/coverage_report_worker_spec.rb
+++ b/spec/workers/ci/pipeline_artifacts/coverage_report_worker_spec.rb
@@ -2,28 +2,71 @@
require 'spec_helper'
-RSpec.describe ::Ci::PipelineArtifacts::CoverageReportWorker do
+RSpec.describe Ci::PipelineArtifacts::CoverageReportWorker do
describe '#perform' do
+ let(:pipeline_id) { pipeline.id }
+
subject { described_class.new.perform(pipeline_id) }
context 'when pipeline exists' do
- let(:pipeline) { create(:ci_pipeline) }
- let(:pipeline_id) { pipeline.id }
+ let(:pipeline) { create(:ci_pipeline, :success) }
- it 'calls pipeline report result service' do
- expect_next_instance_of(::Ci::PipelineArtifacts::CoverageReportService) do |create_artifact_service|
- expect(create_artifact_service).to receive(:execute)
+ it 'calls the pipeline coverage report service' do
+ expect_next_instance_of(::Ci::PipelineArtifacts::CoverageReportService, pipeline) do |service|
+ expect(service).to receive(:execute)
end
subject
end
end
+ context 'when the pipeline is part of a hierarchy' do
+ let_it_be(:root_ancestor_pipeline) { create(:ci_pipeline, :success) }
+ let_it_be(:pipeline) { create(:ci_pipeline, :success, child_of: root_ancestor_pipeline) }
+ let_it_be(:another_child_pipeline) { create(:ci_pipeline, :success, child_of: root_ancestor_pipeline) }
+
+ context 'when all pipelines is complete' do
+ it 'calls the pipeline coverage report service on the root ancestor pipeline' do
+ expect_next_instance_of(::Ci::PipelineArtifacts::CoverageReportService, root_ancestor_pipeline) do |service|
+ expect(service).to receive(:execute)
+ end
+
+ subject
+ end
+ end
+
+ context 'when the pipeline hierarchy has incomplete pipeline' do
+ before do
+ another_child_pipeline.update!(status: :running)
+ end
+
+ it 'does not call pipeline coverage report service' do
+ expect(Ci::PipelineArtifacts::CoverageReportService).not_to receive(:new)
+
+ subject
+ end
+ end
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(ci_child_pipeline_coverage_reports: false)
+ end
+
+ it 'calls the pipeline coverage report service on the pipeline' do
+ expect_next_instance_of(::Ci::PipelineArtifacts::CoverageReportService, pipeline) do |service|
+ expect(service).to receive(:execute)
+ end
+
+ subject
+ end
+ end
+ end
+
context 'when pipeline does not exist' do
let(:pipeline_id) { non_existing_record_id }
it 'does not call pipeline create artifact service' do
- expect(Ci::PipelineArtifacts::CoverageReportService).not_to receive(:execute)
+ expect(Ci::PipelineArtifacts::CoverageReportService).not_to receive(:new)
subject
end