Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9044365a91
commit
571d993b49
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
1.5.0
|
||||
2.0.0
|
||||
|
|
|
|||
2
Gemfile
2
Gemfile
|
|
@ -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]"
|
||||
/>
|
||||
<gl-form-input :value="csrfToken" class="hidden" name="authenticity_token" />
|
||||
<div class="d-inline-flex">
|
||||
<loading-button
|
||||
v-if="!error.gitlab_issue"
|
||||
class="btn-success"
|
||||
:label="__('Create issue')"
|
||||
:loading="issueCreationInProgress"
|
||||
data-qa-selector="create_issue_button"
|
||||
@click="createIssue"
|
||||
:label="__('Ignore')"
|
||||
:loading="updatingIgnoreStatus"
|
||||
@click="updateIssueStatus('ignored')"
|
||||
/>
|
||||
</form>
|
||||
<loading-button
|
||||
class="btn-outline-info ml-2"
|
||||
:label="__('Resolve')"
|
||||
:loading="updatingResolveStatus"
|
||||
@click="updateIssueStatus('resolved')"
|
||||
/>
|
||||
<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 });
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
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,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
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,
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
export const SET_UPDATING_RESOLVE_STATUS = 'SET_UPDATING_RESOLVE_STATUS';
|
||||
export const SET_UPDATING_IGNORE_STATUS = 'SET_UPDATING_IGNORE_STATUS';
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Drop support for ES5 add support for ES7
|
||||
merge_request: 22859
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
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
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add ability to ignore/resolve errors from error tracking detail page
|
||||
merge_request: 22475
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add deprecation warning to Rake tasks in sidekiq namespace
|
||||
merge_request:
|
||||
author:
|
||||
type: removed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Include subgroups when searching inside a group
|
||||
merge_request: 22991
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add slug to services API response
|
||||
merge_request: 22518
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
# 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
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# 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
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
# 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
|
||||
12
db/schema.rb
12
db/schema.rb
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
# 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],
|
||||
|
|
|
|||
|
|
@ -1,78 +1,53 @@
|
|||
# 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) : []
|
||||
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}:/)]
|
||||
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 self.config_queues
|
||||
@config_queues ||= begin
|
||||
config = YAML.load_file(Rails.root.join('config/sidekiq_queues.yml'))
|
||||
config[:queues].map(&:first)
|
||||
end
|
||||
end
|
||||
|
||||
def self.cron_workers
|
||||
@cron_workers ||= Settings.cron_jobs.map { |job_name, options| options['job_class'].constantize }
|
||||
end
|
||||
|
||||
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 self.find_workers(root)
|
||||
concerns = root.join('concerns').to_s
|
||||
|
||||
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', '')
|
||||
|
||||
ns.camelize.constantize
|
||||
def redis_queues
|
||||
# Not memoized, because this can change during the life of the application
|
||||
Sidekiq::Queue.all.map(&:name)
|
||||
end
|
||||
|
||||
# Skip things that aren't workers
|
||||
workers.select { |w| w < Sidekiq::Worker }
|
||||
def config_queues
|
||||
@config_queues ||= begin
|
||||
config = YAML.load_file(Rails.root.join('config/sidekiq_queues.yml'))
|
||||
config[:queues].map(&:first)
|
||||
end
|
||||
end
|
||||
|
||||
def cron_workers
|
||||
@cron_workers ||= Settings.cron_jobs.map { |job_name, options| options['job_class'].constantize }
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
private
|
||||
|
||||
def find_workers(root)
|
||||
concerns = root.join('concerns').to_s
|
||||
|
||||
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', '')
|
||||
|
||||
ns.camelize.constantize
|
||||
end
|
||||
|
||||
# Skip things that aren't workers
|
||||
workers.select { |w| w < Sidekiq::Worker }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
# 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
|
||||
|
|
@ -1,21 +1,38 @@
|
|||
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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,119 @@
|
|||
# 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();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
# 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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
# 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
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
# 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"
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
# 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
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
# 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
|
||||
Loading…
Reference in New Issue