Commit 88542a5e authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent b570d73e
<script>
import { mapActions, mapState, mapGetters } from 'vuex';
import { mapActions, mapState } from 'vuex';
import {
GlEmptyState,
GlButton,
GlLink,
GlLoadingIcon,
GlTable,
GlSearchBoxByType,
GlSearchBoxByClick,
} from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
import Icon from '~/vue_shared/components/icon.vue';
......@@ -28,7 +28,7 @@ export default {
GlLink,
GlLoadingIcon,
GlTable,
GlSearchBoxByType,
GlSearchBoxByClick,
Icon,
TimeAgo,
},
......@@ -64,10 +64,6 @@ export default {
},
computed: {
...mapState('list', ['errors', 'externalUrl', 'loading']),
...mapGetters('list', ['filterErrorsByTitle']),
filteredErrors() {
return this.errorSearchQuery ? this.filterErrorsByTitle(this.errorSearchQuery) : this.errors;
},
},
created() {
if (this.errorTrackingEnabled) {
......@@ -76,6 +72,9 @@ export default {
},
methods: {
...mapActions('list', ['startPolling', 'restartPolling']),
filterErrors() {
this.startPolling(`${this.indexPath}?search_term=${this.errorSearchQuery}`);
},
trackViewInSentryOptions,
viewDetails(errorId) {
visitUrl(`error_tracking/${errorId}/details`);
......@@ -87,17 +86,15 @@ export default {
<template>
<div>
<div v-if="errorTrackingEnabled">
<div v-if="loading" class="py-3">
<gl-loading-icon :size="3" />
</div>
<div v-else>
<div>
<div class="d-flex flex-row justify-content-around bg-secondary border">
<gl-search-box-by-type
<gl-search-box-by-click
v-model="errorSearchQuery"
class="col-lg-10 m-3 p-0"
:placeholder="__('Search or filter results...')"
type="search"
autofocus
@submit="filterErrors"
/>
<gl-button
v-track-event="trackViewInSentryOptions(externalUrl)"
......@@ -111,9 +108,14 @@ export default {
</gl-button>
</div>
<div v-if="loading" class="py-3">
<gl-loading-icon size="md" />
</div>
<gl-table
v-else
class="mt-3"
:items="filteredErrors"
:items="errors"
:fields="$options.fields"
:show-empty="true"
fixed
......
......@@ -4,7 +4,6 @@ import Vuex from 'vuex';
import * as listActions from './list/actions';
import listMutations from './list/mutations';
import listState from './list/state';
import * as listGetters from './list/getters';
import * as detailsActions from './details/actions';
import detailsMutations from './details/mutations';
......@@ -21,7 +20,6 @@ export const createStore = () =>
state: listState(),
actions: listActions,
mutations: listMutations,
getters: listGetters,
},
details: {
namespaced: true,
......
......@@ -7,6 +7,8 @@ import { __, sprintf } from '~/locale';
let eTagPoll;
export function startPolling({ commit, dispatch }, endpoint) {
commit(types.SET_LOADING, true);
eTagPoll = new Poll({
resource: Service,
method: 'getSentryData',
......
export const filterErrorsByTitle = state => errorQuery =>
state.errors.filter(error => error.title.match(new RegExp(`${errorQuery}`, 'i')));
export default () => {};
......@@ -44,7 +44,11 @@ class Projects::ErrorTrackingController < Projects::ApplicationController
private
def render_index_json
service = ErrorTracking::ListIssuesService.new(project, current_user)
service = ErrorTracking::ListIssuesService.new(
project,
current_user,
list_issues_params
)
result = service.execute
return if handle_errors(result)
......@@ -106,6 +110,10 @@ class Projects::ErrorTrackingController < Projects::ApplicationController
end
end
def list_issues_params
params.permit(:search_term)
end
def list_projects_params
params.require(:error_tracking_setting).permit([:api_host, :token])
end
......
......@@ -5,6 +5,28 @@ module ErrorTracking
DEFAULT_ISSUE_STATUS = 'unresolved'
DEFAULT_LIMIT = 20
def execute
return error('Error Tracking is not enabled') unless enabled?
return error('Access denied', :unauthorized) unless can_read?
result = project_error_tracking_setting.list_sentry_issues(
issue_status: issue_status,
limit: limit,
search_term: search_term
)
# our results are not yet ready
unless result
return error('Not ready. Try again later', :no_content)
end
if result[:error].present?
return error(result[:error], http_status_for(result[:error_type]))
end
success(issues: result[:issues])
end
def external_url
project_error_tracking_setting&.sentry_external_url
end
......@@ -26,5 +48,17 @@ module ErrorTracking
def limit
params[:limit] || DEFAULT_LIMIT
end
def search_term
params[:search_term].presence
end
def enabled?
project_error_tracking_setting&.enabled?
end
def can_read?
can?(current_user, :read_sentry_issue, project)
end
end
end
......@@ -62,8 +62,6 @@ module MergeRequests
end
def updated_check!
return unless Feature.enabled?(:validate_merge_sha, merge_request.target_project, default_enabled: false)
unless source_matches?
raise_error('Branch has been updated since the merge was requested. '\
'Please review the changes.')
......
---
title: Validate the merge sha before merging, confirming that the merge will only
contain what the user saw
merge_request: 20348
author:
type: fixed
---
title: Remove done callbacks from vue_shared/components/markdown
merge_request: 16842
author: Lee Tickett
type: other
---
title: Search list of Sentry errors by title in GitLab
merge_request: 19439
author:
type: added
# frozen_string_literal: true
# for secondary email confirmations - uses the same confirmation controller as :users
devise_for :emails, path: 'profile/emails', controllers: { confirmations: :confirmations }
......@@ -42,14 +44,6 @@ resource :profile, only: [:show, :update] do
end
end
Gitlab.ee do
resource :slack, only: [:edit] do
member do
get :slack_link
end
end
end
resources :chat_names, only: [:index, :new, :create, :destroy] do
collection do
delete :deny
......@@ -73,10 +67,5 @@ resource :profile, only: [:show, :update] do
end
resources :u2f_registrations, only: [:destroy]
Gitlab.ee do
resources :pipeline_quota, only: [:index]
resources :billings, only: [:index]
end
end
end
resources :projects, only: [:index, :new, :create]
# frozen_string_literal: true
Gitlab.ee do
scope "/-/push_from_secondary/:geo_node_id" do
draw :git_http
end
end
resources :projects, only: [:index, :new, :create]
draw :git_http
......@@ -87,22 +83,12 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resource :operations, only: [:show, :update]
resource :integrations, only: [:show]
Gitlab.ee do
resource :slack, only: [:destroy, :edit, :update] do
get :slack_auth
end
end
resource :repository, only: [:show], controller: :repository do
post :create_deploy_token, path: 'deploy_token/create'
post :cleanup
end
end
Gitlab.ee do
resources :feature_flags
end
resources :autocomplete_sources, only: [] do
collection do
get 'members'
......@@ -268,16 +254,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
namespace :prometheus do
resources :metrics, constraints: { id: %r{[^\/]+} }, only: [:index, :new, :create, :edit, :update, :destroy] do
get :active_common, on: :collection
Gitlab.ee do
post :validate_query, on: :collection
end
end
Gitlab.ee do
resources :alerts, constraints: { id: /\d+/ }, only: [:index, :create, :show, :update, :destroy] do
post :notify, on: :collection
end
end
end
......@@ -290,15 +266,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get :pipeline_status
get :ci_environments_status
post :toggle_subscription
Gitlab.ee do
get :approvals
post :approvals, action: :approve
delete :approvals, action: :unapprove
post :rebase
end
post :remove_wip
post :assign_related_issues
get :discussions, format: :json
......@@ -336,21 +303,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
post :bulk_update
end
Gitlab.ee do
resources :approvers, only: :destroy
delete 'approvers', to: 'approvers#destroy_via_user_id', as: :approver_via_user_id
resources :approver_groups, only: :destroy
scope module: :merge_requests do
resources :drafts, only: [:index, :update, :create, :destroy] do
collection do
post :publish
delete :discard
end
end
end
end
resources :discussions, only: [:show], constraints: { id: /\h{40}/ } do
member do
post :resolve
......@@ -381,21 +333,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
Gitlab.ee do
resources :path_locks, only: [:index, :destroy] do
collection do
post :toggle
end
end
get '/service_desk' => 'service_desk#show', as: :service_desk
put '/service_desk' => 'service_desk#update', as: :service_desk_refresh
end
Gitlab.ee do
resources :push_rules, constraints: { id: /\d+/ }, only: [:update]
end
resources :pipelines, only: [:index, :new, :create, :show] do
collection do
resource :pipelines_settings, path: 'settings', only: [:show, :update]
......@@ -414,11 +351,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get :failures
get :status
get :test_report
Gitlab.ee do
get :security
get :licenses
end
end
member do
......@@ -447,21 +379,12 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get '/terminal.ws/authorize', to: 'environments#terminal_websocket_authorize', constraints: { format: nil }
get '/prometheus/api/v1/*proxy_path', to: 'environments/prometheus_api#proxy', as: :prometheus_api
Gitlab.ee do
get :logs
get '/pods/(:pod_name)/containers/(:container_name)/logs', to: 'environments#k8s_pod_logs', as: :k8s_pod_logs
end
end
collection do
get :metrics, action: :metrics_redirect
get :folder, path: 'folders/*id', constraints: { format: /(html|json)/ }
get :search
Gitlab.ee do
get :logs, action: :logs_redirect
end
end
resources :deployments, only: [:index] do
......@@ -472,14 +395,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
Gitlab.ee do
resources :protected_environments, only: [:create, :update, :destroy], constraints: { id: /\d+/ } do
collection do
get 'search'
end
end
end
namespace :serverless do
scope :functions do
get '/:environment_id/:id', to: 'functions#show'
......@@ -522,14 +437,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
Gitlab.ee do
namespace :security do
resource :dashboard, only: [:show], controller: :dashboard
end
resources :vulnerability_feedback, only: [:index, :create, :update, :destroy], constraints: { id: /\d+/ }
end
get :issues, to: 'issues#calendar', constraints: lambda { |req| req.format == :ics }
resources :issues, concerns: :awardable, constraints: { id: /\d+/ } do
......@@ -543,24 +450,11 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get :realtime_changes
post :create_merge_request
get :discussions, format: :json
Gitlab.ee do
get 'designs(/*vueroute)', to: 'issues#designs', as: :designs, format: false
end
end
collection do
post :bulk_update
post :import_csv
Gitlab.ee do
post :export_csv
get :service_desk
end
end
Gitlab.ee do
resources :issue_links, only: [:index, :create, :destroy], as: 'links', path: 'links'
end
end
......@@ -595,11 +489,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
Gitlab.ee do
resources :approvers, only: :destroy
resources :approver_groups, only: :destroy
end
resources :runner_projects, only: [:create, :destroy]
resources :badges, only: [:index] do
collection do
......@@ -614,10 +503,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
Gitlab.ee do
resources :audit_events, only: [:index]
end
resources :error_tracking, only: [:index], controller: :error_tracking do
collection do
get ':issue_id/details',
......@@ -639,9 +524,15 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
draw :wiki
draw :repository
Gitlab.ee do
resources :managed_licenses, only: [:index, :show, :new, :create, :edit, :update, :destroy]
end
# Legacy routes.
# Introduced in 12.0.
# Should be removed with https://gitlab.com/gitlab-org/gitlab/issues/28848.
Gitlab::Routing.redirect_legacy_paths(self, :settings, :branches, :tags,
:network, :graphs, :autocomplete_sources,
:project_members, :deploy_keys, :deploy_tokens,
:labels, :milestones, :services, :boards, :releases,
:forks, :group_links, :import, :avatar, :mirror,
:cycle_analytics, :mattermost, :variables, :triggers)
end
resources(:projects,
......@@ -666,23 +557,4 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
end
# Legacy routes.
# Introduced in 12.0.
# Should be removed with https://gitlab.com/gitlab-org/gitlab/issues/28848.
scope(path: '*namespace_id',
as: :namespace,
namespace_id: Gitlab::PathRegex.full_namespace_route_regex) do
scope(path: ':project_id',
constraints: { project_id: Gitlab::PathRegex.project_route_regex },
module: :projects,
as: :project) do
Gitlab::Routing.redirect_legacy_paths(self, :settings, :branches, :tags,
:network, :graphs, :autocomplete_sources,
:project_members, :deploy_keys, :deploy_tokens,
:labels, :milestones, :services, :boards, :releases,
:forks, :group_links, :import, :avatar, :mirror,
:cycle_analytics, :mattermost, :variables, :triggers)
end
end
end
Gitlab.ee do
get 'unsubscribes/:email', to: 'unsubscribes#show', as: :unsubscribe
post 'unsubscribes/:email', to: 'unsubscribes#create'
end
# frozen_string_literal: true
# Allows individual providers to be directed to a chosen controller
# Call from inside devise_scope
......@@ -30,10 +27,6 @@ devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks,
devise_scope :user do
get '/users/auth/:provider/omniauth_error' => 'omniauth_callbacks#omniauth_error', as: :omniauth_error
get '/users/almost_there' => 'confirmations#almost_there'
Gitlab.ee do
get '/users/auth/kerberos_spnego/negotiate' => 'omniauth_kerberos_spnego#negotiate'
end
end
scope '-/users', module: :users do
......
......@@ -25,10 +25,6 @@ class ReworkRedirectRoutesIndexes < ActiveRecord::Migration[4.2]
remove_concurrent_index(:redirect_routes, :permanent)
end
# If we're on MySQL then the existing index on path is ok. But on
# Postgres we need to clean things up:
break unless Gitlab::Database.postgresql?
if_not_exists = Gitlab::Database.version.to_f >= 9.5 ? "IF NOT EXISTS" : ""
# Unique index on lower(path) across both types of redirect_routes:
......@@ -53,8 +49,6 @@ class ReworkRedirectRoutesIndexes < ActiveRecord::Migration[4.2]
disable_statement_timeout do
add_concurrent_index(:redirect_routes, :permanent)
break unless Gitlab::Database.postgresql?
execute("CREATE INDEX CONCURRENTLY #{OLD_INDEX_NAME_PATH_TPOPS} ON redirect_routes (path varchar_pattern_ops);")
execute("CREATE INDEX CONCURRENTLY #{OLD_INDEX_NAME_PATH_LOWER} ON redirect_routes (LOWER(path));")
......
......@@ -8,16 +8,10 @@ class AddSectionNameIdIndexOnCiBuildTraceSections < ActiveRecord::Migration[4.2]
disable_ddl_transaction!
def up
# MySQL may already have this as a foreign key
unless index_exists?(:ci_build_trace_sections, :section_name_id, name: INDEX_NAME)
add_concurrent_index :ci_build_trace_sections, :section_name_id, name: INDEX_NAME
end
add_concurrent_index :ci_build_trace_sections, :section_name_id, name: INDEX_NAME
end
def down
# We cannot remove index for MySQL because it's needed for foreign key
if Gitlab::Database.postgresql?
remove_concurrent_index :ci_build_trace_sections, :section_name_id, name: INDEX_NAME
end
remove_concurrent_index :ci_build_trace_sections, :section_name_id, name: INDEX_NAME
end
end
......@@ -13,28 +13,6 @@ class AddIndexesForUserActivityQueries < ActiveRecord::Migration[4.2]
def down
remove_concurrent_index :events, [:author_id, :project_id] if index_exists?(:events, [:author_id, :project_id])
patch_foreign_keys do
remove_concurrent_index :user_interacted_projects, :user_id if index_exists?(:user_interacted_projects, :user_id)
end
end
private
def patch_foreign_keys
return yield if Gitlab::Database.postgresql?
# MySQL doesn't like to remove the index with a foreign key using it.
remove_foreign_key :user_interacted_projects, :users if fk_exists?(:user_interacted_projects, :user_id)
yield
# Let's re-add the foreign key using the existing index on (user_id, project_id)
add_concurrent_foreign_key :user_interacted_projects, :users, column: :user_id unless fk_exists?(:user_interacted_projects, :user_id)
end
def fk_exists?(table, column)
foreign_keys(table).any? do |key|
key.options[:column] == column.to_s
end
remove_concurrent_index :user_interacted_projects, :user_id if index_exists?(:user_interacted_projects, :user_id)
end
end
......@@ -29,14 +29,7 @@ class DropDuplicateProtectedTags < ActiveRecord::Migration[4.2]
.where(project_id: projects)
.where.not(id: ids)
if Gitlab::Database.postgresql?
tags.delete_all
else
# Workaround needed for MySQL
sql = "SELECT id FROM (#{tags.to_sql}) protected_tags"
ProtectedTag.where("id IN (#{sql})").delete_all # rubocop:disable GitlabSecurity/SqlInjection
end
tags.delete_all
end
end
......
......@@ -8,11 +8,10 @@ class AddIndexToProjectDeployTokensDeployTokenId < ActiveRecord::Migration[4.2]
disable_ddl_transaction!
def up
# MySQL already has index inserted
add_concurrent_index :project_deploy_tokens, :deploy_token_id if Gitlab::Database.postgresql?
add_concurrent_index :project_deploy_tokens, :deploy_token_id
end
def down
remove_concurrent_index(:project_deploy_tokens, :deploy_token_id) if Gitlab::Database.postgresql?
remove_concurrent_index(:project_deploy_tokens, :deploy_token_id)
end
end
......@@ -50,17 +50,9 @@ class RemoveRedundantPipelineStages < ActiveRecord::Migration[4.2]
UPDATE ci_builds SET stage_id = NULL WHERE stage_id IN (#{redundant_stages_ids})
SQL
if Gitlab::Database.postgresql?
execute <<~SQL
DELETE FROM ci_stages WHERE id IN (#{redundant_stages_ids})
SQL
else # We can't modify a table we are selecting from on MySQL
execute <<~SQL
DELETE a FROM ci_stages AS a, ci_stages AS b
WHERE a.pipeline_id = b.pipeline_id AND a.name = b.name
AND a.id <> b.id
SQL
end
execute <<~SQL
DELETE FROM ci_stages WHERE id IN (#{redundant_stages_ids})
SQL
end
end
end
......@@ -12,11 +12,7 @@ class BuildUserInteractedProjectsTable < ActiveRecord::Migration[4.2]
disable_ddl_transaction!
def up
if Gitlab::Database.postgresql?
PostgresStrategy.new
else
MysqlStrategy.new
end.up
PostgresStrategy.new.up
if index_exists_by_name?(:user_interacted_projects, CreateUserInteractedProjectsTable::INDEX_NAME)
remove_concurrent_index_by_name :user_interacted_projects, CreateUserInteractedProjectsTable::INDEX_NAME
......@@ -140,30 +136,4 @@ class BuildUserInteractedProjectsTable < ActiveRecord::Migration[4.2]
remove_concurrent_index(*args) if index_exists?(*args)
end
end
class MysqlStrategy < ActiveRecord::Migration[4.2]
include Gitlab::Database::MigrationHelpers
def up
execute <<~SQL
INSERT INTO user_interacted_projects (user_id, project_id)
SELECT e.user_id, e.project_id
FROM (SELECT DISTINCT author_id AS user_id, project_id FROM events WHERE project_id IS NOT NULL) AS e
LEFT JOIN user_interacted_projects ucp USING (user_id, project_id)
WHERE ucp.user_id IS NULL
SQL
unless index_exists?(:user_interacted_projects, [:project_id, :user_id])
add_concurrent_index :user_interacted_projects, [:project_id, :user_id], unique: true, name: UNIQUE_INDEX_NAME
end
unless foreign_key_exists?(:user_interacted_projects, :users, column: :user_id)
add_concurrent_foreign_key :user_interacted_projects, :users, column: :user_id, on_delete: :cascade
end
unless foreign_key_exists?(:user_interacted_projects, :projects, column: :project_id)
add_concurrent_foreign_key :user_interacted_projects, :projects, column: :project_id, on_delete: :cascade
end
end
end
end
......@@ -327,7 +327,12 @@ module Gitlab
end
def find_or_create_object!
return relation_class.find_or_create_by(project_id: @project.id) if UNIQUE_RELATIONS.include?(@relation_name)
if UNIQUE_RELATIONS.include?(@relation_name)
unique_relation_object = relation_class.find_or_create_by(project_id: @project.id)
unique_relation_object.assign_attributes(parsed_relation_hash)
return unique_relation_object
end
# Can't use IDs as validation exists calling `group` or `project` attributes
finder_hash = parsed_relation_hash.tap do |hash|
......
......@@ -10,7 +10,7 @@ module Gitlab
RoutesNotFound = Class.new(StandardError)
def draw(routes_name)
drawn_any = draw_ce(routes_name) | draw_ee(routes_name)
drawn_any = draw_ee(routes_name) | draw_ce(routes_name)
drawn_any || raise(RoutesNotFound.new("Cannot find #{routes_name}"))
end
......
......@@ -25,8 +25,12 @@ module Sentry
map_to_event(latest_event)
end
def list_issues(issue_status:, limit:)
issues = get_issues(issue_status: issue_status, limit: limit)
def list_issues(issue_status:, limit:, search_term: '')
issues = get_issues(
issue_status: issue_status,
limit: limit,
search_term: search_term
)
validate_size(issues)
......@@ -71,13 +75,14 @@ module Sentry
response = handle_request_exceptions do
Gitlab::HTTP.get(url, **request_params.merge(params))
end
handle_response(response)
end
def get_issues(issue_status:, limit:)
def get_issues(issue_status:, limit:, search_term: '')
query = "is:#{issue_status} #{search_term}".strip
http_get(issues_api_url, query: {
query: "is:#{issue_status}",
query: query,
limit: limit
})
end
......
......@@ -17422,6 +17422,9 @@ msgstr ""
msgid "There was an error fetching cycle analytics stages."
msgstr ""
msgid "There was an error fetching data for the chart"
msgstr ""
msgid "There was an error fetching data for the selected stage"
msgstr ""
......
......@@ -16,37 +16,21 @@ retry gem install knapsack --no-document
cp config/gitlab.yml.example config/gitlab.yml
sed -i 's/bin_path: \/usr\/bin\/git/bin_path: \/usr\/local\/bin\/git/' config/gitlab.yml
# Determine the database by looking at the job name.
# This would make the default database postgresql.
if [[ "${CI_JOB_NAME#*mysql}" != "$CI_JOB_NAME" ]]; then
export GITLAB_DATABASE='mysql'
else
export GITLAB_DATABASE='postgresql'
fi
cp config/database.yml.$GITLAB_DATABASE config/database.yml
cp config/database.yml.postgresql config/database.yml
if [ -f config/database_geo.yml.$GITLAB_DATABASE ]; then
cp config/database_geo.yml.$GITLAB_DATABASE config/database_geo.yml
if [ -f config/database_geo.yml.postgresql ]; then
cp config/database_geo.yml.postgresql config/database_geo.yml
fi
# Set user to a non-superuser to ensure we test permissions
sed -i 's/username: root/username: gitlab/g' config/database.yml
if [ "$GITLAB_DATABASE" = 'postgresql' ]; then
sed -i 's/localhost/postgres/g' config/database.yml
sed -i 's/username: git/username: postgres/g' config/database.yml
if [ -f config/database_geo.yml ]; then
sed -i 's/localhost/postgres/g' config/database_geo.yml
sed -i 's/username: git/username: postgres/g' config/database_geo.yml
fi
else # Assume it's mysql
sed -i 's/localhost/mysql/g' config/database.yml
sed -i 's/localhost/postgres/g' config/database.yml
sed -i 's/username: git/username: postgres/g' config/database.yml
if [ -f config/database_geo.yml ]; then
sed -i 's/localhost/mysql/g' config/database_geo.yml
fi
if [ -f config/database_geo.yml ]; then
sed -i 's/localhost/postgres/g' config/database_geo.yml
sed -i 's/username: git/username: postgres/g' config/database_geo.yml
fi
cp config/resque.yml.example config/resque.yml
......@@ -63,6 +47,6 @@ sed -i 's|url:.*$|url: redis://redis:6379/12|g' config/redis.shared_state.yml
if [ "$SETUP_DB" != "false" ]; then
setup_db
elif getent hosts postgres || getent hosts mysql; then
elif getent hosts postgres; then
setup_db_user_only
fi
[[ "$TRACE" ]] && set -x
export TILLER_NAMESPACE="$KUBE_NAMESPACE"
function deploy_exists() {
local namespace="${1}"
......@@ -13,18 +14,16 @@ function deploy_exists() {
}
function previous_deploy_failed() {
local namespace="${1}"
local deploy="${2}"
local deploy="${1}"
echoinfo "Checking for previous deployment of ${deploy}" true
helm status --tiller-namespace "${namespace}" "${deploy}" >/dev/null 2>&1
helm status "${deploy}" >/dev/null 2>&1
local status=$?
# if `status` is `0`, deployment exists, has a status
if [ $status -eq 0 ]; then
echoinfo "Previous deployment found, checking status..."
deployment_status=$(helm status --tiller-namespace "${namespace}" "${deploy}" | grep ^STATUS | cut -d' ' -f2)
deployment_status=$(helm status "${deploy}" | grep ^STATUS | cut -d' ' -f2)
echoinfo "Previous deployment state: ${deployment_status}"
if [[ "$deployment_status" == "FAILED" || "$deployment_status" == "PENDING_UPGRADE" || "$deployment_status" == "PENDING_INSTALL" ]]; then
status=0;
......@@ -38,17 +37,16 @@ function previous_deploy_failed() {
}
function delete_release() {
local namespace="${KUBE_NAMESPACE}"
local deploy="${CI_ENVIRONMENT_SLUG}"
if [ -z "$deploy" ]; then
if [ -z "$CI_ENVIRONMENT_SLUG" ]; then
echoerr "No release given, aborting the delete!"
return
fi
echoinfo "Deleting release '$deploy'..." true
local name="$CI_ENVIRONMENT_SLUG"
echoinfo "Deleting release '$name'..." true
helm delete --purge --tiller-namespace "${namespace}" "${deploy}"
helm delete --purge "$name"
}
function delete_failed_release() {
......@@ -61,7 +59,7 @@ function delete_failed_release() {
echoinfo "No Review App with ${CI_ENVIRONMENT_SLUG} is currently deployed."
else
# Cleanup and previous installs, as FAILED and PENDING_UPGRADE will cause errors with `upgrade`
if previous_deploy_failed "${KUBE_NAMESPACE}" "$CI_ENVIRONMENT_SLUG" ; then
if previous_deploy_failed "$CI_ENVIRONMENT_SLUG" ; then
echoinfo "Review App deployment in bad state, cleaning up $CI_ENVIRONMENT_SLUG"
delete_release
else
......@@ -119,7 +117,6 @@ function ensure_namespace() {
}
function install_tiller() {
local TILLER_NAMESPACE="$KUBE_NAMESPACE"
echoinfo "Checking deployment/tiller-deploy status in the ${TILLER_NAMESPACE} namespace..." true
echoinfo "Initiating the Helm client..."
......@@ -134,12 +131,11 @@ function install_tiller() {
--override "spec.template.spec.tolerations[0].key"="dedicated" \
--override "spec.template.spec.tolerations[0].operator"="Equal" \
--override "spec.template.spec.tolerations[0].value"="helm" \
--override "spec.template.spec.tolerations[0].effect"="NoSchedule" \
--tiller-namespace "${TILLER_NAMESPACE}"
--override "spec.template.spec.tolerations[0].effect"="NoSchedule"
kubectl rollout status -n "$TILLER_NAMESPACE" -w "deployment/tiller-deploy"
if ! helm version --debug --tiller-namespace "${TILLER_NAMESPACE}"; then
if ! helm version --debug; then
echo "Failed to init Tiller."
return 1
fi
......@@ -151,7 +147,7 @@ function install_external_dns() {
domain=$(echo "${REVIEW_APPS_DOMAIN}" | awk -F. '{printf "%s.%s", $(NF-1), $NF}')
echoinfo "Installing external DNS for domain ${domain}..." true
if ! deploy_exists "${KUBE_NAMESPACE}" "${release_name}" || previous_deploy_failed "${KUBE_NAMESPACE}" "${release_name}" ; then
if ! deploy_exists "${KUBE_NAMESPACE}" "${release_name}" || previous_deploy_failed "${release_name}" ; then
echoinfo "Installing external-dns Helm chart"
helm repo update
# Default requested: CPU => 0, memory => 0
......
......@@ -14,11 +14,7 @@ function retry() {
}
function setup_db_user_only() {
if [ "$GITLAB_DATABASE" = "postgresql" ]; then
source scripts/create_postgres_user.sh
else
source scripts/create_mysql_user.sh
fi
source scripts/create_postgres_user.sh
}
function setup_db() {
......@@ -26,10 +22,6 @@ function setup_db() {
bundle exec rake db:drop db:create db:schema:load db:migrate
if [ "$GITLAB_DATABASE" = "mysql" ]; then
bundle exec rake add_limits_mysql
fi
bundle exec rake gitlab:db:setup_ee
}
......
......@@ -48,15 +48,22 @@ describe Projects::ErrorTrackingController do
describe 'format json' do
let(:list_issues_service) { spy(:list_issues_service) }
let(:external_url) { 'http://example.com' }
before do
expect(ErrorTracking::ListIssuesService)
.to receive(:new).with(project, user)
.and_return(list_issues_service)
let(:search_term) do
ActionController::Parameters.new(
search_term: 'something'
).permit!
end
context 'no data' do
let(:search_term) do
ActionController::Parameters.new({}).permit!
end
before do
expect(ErrorTracking::ListIssuesService)
.to receive(:new).with(project, user, search_term)
.and_return(list_issues_service)
expect(list_issues_service).to receive(:execute)
.and_return(status: :error, http_status: :no_content)
end
......@@ -68,59 +75,95 @@ describe Projects::ErrorTrackingController do
end
end
context 'service result is successful' do
context 'with a search_term param' do
before do
expect(list_issues_service).to receive(:execute)
.and_return(status: :success, issues: [error])
expect(list_issues_service).to receive(:external_url)
.and_return(external_url)
expect(ErrorTracking::ListIssuesService)
.to receive(:new).with(project, user, search_term)
.and_return(list_issues_service)
end
let(:error) { build(:error_tracking_error) }
context 'service result is successful' do
before do
expect(list_issues_service).to receive(:execute)
.and_return(status: :success, issues: [error])
expect(list_issues_service).to receive(:external_url)
.and_return(external_url)
end
it 'returns a list of errors' do
get :index, params: project_params(format: :json)
let(:error) { build(:error_tracking_error) }
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('error_tracking/index')
expect(json_response['external_url']).to eq(external_url)
expect(json_response['errors']).to eq([error].as_json)
it 'returns a list of errors' do
get :index, params: project_params(format: :json, search_term: 'something')
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('error_tracking/index')
expect(json_response['external_url']).to eq(external_url)
expect(json_response['errors']).to eq([error].as_json)
end
end
end
context 'service result is erroneous' do
let(:error_message) { 'error message' }
context 'without a search_term param' do
before do
expect(ErrorTracking::ListIssuesService)
.to receive(:new).with(project, user, {})
.and_return(list_issues_service)
end
context 'without http_status' do
context 'service result is successful' do
before do
expect(list_issues_service).to receive(:execute)
.and_return(status: :error, message: error_message)
.and_return(status: :success, issues: [error])
expect(list_issues_service).to receive(:external_url)
.and_return(external_url)
end
it 'returns 400 with message' do
let(:error) { build(:error_tracking_error) }
it 'returns a list of errors' do
get :index, params: project_params(format: :json)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']).to eq(error_message)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('error_tracking/index')
expect(json_response['external_url']).to eq(external_url)
expect(json_response['errors']).to eq([error].as_json)
end
end
context 'with explicit http_status' do
let(:http_status) { :no_content }
context 'service result is erroneous' do
let(:error_message) { 'error message' }
before do
expect(list_issues_service).to receive(:execute).and_return(
status: :error,
message: error_message,
http_status: http_status
)
context 'without http_status' do
before do
expect(list_issues_service).to receive(:execute)
.and_return(status: :error, message: error_message)
end
it 'returns 400 with message' do
get :index, params: project_params(format: :json)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']).to eq(error_message)
end
end
it 'returns http_status with message' do
get :index, params: project_params(format: :json)
context 'with explicit http_status' do
let(:http_status) { :no_content }
expect(response).to have_gitlab_http_status(http_status)
expect(json_response['message']).to eq(error_message)
before do
expect(list_issues_service).to receive(:execute).and_return(
status: :error,
message: error_message,
http_status: http_status
)
end
it 'returns http_status with message' do
get :index, params: project_params(format: :json)
expect(response).to have_gitlab_http_status(http_status)
expect(json_response['message']).to eq(error_message)
end
end
end
end
......
......@@ -7209,15 +7209,15 @@
}
],
"project_feature": {
"builds_access_level": 0,
"builds_access_level": 10,
"created_at": "2014-12-26T09:26:45.000Z",
"id": 2,
"issues_access_level": 0,
"merge_requests_access_level": 20,
"issues_access_level": 10,
"merge_requests_access_level": 10,
"project_id": 4,
"snippets_access_level": 20,
"snippets_access_level": 10,
"updated_at": "2016-09-23T11:58:28.000Z",
"wiki_access_level": 20
"wiki_access_level": 10
},
"custom_attributes": [
{
......@@ -7257,6 +7257,9 @@
"image_url": "http://www.example.com"
}
],
"ci_cd_settings": {
"group_runners_enabled": false
},
"boards": [
{
"id": 29,
......
import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import ErrorTrackingList from '~/error_tracking/components/error_tracking_list.vue';
import { GlButton, GlEmptyState, GlLoadingIcon, GlTable, GlLink } from '@gitlab/ui';
import {
GlButton,
GlEmptyState,
GlLoadingIcon,
GlTable,
GlLink,
GlSearchBoxByClick,
} from '@gitlab/ui';
const localVue = createLocalVue();
localVue.use(Vuex);
......@@ -34,8 +41,8 @@ describe('ErrorTrackingList', () => {
beforeEach(() => {
actions = {
getSentryData: () => {},
startPolling: () => {},
getErrorList: () => {},
startPolling: jest.fn(),
restartPolling: jest.fn().mockName('restartPolling'),
};
......@@ -63,13 +70,13 @@ describe('ErrorTrackingList', () => {
describe('loading', () => {
beforeEach(() => {
store.state.list.loading = true;
mountComponent();
});
it('shows spinner', () => {
expect(wrapper.find(GlLoadingIcon).exists()).toBeTruthy();
expect(wrapper.find(GlTable).exists()).toBeFalsy();
expect(wrapper.find(GlButton).exists()).toBeFalsy();
});
});
......@@ -85,6 +92,20 @@ describe('ErrorTrackingList', () => {
expect(wrapper.find(GlTable).exists()).toBeTruthy();
expect(wrapper.find(GlButton).exists()).toBeTruthy();
});
describe('filtering', () => {
it('shows search box', () => {
expect(wrapper.find(GlSearchBoxByClick).exists()).toBeTruthy();
});
it('makes network request on submit', () => {
expect(actions.startPolling).toHaveBeenCalledTimes(1);
wrapper.find(GlSearchBoxByClick).vm.$emit('submit');
expect(actions.startPolling).toHaveBeenCalledTimes(2);
});
});
});
describe('no results', () => {
......
import axios from '~/lib/utils/axios_utils';
import MockAdapter from 'axios-mock-adapter';
import * as actions from '~/error_tracking/store/list/actions';
import * as types from '~/error_tracking/store/list/mutation_types';
describe('error tracking actions', () => {
let mock;
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
});
describe('startPolling', () => {
it('commits SET_LOADING', () => {
mock.onGet().reply(200);
const endpoint = '/errors';
const commit = jest.fn();
const state = {};
actions.startPolling({ commit, state }, endpoint);
expect(commit).toHaveBeenCalledWith(types.SET_LOADING, true);
});
});
});
import * as getters from '~/error_tracking/store/list/getters';
describe('Error Tracking getters', () => {
let state;
const mockErrors = [
{ title: 'ActiveModel::MissingAttributeError: missing attribute: encrypted_password' },
{ title: 'Grape::Exceptions::MethodNotAllowed: Grape::Exceptions::MethodNotAllowed' },
{ title: 'NoMethodError: undefined method `sanitize_http_headers=' },
{ title: 'NoMethodError: undefined method `pry' },
];
beforeEach(() => {
state = {
errors: mockErrors,
};
});
describe('search results', () => {
it('should return errors filtered by words in title matching the query', () => {
const filteredErrors = getters.filterErrorsByTitle(state)('NoMethod');
expect(filteredErrors).not.toContainEqual(mockErrors[0]);
expect(filteredErrors.length).toBe(2);
});
it('should not return results if there is no matching query', () => {
const filteredErrors = getters.filterErrorsByTitle(state)('GitLab');
expect(filteredErrors.length).toBe(0);
});
});
});
......@@ -5,7 +5,7 @@ import headerComponent from '~/vue_shared/components/markdown/header.vue';
describe('Markdown field header component', () => {
let vm;
beforeEach(done => {
beforeEach(() => {
const Component = Vue.extend(headerComponent);
vm = new Component({
......@@ -13,8 +13,6 @@ describe('Markdown field header component', () => {
previewMarkdown: false,
},
}).$mount();
Vue.nextTick(done);
});
it('renders markdown header buttons', () => {
......@@ -42,13 +40,11 @@ describe('Markdown field header component', () => {
expect(vm.$el.querySelector('li:nth-child(1)').classList.contains('active')).toBeTruthy();
});
it('renders `preview` link as active when previewMarkdown is true', done => {
it('renders `preview` link as active when previewMarkdown is true', () => {
vm.previewMarkdown = true;
Vue.nextTick(() => {
expect(vm.$el.querySelector('li:nth-child(2)').classList.contains('active')).toBeTruthy();
done();
});
});
......
......@@ -64,12 +64,10 @@ describe('Suggestion Diff component', () => {
});
describe('when apply suggestion is clicked', () => {
beforeEach(done => {
beforeEach(() => {
createComponent();
findApplyButton().vm.$emit('click');
wrapper.vm.$nextTick(done);
});
it('emits apply', () => {
......@@ -88,19 +86,15 @@ describe('Suggestion Diff component', () => {
expect(wrapper.text()).toContain('Applying suggestion');
});
it('when callback of apply is called, hides loading', done => {
it('when callback of apply is called, hides loading', () => {
const [callback] = wrapper.emitted().apply[0];
callback();
wrapper.vm
.$nextTick()
.then(() => {
expect(findApplyButton().exists()).toBe(true);
expect(findLoading().exists()).toBe(false);
})
.then(done)
.catch(done.fail);
return wrapper.vm.$nextTick().then(() => {
expect(findApplyButton().exists()).toBe(true);
expect(findLoading().exists()).toBe(false);
});
});
});
});
......@@ -48,11 +48,11 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
it 'restore correct project features' do
project = Project.find_by_path('project')
expect(project.project_feature.issues_access_level).to eq(ProjectFeature::DISABLED)
expect(project.project_feature.builds_access_level).to eq(ProjectFeature::ENABLED)
expect(project.project_feature.snippets_access_level).to eq(ProjectFeature::ENABLED)
expect(project.project_feature.wiki_access_level).to eq(ProjectFeature::ENABLED)
expect(project.project_feature.merge_requests_access_level).to eq(ProjectFeature::ENABLED)
expect(project.project_feature.issues_access_level).to eq(ProjectFeature::PRIVATE)
expect(project.project_feature.builds_access_level).to eq(ProjectFeature::PRIVATE)
expect(project.project_feature.snippets_access_level).to eq(ProjectFeature::PRIVATE)
expect(project.project_feature.wiki_access_level).to eq(ProjectFeature::PRIVATE)
expect(project.project_feature.merge_requests_access_level).to eq(ProjectFeature::PRIVATE)
end
it 'has the project description' do
......@@ -220,6 +220,10 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect(award_emoji.map(&:name)).to contain_exactly('thumbsup', 'coffee')
end
it 'restores `ci_cd_settings` : `group_runners_enabled` setting' do
expect(@project.ci_cd_settings.group_runners_enabled?).to eq(false)
end
it 'restores the correct service' do
expect(CustomIssueTrackerService.first).not_to be_nil
end
......
......@@ -88,12 +88,13 @@ describe Sentry::Client do
describe '#list_issues' do
let(:issue_status) { 'unresolved' }
let(:limit) { 20 }
let(:search_term) { '' }
let(:sentry_api_response) { issues_sample_response }
let(:sentry_request_url) { sentry_url + '/issues/?limit=20&query=is:unresolved' }
let!(:sentry_api_request) { stub_sentry_request(sentry_request_url, body: sentry_api_response) }
subject { client.list_issues(issue_status: issue_status, limit: limit) }
subject { client.list_issues(issue_status: issue_status, limit: limit, search_term: search_term) }
it_behaves_like 'calls sentry api'
......@@ -202,6 +203,16 @@ describe Sentry::Client do
end
it_behaves_like 'maps exceptions'
context 'when search term is present' do
let(:search_term) { 'NoMethodError'}
let(:sentry_request_url) { "#{sentry_url}/issues/?limit=20&query=is:unresolved NoMethodError" }
it_behaves_like 'calls sentry api'
it_behaves_like 'has correct return type', Gitlab::ErrorTracking::Error
it_behaves_like 'has correct length', 1
end
end
describe '#list_projects' do
......
......@@ -5,6 +5,14 @@ require 'spec_helper'
describe ErrorTracking::ListIssuesService do
set(:user) { create(:user) }
set(:project) { create(:project) }
let(:params) { { search_term: 'something' } }
let(:list_sentry_issues_args) do
{
issue_status: 'unresolved',
limit: 20,
search_term: params[:search_term]
}
end
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
let(:token) { 'test-token' }
......@@ -14,7 +22,7 @@ describe ErrorTracking::ListIssuesService do
create(:project_error_tracking_setting, api_url: sentry_url, token: token, project: project)
end
subject { described_class.new(project, user) }
subject { described_class.new(project, user, params) }
before do
expect(project).to receive(:error_tracking_setting).at_least(:once).and_return(error_tracking_setting)
......@@ -29,7 +37,9 @@ describe ErrorTracking::ListIssuesService do
before do
expect(error_tracking_setting)
.to receive(:list_sentry_issues).and_return(issues: issues)
.to receive(:list_sentry_issues)
.with(list_sentry_issues_args)
.and_return(issues: issues)
end
it 'returns the issues' do
......
......@@ -104,14 +104,6 @@ describe MergeRequests::MergeService do
.to change { merge_request.merge_error }
.from(nil).to(merge_error)
end
it 'merges the MR when the feature is disabled' do
stub_feature_flags(validate_merge_sha: false)
service.execute(merge_request)
expect(merge_request).to be_merged
end
end
context 'closes related issues' do
......
......@@ -18,7 +18,6 @@ rspec_profiling_is_configured =
ENV['RSPEC_PROFILING_POSTGRES_URL'].present? ||
ENV['RSPEC_PROFILING']
branch_can_be_profiled =
ENV['GITLAB_DATABASE'] == 'postgresql' &&
(ENV['CI_COMMIT_REF_NAME'] == 'master' ||
ENV['CI_COMMIT_REF_NAME'] =~ /rspec-profile/)
......
# frozen_string_literal: true
require 'database_cleaner/active_record/deletion'
require_relative 'db_cleaner'
module FakeInformationSchema
# Work around a bug in DatabaseCleaner when using the deletion strategy:
# https://github.com/DatabaseCleaner/database_cleaner/issues/347
#
# On MySQL, if the information schema is said to exist, we use an inaccurate
# row count leading to some tables not being cleaned when they should
def information_schema_exists?(_connection)
false
end
end
DatabaseCleaner::ActiveRecord::Deletion.prepend(FakeInformationSchema)
RSpec.configure do |config|
include DbCleaner
......
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