Commit 571d993b authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 9044365a
......@@ -213,7 +213,7 @@
- name: postgres:9.6
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine
- name: elasticsearch:5.6.12
- name: elasticsearch:6.4.2
.use-pg10-ee:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.12-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.16-postgresql-10-graphicsmagick-1.3.33"
......@@ -221,7 +221,7 @@
- name: postgres:10.9
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine
- name: elasticsearch:5.6.12
- name: elasticsearch:6.4.2
.only-ee:
only:
......
......@@ -361,6 +361,9 @@ RSpec/MissingExampleGroupArgument:
RSpec/UnspecifiedException:
Enabled: false
RSpec/HaveGitlabHttpStatus:
Enabled: false
Style/MultilineWhenThen:
Enabled: false
......
......@@ -19,7 +19,7 @@ gem 'default_value_for', '~> 3.3.0'
gem 'pg', '~> 1.1'
gem 'rugged', '~> 0.28'
gem 'grape-path-helpers', '~> 1.1'
gem 'grape-path-helpers', '~> 1.2'
gem 'faraday', '~> 0.12'
gem 'marginalia', '~> 1.8.0'
......
......@@ -432,7 +432,7 @@ GEM
grape-entity (0.7.1)
activesupport (>= 4.0)
multi_json (>= 1.3.2)
grape-path-helpers (1.1.0)
grape-path-helpers (1.2.0)
activesupport
grape (~> 1.0)
rake (~> 12)
......@@ -1230,7 +1230,7 @@ DEPENDENCIES
gpgme (~> 2.0.19)
grape (~> 1.1.0)
grape-entity (~> 0.7.1)
grape-path-helpers (~> 1.1)
grape-path-helpers (~> 1.2)
grape_logging (~> 1.7)
graphiql-rails (~> 1.4.10)
graphql (~> 1.9.11)
......
......@@ -30,6 +30,14 @@ export default {
},
mixins: [timeagoMixin],
props: {
listPath: {
type: String,
required: true,
},
issueUpdatePath: {
type: String,
required: true,
},
issueId: {
type: String,
required: true,
......@@ -81,7 +89,14 @@ export default {
};
},
computed: {
...mapState('details', ['error', 'loading', 'loadingStacktrace', 'stacktraceData']),
...mapState('details', [
'error',
'loading',
'loadingStacktrace',
'stacktraceData',
'updatingResolveStatus',
'updatingIgnoreStatus',
]),
...mapGetters('details', ['stacktrace']),
reported() {
return sprintf(
......@@ -137,12 +152,15 @@ export default {
this.startPollingStacktrace(this.issueStackTracePath);
},
methods: {
...mapActions('details', ['startPollingDetails', 'startPollingStacktrace']),
...mapActions('details', ['startPollingDetails', 'startPollingStacktrace', 'updateStatus']),
trackClickErrorLinkToSentryOptions,
createIssue() {
this.issueCreationInProgress = true;
this.$refs.sentryIssueForm.submit();
},
updateIssueStatus(status) {
this.updateStatus({ endpoint: this.issueUpdatePath, redirectUrl: this.listPath, status });
},
formatDate(date) {
return `${this.timeFormatted(date)} (${dateFormat(date, 'UTC:yyyy-mm-dd h:MM:ssTT Z')})`;
},
......@@ -158,24 +176,42 @@ export default {
<div v-else-if="showDetails" class="error-details">
<div class="top-area align-items-center justify-content-between py-3">
<span v-if="!loadingStacktrace && stacktrace" v-html="reported"></span>
<form ref="sentryIssueForm" :action="projectIssuesPath" method="POST">
<gl-form-input class="hidden" name="issue[title]" :value="issueTitle" />
<input name="issue[description]" :value="issueDescription" type="hidden" />
<gl-form-input
:value="GQLerror.id"
class="hidden"
name="issue[sentry_issue_attributes][sentry_issue_identifier]"
<div class="d-inline-flex">
<loading-button
:label="__('Ignore')"
:loading="updatingIgnoreStatus"
@click="updateIssueStatus('ignored')"
/>
<gl-form-input :value="csrfToken" class="hidden" name="authenticity_token" />
<loading-button
v-if="!error.gitlab_issue"
class="btn-success"
:label="__('Create issue')"
:loading="issueCreationInProgress"
data-qa-selector="create_issue_button"
@click="createIssue"
class="btn-outline-info ml-2"
:label="__('Resolve')"
:loading="updatingResolveStatus"
@click="updateIssueStatus('resolved')"
/>
</form>
<form
ref="sentryIssueForm"
:action="projectIssuesPath"
method="POST"
class="d-inline-block ml-2"
>
<gl-form-input class="hidden" name="issue[title]" :value="issueTitle" />
<input name="issue[description]" :value="issueDescription" type="hidden" />
<gl-form-input
:value="GQLerror.id"
class="hidden"
name="issue[sentry_issue_attributes][sentry_issue_identifier]"
/>
<gl-form-input :value="csrfToken" class="hidden" name="authenticity_token" />
<loading-button
v-if="!error.gitlab_issue"
class="btn-success"
:label="__('Create issue')"
:loading="issueCreationInProgress"
data-qa-selector="create_issue_button"
@click="createIssue"
/>
</form>
</div>
</div>
<div>
<tooltip-on-truncate :title="GQLerror.title" truncate-target="child" placement="top">
......
......@@ -25,6 +25,8 @@ export default () => {
const {
issueId,
projectPath,
listPath,
issueUpdatePath,
issueDetailsPath,
issueStackTracePath,
projectIssuesPath,
......@@ -34,6 +36,8 @@ export default () => {
props: {
issueId,
projectPath,
listPath,
issueUpdatePath,
issueDetailsPath,
issueStackTracePath,
projectIssuesPath,
......
......@@ -4,4 +4,7 @@ export default {
getSentryData({ endpoint, params }) {
return axios.get(endpoint, { params });
},
updateErrorStatus(endpoint, status) {
return axios.put(endpoint, { status });
},
};
import service from './../services';
import * as types from './mutation_types';
import createFlash from '~/flash';
import { visitUrl } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
export function updateStatus({ commit }, { endpoint, redirectUrl, status }) {
const type =
status === 'resolved' ? types.SET_UPDATING_RESOLVE_STATUS : types.SET_UPDATING_IGNORE_STATUS;
commit(type, true);
return service
.updateErrorStatus(endpoint, status)
.then(() => visitUrl(redirectUrl))
.catch(() => createFlash(__('Failed to update issue status')))
.finally(() => commit(type, false));
}
export default () => {};
......@@ -3,4 +3,6 @@ export default () => ({
stacktraceData: {},
loading: true,
loadingStacktrace: true,
updatingResolveStatus: false,
updatingIgnoreStatus: false,
});
import Vue from 'vue';
import Vuex from 'vuex';
import * as actions from './actions';
import mutations from './mutations';
import * as listActions from './list/actions';
import listMutations from './list/mutations';
import listState from './list/state';
......@@ -24,8 +27,8 @@ export const createStore = () =>
details: {
namespaced: true,
state: detailsState(),
actions: detailsActions,
mutations: detailsMutations,
actions: { ...actions, ...detailsActions },
mutations: { ...mutations, ...detailsMutations },
getters: detailsGetters,
},
},
......
export const SET_UPDATING_RESOLVE_STATUS = 'SET_UPDATING_RESOLVE_STATUS';
export const SET_UPDATING_IGNORE_STATUS = 'SET_UPDATING_IGNORE_STATUS';
import * as types from './mutation_types';
export default {
[types.SET_UPDATING_IGNORE_STATUS](state, updating) {
state.updatingIgnoreStatus = updating;
},
[types.SET_UPDATING_RESOLVE_STATUS](state, updating) {
state.updatingResolveStatus = updating;
},
};
......@@ -2,6 +2,11 @@
li {
@include gl-line-height-32;
}
.btn-outline-info {
color: $blue-500;
border-color: $blue-500;
}
}
.stacktrace {
......
......@@ -222,7 +222,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
def metrics_dashboard_params
params
.permit(:embedded, :group, :title, :y_label, :dashboard_path, :environment)
.permit(:embedded, :group, :title, :y_label, :dashboard_path, :environment, :sample_metrics)
.merge(dashboard_path: params[:dashboard], environment: environment)
end
......
......@@ -51,7 +51,7 @@ class ProjectsController < Projects::ApplicationController
def edit
@badge_api_endpoint = expose_url(api_v4_projects_badges_path(id: @project.id))
render 'edit'
render_edit
end
def create
......@@ -85,7 +85,7 @@ class ProjectsController < Projects::ApplicationController
else
flash.now[:alert] = result[:message]
format.html { render 'edit' }
format.html { render_edit }
end
format.js
......@@ -387,7 +387,6 @@ class ProjectsController < Projects::ApplicationController
:merge_method,
:initialize_with_readme,
:autoclose_referenced_issues,
:suggestion_commit_message,
project_feature_attributes: %i[
builds_access_level
......@@ -488,6 +487,10 @@ class ProjectsController < Projects::ApplicationController
def rate_limiter
::Gitlab::ApplicationRateLimiter
end
def render_edit
render 'edit'
end
end
ProjectsController.prepend_if_ee('EE::ProjectsController')
......@@ -20,6 +20,7 @@ module Projects::ErrorTrackingHelper
{
'issue-id' => issue_id,
'project-path' => project.full_path,
'list-path' => project_error_tracking_index_path(project),
'issue-details-path' => details_project_error_tracking_index_path(*opts),
'issue-update-path' => update_project_error_tracking_index_path(*opts),
'project-issues-path' => project_issues_path(project),
......
......@@ -10,6 +10,8 @@ module ProtectedRef
validates :project, presence: true
delegate :matching, :matches?, :wildcard?, to: :ref_matcher
scope :for_project, ->(project) { where(project: project) }
end
def commit
......
......@@ -4,7 +4,7 @@ module DiffViewer
class Base
PARTIAL_PATH_PREFIX = 'projects/diffs/viewers'
class_attribute :partial_name, :type, :extensions, :file_types, :binary, :switcher_icon, :switcher_title
class_attribute :partial_name, :type, :extensions, :binary, :switcher_icon, :switcher_title
# These limits relate to the sum of the old and new blob sizes.
# Limits related to the actual size of the diff are enforced in Gitlab::Diff::File.
......@@ -50,7 +50,6 @@ module DiffViewer
return true if blob.nil?
return false if verify_binary && binary? != blob.binary_in_repo?
return true if extensions&.include?(blob.extension)
return true if file_types&.include?(blob.file_type)
false
end
......
---
title: Drop support for ES5 add support for ES7
merge_request: 22859
author:
type: added
---
title: Migrate the database to activate projects prometheus service integration for projects with prometheus installed on shared k8s cluster.
merge_request: 19956
author:
type: fixed
---
title: Add ability to ignore/resolve errors from error tracking detail page
merge_request: 22475
author:
type: added
---
title: Add deprecation warning to Rake tasks in sidekiq namespace
merge_request:
author:
type: removed
---
title: Include subgroups when searching inside a group
merge_request: 22991
author:
type: fixed
---
title: Add slug to services API response
merge_request: 22518
author:
type: added
---
title: Backend for allowing sample metrics to be toggled from ui
merge_request: 22901
author:
type: added
......@@ -18,6 +18,32 @@ Gitlab.ee do
Elasticsearch::Model::ClassMethods.prepend GemExtensions::Elasticsearch::Model::Client
Elasticsearch::Model.singleton_class.prepend GemExtensions::Elasticsearch::Model::Client
# This monkey patch cannot be handled by prepend like the above since this
# module is included into other classes.
module Elasticsearch
module Model
module Response
module Base
if Gem::Version.new(Elasticsearch::Model::VERSION) >= Gem::Version.new('7.0.0')
raise "elasticsearch-model was upgraded, please remove this monkey patch in #{__FILE__}"
end
# Handle ES7 API where total is returned as an object. This
# change is taken from the V7 gem
# https://github.com/elastic/elasticsearch-rails/commit/9c40f630e1b549f0b7889fe33dcd826b485af6fc
# and can be removed when we upgrade the gem to V7
def total
if response.response['hits']['total'].respond_to?(:keys)
response.response['hits']['total']['value']
else
response.response['hits']['total']
end
end
end
end
end
end
### Modified from elasticsearch-model/lib/elasticsearch/model.rb
[
......
......@@ -242,7 +242,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get '/prometheus/api/v1/*proxy_path', to: 'environments/prometheus_api#proxy', as: :prometheus_api
get '/sample_metrics', to: 'environments/sample_metrics#query' if ENV['USE_SAMPLE_METRICS']
get '/sample_metrics', to: 'environments/sample_metrics#query'
end
collection do
......
......@@ -9,8 +9,7 @@ Gitlab::Seeder.quiet do
state: [:active, :closed].sample,
}
milestone = Milestones::CreateService.new(
project, project.team.users.sample, milestone_params).execute
Milestones::CreateService.new(project, project.team.users.sample, milestone_params).execute
print '.'
end
......
# frozen_string_literal: true
class CreateApprovalProjectRulesProtectedBranches < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
create_table :approval_project_rules_protected_branches, id: false do |t|
t.references :approval_project_rule,
null: false,
index: false,
foreign_key: { on_delete: :cascade }
t.references :protected_branch,
null: false,
index: { name: 'index_approval_project_rules_protected_branches_pb_id' },
foreign_key: { on_delete: :cascade }
t.index [:approval_project_rule_id, :protected_branch_id], name: 'index_approval_project_rules_protected_branches_unique', unique: true, using: :btree
end
end
end
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddTemporaryPartialIndexOnProjectIdToServices < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
INDEX_NAME = 'tmp_index_on_project_id_partial_with_prometheus_services'
PARTIAL_FILTER = "type = 'PrometheusService'"
disable_ddl_transaction!
def up
add_concurrent_index :services, :project_id, where: PARTIAL_FILTER, name: INDEX_NAME
end
def down
remove_concurrent_index :services, :project_id, where: PARTIAL_FILTER, name: INDEX_NAME
end
end
# frozen_string_literal: true
class PatchPrometheusServicesForSharedClusterApplications < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
MIGRATION = 'ActivatePrometheusServicesForSharedClusterApplications'.freeze
BATCH_SIZE = 500
DELAY = 2.minutes
disable_ddl_transaction!
module Migratable
module Applications
class Prometheus < ActiveRecord::Base
self.table_name = 'clusters_applications_prometheus'
enum status: {
errored: -1,
installed: 3,
updated: 5
}
end
end
class Project < ActiveRecord::Base
self.table_name = 'projects'
include ::EachBatch
scope :with_application_on_group_clusters, -> {
joins("INNER JOIN namespaces ON namespaces.id = projects.namespace_id")
.joins("INNER JOIN cluster_groups ON cluster_groups.group_id = namespaces.id")
.joins("INNER JOIN clusters ON clusters.id = cluster_groups.cluster_id AND clusters.cluster_type = #{Cluster.cluster_types['group_type']}")
.joins("INNER JOIN clusters_applications_prometheus ON clusters_applications_prometheus.cluster_id = clusters.id
AND clusters_applications_prometheus.status IN (#{Applications::Prometheus.statuses[:installed]}, #{Applications::Prometheus.statuses[:updated]})")
}
scope :without_active_prometheus_services, -> {
joins("LEFT JOIN services ON services.project_id = projects.id AND services.type = 'PrometheusService'")
.where("services.id IS NULL OR (services.active = FALSE AND services.properties = '{}')")
}
end
class Cluster < ActiveRecord::Base
self.table_name = 'clusters'
enum cluster_type: {
instance_type: 1,
group_type: 2
}
def self.has_prometheus_application?
joins("INNER JOIN clusters_applications_prometheus ON clusters_applications_prometheus.cluster_id = clusters.id
AND clusters_applications_prometheus.status IN (#{Applications::Prometheus.statuses[:installed]}, #{Applications::Prometheus.statuses[:updated]})").exists?
end
end
end
def up
projects_without_active_prometheus_service.group('projects.id').each_batch(of: BATCH_SIZE) do |batch, index|
bg_migrations_batch = batch.select('projects.id').map { |project| [MIGRATION, project.id] }
delay = index * DELAY
BackgroundMigrationWorker.bulk_perform_in(delay.seconds, bg_migrations_batch)
end
end
def down
# no-op
end
private
def projects_without_active_prometheus_service
scope = Migratable::Project.without_active_prometheus_services
return scope if migrate_instance_cluster?
scope.with_application_on_group_clusters
end
def migrate_instance_cluster?
if instance_variable_defined?('@migrate_instance_cluster')
@migrate_instance_cluster
else
@migrate_instance_cluster = Migratable::Cluster.instance_type.has_prometheus_application?
end
end
end
......@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_01_13_133352) do
ActiveRecord::Schema.define(version: 2020_01_14_113341) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
......@@ -437,6 +437,13 @@ ActiveRecord::Schema.define(version: 2020_01_13_133352) do
t.index ["group_id"], name: "index_approval_project_rules_groups_2"
end
create_table "approval_project_rules_protected_branches", id: false, force: :cascade do |t|
t.bigint "approval_project_rule_id", null: false
t.bigint "protected_branch_id", null: false
t.index ["approval_project_rule_id", "protected_branch_id"], name: "index_approval_project_rules_protected_branches_unique", unique: true
t.index ["protected_branch_id"], name: "index_approval_project_rules_protected_branches_pb_id"
end
create_table "approval_project_rules_users", force: :cascade do |t|
t.bigint "approval_project_rule_id", null: false
t.integer "user_id", null: false
......@@ -3769,6 +3776,7 @@ ActiveRecord::Schema.define(version: 2020_01_13_133352) do
t.string "description", limit: 500
t.boolean "comment_on_event_enabled", default: true, null: false
t.index ["project_id"], name: "index_services_on_project_id"
t.index ["project_id"], name: "tmp_index_on_project_id_partial_with_prometheus_services", where: "((type)::text = 'PrometheusService'::text)"
t.index ["template"], name: "index_services_on_template"
t.index ["type"], name: "index_services_on_type"
end
......@@ -4448,6 +4456,8 @@ ActiveRecord::Schema.define(version: 2020_01_13_133352) do
add_foreign_key "approval_project_rules", "projects", on_delete: :cascade
add_foreign_key "approval_project_rules_groups", "approval_project_rules", on_delete: :cascade
add_foreign_key "approval_project_rules_groups", "namespaces", column: "group_id", on_delete: :cascade
add_foreign_key "approval_project_rules_protected_branches", "approval_project_rules", on_delete: :cascade
add_foreign_key "approval_project_rules_protected_branches", "protected_branches", on_delete: :cascade
add_foreign_key "approval_project_rules_users", "approval_project_rules", on_delete: :cascade
add_foreign_key "approval_project_rules_users", "users", on_delete: :cascade
add_foreign_key "approvals", "merge_requests", name: "fk_310d714958", on_delete: :cascade
......
......@@ -81,7 +81,15 @@ Example response:
},
"created_at": "2019-11-27T03:37:38.711Z",
"build_info": {
"pipeline_id": 123
"pipeline": {
"id": 123,
"status": "pending",
"ref": "new-pipeline",
"sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
"web_url": "https://example.com/foo/bar/pipelines/47",
"created_at": "2016-08-11T11:28:34.085Z",
"updated_at": "2016-08-11T11:32:35.169Z",
}
}
},
{
......@@ -95,7 +103,15 @@ Example response:
},
"created_at": "2019-11-27T03:37:38.711Z",
"build_info": {
"pipeline_id": 123
"pipeline": {
"id": 123,
"status": "pending",
"ref": "new-pipeline",
"sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
"web_url": "https://example.com/foo/bar/pipelines/47",
"created_at": "2016-08-11T11:28:34.085Z",
"updated_at": "2016-08-11T11:32:35.169Z",
}
}
}
]
......@@ -141,7 +157,15 @@ Example response:
},
"created_at": "2019-11-27T03:37:38.711Z",
"build_info": {
"pipeline_id": 123
"pipeline": {
"id": 123,
"status": "pending",
"ref": "new-pipeline",
"sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
"web_url": "https://example.com/foo/bar/pipelines/47",
"created_at": "2016-08-11T11:28:34.085Z",
"updated_at": "2016-08-11T11:32:35.169Z",
}
}
}
```
......
......@@ -19,6 +19,7 @@ Example response:
{
"id": 75,
"title": "Jenkins CI",
"slug": "jenkins",
"created_at": "2019-11-20T11:20:25.297Z",
"updated_at": "2019-11-20T12:24:37.498Z",
"active": true,
......@@ -38,6 +39,7 @@ Example response:
{
"id": 76,
"title": "Alerts endpoint",
"slug": "alerts",
"created_at": "2019-11-20T11:20:25.297Z",
"updated_at": "2019-11-20T12:24:37.498Z",
"active": true,
......@@ -753,6 +755,7 @@ Example response:
{
"id": 4,
"title": "Slack slash commands",
"slug": "slack-slash-commands",
"created_at": "2017-06-27T05:51:39-07:00",
"updated_at": "2017-06-27T05:51:39-07:00",
"active": true,
......
......@@ -39,8 +39,25 @@ Some credentials are required to be able to run `aws` commands:
```yml
deploy:
stage: deploy
image: registry.gitlab.com/gitlab-org/cloud-deploy:latest
image: registry.gitlab.com/gitlab-org/cloud-deploy:latest # see the note below
script:
- aws s3 ...
- aws create-deployment ...
```
NOTE: **Note:**
Please note that the image used in the example above
(`registry.gitlab.com/gitlab-org/cloud-deploy:latest`) is hosted on the [GitLab
Container Registry](../../user/packages/container_registry/index.md) and is
ready to use. Alternatively, replace the image with another one hosted on [AWS ECR](#aws-ecr).
### AWS ECR
Instead of referencing an image hosted on the GitLab Registry, you are free to
reference any other image hosted on any third-party registry, such as
[Amazon Elastic Container Registry (ECR)](https://aws.amazon.com/ecr).
To do so, please make sure to [push your image into your ECR
repository](https://docs.aws.amazon.com/AmazonECR/latest/userguide/docker-push-ecr-image.html)
before referencing it in your `.gitlab-ci.yml` file and replace the `image`
path to point to your ECR.
......@@ -26,7 +26,7 @@ describe API::Labels do
get api("/projects/#{project.id}/labels", user)
expect(response).to have_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.first['name']).to eq('label1')
end
......@@ -35,7 +35,7 @@ describe API::Labels do
get api("/projects/#{project.id}/labels", user)
expect(response).to have_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.first['name']).to eq('label1')
end
end
......@@ -77,7 +77,7 @@ describe API::Labels do
get api("/projects/#{project.id}/labels", user)
expect(response).to have_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.first['name']).to eq('foo')
end
......@@ -86,7 +86,7 @@ describe API::Labels do
get api("/projects/#{project.id}/labels", user)
expect(response).to have_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.first['name']).to eq('bar')
end
end
......
......@@ -17,9 +17,10 @@ special searches:
| GitLab version | Elasticsearch version |
| -------------- | --------------------- |
| GitLab Enterprise Edition 8.4 - 8.17 | Elasticsearch 2.4 with [Delete By Query Plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/2.4/plugins-delete-by-query.html) installed |
| GitLab Enterprise Edition 8.4 - 8.17 | Elasticsearch 2.4 with [Delete By Query Plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/2.4/plugins-delete-by-query.html) installed |
| GitLab Enterprise Edition 9.0 - 11.4 | Elasticsearch 5.1 - 5.5 |
| GitLab Enterprise Edition 11.5+ | Elasticsearch 5.6 - 6.x |
| GitLab Enterprise Edition 11.5 - 12.6 | Elasticsearch 5.6 - 6.x |
| GitLab Enterprise Edition 12.7+ | Elasticsearch 6.x - 7.x |
## Installing Elasticsearch
......
......@@ -786,7 +786,9 @@ A footnote reference tag looks like this:[^1]
Reference tags can use letters and other characters.[^footnote-note]
[^footnote-note]: Avoid using lowercase `w` or an underscore (`_`) in your tag name until until an [upstream bug](https://gitlab.com/gitlab-org/gitlab/issues/24423) is resolved.
[^footnote-note]: Avoid using lowercase `w` or an underscore (`_`)
in your footnote tag name until an
[upstream bug](https://gitlab.com/gitlab-org/gitlab/issues/24423) is resolved.
```
A footnote reference tag looks like this:[^1]
......@@ -795,7 +797,9 @@ A footnote reference tag looks like this:[^1]
Reference tags can use letters and other characters.[^footnote-note]
[^footnote-note]: Avoid using lowercase `w` or an underscore (`_`) in your tag name until until an [upstream bug](https://gitlab.com/gitlab-org/gitlab/issues/24423) is resolved.
[^footnote-note]: Avoid using lowercase `w` or an underscore (`_`)
in your footnote tag name until an
[upstream bug](https://gitlab.com/gitlab-org/gitlab/issues/24423) is resolved.
### Headers
......
......@@ -613,6 +613,7 @@ module API
end
class ProtectedBranch < Grape::Entity
expose :id
expose :name
expose :push_access_levels, using: Entities::ProtectedRefAccess
expose :merge_access_levels, using: Entities::ProtectedRefAccess
......@@ -1128,7 +1129,11 @@ module API
end
class ProjectServiceBasic < Grape::Entity
expose :id, :title, :created_at, :updated_at, :active
expose :id, :title
expose :slug do |service|
service.to_param.dasherize
end
expose :created_at, :updated_at, :active
expose :commit_events, :push_events, :issues_events, :confidential_issues_events
expose :merge_requests_events, :tag_push_events, :note_events
expose :confidential_note_events, :pipeline_events, :wiki_page_events
......
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# Create missing PrometheusServices records or sets active attribute to true
# for all projects which belongs to cluster with Prometheus Application installed.
class ActivatePrometheusServicesForSharedClusterApplications
module Migratable
# Migration model namespace isolated from application code.
class PrometheusService < ActiveRecord::Base
self.inheritance_column = :_type_disabled
self.table_name = 'services'
default_scope { where("services.type = 'PrometheusService'") }
def self.for_project(project_id)
new(
project_id: project_id,
active: true,
properties: '{}',
type: 'PrometheusService',
template: false,
push_events: true,
issues_events: true,
merge_requests_events: true,
tag_push_events: true,
note_events: true,
category: 'monitoring',
default: false,
wiki_page_events: true,
pipeline_events: true,
confidential_issues_events: true,
commit_events: true,
job_events: true,
confidential_note_events: true,
deployment_events: false
)
end
def managed?
properties == '{}'
end
end
end
def perform(project_id)
service = Migratable::PrometheusService.find_by(project_id: project_id) || Migratable::PrometheusService.for_project(project_id)
service.update!(active: true) if service.managed?
end
end
end
end
......@@ -71,7 +71,7 @@ module Gitlab
# Convert from an indexed by name to an array indexed by path
# If a submodule doesn't have a path, it is considered bogus
# and is ignored
submodules_by_name.each_with_object({}) do |(name, data), results|
submodules_by_name.each_with_object({}) do |(_name, data), results|
path = data.delete 'path'
next unless path
......
......@@ -30,7 +30,7 @@ module Gitlab
# rubocop:enable CodeReuse/ActiveRecord
def issuable_params
super.merge(group_id: group.id)
super.merge(group_id: group.id, include_subgroups: true)
end
end
end
......@@ -16,7 +16,7 @@ module Gitlab
private
def endpoint_for_metric(metric)
if ENV['USE_SAMPLE_METRICS']
if params[:sample_metrics]
Gitlab::Routing.url_helpers.sample_metrics_project_environment_path(
project,
params[:environment],
......
# frozen_string_literal: true
require 'yaml'
require 'set'
module Gitlab
module SidekiqConfig
QUEUE_CONFIG_PATHS = begin
result = %w[app/workers/all_queues.yml]
result << 'ee/app/workers/all_queues.yml' if Gitlab.ee?
result
end.freeze
class << self
include Gitlab::SidekiqConfig::CliMethods
# This method is called by `ee/bin/sidekiq-cluster` in EE, which runs outside
# of bundler/Rails context, so we cannot use any gem or Rails methods.
def self.worker_queues(rails_path = Rails.root.to_s)
@worker_queues ||= {}
@worker_queues[rails_path] ||= QUEUE_CONFIG_PATHS.flat_map do |path|
full_path = File.join(rails_path, path)
File.exist?(full_path) ? YAML.load_file(full_path) : []
def redis_queues
# Not memoized, because this can change during the life of the application
Sidekiq::Queue.all.map(&:name)
end
end
# This method is called by `ee/bin/sidekiq-cluster` in EE, which runs outside
# of bundler/Rails context, so we cannot use any gem or Rails methods.
def self.expand_queues(queues, all_queues = self.worker_queues)
return [] if queues.empty?
queues_set = all_queues.to_set
queues.flat_map do |queue|
[queue, *queues_set.grep(/\A#{queue}:/)]
def config_queues
@config_queues ||= begin
config = YAML.load_file(Rails.root.join('config/sidekiq_queues.yml'))
config[:queues].map(&:first)
end
end
end
def self.redis_queues
# Not memoized, because this can change during the life of the application
Sidekiq::Queue.all.map(&:name)
end
def cron_workers
@cron_workers ||= Settings.cron_jobs.map { |job_name, options| options['job_class'].constantize }
end
def self.config_queues
@config_queues ||= begin
config = YAML.load_file(Rails.root.join('config/sidekiq_queues.yml'))
config[:queues].map(&:first)
def workers
@workers ||= begin
result = find_workers(Rails.root.join('app', 'workers'))
result.concat(find_workers(Rails.root.join('ee', 'app', 'workers'))) if Gitlab.ee?
result
end
end
end
def self.cron_workers
@cron_workers ||= Settings.cron_jobs.map { |job_name, options| options['job_class'].constantize }
end
private
def self.workers
@workers ||= begin
result = find_workers(Rails.root.join('app', 'workers'))
result.concat(find_workers(Rails.root.join('ee', 'app', 'workers'))) if Gitlab.ee?
result
end
end
def find_workers(root)
concerns = root.join('concerns').to_s
def self.find_workers(root)
concerns = root.join('concerns').to_s
workers = Dir[root.join('**', '*.rb')]
.reject { |path| path.start_with?(concerns) }
workers = Dir[root.join('**', '*.rb')]
.reject { |path| path.start_with?(concerns) }
workers.map! do |path|
ns = Pathname.new(path).relative_path_from(root).to_s.gsub('.rb', '')
workers.map! do |path|
ns = Pathname.new(path).relative_path_from(root).to_s.gsub('.rb', '')
ns.camelize.constantize
end
ns.camelize.constantize
# Skip things that aren't workers
workers.select { |w| w < Sidekiq::Worker }
end
# Skip things that aren't workers
workers.select { |w| w < Sidekiq::Worker }
end
end
end
# frozen_string_literal: true
require 'yaml'
require 'set'
# These methods are called by `sidekiq-cluster`, which runs outside of
# the bundler/Rails context, so we cannot use any gem or Rails methods.
module Gitlab
module SidekiqConfig
module CliMethods
# The methods in this module are used as module methods
# rubocop:disable Gitlab/ModuleWithInstanceVariables
extend self
QUEUE_CONFIG_PATHS = begin
result = %w[app/workers/all_queues.yml]
result << 'ee/app/workers/all_queues.yml' if Gitlab.ee?
result
end.freeze
def worker_queues(rails_path = Rails.root.to_s)
@worker_queues ||= {}
@worker_queues[rails_path] ||= QUEUE_CONFIG_PATHS.flat_map do |path|
full_path = File.join(rails_path, path)
File.exist?(full_path) ? YAML.load_file(full_path) : []
end
end
def expand_queues(queues, all_queues = self.worker_queues)
return [] if queues.empty?
queues_set = all_queues.to_set
queues.flat_map do |queue|
[queue, *queues_set.grep(/\A#{queue}:/)]
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end
end
end
namespace :sidekiq do
desc "GitLab | Stop sidekiq"
def deprecation_warning!
warn <<~WARNING
This task is deprecated and will be removed in 13.0 as it is thought to be unused.
If you are using this task, please comment on the below issue:
https://gitlab.com/gitlab-org/gitlab/issues/196731
WARNING
end
desc "[DEPRECATED] GitLab | Stop sidekiq"
task :stop do
deprecation_warning!
system(*%w(bin/background_jobs stop))
end
desc "GitLab | Start sidekiq"
desc "[DEPRECATED] GitLab | Start sidekiq"
task :start do
deprecation_warning!
system(*%w(bin/background_jobs start))
end
desc 'GitLab | Restart sidekiq'
desc '[DEPRECATED] GitLab | Restart sidekiq'
task :restart do
deprecation_warning!
system(*%w(bin/background_jobs restart))
end
desc "GitLab | Start sidekiq with launchd on Mac OS X"
desc "[DEPRECATED] GitLab | Start sidekiq with launchd on Mac OS X"
task :launchd do
deprecation_warning!
system(*%w(bin/background_jobs start_no_deamonize))
end
end
......@@ -7782,6 +7782,9 @@ msgstr ""
msgid "Failed to update environment!"
msgstr ""
msgid "Failed to update issue status"
msgstr ""
msgid "Failed to update issues, please try again."
msgstr ""
......@@ -9778,6 +9781,9 @@ msgstr ""
msgid "Iglu registry URL (optional)"
msgstr ""
msgid "Ignore"
msgstr ""
msgid "Image %{imageName} was scheduled for deletion from the registry."
msgstr ""
......@@ -15566,6 +15572,9 @@ msgstr ""
msgid "Resetting the authorization key will invalidate the previous key. Existing alert configurations will need to be updated with the new key."
msgstr ""
msgid "Resolve"
msgstr ""
msgid "Resolve all threads in new issue"
msgstr ""
......
......@@ -27,11 +27,10 @@ module QA
Flow::Project.add_member(project: project, username: user.username)
issue = Resource::Issue.fabricate_via_api! do |issue|
Resource::Issue.fabricate_via_api! do |issue|
issue.title = 'issue title'
issue.project = project
end
issue.visit!
end.visit!
Page::Project::Issue::Show.perform do |show|
show.select_all_activities_filter
......
# frozen_string_literal: true
require 'rack/utils'
module RuboCop
module Cop
module RSpec
# This cops checks for `have_http_status` usages in specs.
# It also discourages the usage of numeric HTTP status codes in
# `have_gitlab_http_status`.
#
# @example
#
# # bad
# expect(response).to have_http_status(200)
# expect(response).to have_http_status(:ok)
# expect(response).to have_gitlab_http_status(200)
#
# # good
# expect(response).to have_gitlab_http_status(:ok)
#
class HaveGitlabHttpStatus < RuboCop::Cop::Cop
CODE_TO_SYMBOL = Rack::Utils::SYMBOL_TO_STATUS_CODE.invert
MSG_MATCHER_NAME =
'Use `have_gitlab_http_status` instead of `have_http_status`.'
MSG_STATUS =
'Prefer named HTTP status `%{name}` over ' \
'its numeric representation `%{code}`.'
MSG_UNKNOWN = 'HTTP status `%{code}` is unknown. ' \
'Please provide a valid one or disable this cop.'
MSG_DOCS_LINK = 'https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#have_gitlab_http_status'
REPLACEMENT = 'have_gitlab_http_status(%{arg})'
def_node_matcher :have_http_status?, <<~PATTERN
(
send nil?
{
:have_http_status
:have_gitlab_http_status
}
_
)
PATTERN
def on_send(node)
return unless have_http_status?(node)
offenses = [
offense_for_name(node),
offense_for_status(node)
].compact
return if offenses.empty?
add_offense(node, message: message_for(offenses))
end
def autocorrect(node)
lambda do |corrector|
corrector.replace(node.source_range, replacement(node))
end
end
private
def offense_for_name(node)
return if method_name(node) == :have_gitlab_http_status
MSG_MATCHER_NAME
end
def offense_for_status(node)
code = extract_numeric_code(node)
return unless code
symbol = code_to_symbol(code)
return format(MSG_UNKNOWN, code: code) unless symbol
format(MSG_STATUS, name: symbol, code: code)
end
def message_for(offenses)
(offenses + [MSG_DOCS_LINK]).join(' ')
end
def replacement(node)
code = extract_numeric_code(node)
arg = code_to_symbol(code) || argument(node).source
format(REPLACEMENT, arg: arg)
end
def code_to_symbol(code)
CODE_TO_SYMBOL[code]&.inspect
end
def extract_numeric_code(node)
arg_node = argument(node)
return unless arg_node&.type == :int
arg_node.children[0]
end
def method_name(node)
node.children[1]
end
def argument(node)
node.children[2]
end
end
end
end
end
......@@ -40,6 +40,7 @@ require_relative 'cop/rspec/be_success_matcher'
require_relative 'cop/rspec/env_assignment'
require_relative 'cop/rspec/factories_in_migration_specs'
require_relative 'cop/rspec/top_level_describe_path'
require_relative 'cop/rspec/have_gitlab_http_status'
require_relative 'cop/qa/element_with_pattern'
require_relative 'cop/qa/ambiguous_page_object_name'
require_relative 'cop/sidekiq_options_queue'
......
......@@ -9,17 +9,6 @@ describe Projects::Environments::SampleMetricsController do
let_it_be(:environment) { create(:environment, project: project) }
let_it_be(:user) { create(:user) }
before(:context) do
RSpec::Mocks.with_temporary_scope do
stub_env('USE_SAMPLE_METRICS', 'true')
Rails.application.reload_routes!
end
end
after(:context) do
Rails.application.reload_routes!
end
before do
project.add_reporter(user)
sign_in(user)
......
......@@ -3,6 +3,7 @@
"properties": {
"id": { "type": "integer" },
"title": { "type": "string" },
"slug": { "type": "string" },
"created_at": { "type": "date-time" },
"updated_at": { "type": "date-time" },
"active": { "type": "boolean" },
......
......@@ -29,6 +29,8 @@ describe('ErrorDetails', () => {
propsData: {
issueId: '123',
projectPath: '/root/gitlab-test',
listPath: '/error_tracking',
issueUpdatePath: '/123',
issueDetailsPath: '/123/details',
issueStackTracePath: '/stacktrace',
projectIssuesPath: '/test-project/issues/',
......@@ -122,6 +124,7 @@ describe('ErrorDetails', () => {
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
expect(wrapper.find(Stacktrace).exists()).toBe(false);
expect(wrapper.find(GlBadge).exists()).toBe(false);
expect(wrapper.findAll('button').length).toBe(3);
});
describe('Badges', () => {
......@@ -185,7 +188,7 @@ describe('ErrorDetails', () => {
it('should submit the form', () => {
window.HTMLFormElement.prototype.submit = () => {};
const submitSpy = jest.spyOn(wrapper.vm.$refs.sentryIssueForm, 'submit');
wrapper.find('button').trigger('click');
wrapper.find('[data-qa-selector="create_issue_button"]').trigger('click');
expect(submitSpy).toHaveBeenCalled();
submitSpy.mockRestore();
});
......
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
import * as actions from '~/error_tracking/store/actions';
import * as types from '~/error_tracking/store/mutation_types';
import { visitUrl } from '~/lib/utils/url_utility';
jest.mock('~/flash.js');
jest.mock('~/lib/utils/url_utility');
let mock;
describe('Sentry common store actions', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
createFlash.mockClear();
});
describe('updateStatus', () => {
const endpoint = '123/stacktrace';
const redirectUrl = '/list';
const status = 'resolved';
it('should handle successful status update', done => {
mock.onPut().reply(200, {});
testAction(
actions.updateStatus,
{ endpoint, redirectUrl, status },
{},
[
{
payload: true,
type: types.SET_UPDATING_RESOLVE_STATUS,
},
{
payload: false,
type: 'SET_UPDATING_RESOLVE_STATUS',
},
],
[],
() => {
done();
expect(visitUrl).toHaveBeenCalledWith(redirectUrl);
},
);
});
it('should handle unsuccessful status update', done => {
mock.onPut().reply(400, {});
testAction(
actions.updateStatus,
{ endpoint, redirectUrl, status },
{},
[
{
payload: true,
type: types.SET_UPDATING_RESOLVE_STATUS,
},
{
payload: false,
type: types.SET_UPDATING_RESOLVE_STATUS,
},
],
[],
() => {
expect(visitUrl).not.toHaveBeenCalled();
expect(createFlash).toHaveBeenCalledTimes(1);
done();
},
);
});
});
});
......@@ -6,6 +6,8 @@ import * as actions from '~/error_tracking/store/details/actions';
import * as types from '~/error_tracking/store/details/mutation_types';
jest.mock('~/flash.js');
jest.mock('~/lib/utils/url_utility');
let mock;
describe('Sentry error details store actions', () => {
......
......@@ -79,6 +79,7 @@ describe Projects::ErrorTrackingHelper do
describe '#error_details_data' do
let(:issue_id) { 1234 }
let(:route_params) { [project.owner, project, issue_id, { format: :json }] }
let(:list_path) { project_error_tracking_index_path(project) }
let(:details_path) { details_namespace_project_error_tracking_index_path(*route_params) }
let(:project_path) { project.full_path }
let(:stack_trace_path) { stack_trace_namespace_project_error_tracking_index_path(*route_params) }
......@@ -86,6 +87,10 @@ describe Projects::ErrorTrackingHelper do
let(:result) { helper.error_details_data(project, issue_id) }
it 'returns the correct list path' do
expect(result['list-path']).to eq list_path
end
it 'returns the correct issue id' do
expect(result['issue-id']).to eq issue_id
end
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::BackgroundMigration::ActivatePrometheusServicesForSharedClusterApplications, :migration, schema: 2020_01_14_113341 do
include MigrationHelpers::PrometheusServiceHelpers
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:services) { table(:services) }
let(:namespace) { namespaces.create(name: 'user', path: 'user') }
let(:project) { projects.create(namespace_id: namespace.id) }
let(:columns) do
%w(project_id active properties type template push_events
issues_events merge_requests_events tag_push_events
note_events category default wiki_page_events pipeline_events
confidential_issues_events commit_events job_events
confidential_note_events deployment_events)
end
describe '#perform' do
it 'is idempotent' do
expect { subject.perform(project.id) }.to change { services.order(:id).map { |row| row.attributes } }
expect { subject.perform(project.id) }.not_to change { services.order(:id).map { |row| row.attributes } }
end
context 'non prometheus services' do
it 'does not change them' do
other_type = 'SomeOtherService'
services.create(service_params_for(project.id, active: true, type: other_type))
expect { subject.perform(project.id) }.not_to change { services.where(type: other_type).order(:id).map { |row| row.attributes } }
end
end
context 'prometheus services are configured manually ' do
it 'does not change them' do
properties = '{"api_url":"http://test.dev","manual_configuration":"1"}'
services.create(service_params_for(project.id, properties: properties, active: false))
expect { subject.perform(project.id) }.not_to change { services.order(:id).map { |row| row.attributes } }
end
end
context 'prometheus integration services do not exist' do
it 'creates missing services entries' do
subject.perform(project.id)
rows = services.order(:id).map { |row| row.attributes.slice(*columns).symbolize_keys }
expect([service_params_for(project.id, active: true)]).to eq rows
end
end
context 'prometheus integration services exist' do
context 'in active state' do
it 'does not change them' do
services.create(service_params_for(project.id, active: true))
expect { subject.perform(project.id) }.not_to change { services.order(:id).map { |row| row.attributes } }
end
end
context 'not in active state' do
it 'sets active attribute to true' do
service = services.create(service_params_for(project.id))
expect { subject.perform(project.id) }.to change { service.reload.active? }.from(false).to(true)
end
end
end
end
end
......@@ -67,5 +67,11 @@ describe Gitlab::GroupSearchResults do
expect(result).to eq []
end
it 'sets include_subgroups flag by default' do
result = described_class.new(user, anything, group, 'gob')
expect(result.issuable_params[:include_subgroups]).to eq(true)
end
end
end
......@@ -86,6 +86,16 @@ describe Gitlab::Metrics::Dashboard::Processor do
expect(metrics).to eq %w(metric_b metric_a2 metric_a1)
end
end
context 'when sample_metrics are requested' do
let(:process_params) { [project, dashboard_yml, sequence, { environment: environment, sample_metrics: true }] }
it 'includes a sample metrics path for the prometheus endpoint with each metric' do
expect(all_metrics).to satisfy_all do |metric|
metric[:prometheus_endpoint_path] == sample_metrics_path(metric[:id])
end
end
end
end
shared_examples_for 'errors with message' do |expected_message|
......@@ -147,4 +157,12 @@ describe Gitlab::Metrics::Dashboard::Processor do
query: query
)
end
def sample_metrics_path(metric)
Gitlab::Routing.url_helpers.sample_metrics_project_environment_path(
project,
environment,
identifier: metric
)
end
end
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20200114112932_add_temporary_partial_index_on_project_id_to_services.rb')
describe AddTemporaryPartialIndexOnProjectIdToServices, :migration do
let(:migration) { described_class.new }
describe '#up' do
it 'creates temporary partial index on type' do
expect { migration.up }.to change { migration.index_exists?(:services, :project_id, name: described_class::INDEX_NAME) }.from(false).to(true)
end
end
describe '#down' do
it 'removes temporary partial index on type' do
migration.up
expect { migration.down }.to change { migration.index_exists?(:services, :project_id, name: described_class::INDEX_NAME) }.from(true).to(false)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20200114113341_patch_prometheus_services_for_shared_cluster_applications.rb')
describe PatchPrometheusServicesForSharedClusterApplications, :migration, :sidekiq do
include MigrationHelpers::PrometheusServiceHelpers
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:services) { table(:services) }
let(:clusters) { table(:clusters) }
let(:cluster_groups) { table(:cluster_groups) }
let(:clusters_applications_prometheus) { table(:clusters_applications_prometheus) }
let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
let(:application_statuses) do
{
errored: -1,
installed: 3,
updated: 5
}
end
let(:cluster_types) do
{
instance_type: 1,
group_type: 2
}
end
describe '#up' do
let!(:project_with_missing_service) { projects.create!(name: 'gitlab', path: 'gitlab-ce', namespace_id: namespace.id) }
let(:project_with_inactive_service) { projects.create!(name: 'gitlab', path: 'gitlab-ee', namespace_id: namespace.id) }
let(:project_with_active_service) { projects.create!(name: 'gitlab', path: 'gitlab-ee', namespace_id: namespace.id) }
let(:project_with_manual_active_service) { projects.create!(name: 'gitlab', path: 'gitlab-ee', namespace_id: namespace.id) }
let(:project_with_manual_inactive_service) { projects.create!(name: 'gitlab', path: 'gitlab-ee', namespace_id: namespace.id) }
let(:project_with_active_not_prometheus_service) { projects.create!(name: 'gitlab', path: 'gitlab-ee', namespace_id: namespace.id) }
let(:project_with_inactive_not_prometheus_service) { projects.create!(name: 'gitlab', path: 'gitlab-ee', namespace_id: namespace.id) }
before do
services.create(service_params_for(project_with_inactive_service.id, active: false))
services.create(service_params_for(project_with_active_service.id, active: true))
services.create(service_params_for(project_with_active_not_prometheus_service.id, active: true, type: 'other'))
services.create(service_params_for(project_with_inactive_not_prometheus_service.id, active: false, type: 'other'))
services.create(service_params_for(project_with_manual_inactive_service.id, active: false, properties: { some: 'data' }.to_json))
services.create(service_params_for(project_with_manual_active_service.id, active: true, properties: { some: 'data' }.to_json))
end
shared_examples 'patch prometheus services post migration' do
context 'prometheus application is installed on the cluster' do
it 'schedules a background migration' do
clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:installed], version: '123')
Sidekiq::Testing.fake! do
Timecop.freeze do
background_migrations = [["ActivatePrometheusServicesForSharedClusterApplications", project_with_missing_service.id],
["ActivatePrometheusServicesForSharedClusterApplications", project_with_inactive_service.id],
["ActivatePrometheusServicesForSharedClusterApplications", project_with_active_not_prometheus_service.id],
["ActivatePrometheusServicesForSharedClusterApplications", project_with_inactive_not_prometheus_service.id]]
migrate!
enqueued_migrations = BackgroundMigrationWorker.jobs.map { |job| job['args'] }
expect(enqueued_migrations).to match_array(background_migrations)
end
end
end
end
context 'prometheus application was recently updated on the cluster' do
it 'schedules a background migration' do
clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:updated], version: '123')
Sidekiq::Testing.fake! do
Timecop.freeze do
background_migrations = [["ActivatePrometheusServicesForSharedClusterApplications", project_with_missing_service.id],
["ActivatePrometheusServicesForSharedClusterApplications", project_with_inactive_service.id],
["ActivatePrometheusServicesForSharedClusterApplications", project_with_active_not_prometheus_service.id],
["ActivatePrometheusServicesForSharedClusterApplications", project_with_inactive_not_prometheus_service.id]]
migrate!
enqueued_migrations = BackgroundMigrationWorker.jobs.map { |job| job['args'] }
expect(enqueued_migrations).to match_array(background_migrations)
end
end
end
end
context 'prometheus application failed to install on the cluster' do
it 'does not schedule a background migration' do
clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:errored], version: '123')
Sidekiq::Testing.fake! do
Timecop.freeze do
migrate!
expect(BackgroundMigrationWorker.jobs.size).to eq 0
end
end
end
end
context 'prometheus application is NOT installed on the cluster' do
it 'does not schedule a background migration' do
Sidekiq::Testing.fake! do
Timecop.freeze do
migrate!
expect(BackgroundMigrationWorker.jobs.size).to eq 0
end
end
end
end
end
context 'Cluster is group_type' do
let(:cluster) { clusters.create(name: 'cluster', cluster_type: cluster_types[:group_type]) }
before do
cluster_groups.create(group_id: namespace.id, cluster_id: cluster.id)
end
it_behaves_like 'patch prometheus services post migration'
end
context 'Cluster is instance_type' do
let(:cluster) { clusters.create(name: 'cluster', cluster_type: cluster_types[:instance_type]) }
it_behaves_like 'patch prometheus services post migration'
end
end
end
......@@ -43,34 +43,6 @@ describe DiffViewer::Base do
end
end
context 'when the file type is supported' do
let(:commit) { project.commit('1a0b36b3cdad1d2ee32457c102a8c0b7056fa863') }
let(:diff_file) { commit.diffs.diff_file_with_new_path('LICENSE') }
before do
viewer_class.file_types = %i(license)
viewer_class.binary = false
end
context 'when the binaryness matches' do
it 'returns true' do
expect(viewer_class.can_render?(diff_file)).to be_truthy
end
end
context 'when the binaryness does not match' do
before do
allow_next_instance_of(Blob) do |instance|
allow(instance).to receive(:binary_in_repo?).and_return(true)
end
end
it 'returns false' do
expect(viewer_class.can_render?(diff_file)).to be_falsey
end
end
end
context 'when the extension and file type are not supported' do
it 'returns false' do
expect(viewer_class.can_render?(diff_file)).to be_falsey
......
......@@ -35,6 +35,7 @@ describe API::Services do
expect(response).to have_gitlab_http_status(200)
expect(json_response).to be_an Array
expect(json_response.count).to eq(1)
expect(json_response.first['slug']).to eq('emails-on-push')
expect(response).to match_response_schema('public_api/v4/services')
end
end
......@@ -61,6 +62,7 @@ describe API::Services do
put api("/projects/#{project.id}/services/#{dashed_service}?#{query_strings}", user), params: service_attrs
expect(response).to have_gitlab_http_status(200)
expect(json_response['slug']).to eq(dashed_service)
events.each do |event|
next if event == "foo"
......
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rspec-parameterized'
require 'rubocop'
require 'rubocop/rspec/support'
require_relative '../../../../rubocop/cop/rspec/have_gitlab_http_status'
describe RuboCop::Cop::RSpec::HaveGitlabHttpStatus do
include CopHelper
using RSpec::Parameterized::TableSyntax
let(:source_file) { 'spec/foo_spec.rb' }
subject(:cop) { described_class.new }
shared_examples 'offense' do |code|
it 'registers an offense' do
inspect_source(code, source_file)
expect(cop.offenses.size).to eq(1)
expect(cop.offenses.map(&:line)).to eq([1])
expect(cop.highlights).to eq([code])
end
end
shared_examples 'no offense' do |code|
it 'does not register an offense' do
inspect_source(code)
expect(cop.offenses).to be_empty
end
end
shared_examples 'autocorrect' do |bad, good|
it 'autocorrects' do
autocorrected = autocorrect_source(bad, source_file)
expect(autocorrected).to eql(good)
end
end
shared_examples 'no autocorrect' do |code|
it 'does not autocorrect' do
autocorrected = autocorrect_source(code, source_file)
expect(autocorrected).to eql(code)
end
end
describe 'offenses and autocorrections' do
where(:bad, :good) do
'have_http_status(:ok)' | 'have_gitlab_http_status(:ok)'
'have_http_status(204)' | 'have_gitlab_http_status(:no_content)'
'have_gitlab_http_status(201)' | 'have_gitlab_http_status(:created)'
'have_http_status(var)' | 'have_gitlab_http_status(var)'
'have_http_status(:success)' | 'have_gitlab_http_status(:success)'
'have_http_status(:invalid)' | 'have_gitlab_http_status(:invalid)'
end
with_them do
include_examples 'offense', params[:bad]
include_examples 'no offense', params[:good]
include_examples 'autocorrect', params[:bad], params[:good]
include_examples 'no autocorrect', params[:good]
end
end
describe 'partially autocorrects invalid numeric status' do
where(:bad, :good) do
'have_http_status(-1)' | 'have_gitlab_http_status(-1)'
end
with_them do
include_examples 'offense', params[:bad]
include_examples 'offense', params[:good]
include_examples 'autocorrect', params[:bad], params[:good]
include_examples 'no autocorrect', params[:good]
end
end
describe 'ignore' do
where(:code) do
[
'have_http_status',
'have_http_status { }',
'have_http_status(200, arg)',
'have_gitlab_http_status',
'have_gitlab_http_status { }',
'have_gitlab_http_status(200, arg)'
]
end
with_them do
include_examples 'no offense', params[:code]
include_examples 'no autocorrect', params[:code]
end
end
end
......@@ -117,7 +117,7 @@ module CycleAnalyticsHelpers
data = data_fn[self]
end_time = rand(1..10).days.from_now
end_time_conditions.each_with_index do |(condition_name, condition_fn), index|
end_time_conditions.each_with_index do |(_condition_name, condition_fn), index|
Timecop.freeze(end_time + index.days) { condition_fn[self, data] }
end
......
# frozen_string_literal: true
module MigrationHelpers
module PrometheusServiceHelpers
def service_params_for(project_id, params = {})
{
project_id: project_id,
active: false,
properties: '{}',
type: 'PrometheusService',
template: false,
push_events: true,
issues_events: true,
merge_requests_events: true,
tag_push_events: true,
note_events: true,
category: 'monitoring',
default: false,
wiki_page_events: true,
pipeline_events: true,
confidential_issues_events: true,
commit_events: true,
job_events: true,
confidential_note_events: true,
deployment_events: false
}.merge(params)
end
def row_attributes(entity)
entity.attributes.with_indifferent_access.tap do |hash|
hash.merge!(hash.slice(:created_at, :updated_at).transform_values { |v| v.to_s(:db) })
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment