Commit f444f745 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@12-6-stable-ee

parent 58fa510a
...@@ -12,6 +12,14 @@ module Clusters ...@@ -12,6 +12,14 @@ module Clusters
cluster.kubeclient&.get_namespace(Clusters::Kubernetes::KNATIVE_SERVING_NAMESPACE) cluster.kubeclient&.get_namespace(Clusters::Kubernetes::KNATIVE_SERVING_NAMESPACE)
rescue Kubeclient::ResourceNotFoundError rescue Kubeclient::ResourceNotFoundError
nil nil
rescue Kubeclient::HttpError => e
# If the kubernetes auth engine is enabled, it will return 403
if e.error_code == 403
Gitlab::ErrorTracking.track_exception(e)
nil
else
raise
end
end end
end end
end end
...@@ -32,6 +32,7 @@ module Ci ...@@ -32,6 +32,7 @@ module Ci
has_many :stages, -> { order(position: :asc) }, inverse_of: :pipeline has_many :stages, -> { order(position: :asc) }, inverse_of: :pipeline
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
has_many :latest_statuses_ordered_by_stage, -> { latest.order(:stage_idx, :stage) }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
has_many :processables, -> { processables }, has_many :processables, -> { processables },
class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline
...@@ -45,6 +46,7 @@ module Ci ...@@ -45,6 +46,7 @@ module Ci
has_many :merge_requests_as_head_pipeline, foreign_key: "head_pipeline_id", class_name: 'MergeRequest' has_many :merge_requests_as_head_pipeline, foreign_key: "head_pipeline_id", class_name: 'MergeRequest'
has_many :pending_builds, -> { pending }, foreign_key: :commit_id, class_name: 'Ci::Build' has_many :pending_builds, -> { pending }, foreign_key: :commit_id, class_name: 'Ci::Build'
has_many :failed_builds, -> { latest.failed }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline
has_many :retryable_builds, -> { latest.failed_or_canceled.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build' has_many :retryable_builds, -> { latest.failed_or_canceled.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build'
has_many :cancelable_statuses, -> { cancelable }, foreign_key: :commit_id, class_name: 'CommitStatus' has_many :cancelable_statuses, -> { cancelable }, foreign_key: :commit_id, class_name: 'CommitStatus'
has_many :manual_actions, -> { latest.manual_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build' has_many :manual_actions, -> { latest.manual_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build'
...@@ -377,9 +379,7 @@ module Ci ...@@ -377,9 +379,7 @@ module Ci
end end
def legacy_stages_using_composite_status def legacy_stages_using_composite_status
stages = statuses.latest stages = latest_statuses_ordered_by_stage.group_by(&:stage)
.order(:stage_idx, :stage)
.group_by(&:stage)
stages.map do |stage_name, jobs| stages.map do |stage_name, jobs|
composite_status = Gitlab::Ci::Status::Composite composite_status = Gitlab::Ci::Status::Composite
......
...@@ -78,7 +78,7 @@ class PipelineEntity < Grape::Entity ...@@ -78,7 +78,7 @@ class PipelineEntity < Grape::Entity
end end
expose :failed_builds, if: -> (*) { can_retry? }, using: JobEntity do |pipeline| expose :failed_builds, if: -> (*) { can_retry? }, using: JobEntity do |pipeline|
pipeline.builds.failed pipeline.failed_builds
end end
private private
......
...@@ -40,7 +40,11 @@ class PipelineSerializer < BaseSerializer ...@@ -40,7 +40,11 @@ class PipelineSerializer < BaseSerializer
def preloaded_relations def preloaded_relations
[ [
:latest_statuses_ordered_by_stage,
:stages, :stages,
{
failed_builds: %i(project metadata)
},
:retryable_builds, :retryable_builds,
:cancelable_statuses, :cancelable_statuses,
:trigger_requests, :trigger_requests,
...@@ -48,6 +52,7 @@ class PipelineSerializer < BaseSerializer ...@@ -48,6 +52,7 @@ class PipelineSerializer < BaseSerializer
:scheduled_actions, :scheduled_actions,
:artifacts, :artifacts,
:merge_request, :merge_request,
:user,
{ {
pending_builds: :project, pending_builds: :project,
project: [:route, { namespace: :route }], project: [:route, { namespace: :route }],
......
---
title: Fix stack trace highlight for PHP
merge_request: 22258
author:
type: fixed
---
title: Handle forbidden error when checking for knative
merge_request: 22170
author:
type: fixed
---
title: Eliminate N+1 queries in PipelinesController#index
merge_request: 22189
author:
type: performance
...@@ -57,7 +57,7 @@ Parameters: ...@@ -57,7 +57,7 @@ Parameters:
Creates a two-way relation between two issues. User must be allowed to update both issues in order to succeed. Creates a two-way relation between two issues. User must be allowed to update both issues in order to succeed.
``` ```
POST /projects/:id/issues/:issue_iid/links/:target_project_id/:target_issue_iid POST /projects/:id/issues/:issue_iid/links
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
...@@ -67,6 +67,12 @@ POST /projects/:id/issues/:issue_iid/links/:target_project_id/:target_issue_iid ...@@ -67,6 +67,12 @@ POST /projects/:id/issues/:issue_iid/links/:target_project_id/:target_issue_iid
| `target_project_id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) of a target project | | `target_project_id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) of a target project |
| `target_issue_iid` | integer/string | yes | The internal ID of a target project's issue | | `target_issue_iid` | integer/string | yes | The internal ID of a target project's issue |
```bash
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/4/issues/1/links?target_project_id=5&target_issue_iid=1"
```
Example response:
```json ```json
{ {
"source_issue" : { "source_issue" : {
......
...@@ -116,6 +116,35 @@ rendered to HTML when viewed. ...@@ -116,6 +116,35 @@ rendered to HTML when viewed.
Interactive features, including JavaScript plots, will not work when viewed in Interactive features, including JavaScript plots, will not work when viewed in
GitLab. GitLab.
### OpenAPI viewer
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/19515) in GitLab 12.6.
GitLab can render OpenAPI specification files with its file viewer, provided
their filenames include `openapi` or `swagger` and their extension is `yaml`,
`yml`, or `json`. The following examples are all correct:
- `openapi.yml`
- `openapi.yaml`
- `openapi.json`
- `swagger.yml`
- `swagger.yaml`
- `swagger.json`
- `gitlab_swagger.yml`
- `openapi_gitlab.yml`
- `OpenAPI.YML`
- `openapi.Yaml`
- `openapi.JSON`
- `openapi.gitlab.yml`
- `gitlab.openapi.yml`
Then, to render them:
1. Navigate to the OpenAPI file in your repository in GitLab's UI.
1. Click the "Display OpenAPI" button which is located between the "Display source"
and "Edit" buttons (when an OpenAPI file is found, it replaces the
"Display rendered file" button).
## Branches ## Branches
For details, see [Branches](branches/index.md). For details, see [Branches](branches/index.md).
......
...@@ -28,7 +28,7 @@ module Gitlab ...@@ -28,7 +28,7 @@ module Gitlab
end end
def highlight_entry_context(filename, context) def highlight_entry_context(filename, context)
language = Rouge::Lexer.guess_by_filename(filename).tag language = guess_language_by_filename(filename)
context.map do |line_number, line_of_code| context.map do |line_number, line_of_code|
[ [
...@@ -38,6 +38,12 @@ module Gitlab ...@@ -38,6 +38,12 @@ module Gitlab
] ]
end end
end end
def guess_language_by_filename(filename)
Rouge::Lexer.guess_by_filename(filename).tag
rescue Rouge::Guesser::Ambiguous => e
e.alternatives.min_by(&:tag)&.tag
end
end end
end end
end end
...@@ -6,7 +6,7 @@ describe Projects::PipelinesController do ...@@ -6,7 +6,7 @@ describe Projects::PipelinesController do
include ApiHelpers include ApiHelpers
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) } let_it_be(:project) { create(:project, :public, :repository) }
let(:feature) { ProjectFeature::ENABLED } let(:feature) { ProjectFeature::ENABLED }
before do before do
...@@ -19,12 +19,12 @@ describe Projects::PipelinesController do ...@@ -19,12 +19,12 @@ describe Projects::PipelinesController do
describe 'GET index.json' do describe 'GET index.json' do
before do before do
%w(pending running success failed canceled).each_with_index do |status, index| create_all_pipeline_types
create_pipeline(status, project.commit("HEAD~#{index}"))
end
end end
context 'when using persisted stages', :request_store do context 'when using persisted stages', :request_store do
render_views
before do before do
stub_feature_flags(ci_pipeline_persisted_stages: true) stub_feature_flags(ci_pipeline_persisted_stages: true)
end end
...@@ -32,9 +32,7 @@ describe Projects::PipelinesController do ...@@ -32,9 +32,7 @@ describe Projects::PipelinesController do
it 'returns serialized pipelines', :request_store do it 'returns serialized pipelines', :request_store do
expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original
queries = ActiveRecord::QueryRecorder.new do get_pipelines_index_json
get_pipelines_index_json
end
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('pipeline') expect(response).to match_response_schema('pipeline')
...@@ -49,8 +47,22 @@ describe Projects::PipelinesController do ...@@ -49,8 +47,22 @@ describe Projects::PipelinesController do
json_response.dig('pipelines', 0, 'details', 'stages').tap do |stages| json_response.dig('pipelines', 0, 'details', 'stages').tap do |stages|
expect(stages.count).to eq 3 expect(stages.count).to eq 3
end end
end
it 'does not execute N+1 queries' do
get_pipelines_index_json
control_count = ActiveRecord::QueryRecorder.new do
get_pipelines_index_json
end.count
expect(queries.count).to be create_all_pipeline_types
# There appears to be one extra query for Pipelines#has_warnings? for some reason
expect { get_pipelines_index_json }.not_to exceed_query_limit(control_count + 1)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['pipelines'].count).to eq 10
end end
end end
...@@ -133,19 +145,27 @@ describe Projects::PipelinesController do ...@@ -133,19 +145,27 @@ describe Projects::PipelinesController do
format: :json format: :json
end end
def create_all_pipeline_types
%w(pending running success failed canceled).each_with_index do |status, index|
create_pipeline(status, project.commit("HEAD~#{index}"))
end
end
def create_pipeline(status, sha) def create_pipeline(status, sha)
user = create(:user)
pipeline = create(:ci_empty_pipeline, status: status, pipeline = create(:ci_empty_pipeline, status: status,
project: project, project: project,
sha: sha) sha: sha,
user: user)
create_build(pipeline, 'build', 1, 'build') create_build(pipeline, 'build', 1, 'build', user)
create_build(pipeline, 'test', 2, 'test') create_build(pipeline, 'test', 2, 'test', user)
create_build(pipeline, 'deploy', 3, 'deploy') create_build(pipeline, 'deploy', 3, 'deploy', user)
end end
def create_build(pipeline, stage, stage_idx, name) def create_build(pipeline, stage, stage_idx, name, user = nil)
status = %w[created running pending success failed canceled].sample status = %w[created running pending success failed canceled].sample
create(:ci_build, pipeline: pipeline, stage: stage, stage_idx: stage_idx, name: name, status: status) create(:ci_build, pipeline: pipeline, stage: stage, stage_idx: stage_idx, name: name, status: status, user: user)
end end
end end
......
...@@ -35,6 +35,16 @@ FactoryBot.define do ...@@ -35,6 +35,16 @@ FactoryBot.define do
[8, "}\n"] [8, "}\n"]
] ]
}, },
{
'function' => 'print',
'lineNo' => 3,
'filename' => 'hello_world.php',
'context' => [
[1, "// PHP/Hack example\n"],
[2, "<?php\n"],
[3, "echo 'Hello, World!';\n"]
]
},
{ {
'filename' => 'blank.txt' 'filename' => 'blank.txt'
} }
......
# frozen_string_literal: true
require 'spec_helper'
describe Clusters::KnativeServingNamespaceFinder do
include KubernetesHelpers
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:service) { environment.deployment_platform }
let(:project) { cluster.cluster_project.project }
let(:environment) { create(:environment, project: project) }
subject { described_class.new(cluster) }
before do
stub_kubeclient_discover(service.api_url)
end
it 'finds the namespace in a cluster where it exists' do
stub_kubeclient_get_namespace(service.api_url, namespace: Clusters::Kubernetes::KNATIVE_SERVING_NAMESPACE)
expect(subject.execute).to be_a Kubeclient::Resource
end
it 'returns nil in a cluster where it does not' do
stub_kubeclient_get_namespace(
service.api_url,
namespace: Clusters::Kubernetes::KNATIVE_SERVING_NAMESPACE,
response: {
status: [404, "Resource Not Found"]
}
)
expect(subject.execute).to be nil
end
it 'returns nil in a cluster where the lookup results in a 403 as it will in some versions of kubernetes' do
stub_kubeclient_get_namespace(
service.api_url,
namespace: Clusters::Kubernetes::KNATIVE_SERVING_NAMESPACE,
response: {
status: [403, "Resource Not Found"]
}
)
expect(subject.execute).to be nil
end
it 'raises an error if error code is not 404 or 403' do
stub_kubeclient_get_namespace(
service.api_url,
namespace: Clusters::Kubernetes::KNATIVE_SERVING_NAMESPACE,
response: {
status: [500, "Internal Server Error"]
}
)
expect { subject.execute }.to raise_error(Kubeclient::HttpError)
end
end
...@@ -48,6 +48,16 @@ describe Gitlab::ErrorTracking::StackTraceHighlightDecorator do ...@@ -48,6 +48,16 @@ describe Gitlab::ErrorTracking::StackTraceHighlightDecorator do
[8, '<span id="LC1" class="line" lang="swift"><span class="p">}</span></span>'] [8, '<span id="LC1" class="line" lang="swift"><span class="p">}</span></span>']
] ]
}, },
{
'function' => 'print',
'lineNo' => 3,
'filename' => 'hello_world.php',
'context' => [
[1, '<span id="LC1" class="line" lang="hack"><span class="c1">// PHP/Hack example</span></span>'],
[2, '<span id="LC1" class="line" lang="hack"><span class="cp">&lt;?php</span></span>'],
[3, '<span id="LC1" class="line" lang="hack"><span class="k">echo</span> <span class="s1">\'Hello, World!\'</span><span class="p">;</span></span>']
]
},
{ {
'filename' => 'blank.txt' 'filename' => 'blank.txt'
} }
......
...@@ -160,6 +160,7 @@ ci_pipelines: ...@@ -160,6 +160,7 @@ ci_pipelines:
- user - user
- stages - stages
- statuses - statuses
- latest_statuses_ordered_by_stage
- builds - builds
- processables - processables
- trigger_requests - trigger_requests
...@@ -168,6 +169,7 @@ ci_pipelines: ...@@ -168,6 +169,7 @@ ci_pipelines:
- auto_canceled_pipelines - auto_canceled_pipelines
- auto_canceled_jobs - auto_canceled_jobs
- pending_builds - pending_builds
- failed_builds
- retryable_builds - retryable_builds
- cancelable_statuses - cancelable_statuses
- manual_actions - manual_actions
......
...@@ -159,7 +159,7 @@ describe PipelineSerializer do ...@@ -159,7 +159,7 @@ describe PipelineSerializer do
it 'verifies number of queries', :request_store do it 'verifies number of queries', :request_store do
recorded = ActiveRecord::QueryRecorder.new { subject } recorded = ActiveRecord::QueryRecorder.new { subject }
expected_queries = Gitlab.ee? ? 38 : 35 expected_queries = Gitlab.ee? ? 42 : 39
expect(recorded.count).to be_within(2).of(expected_queries) expect(recorded.count).to be_within(2).of(expected_queries)
expect(recorded.cached_count).to eq(0) expect(recorded.cached_count).to eq(0)
...@@ -180,7 +180,7 @@ describe PipelineSerializer do ...@@ -180,7 +180,7 @@ describe PipelineSerializer do
# pipeline. With the same ref this check is cached but if refs are # pipeline. With the same ref this check is cached but if refs are
# different then there is an extra query per ref # different then there is an extra query per ref
# https://gitlab.com/gitlab-org/gitlab-foss/issues/46368 # https://gitlab.com/gitlab-org/gitlab-foss/issues/46368
expected_queries = Gitlab.ee? ? 41 : 38 expected_queries = Gitlab.ee? ? 45 : 42
expect(recorded.count).to be_within(2).of(expected_queries) expect(recorded.count).to be_within(2).of(expected_queries)
expect(recorded.cached_count).to eq(0) expect(recorded.cached_count).to eq(0)
......
...@@ -229,9 +229,9 @@ module KubernetesHelpers ...@@ -229,9 +229,9 @@ module KubernetesHelpers
.to_return(kube_response(kube_v1_namespace_list_body)) .to_return(kube_response(kube_v1_namespace_list_body))
end end
def stub_kubeclient_get_namespace(api_url, namespace: 'default') def stub_kubeclient_get_namespace(api_url, namespace: 'default', response: kube_response({}))
WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}") WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}")
.to_return(kube_response({})) .to_return(response)
end end
def stub_kubeclient_put_cluster_role(api_url, name) def stub_kubeclient_put_cluster_role(api_url, name)
......
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