提交 c7b780b5 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 0bc6d001
......@@ -13,6 +13,7 @@ class Projects::PipelinesController < Projects::ApplicationController
before_action :authorize_update_pipeline!, only: [:retry, :cancel]
before_action do
push_frontend_feature_flag(:junit_pipeline_view, project)
push_frontend_feature_flag(:build_report_summary, project)
push_frontend_feature_flag(:filter_pipelines_search, project, default_enabled: true)
push_frontend_feature_flag(:dag_pipeline_tab, project, default_enabled: false)
push_frontend_feature_flag(:pipelines_security_report_summary, project)
......
......@@ -10,13 +10,13 @@ module Notes
def execute
# Skip system notes, like status changes and cross-references and awards
unless @note.system?
EventCreateService.new.leave_note(@note, @note.author)
unless note.system?
EventCreateService.new.leave_note(note, note.author)
return if @note.for_personal_snippet?
return if note.for_personal_snippet?
@note.create_cross_references!
::SystemNoteService.design_discussion_added(@note) if create_design_discussion_system_note?
note.create_cross_references!
::SystemNoteService.design_discussion_added(note) if create_design_discussion_system_note?
execute_note_hooks
end
......@@ -25,21 +25,21 @@ module Notes
private
def create_design_discussion_system_note?
@note && @note.for_design? && @note.start_of_discussion?
note && note.for_design? && note.start_of_discussion?
end
def hook_data
Gitlab::DataBuilder::Note.build(@note, @note.author)
Gitlab::DataBuilder::Note.build(note, note.author)
end
def execute_note_hooks
return unless @note.project
return unless note.project
note_data = hook_data
hooks_scope = @note.confidential?(include_noteable: true) ? :confidential_note_hooks : :note_hooks
hooks_scope = note.confidential?(include_noteable: true) ? :confidential_note_hooks : :note_hooks
@note.project.execute_hooks(note_data, hooks_scope)
@note.project.execute_services(note_data, hooks_scope)
note.project.execute_hooks(note_data, hooks_scope)
note.project.execute_services(note_data, hooks_scope)
end
end
end
......
......@@ -41,7 +41,7 @@ module Notes
@interpret_service = QuickActions::InterpretService.new(project, current_user, options)
@interpret_service.execute(note.note, note.noteable)
interpret_service.execute(note.note, note.noteable)
end
# Applies updates extracted to note#noteable
......
......@@ -6,20 +6,20 @@
.col-form-label.col-sm-2
= f.label :title, "Title"
.col-sm-10
= f.text_field :title, maxlength: 255, class: "form-control", required: true, autofocus: true
= f.text_field :title, maxlength: 255, class: "form-control", data: { qa_selector: "milestone_title_field" }, required: true, autofocus: true
.form-group.row.milestone-description
.col-form-label.col-sm-2
= f.label :description, "Description"
.col-sm-10
= render layout: 'shared/md_preview', locals: { url: group_preview_markdown_path } do
= render 'shared/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: 'Write milestone description...', supports_autocomplete: false
= render 'shared/zen', f: f, attr: :description, classes: 'note-textarea', qa_selector: 'milestone_description_field', placeholder: 'Write milestone description...', supports_autocomplete: false
.clearfix
.error-alert
= render "shared/milestones/form_dates", f: f
.form-actions
- if @milestone.new_record?
= f.submit 'Create milestone', class: "btn-success btn"
= f.submit 'Create milestone', class: "btn-success btn", data: { qa_selector: "create_milestone_button" }
= link_to "Cancel", group_milestones_path(@group), class: "btn btn-cancel"
- else
= f.submit 'Update milestone', class: "btn-success btn"
......
......@@ -7,7 +7,7 @@
= render 'shared/milestones/search_form'
= render 'shared/milestones_sort_dropdown'
- if can?(current_user, :admin_milestone, @group)
= link_to "New milestone", new_group_milestone_path(@group), class: "btn btn-success"
= link_to "New milestone", new_group_milestone_path(@group), class: "btn btn-success", data: { qa_selector: "new_group_milestone_link" }
.milestones
%ul.content-list
......
......@@ -81,7 +81,7 @@
- if group_sidebar_link?(:milestones)
= nav_link(path: 'milestones#index') do
= link_to group_milestones_path(@group), title: _('Milestones') do
= link_to group_milestones_path(@group), title: _('Milestones'), data: { qa_selector: 'group_milestones_link' } do
%span
= _('Milestones')
......
---
title: Add background_migration_jobs table to trace background migrations
merge_request: 35913
author:
type: added
---
title: Add migration to drop unused daily report results table
merge_request: 36102
author:
type: other
---
title: Add number of approval project rules to usage ping
merge_request: 36316
author:
type: added
---
title: Move approvals endpoints to FOSS version
merge_request: 36237
author:
type: added
# frozen_string_literal: true
class CreateBackgroundMigrationJobs < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
unless table_exists?(:background_migration_jobs)
create_table :background_migration_jobs do |t|
t.timestamps_with_timezone
t.integer :status, null: false, limit: 2, default: 0
t.text :class_name, null: false
t.jsonb :arguments, null: false
t.index [:class_name, :arguments]
t.index [:class_name, :status, :id]
end
end
add_text_limit :background_migration_jobs, :class_name, 200
end
def down
drop_table :background_migration_jobs
end
end
# frozen_string_literal: true
class RemoveFKeysFromCiDailyReportResultsTable < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
with_lock_retries do
remove_foreign_key_if_exists :ci_daily_report_results, :projects
remove_foreign_key_if_exists :ci_daily_report_results, :ci_pipelines
end
end
def down
add_concurrent_foreign_key :ci_daily_report_results, :projects, column: :project_id, on_delete: :cascade
add_concurrent_foreign_key :ci_daily_report_results, :ci_pipelines, column: :last_pipeline_id, on_delete: :cascade
end
end
# frozen_string_literal: true
class DropCiDailyReportResultsTable < ActiveRecord::Migration[6.0]
DOWNTIME = false
def up
drop_table :ci_daily_report_results
end
def down
create_table :ci_daily_report_results do |t|
t.date :date, null: false
t.bigint :project_id, null: false
t.bigint :last_pipeline_id, null: false
t.float :value, null: false
t.integer :param_type, limit: 8, null: false
t.string :ref_path, null: false
t.string :title, null: false
t.index :last_pipeline_id
t.index [:project_id, :ref_path, :param_type, :date, :title], name: 'index_daily_report_results_unique_columns', unique: true
end
end
end
......@@ -9413,6 +9413,25 @@ CREATE TABLE public.aws_roles (
role_external_id character varying(64) NOT NULL
);
CREATE TABLE public.background_migration_jobs (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
status smallint DEFAULT 0 NOT NULL,
class_name text NOT NULL,
arguments jsonb NOT NULL,
CONSTRAINT check_b0de0a5852 CHECK ((char_length(class_name) <= 200))
);
CREATE SEQUENCE public.background_migration_jobs_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.background_migration_jobs_id_seq OWNED BY public.background_migration_jobs.id;
CREATE TABLE public.backup_labels (
id integer NOT NULL,
title character varying,
......@@ -9816,26 +9835,6 @@ CREATE SEQUENCE public.ci_daily_build_group_report_results_id_seq
ALTER SEQUENCE public.ci_daily_build_group_report_results_id_seq OWNED BY public.ci_daily_build_group_report_results.id;
CREATE TABLE public.ci_daily_report_results (
id bigint NOT NULL,
date date NOT NULL,
project_id bigint NOT NULL,
last_pipeline_id bigint NOT NULL,
value double precision NOT NULL,
param_type bigint NOT NULL,
ref_path character varying NOT NULL,
title character varying NOT NULL
);
CREATE SEQUENCE public.ci_daily_report_results_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.ci_daily_report_results_id_seq OWNED BY public.ci_daily_report_results.id;
CREATE TABLE public.ci_freeze_periods (
id bigint NOT NULL,
project_id bigint NOT NULL,
......@@ -16370,6 +16369,8 @@ ALTER TABLE ONLY public.audit_events ALTER COLUMN id SET DEFAULT nextval('public
ALTER TABLE ONLY public.award_emoji ALTER COLUMN id SET DEFAULT nextval('public.award_emoji_id_seq'::regclass);
ALTER TABLE ONLY public.background_migration_jobs ALTER COLUMN id SET DEFAULT nextval('public.background_migration_jobs_id_seq'::regclass);
ALTER TABLE ONLY public.badges ALTER COLUMN id SET DEFAULT nextval('public.badges_id_seq'::regclass);
ALTER TABLE ONLY public.board_assignees ALTER COLUMN id SET DEFAULT nextval('public.board_assignees_id_seq'::regclass);
......@@ -16406,8 +16407,6 @@ ALTER TABLE ONLY public.ci_builds_runner_session ALTER COLUMN id SET DEFAULT nex
ALTER TABLE ONLY public.ci_daily_build_group_report_results ALTER COLUMN id SET DEFAULT nextval('public.ci_daily_build_group_report_results_id_seq'::regclass);
ALTER TABLE ONLY public.ci_daily_report_results ALTER COLUMN id SET DEFAULT nextval('public.ci_daily_report_results_id_seq'::regclass);
ALTER TABLE ONLY public.ci_freeze_periods ALTER COLUMN id SET DEFAULT nextval('public.ci_freeze_periods_id_seq'::regclass);
ALTER TABLE ONLY public.ci_group_variables ALTER COLUMN id SET DEFAULT nextval('public.ci_group_variables_id_seq'::regclass);
......@@ -17255,6 +17254,9 @@ ALTER TABLE ONLY public.award_emoji
ALTER TABLE ONLY public.aws_roles
ADD CONSTRAINT aws_roles_pkey PRIMARY KEY (user_id);
ALTER TABLE ONLY public.background_migration_jobs
ADD CONSTRAINT background_migration_jobs_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.backup_labels
ADD CONSTRAINT backup_labels_pkey PRIMARY KEY (id);
......@@ -17318,9 +17320,6 @@ ALTER TABLE ONLY public.ci_builds_runner_session
ALTER TABLE ONLY public.ci_daily_build_group_report_results
ADD CONSTRAINT ci_daily_build_group_report_results_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.ci_daily_report_results
ADD CONSTRAINT ci_daily_report_results_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.ci_freeze_periods
ADD CONSTRAINT ci_freeze_periods_pkey PRIMARY KEY (id);
......@@ -18620,6 +18619,10 @@ CREATE UNIQUE INDEX index_aws_roles_on_role_external_id ON public.aws_roles USIN
CREATE UNIQUE INDEX index_aws_roles_on_user_id ON public.aws_roles USING btree (user_id);
CREATE INDEX index_background_migration_jobs_on_class_name_and_arguments ON public.background_migration_jobs USING btree (class_name, arguments);
CREATE INDEX index_background_migration_jobs_on_class_name_and_status_and_id ON public.background_migration_jobs USING btree (class_name, status, id);
CREATE INDEX index_badges_on_group_id ON public.badges USING btree (group_id);
CREATE INDEX index_badges_on_project_id ON public.badges USING btree (project_id);
......@@ -18740,8 +18743,6 @@ CREATE UNIQUE INDEX index_ci_builds_runner_session_on_build_id ON public.ci_buil
CREATE INDEX index_ci_daily_build_group_report_results_on_last_pipeline_id ON public.ci_daily_build_group_report_results USING btree (last_pipeline_id);
CREATE INDEX index_ci_daily_report_results_on_last_pipeline_id ON public.ci_daily_report_results USING btree (last_pipeline_id);
CREATE INDEX index_ci_freeze_periods_on_project_id ON public.ci_freeze_periods USING btree (project_id);
CREATE UNIQUE INDEX index_ci_group_variables_on_group_id_and_key ON public.ci_group_variables USING btree (group_id, key);
......@@ -18958,8 +18959,6 @@ CREATE UNIQUE INDEX index_custom_emoji_on_namespace_id_and_name ON public.custom
CREATE UNIQUE INDEX index_daily_build_group_report_results_unique_columns ON public.ci_daily_build_group_report_results USING btree (project_id, ref_path, date, group_name);
CREATE UNIQUE INDEX index_daily_report_results_unique_columns ON public.ci_daily_report_results USING btree (project_id, ref_path, param_type, date, title);
CREATE INDEX index_dependency_proxy_blobs_on_group_id_and_file_name ON public.dependency_proxy_blobs USING btree (group_id, file_name);
CREATE INDEX index_dependency_proxy_group_settings_on_group_id ON public.dependency_proxy_group_settings USING btree (group_id);
......@@ -22317,9 +22316,6 @@ ALTER TABLE ONLY public.gpg_signatures
ALTER TABLE ONLY public.board_group_recent_visits
ADD CONSTRAINT fk_rails_ca04c38720 FOREIGN KEY (board_id) REFERENCES public.boards(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.ci_daily_report_results
ADD CONSTRAINT fk_rails_cc5caec7d9 FOREIGN KEY (last_pipeline_id) REFERENCES public.ci_pipelines(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.issues_self_managed_prometheus_alert_events
ADD CONSTRAINT fk_rails_cc5d88bbb0 FOREIGN KEY (issue_id) REFERENCES public.issues(id) ON DELETE CASCADE;
......@@ -22440,9 +22436,6 @@ ALTER TABLE ONLY public.alert_management_alert_user_mentions
ALTER TABLE ONLY public.snippet_statistics
ADD CONSTRAINT fk_rails_ebc283ccf1 FOREIGN KEY (snippet_id) REFERENCES public.snippets(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.ci_daily_report_results
ADD CONSTRAINT fk_rails_ebc2931b90 FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.cluster_providers_aws
ADD CONSTRAINT fk_rails_ed1fdfaeb2 FOREIGN KEY (created_by_user_id) REFERENCES public.users(id) ON DELETE SET NULL;
......@@ -23641,11 +23634,14 @@ COPY "schema_migrations" (version) FROM STDIN;
20200630091656
20200630110826
20200701093859
20200701205710
20200702123805
20200703121557
20200703154822
20200704143633
20200704161600
20200706005325
20200706154619
20200706170536
20200707071941
20200707094341
......
......@@ -867,7 +867,7 @@ versions of the app, all without leaving GitLab.
#### Embedding metrics in GitLab Flavored Markdown
Metric charts can be embedded within GitLab Flavored Markdown. See [Embedding Metrics within GitLab Flavored Markdown](../../user/project/integrations/prometheus.md#embedding-metric-charts-within-gitlab-flavored-markdown) for more details.
Metric charts can be embedded within GitLab Flavored Markdown. See [Embedding Metrics within GitLab Flavored Markdown](../../operations/metrics/embed.md) for more details.
### Web terminals
......
......@@ -396,6 +396,8 @@ appear to be associated to any of the services running, since they all appear to
| `installation_type` | | | | | |
| `active_user_count` | | | | | |
| `recorded_at` | | | | | |
| `recording_ce_finished_at` | | | | CE+EE | When the core features were computed |
| `recording_ee_finished_at` | | | | EE | When the EE-specific features were computed |
| `edition` | | | | | |
| `license_md5` | | | | | |
| `license_id` | | | | | |
......@@ -662,6 +664,8 @@ appear to be associated to any of the services running, since they all appear to
| `projects_with_repositories_enabled` | `usage_activity_by_stage` | `create` | | EE | |
| `protected_branches` | `usage_activity_by_stage` | `create` | | EE | |
| `suggestions` | `usage_activity_by_stage` | `create` | | EE | |
| `approval_project_rules` | `usage_activity_by_stage` | `create` | | EE | Number of project approval rules |
| `approval_project_rules_with_target_branch` | `usage_activity_by_stage` | `create` | | EE | Number of project approval rules with not default target branch |
| `clusters` | `usage_activity_by_stage` | `monitor` | | CE+EE | |
| `clusters_applications_prometheus` | `usage_activity_by_stage` | `monitor` | | CE+EE | |
| `operations_dashboard_default_dashboard` | `usage_activity_by_stage` | `monitor` | | CE+EE | |
......@@ -699,7 +703,7 @@ appear to be associated to any of the services running, since they all appear to
| `projects_mirrored_with_pipelines_enabled` | `usage_activity_by_stage` | `release` | | EE | Projects with repository mirroring enabled |
| `releases` | `usage_activity_by_stage` | `release` | | CE+EE | Unique release tags in project |
| `successful_deployments` | `usage_activity_by_stage` | `release` | | CE+EE | Total successful deployments |
| `user_preferences_group_overview_security_dashboard: 0` | `usage_activity_by_stage` | `secure` | | | |
| `user_preferences_group_overview_security_dashboard` | `usage_activity_by_stage` | `secure` | | | |
| `ci_builds` | `usage_activity_by_stage` | `verify` | | CE+EE | Unique builds in project |
| `ci_external_pipelines` | `usage_activity_by_stage` | `verify` | | CE+EE | Total pipelines in external repositories |
| `ci_internal_pipelines` | `usage_activity_by_stage` | `verify` | | CE+EE | Total pipelines in GitLab repositories |
......
......@@ -56,7 +56,7 @@ The options are:
- [Expand panel](#expand-panel)
- [View logs](#view-logs-ultimate)
- [Download CSV](#downloading-data-as-csv)
- [Copy link to chart](../../../user/project/integrations/prometheus.md#embedding-gitlab-managed-kubernetes-metrics)
- [Copy link to chart](../embed.md#embedding-gitlab-managed-kubernetes-metrics)
- [Alerts](../../../user/project/integrations/prometheus.md#setting-up-alerts-for-prometheus-metrics)
### View and edit the source file of a custom dashboard
......
---
stage: Monitor
group: APM
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
# Embedding metric charts within GitLab Flavored Markdown
## Embedding GitLab-managed Kubernetes metrics
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/29691) in GitLab 12.2.
It is possible to display metrics charts within [GitLab Flavored Markdown](../../user/markdown.md#gitlab-flavored-markdown-gfm) fields such as issue or merge request descriptions. The maximum number of embedded charts allowed in a GitLab Flavored Markdown field is 100.
This can be useful if you are sharing an application incident or performance
metrics to others and want to have relevant information directly available.
NOTE: **Note:**
Requires [Kubernetes](../../user/project/integrations/prometheus_library/kubernetes.md) metrics.
To display metric charts, include a link of the form `https://<root_url>/<project>/-/environments/<environment_id>/metrics`:
![Embedded Metrics Markdown](../../user/project/integrations/img/embedded_metrics_markdown_v12_8.png)
GitLab unfurls the link as an embedded metrics panel:
![Embedded Metrics Rendered](../../user/project/integrations/img/embedded_metrics_rendered_v12_8.png)
You can also embed a single chart. To get a link to a chart, click the
**{ellipsis_v}** **More actions** menu in the upper right corner of the chart,
and select **Copy link to chart**, as shown in this example:
![Copy Link To Chart](../../user/project/integrations/img/copy_link_to_chart_v12_10.png)
The following requirements must be met for the metric to unfurl:
- The `<environment_id>` must correspond to a real environment.
- Prometheus must be monitoring the environment.
- The GitLab instance must be configured to receive data from the environment.
- The user must be allowed access to the monitoring dashboard for the environment ([Reporter or higher](../../user/permissions.md)).
- The dashboard must have data within the last 8 hours.
If all of the above are true, then the metric will unfurl as seen below:
![Embedded Metrics](../../user/project/integrations/img/view_embedded_metrics_v12_10.png)
Metric charts may also be hidden:
![Show Hide](../../user/project/integrations/img/hide_embedded_metrics_v12_10.png)
You can open the link directly into your browser for a [detailed view of the data](dashboards/index.md#expand-panel).
## Embedding metrics in issue templates
It is also possible to embed either the default dashboard metrics or individual metrics in issue templates. For charts to render side-by-side, links to the entire metrics dashboard or individual metrics should be separated by either a comma or a space.
![Embedded Metrics in issue templates](../../user/project/integrations/img/embed_metrics_issue_template.png)
## Embedding metrics based on alerts in incident issues
For [GitLab-managed alerting rules](../../user/project/integrations/prometheus.md#setting-up-alerts-for-prometheus-metrics), the issue will include an embedded chart for the query corresponding to the alert. The chart displays an hour of data surrounding the starting point of the incident, 30 minutes before and after.
For [manually configured Prometheus instances](../../user/project/integrations/prometheus.md#manual-configuration-of-prometheus), a chart corresponding to the query can be included if these requirements are met:
- The alert corresponds to an environment managed through GitLab.
- The alert corresponds to a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries).
- The alert contains the required attributes listed in the chart below.
| Attributes | Required | Description |
| ---------- | -------- | ----------- |
| `annotations/gitlab_environment_name` | Yes | Name of the GitLab-managed environment corresponding to the alert |
| One of `annotations/title`, `annotations/summary`, `labels/alertname` | Yes | Will be used as the chart title |
| `annotations/gitlab_y_label` | No | Will be used as the chart's y-axis label |
## Embedding Cluster Health Charts **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/40997) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.9.
[Cluster Health Metrics](../../user/project/clusters/index.md#visualizing-cluster-health-ultimate) can also be embedded in [GitLab-flavored Markdown](../../user/markdown.md).
To embed a metric chart, include a link to that chart in the form `https://<root_url>/<project>/-/cluster/<cluster_id>?<query_params>` anywhere that GitLab-flavored Markdown is supported. To generate and copy a link to the chart, follow the instructions in the [Cluster Health Metric documentation](../../user/project/clusters/index.md#visualizing-cluster-health-ultimate).
The following requirements must be met for the metric to unfurl:
- The `<cluster_id>` must correspond to a real cluster.
- Prometheus must be monitoring the cluster.
- The user must be allowed access to the project cluster metrics.
- The dashboards must be reporting data on the [Cluster Health Page](../../user/project/clusters/index.md#visualizing-cluster-health-ultimate)
If the above requirements are met, then the metric will unfurl as seen below.
![Embedded Cluster Metric in issue descriptions](../../user/project/integrations/img/prometheus_cluster_health_embed_v12_9.png)
---
stage: Monitor
group: APM
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
# Embedding Grafana charts
Grafana metrics can be embedded in [GitLab Flavored Markdown](../../user/markdown.md).
## Embedding charts via Grafana Rendered Images
It is possible to embed live [Grafana](https://docs.gitlab.com/omnibus/settings/grafana.html) charts in issues, as a [direct linked rendered image](https://grafana.com/docs/grafana/latest/reference/share_panel/#direct-link-rendered-image).
The sharing dialog within Grafana provides the link, as highlighted below.
![Grafana Direct Linked Rendered Image](../../user/project/integrations/img/grafana_live_embed.png)
NOTE: **Note:**
For this embed to display correctly, the Grafana instance must be available to the target user, either as a public dashboard, or on the same network.
Copy the link and add an image tag as [inline HTML](../../user/markdown.md#inline-html) in your Markdown. You may tweak the query parameters as required. For instance, removing the `&from=` and `&to=` parameters will give you a live chart. Here is example markup for a live chart from GitLab's public dashboard:
```html
<img src="https://dashboards.gitlab.com/d/RZmbBr7mk/gitlab-triage?orgId=1&refresh=30s&var-env=gprd&var-environment=gprd&var-prometheus=prometheus-01-inf-gprd&var-prometheus_app=prometheus-app-01-inf-gprd&var-backend=All&var-type=All&var-stage=main&from=1580444107655&to=1580465707655"/>
```
This will render like so:
![Grafana dashboard embedded preview](../../user/project/integrations/img/grafana_embedded.png)
## Embedding charts via integration with Grafana HTTP API
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/31376) in GitLab 12.5.
Each project can support integration with one Grafana instance. This configuration allows a user to copy a link to a panel in Grafana, then paste it into a GitLab Markdown field. The chart will be rendered in the GitLab chart format.
Prerequisites for embedding from a Grafana instance:
1. The datasource must be a Prometheus instance.
1. The datasource must be proxyable, so the HTTP Access setting should be set to `Server`.
![HTTP Proxy Access](../../user/project/integrations/img/http_proxy_access_v12_5.png)
## Setting up the Grafana integration
1. [Generate an Admin-level API Token in Grafana.](https://grafana.com/docs/grafana/latest/http_api/auth/#create-api-token)
1. In your GitLab project, navigate to **Settings > Operations > Grafana Authentication**.
1. To enable the integration, check the "Active" checkbox.
1. For "Grafana URL", enter the base URL of the Grafana instance.
1. For "API Token", enter the Admin API Token you just generated.
1. Click **Save Changes**.
## Generating a link to a chart
1. In Grafana, navigate to the dashboard you wish to embed a panel from.
![Grafana Metric Panel](../../user/project/integrations/img/grafana_panel_v12_5.png)
1. In the upper-left corner of the page, select a specific value for each variable required for the queries in the chart.
![Select Query Variables](../../user/project/integrations/img/select_query_variables_v12_5.png)
1. In Grafana, click on a panel's title, then click **Share** to open the panel's sharing dialog to the **Link** tab. If you click the _dashboard's_ share panel instead, GitLab will attempt to embed the first supported panel on the dashboard (if available).
1. If your Prometheus queries use Grafana's custom template variables, ensure that the "Template variables" option is toggled to **On**. Of Grafana global template variables, only `$__interval`, `$__from`, and `$__to` are currently supported. Toggle **On** the "Current time range" option to specify the time range of the chart. Otherwise, the default range will be the last 8 hours.
![Grafana Sharing Dialog](../../user/project/integrations/img/grafana_sharing_dialog_v12_5.png)
1. Click **Copy** to copy the URL to the clipboard.
1. In GitLab, paste the URL into a Markdown field and save. The chart will take a few moments to render.
![GitLab Rendered Grafana Panel](../../user/project/integrations/img/rendered_grafana_embed_v12_5.png)
......@@ -145,7 +145,7 @@ The [Security Scanner Integration](../../../development/integrations/secure.md)
## Analyzers Data
| Property \ Tool | Apex | Bandit | Brakeman | ESLint security | Find Sec Bugs | Flawfinder | Gosec | Kubesec Scanner | NodeJsScan | PHP CS Security Audit | Security code Scan (.NET) | Sobelow | TSLint Security |
| Property \ Tool | Apex | Bandit | Brakeman | ESLint security | SpotBugs | Flawfinder | Gosec | Kubesec Scanner | NodeJsScan | PHP CS Security Audit | Security code Scan (.NET) | Sobelow | TSLint Security |
| --------------------------------------- | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :---------------------: | :-------------------------: | :----------------: | :-------------: |
| Severity | ✓ | ✓ | 𐄂 | 𐄂 | ✓ | 𐄂 | ✓ | ✓ | 𐄂 | ✓ | 𐄂 | 𐄂 | ✓ |
| Title | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
......
......@@ -34,7 +34,7 @@ to create issues when alerts are triggered:
1. Click **Save changes**.
Appropriately configured alerts include an
[embedded chart](../project/integrations/prometheus.md#embedding-metrics-based-on-alerts-in-incident-issues)
[embedded chart](../../operations/metrics/embed.md#embedding-metrics-based-on-alerts-in-incident-issues)
for the query corresponding to the alert. You can also configure GitLab to
[close issues](../project/integrations/prometheus.md#taking-action-on-incidents-ultimate)
when you receive notification that the alert is resolved.
......@@ -71,11 +71,11 @@ You can embed metrics anywhere [GitLab Markdown](../markdown.md) is used, such a
comments on issues, and merge requests. Embedding metrics helps you share them
when discussing incidents or performance issues. You can output the dashboard directly
into any issue, merge request, epic, or any other Markdown text field in GitLab
by [copying and pasting the link to the metrics dashboard](../project/integrations/prometheus.md#embedding-gitlab-managed-kubernetes-metrics).
by [copying and pasting the link to the metrics dashboard](../../operations/metrics/embed.md#embedding-gitlab-managed-kubernetes-metrics).
You can embed both
[GitLab-hosted metrics](../project/integrations/prometheus.md#embedding-metric-charts-within-gitlab-flavored-markdown) and
[Grafana metrics](../project/integrations/prometheus.md#embedding-grafana-charts)
[GitLab-hosted metrics](../../operations/metrics/embed.md) and
[Grafana metrics](../../operations/metrics/embed_grafana.md)
in incidents and issue templates.
### Context menu
......
......@@ -579,7 +579,7 @@ This snippet links to `<wiki_root>/miscellaneous.md`:
### Embedding metrics in GitLab Flavored Markdown
Metric charts can be embedded within GitLab Flavored Markdown. See [Embedding Metrics within GitLab flavored Markdown](../user/project/integrations/prometheus.md#embedding-metric-charts-within-gitlab-flavored-markdown) for more details.
Metric charts can be embedded within GitLab Flavored Markdown. See [Embedding Metrics within GitLab flavored Markdown](../operations/metrics/embed.md#embedding-metric-charts-within-gitlab-flavored-markdown) for more details.
## Standard Markdown and extensions in GitLab
......
......@@ -622,153 +622,6 @@ Prometheus server.
![Merge Request with Performance Impact](img/merge_request_performance.png)
## Embedding metric charts within GitLab Flavored Markdown
### Embedding GitLab-managed Kubernetes metrics
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/29691) in GitLab 12.2.
It is possible to display metrics charts within [GitLab Flavored Markdown](../../markdown.md#gitlab-flavored-markdown-gfm) fields such as issue or merge request descriptions. The maximum number of embedded charts allowed in a GitLab Flavored Markdown field is 100.
This can be useful if you are sharing an application incident or performance
metrics to others and want to have relevant information directly available.
NOTE: **Note:**
Requires [Kubernetes](prometheus_library/kubernetes.md) metrics.
To display metric charts, include a link of the form `https://<root_url>/<project>/-/environments/<environment_id>/metrics`:
![Embedded Metrics Markdown](img/embedded_metrics_markdown_v12_8.png)
GitLab unfurls the link as an embedded metrics panel:
![Embedded Metrics Rendered](img/embedded_metrics_rendered_v12_8.png)
You can also embed a single chart. To get a link to a chart, click the
**{ellipsis_v}** **More actions** menu in the upper right corner of the chart,
and select **Copy link to chart**, as shown in this example:
![Copy Link To Chart](img/copy_link_to_chart_v12_10.png)
The following requirements must be met for the metric to unfurl:
- The `<environment_id>` must correspond to a real environment.
- Prometheus must be monitoring the environment.
- The GitLab instance must be configured to receive data from the environment.
- The user must be allowed access to the monitoring dashboard for the environment ([Reporter or higher](../../permissions.md)).
- The dashboard must have data within the last 8 hours.
If all of the above are true, then the metric will unfurl as seen below:
![Embedded Metrics](img/view_embedded_metrics_v12_10.png)
Metric charts may also be hidden:
![Show Hide](img/hide_embedded_metrics_v12_10.png)
You can open the link directly into your browser for a [detailed view of the data](../../../operations/metrics/dashboards/index.md#expand-panel).
### Embedding metrics in issue templates
It is also possible to embed either the default dashboard metrics or individual metrics in issue templates. For charts to render side-by-side, links to the entire metrics dashboard or individual metrics should be separated by either a comma or a space.
![Embedded Metrics in issue templates](img/embed_metrics_issue_template.png)
### Embedding metrics based on alerts in incident issues
For [GitLab-managed alerting rules](#setting-up-alerts-for-prometheus-metrics), the issue will include an embedded chart for the query corresponding to the alert. The chart displays an hour of data surrounding the starting point of the incident, 30 minutes before and after.
For [manually configured Prometheus instances](#manual-configuration-of-prometheus), a chart corresponding to the query can be included if these requirements are met:
- The alert corresponds to an environment managed through GitLab.
- The alert corresponds to a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries).
- The alert contains the required attributes listed in the chart below.
| Attributes | Required | Description |
| ---------- | -------- | ----------- |
| `annotations/gitlab_environment_name` | Yes | Name of the GitLab-managed environment corresponding to the alert |
| One of `annotations/title`, `annotations/summary`, `labels/alertname` | Yes | Will be used as the chart title |
| `annotations/gitlab_y_label` | No | Will be used as the chart's y-axis label |
### Embedding Cluster Health Charts **(ULTIMATE)**
> [Introduced](<https://gitlab.com/gitlab-org/gitlab/-/issues/40997>) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.9.
[Cluster Health Metrics](../clusters/index.md#visualizing-cluster-health-ultimate) can also be embedded in [GitLab-flavored Markdown](../../markdown.md).
To embed a metric chart, include a link to that chart in the form `https://<root_url>/<project>/-/cluster/<cluster_id>?<query_params>` anywhere that GitLab-flavored Markdown is supported. To generate and copy a link to the chart, follow the instructions in the [Cluster Health Metric documentation](../clusters/index.md#visualizing-cluster-health-ultimate).
The following requirements must be met for the metric to unfurl:
- The `<cluster_id>` must correspond to a real cluster.
- Prometheus must be monitoring the cluster.
- The user must be allowed access to the project cluster metrics.
- The dashboards must be reporting data on the [Cluster Health Page](../clusters/index.md#visualizing-cluster-health-ultimate)
If the above requirements are met, then the metric will unfurl as seen below.
![Embedded Cluster Metric in issue descriptions](img/prometheus_cluster_health_embed_v12_9.png)
### Embedding Grafana charts
Grafana metrics can be embedded in [GitLab Flavored Markdown](../../markdown.md).
#### Embedding charts via Grafana Rendered Images
It is possible to embed live [Grafana](https://docs.gitlab.com/omnibus/settings/grafana.html) charts in issues, as a [direct linked rendered image](https://grafana.com/docs/grafana/latest/reference/share_panel/#direct-link-rendered-image).
The sharing dialog within Grafana provides the link, as highlighted below.
![Grafana Direct Linked Rendered Image](img/grafana_live_embed.png)
NOTE: **Note:**
For this embed to display correctly, the Grafana instance must be available to the target user, either as a public dashboard, or on the same network.
Copy the link and add an image tag as [inline HTML](../../markdown.md#inline-html) in your Markdown. You may tweak the query parameters as required. For instance, removing the `&from=` and `&to=` parameters will give you a live chart. Here is example markup for a live chart from GitLab's public dashboard:
```html
<img src="https://dashboards.gitlab.com/d/RZmbBr7mk/gitlab-triage?orgId=1&refresh=30s&var-env=gprd&var-environment=gprd&var-prometheus=prometheus-01-inf-gprd&var-prometheus_app=prometheus-app-01-inf-gprd&var-backend=All&var-type=All&var-stage=main&from=1580444107655&to=1580465707655"/>
```
This will render like so:
![Grafana dashboard embedded preview](img/grafana_embedded.png)
#### Embedding charts via integration with Grafana HTTP API
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/31376) in GitLab 12.5.
Each project can support integration with one Grafana instance. This configuration allows a user to copy a link to a panel in Grafana, then paste it into a GitLab Markdown field. The chart will be rendered in the GitLab chart format.
Prerequisites for embedding from a Grafana instance:
1. The datasource must be a Prometheus instance.
1. The datasource must be proxyable, so the HTTP Access setting should be set to `Server`.
![HTTP Proxy Access](img/http_proxy_access_v12_5.png)
##### Setting up the Grafana integration
1. [Generate an Admin-level API Token in Grafana.](https://grafana.com/docs/grafana/latest/http_api/auth/#create-api-token)
1. In your GitLab project, navigate to **Settings > Operations > Grafana Authentication**.
1. To enable the integration, check the "Active" checkbox.
1. For "Grafana URL", enter the base URL of the Grafana instance.
1. For "API Token", enter the Admin API Token you just generated.
1. Click **Save Changes**.
##### Generating a link to a chart
1. In Grafana, navigate to the dashboard you wish to embed a panel from.
![Grafana Metric Panel](img/grafana_panel_v12_5.png)
1. In the upper-left corner of the page, select a specific value for each variable required for the queries in the chart.
![Select Query Variables](img/select_query_variables_v12_5.png)
1. In Grafana, click on a panel's title, then click **Share** to open the panel's sharing dialog to the **Link** tab. If you click the _dashboard's_ share panel instead, GitLab will attempt to embed the first supported panel on the dashboard (if available).
1. If your Prometheus queries use Grafana's custom template variables, ensure that the "Template variables" option is toggled to **On**. Of Grafana global template variables, only `$__interval`, `$__from`, and `$__to` are currently supported. Toggle **On** the "Current time range" option to specify the time range of the chart. Otherwise, the default range will be the last 8 hours.
![Grafana Sharing Dialog](img/grafana_sharing_dialog_v12_5.png)
1. Click **Copy** to copy the URL to the clipboard.
1. In GitLab, paste the URL into a Markdown field and save. The chart will take a few moments to render.
![GitLab Rendered Grafana Panel](img/rendered_grafana_embed_v12_5.png)
## Troubleshooting
When troubleshooting issues with a managed Prometheus app, it is often useful to
......
......@@ -189,11 +189,15 @@ deselect the user from the list of assignees, or click **Unassigned**.
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in GitLab 13.1.
NOTE: **Note:**
GitLab currently only supports creating system notes when assigning an Alert.
When you take action on an alert, this is logged as a system note,
which is visible in the Alert Details view. This gives you a linear
timeline of the alert's investigation and assignment history.
The following actions will result in a system note:
Assigning a user an Alert creates a system note, visible in the Alert Details view,
giving you a linear timeline of the alert's investigation and assignment history.
- [Updating the status of an alert](#update-an-alerts-status)
- [Creating an issue based on an alert](#create-an-issue-from-an-alert)
- [Assignment of an alert to a user](#update-an-alerts-assignee)
![Alert Management Details View System Notes](img/alert_detail_system_notes_v13_1.png)
......@@ -217,7 +221,7 @@ for information on setting up alerts.
For externally-managed Prometheus instances, you can configure your alerting rules to
display a chart in the alert. See
[Embedding metrics based on alerts in incident issues](../integrations/prometheus.md#embedding-metrics-based-on-alerts-in-incident-issues)
[Embedding metrics based on alerts in incident issues](../../../operations/metrics/embed.md#embedding-metrics-based-on-alerts-in-incident-issues)
for information on how to appropriately configure your alerting rules. See
[External Prometheus instances](../integrations/prometheus.md#external-prometheus-instances)
for information on setting up alerts for your self-managed Prometheus instance.
......
......@@ -168,6 +168,7 @@ module API
mount ::API::Members
mount ::API::MergeRequestDiffs
mount ::API::MergeRequests
mount ::API::MergeRequestApprovals
mount ::API::Metrics::Dashboard::Annotations
mount ::API::Metrics::UserStarredDashboards
mount ::API::Namespaces
......
# frozen_string_literal: true
module API
module Entities
class MergeRequestApprovals < Grape::Entity
end
end
end
......@@ -368,6 +368,12 @@ module API
render_api_error!(message.join(' '), 404)
end
def check_sha_param!(params, merge_request)
if params[:sha] && merge_request.diff_head_sha != params[:sha]
render_api_error!("SHA does not match HEAD of source branch: #{merge_request.diff_head_sha}", 409)
end
end
def unauthorized!
render_api_error!('401 Unauthorized', 401)
end
......
# frozen_string_literal: true
module API
class MergeRequestApprovals < ::Grape::API::Instance
before { authenticate_non_get! }
helpers do
params :ee_approval_params do
end
def present_approval(merge_request)
present merge_request, with: ::API::Entities::MergeRequestApprovals, current_user: current_user
end
end
resource :projects, requirements: ::API::API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
segment ':id/merge_requests/:merge_request_iid' do
# Get the status of the merge request's approvals
#
# Parameters:
# id (required) - The ID of a project
# merge_request_iid (required) - IID of MR
# Examples:
# GET /projects/:id/merge_requests/:merge_request_iid/approvals
desc 'List approvals for merge request'
get 'approvals' do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
present_approval(merge_request)
end
# Approve a merge request
#
# Parameters:
# id (required) - The ID of a project
# merge_request_iid (required) - IID of MR
# Examples:
# POST /projects/:id/merge_requests/:merge_request_iid/approve
#
desc 'Approve a merge request'
params do
optional :sha, type: String, desc: 'When present, must have the HEAD SHA of the source branch'
use :ee_approval_params
end
post 'approve' do
merge_request = find_merge_request_with_access(params[:merge_request_iid], :approve_merge_request)
check_sha_param!(params, merge_request)
success =
::MergeRequests::ApprovalService
.new(user_project, current_user, params)
.execute(merge_request)
unauthorized! unless success
present_approval(merge_request)
end
desc 'Remove an approval from a merge request'
post 'unapprove' do
merge_request = find_merge_request_with_access(params[:merge_request_iid], :approve_merge_request)
success = ::MergeRequests::RemoveApprovalService
.new(user_project, current_user)
.execute(merge_request)
not_found! unless success
present_approval(merge_request)
end
end
end
end
end
API::MergeRequestApprovals.prepend_if_ee('EE::API::MergeRequestApprovals')
......@@ -68,12 +68,6 @@ module API
mr.all_pipelines
end
def check_sha_param!(params, merge_request)
if params[:sha] && merge_request.diff_head_sha != params[:sha]
render_api_error!("SHA does not match HEAD of source branch: #{merge_request.diff_head_sha}", 409)
end
end
def automatically_mergeable?(merge_when_pipeline_succeeds, merge_request)
pipeline_active = merge_request.head_pipeline_active? || merge_request.actual_head_pipeline_active?
merge_when_pipeline_succeeds && merge_request.mergeable_state?(skip_ci_check: true) && pipeline_active
......
......@@ -51,3 +51,4 @@ stop_dast_environment:
- if: $CI_COMMIT_BRANCH &&
$CI_KUBERNETES_ACTIVE &&
$GITLAB_FEATURES =~ /\bdast\b/
when: always
# frozen_string_literal: true
module Gitlab
module Database
class BackgroundMigrationJob < ActiveRecord::Base # rubocop:disable Rails/ApplicationRecord
self.table_name = :background_migration_jobs
scope :for_migration_execution, -> (class_name, arguments) do
where('class_name = ? AND arguments = ?', class_name, arguments.to_json)
end
enum status: {
pending: 0,
succeeded: 1
}
def self.mark_all_as_succeeded(class_name, arguments)
self.pending.for_migration_execution(class_name, arguments)
.update_all("status = #{statuses[:succeeded]}, updated_at = NOW()")
end
end
end
end
......@@ -64,6 +64,10 @@ module Gitlab
# delay_interval - The duration between each job's scheduled time (must respond to `to_f`)
# batch_size - The maximum number of rows per job
# other_arguments - Other arguments to send to the job
# track_jobs - When this flag is set, creates a record in the background_migration_jobs table for each job that
# is scheduled to be run. These records can be used to trace execution of the background job, but there is no
# builtin support to manage that automatically at this time. You should only set this flag if you are aware of
# how it works, and intend to manually cleanup the database records in your background job.
#
# *Returns the final migration delay*
#
......@@ -83,7 +87,7 @@ module Gitlab
# # do something
# end
# end
def queue_background_migration_jobs_by_range_at_intervals(model_class, job_class_name, delay_interval, batch_size: BACKGROUND_MIGRATION_BATCH_SIZE, other_job_arguments: [], initial_delay: 0)
def queue_background_migration_jobs_by_range_at_intervals(model_class, job_class_name, delay_interval, batch_size: BACKGROUND_MIGRATION_BATCH_SIZE, other_job_arguments: [], initial_delay: 0, track_jobs: false)
raise "#{model_class} does not have an ID to use for batch ranges" unless model_class.column_names.include?('id')
# To not overload the worker too much we enforce a minimum interval both
......@@ -101,7 +105,10 @@ module Gitlab
# the same time, which is not helpful in most cases where we wish to
# spread the work over time.
final_delay = initial_delay + delay_interval * index
migrate_in(final_delay, job_class_name, [start_id, end_id] + other_job_arguments)
full_job_arguments = [start_id, end_id] + other_job_arguments
track_in_database(job_class_name, full_job_arguments) if track_jobs
migrate_in(final_delay, job_class_name, full_job_arguments)
end
final_delay
......@@ -138,6 +145,12 @@ module Gitlab
def with_migration_context(&block)
Gitlab::ApplicationContext.with_context(caller_id: self.class.to_s, &block)
end
private
def track_in_database(class_name, arguments)
Gitlab::Database::BackgroundMigrationJob.create!(class_name: class_name, arguments: arguments)
end
end
end
end
......
......@@ -27,11 +27,13 @@ module Gitlab
parent_batch_relation = relation_scoped_to_range(source_table, source_column, start_id, stop_id)
parent_batch_relation.each_batch(of: SUB_BATCH_SIZE) do |sub_batch|
start_id, stop_id = sub_batch.pluck(Arel.sql("MIN(#{source_column}), MAX(#{source_column})")).first
sub_start_id, sub_stop_id = sub_batch.pluck(Arel.sql("MIN(#{source_column}), MAX(#{source_column})")).first
bulk_copy.copy_between(start_id, stop_id)
bulk_copy.copy_between(sub_start_id, sub_stop_id)
sleep(PAUSE_SECONDS)
end
mark_jobs_as_succeeded(start_id, stop_id, source_table, partitioned_table, source_column)
end
private
......@@ -56,6 +58,10 @@ module Gitlab
define_batchable_model(source_table).where(source_key_column => start_id..stop_id)
end
def mark_jobs_as_succeeded(*arguments)
BackgroundMigrationJob.mark_all_as_succeeded(self.class.name, arguments)
end
# Helper class to copy data between two tables via upserts
class BulkCopy
DELIMITER = ', '
......
......@@ -258,7 +258,8 @@ module Gitlab
MIGRATION_CLASS_NAME,
BATCH_INTERVAL,
batch_size: BATCH_SIZE,
other_job_arguments: [source_table_name.to_s, partitioned_table_name, source_key])
other_job_arguments: [source_table_name.to_s, partitioned_table_name, source_key],
track_jobs: true)
end
end
end
......
......@@ -209,6 +209,11 @@ module QA
autoload :Show, 'qa/page/group/show'
autoload :Menu, 'qa/page/group/menu'
module Milestone
autoload :Index, 'qa/page/group/milestone/index'
autoload :New, 'qa/page/group/milestone/new'
end
module SubMenus
autoload :Common, 'qa/page/group/sub_menus/common'
autoload :Members, 'qa/page/group/sub_menus/members'
......@@ -219,6 +224,12 @@ module QA
end
end
module Milestone
autoload :Index, 'qa/page/milestone/index'
autoload :New, 'qa/page/milestone/new'
autoload :Show, 'qa/page/milestone/show'
end
module File
autoload :Form, 'qa/page/file/form'
autoload :Show, 'qa/page/file/show'
......@@ -320,7 +331,6 @@ module QA
module Milestone
autoload :New, 'qa/page/project/milestone/new'
autoload :Index, 'qa/page/project/milestone/index'
autoload :Show, 'qa/page/project/milestone/show'
end
module Operations
......
......@@ -7,9 +7,11 @@ module QA
include SubMenus::Common
view 'app/views/layouts/nav/sidebar/_group.html.haml' do
element :group_settings_item
element :group_members_item
element :general_settings_link
element :group_issues_item
element :group_members_item
element :group_milestones_link
element :group_settings_item
end
view 'app/views/layouts/nav/sidebar/_analytics_links.html.haml' do
......@@ -44,6 +46,25 @@ module QA
end
end
end
def go_to_milestones
hover_issues do
within_submenu do
click_element(:group_milestones_link)
end
end
end
private
def hover_issues
within_sidebar do
scroll_to_element(:group_issues_item)
find_element(:group_issues_item).hover
yield
end
end
end
end
end
......
# frozen_string_literal: true
module QA
module Page
module Group
module Milestone
class Index < Page::Milestone::Index
view 'app/views/groups/milestones/index.html.haml' do
element :new_group_milestone_link
end
def click_new_milestone_link
click_element(:new_group_milestone_link)
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module Page
module Group
module Milestone
class New < Page::Milestone::New
view 'app/views/groups/milestones/_form.html.haml' do
element :create_milestone_button
element :milestone_description_field
element :milestone_title_field
end
def click_create_milestone_button
click_element(:create_milestone_button)
end
def set_description(description)
fill_element(:milestone_description_field, description)
end
def set_title(title)
fill_element(:milestone_title_field, title)
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module Page
module Milestone
class Index < Page::Base
view 'app/views/shared/milestones/_milestone.html.haml' do
element :milestone_link
end
def click_milestone(milestone)
click_element(:milestone_link, milestone_title: milestone.title)
end
def has_milestone?(milestone)
has_element?(:milestone_link, milestone_title: milestone.title)
end
end
end
end
end
# frozen_string_literal: true
module QA
module Page
module Milestone
class New < Page::Base
view 'app/views/shared/milestones/_form_dates.html.haml' do
element :due_date_field
element :start_date_field
end
def set_due_date(due_date)
fill_element(:due_date_field, due_date.to_s + "\n")
end
def set_start_date(start_date)
fill_element(:start_date_field, start_date.to_s + "\n")
end
end
end
end
end
# frozen_string_literal: true
module QA
module Page
module Milestone
class Show < Page::Base
include Support::Dates
view 'app/views/shared/milestones/_description.html.haml' do
element :milestone_description_content
element :milestone_title_content, required: true
end
view 'app/views/shared/milestones/_sidebar.html.haml' do
element :due_date_content
element :start_date_content
end
def has_due_date?(due_date)
formatted_due_date = format_date(due_date)
has_element?(:due_date_content, text: formatted_due_date)
end
def has_start_date?(start_date)
formatted_start_date = format_date(start_date)
has_element?(:start_date_content, text: formatted_start_date)
end
end
end
end
end
QA::Page::Milestone::Show.prepend_if_ee('QA::EE::Page::Milestone::Show')
......@@ -4,7 +4,7 @@ module QA
module Page
module Project
module Milestone
class Index < Page::Base
class Index < Page::Milestone::Index
view 'app/views/projects/milestones/index.html.haml' do
element :new_project_milestone_link
end
......
......@@ -4,7 +4,7 @@ module QA
module Page
module Project
module Milestone
class New < Page::Base
class New < Page::Milestone::New
view 'app/views/projects/milestones/_form.html.haml' do
element :create_milestone_button
element :milestone_description_field
......
# frozen_string_literal: true
module QA
module Page
module Project
module Milestone
class Show < ::QA::Page::Base
include Support::Dates
view 'app/views/shared/milestones/_description.html.haml' do
element :milestone_title_content, required: true
element :milestone_description_content
end
view 'app/views/shared/milestones/_sidebar.html.haml' do
element :due_date_content
element :start_date_content
end
def has_due_date?(due_date)
formatted_due_date = format_date(due_date)
has_element?(:due_date_content, text: formatted_due_date)
end
def has_start_date?(start_date)
formatted_start_date = format_date(start_date)
has_element?(:start_date_content, text: formatted_start_date)
end
end
end
end
end
end
QA::Page::Project::Milestone::Show.prepend_if_ee('QA::EE::Page::Project::Milestone::Show')
......@@ -7,6 +7,7 @@ module QA
attribute :id
attribute :title
attribute :description
attribute :group do
Group.fabricate_via_api! do |resource|
......@@ -16,6 +17,7 @@ module QA
def initialize
@title = "group-milestone-#{SecureRandom.hex(4)}"
@description = "My awesome group milestone."
end
def api_get_path
......@@ -28,12 +30,28 @@ module QA
def api_post_body
{
title: title
title: title,
description: description
}.tap do |hash|
hash[:start_date] = @start_date if @start_date
hash[:due_date] = @due_date if @due_date
end
end
def fabricate!
group.visit!
Page::Group::Menu.perform(&:go_to_milestones)
Page::Group::Milestone::Index.perform(&:click_new_milestone_link)
Page::Group::Milestone::New.perform do |new_milestone|
new_milestone.set_title(@title)
new_milestone.set_description(@description)
new_milestone.set_start_date(@start_date) if @start_date
new_milestone.set_due_date(@due_date) if @due_date
new_milestone.click_create_milestone_button
end
end
end
end
end
# frozen_string_literal: true
module QA
RSpec.describe 'Plan' do
describe 'Group milestone' do
include Support::Dates
let(:title) { 'Group milestone' }
let(:description) { 'This milestone tests out group milestones.' }
let(:start_date) { current_date_yyyy_mm_dd }
let(:due_date) { next_month_yyyy_mm_dd }
before do
Flow::Login.sign_in
end
it 'creates a group milestone' do
group_milestone = Resource::GroupMilestone.fabricate_via_browser_ui! do |milestone|
milestone.title = title
milestone.description = description
milestone.start_date = start_date
milestone.due_date = due_date
end
Page::Group::Menu.perform(&:go_to_milestones)
Page::Group::Milestone::Index.perform do |milestone_list|
expect(milestone_list).to have_milestone(group_milestone)
milestone_list.click_milestone(group_milestone)
end
Page::Milestone::Show.perform do |milestone|
expect(milestone).to have_element(:milestone_title_content, text: title)
expect(milestone).to have_element(:milestone_description_content, text: description)
expect(milestone).to have_start_date(start_date)
expect(milestone).to have_due_date(due_date)
end
end
end
end
end
# frozen_string_literal: true
module QA
context 'Plan' do
RSpec.describe 'Plan' do
describe 'Project milestone' do
include Support::Dates
......@@ -29,7 +29,7 @@ module QA
milestone_list.click_milestone(project_milestone)
end
Page::Project::Milestone::Show.perform do |milestone|
Page::Milestone::Show.perform do |milestone|
expect(milestone).to have_element(:milestone_title_content, text: title)
expect(milestone).to have_element(:milestone_description_content, text: description)
expect(milestone).to have_start_date(start_date)
......
# frozen_string_literal: true
FactoryBot.define do
factory :background_migration_job, class: '::Gitlab::Database::BackgroundMigrationJob' do
class_name { 'TestJob' }
status { :pending }
arguments { [] }
trait :succeeded do
status { :succeeded }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Database::BackgroundMigrationJob do
it_behaves_like 'having unique enum values'
describe '.for_migration_execution' do
let!(:job1) { create(:background_migration_job) }
let!(:job2) { create(:background_migration_job, arguments: ['hi', 2]) }
let!(:job3) { create(:background_migration_job, class_name: 'OtherJob', arguments: ['hi', 2]) }
it 'returns jobs matching class_name and arguments' do
relation = described_class.for_migration_execution('TestJob', ['hi', 2])
expect(relation.count).to eq(1)
expect(relation.first).to have_attributes(class_name: 'TestJob', arguments: ['hi', 2])
end
end
describe '.mark_all_as_succeeded' do
let!(:job1) { create(:background_migration_job, arguments: [1, 100]) }
let!(:job2) { create(:background_migration_job, arguments: [1, 100]) }
let!(:job3) { create(:background_migration_job, arguments: [101, 200]) }
let!(:job4) { create(:background_migration_job, class_name: 'OtherJob', arguments: [1, 100]) }
it 'marks all matching jobs as succeeded' do
expect { described_class.mark_all_as_succeeded('TestJob', [1, 100]) }
.to change { described_class.succeeded.count }.from(0).to(2)
expect(job1.reload).to be_succeeded
expect(job2.reload).to be_succeeded
expect(job3.reload).to be_pending
expect(job4.reload).to be_pending
end
context 'when previous matching jobs have already succeeded' do
let(:initial_time) { Time.now.round }
let!(:job1) { create(:background_migration_job, :succeeded, created_at: initial_time, updated_at: initial_time) }
it 'does not update non-pending jobs' do
Timecop.freeze(initial_time + 1.day) do
expect { described_class.mark_all_as_succeeded('TestJob', [1, 100]) }
.to change { described_class.succeeded.count }.from(1).to(2)
end
expect(job1.reload.updated_at).to eq(initial_time)
expect(job2.reload).to be_succeeded
expect(job3.reload).to be_pending
expect(job4.reload).to be_pending
end
end
end
end
......@@ -156,6 +156,37 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
end
end
end
context 'with track_jobs option' do
it 'creates a record for each job in the database' do
Sidekiq::Testing.fake! do
expect do
model.queue_background_migration_jobs_by_range_at_intervals(User, 'FooJob', 10.minutes,
other_job_arguments: [1, 2], track_jobs: true)
end.to change { Gitlab::Database::BackgroundMigrationJob.count }.from(0).to(1)
expect(BackgroundMigrationWorker.jobs.size).to eq(1)
tracked_job = Gitlab::Database::BackgroundMigrationJob.first
expect(tracked_job.class_name).to eq('FooJob')
expect(tracked_job.arguments).to eq([id1, id3, 1, 2])
expect(tracked_job).to be_pending
end
end
end
context 'without track_jobs option' do
it 'does not create records in the database' do
Sidekiq::Testing.fake! do
expect do
model.queue_background_migration_jobs_by_range_at_intervals(User, 'FooJob', 10.minutes, other_job_arguments: [1, 2])
end.not_to change { Gitlab::Database::BackgroundMigrationJob.count }
expect(BackgroundMigrationWorker.jobs.size).to eq(1)
end
end
end
end
context "when the model doesn't have an ID column" do
......
......@@ -96,6 +96,17 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::BackfillPartition
subject.perform(source1.id, source3.id, source_table, destination_table, unique_key)
end
it 'marks each job record as succeeded after processing' do
create(:background_migration_job, class_name: described_class.name,
arguments: [source1.id, source3.id, source_table, destination_table, unique_key])
expect(::Gitlab::Database::BackgroundMigrationJob).to receive(:mark_all_as_succeeded).and_call_original
expect do
subject.perform(source1.id, source3.id, source_table, destination_table, unique_key)
end.to change { ::Gitlab::Database::BackgroundMigrationJob.succeeded.count }.from(0).to(1)
end
context 'when the feature flag is disabled' do
let(:mock_connection) { double('connection') }
......
......@@ -197,6 +197,8 @@ RSpec.describe MergeRequest do
end
describe 'validation' do
subject { build_stubbed(:merge_request) }
it { is_expected.to validate_presence_of(:target_branch) }
it { is_expected.to validate_presence_of(:source_branch) }
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::MergeRequestApprovals do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public, :repository, creator: user, namespace: user.namespace) }
let_it_be(:approver) { create :user }
let_it_be(:group) { create :group }
let(:merge_request) { create(:merge_request, :simple, author: user, source_project: project) }
describe 'GET :id/merge_requests/:merge_request_iid/approvals' do
it 'retrieves the approval status' do
project.add_developer(approver)
project.add_developer(create(:user))
create(:approval, user: approver, merge_request: merge_request)
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/approvals", user)
expect(response).to have_gitlab_http_status(:ok)
end
end
describe 'POST :id/merge_requests/:merge_request_iid/approve' do
context 'as a valid approver' do
let_it_be(:approver) { create(:user) }
before do
project.add_developer(approver)
project.add_developer(create(:user))
end
def approve(extra_params = {})
post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/approve", approver), params: extra_params
end
context 'when the sha param is not set' do
it 'approves the merge request' do
approve
expect(response).to have_gitlab_http_status(:created)
end
end
context 'when the sha param is correct' do
it 'approves the merge request' do
approve(sha: merge_request.diff_head_sha)
expect(response).to have_gitlab_http_status(:created)
end
end
context 'when the sha param is incorrect' do
it 'does not approve the merge request' do
approve(sha: merge_request.diff_head_sha.reverse)
expect(response).to have_gitlab_http_status(:conflict)
expect(merge_request.approvals).to be_empty
end
end
end
end
describe 'POST :id/merge_requests/:merge_request_iid/unapprove' do
context 'as a user who has approved the merge request' do
it 'unapproves the merge request' do
unapprover = create(:user)
project.add_developer(approver)
project.add_developer(unapprover)
project.add_developer(create(:user))
create(:approval, user: approver, merge_request: merge_request)
create(:approval, user: unapprover, merge_request: merge_request)
post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/unapprove", unapprover)
expect(response).to have_gitlab_http_status(:created)
end
end
end
end
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册