Commit 1aed0a3e authored by Mikołaj Wawrzyniak's avatar Mikołaj Wawrzyniak

Merge branch 'ci-add-extra-properties-to-external-validation-payload' into 'master'

Add extra fields to the pipeline validation payload

See merge request gitlab-org/gitlab!56969
parents daf40c42 8a4328d0
---
title: Add extra fields to the external pipeline validation payload
merge_request: 56969
author:
type: changed
...@@ -38,18 +38,21 @@ Set the `EXTERNAL_VALIDATION_SERVICE_URL` to the external service URL and enable ...@@ -38,18 +38,21 @@ Set the `EXTERNAL_VALIDATION_SERVICE_URL` to the external service URL and enable
"project", "project",
"user", "user",
"pipeline", "pipeline",
"builds" "builds",
"namespace"
], ],
"properties" : { "properties" : {
"project": { "project": {
"type": "object", "type": "object",
"required": [ "required": [
"id", "id",
"path" "path",
"created_at"
], ],
"properties": { "properties": {
"id": { "type": "integer" }, "id": { "type": "integer" },
"path": { "type": "string" } "path": { "type": "string" },
"created_at": { "type": ["string", "null"], "format": "date-time" }
} }
}, },
"user": { "user": {
...@@ -57,12 +60,14 @@ Set the `EXTERNAL_VALIDATION_SERVICE_URL` to the external service URL and enable ...@@ -57,12 +60,14 @@ Set the `EXTERNAL_VALIDATION_SERVICE_URL` to the external service URL and enable
"required": [ "required": [
"id", "id",
"username", "username",
"email" "email",
"created_at"
], ],
"properties": { "properties": {
"id": { "type": "integer" }, "id": { "type": "integer" },
"username": { "type": "string" }, "username": { "type": "string" },
"email": { "type": "string" } "email": { "type": "string" },
"created_at": { "type": ["string", "null"], "format": "date-time" }
} }
}, },
"pipeline": { "pipeline": {
...@@ -103,8 +108,18 @@ Set the `EXTERNAL_VALIDATION_SERVICE_URL` to the external service URL and enable ...@@ -103,8 +108,18 @@ Set the `EXTERNAL_VALIDATION_SERVICE_URL` to the external service URL and enable
} }
} }
} }
},
"namespace": {
"type": "object",
"required": [
"plan",
"trial"
],
"properties": {
"plan": { "type": "string" },
"trial": { "type": "boolean" }
}
} }
}, }
"additionalProperties": false
} }
``` ```
# frozen_string_literal: true
module EE
module Gitlab
module Ci
module Pipeline
module Chain
module Validate
module External
extend ::Gitlab::Utils::Override
private
delegate :namespace, to: :project
override :validation_service_payload
def validation_service_payload
super.deep_merge(
namespace: {
plan: namespace.actual_plan_name,
trial: namespace.trial_active?
}
)
end
end
end
end
end
end
end
end
{
"type": "object",
"allOf": [
{ "$ref": "../../../../../spec/fixtures/api/schemas/external_validation.json" },
{
"required" : [
"namespace"
],
"properties": {
"namespace": {
"type": "object",
"required": [
"plan",
"trial"
],
"properties": {
"plan": { "type": "string" },
"trial": { "type": "boolean" }
}
}
}
}
]
}
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:subscription) { create(:gitlab_subscription, :default, namespace: user.namespace) }
let(:pipeline) { build(:ci_empty_pipeline, user: user, project: project) }
let(:ci_yaml) do
<<-CI_YAML
job:
script: ls
parallel: 5
CI_YAML
end
let(:yaml_processor_result) do
::Gitlab::Ci::YamlProcessor.new(
ci_yaml, {
project: project,
sha: pipeline.sha,
user: user
}
).execute
end
let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(
project: project, current_user: user, yaml_processor_result: yaml_processor_result, save_incompleted: true
)
end
let(:step) { described_class.new(pipeline, command) }
let(:validation_service_url) { 'https://validation-service.external/' }
describe '#validation_service_payload' do
before do
stub_env('EXTERNAL_VALIDATION_SERVICE_URL', validation_service_url)
end
it 'respects the defined schema' do
expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
expect(params[:body]).to match_schema('/external_validation', dir: 'ee')
end
step.perform!
end
it 'does not fire N+1 SQL queries' do
stub_request(:post, validation_service_url)
expect { step.perform! }.not_to exceed_query_limit(4)
end
end
end
...@@ -21,13 +21,13 @@ module Gitlab ...@@ -21,13 +21,13 @@ module Gitlab
pipeline_authorized = validate_external pipeline_authorized = validate_external
log_message = pipeline_authorized ? 'authorized' : 'not authorized' log_message = pipeline_authorized ? 'authorized' : 'not authorized'
Gitlab::AppLogger.info(message: "Pipeline #{log_message}", project_id: @pipeline.project.id, user_id: @pipeline.user.id) Gitlab::AppLogger.info(message: "Pipeline #{log_message}", project_id: project.id, user_id: current_user.id)
error('External validation failed', drop_reason: :external_validation_failure) unless pipeline_authorized error('External validation failed', drop_reason: :external_validation_failure) unless pipeline_authorized
end end
def break? def break?
@pipeline.errors.any? pipeline.errors.any?
end end
private private
...@@ -71,7 +71,7 @@ module Gitlab ...@@ -71,7 +71,7 @@ module Gitlab
def validate_service_request def validate_service_request
Gitlab::HTTP.post( Gitlab::HTTP.post(
validation_service_url, timeout: VALIDATION_REQUEST_TIMEOUT, validation_service_url, timeout: VALIDATION_REQUEST_TIMEOUT,
body: validation_service_payload(@pipeline, @command.yaml_processor_result.stages_attributes) body: validation_service_payload.to_json
) )
end end
...@@ -79,28 +79,30 @@ module Gitlab ...@@ -79,28 +79,30 @@ module Gitlab
ENV['EXTERNAL_VALIDATION_SERVICE_URL'] ENV['EXTERNAL_VALIDATION_SERVICE_URL']
end end
def validation_service_payload(pipeline, stages_attributes) def validation_service_payload
{ {
project: { project: {
id: pipeline.project.id, id: project.id,
path: pipeline.project.full_path path: project.full_path,
created_at: project.created_at&.iso8601
}, },
user: { user: {
id: pipeline.user.id, id: current_user.id,
username: pipeline.user.username, username: current_user.username,
email: pipeline.user.email email: current_user.email,
created_at: current_user.created_at&.iso8601
}, },
pipeline: { pipeline: {
sha: pipeline.sha, sha: pipeline.sha,
ref: pipeline.ref, ref: pipeline.ref,
type: pipeline.source type: pipeline.source
}, },
builds: builds_validation_payload(stages_attributes) builds: builds_validation_payload
}.to_json }
end end
def builds_validation_payload(stages_attributes) def builds_validation_payload
stages_attributes.map { |stage| stage[:builds] }.flatten stages_attributes.flat_map { |stage| stage[:builds] }
.map(&method(:build_validation_payload)) .map(&method(:build_validation_payload))
end end
...@@ -117,9 +119,15 @@ module Gitlab ...@@ -117,9 +119,15 @@ module Gitlab
].flatten.compact ].flatten.compact
} }
end end
def stages_attributes
command.yaml_processor_result.stages_attributes
end
end end
end end
end end
end end
end end
end end
Gitlab::Ci::Pipeline::Chain::Validate::External.prepend_if_ee('EE::Gitlab::Ci::Pipeline::Chain::Validate::External')
...@@ -11,11 +11,13 @@ ...@@ -11,11 +11,13 @@
"type": "object", "type": "object",
"required": [ "required": [
"id", "id",
"path" "path",
"created_at"
], ],
"properties": { "properties": {
"id": { "type": "integer" }, "id": { "type": "integer" },
"path": { "type": "string" } "path": { "type": "string" },
"created_at": { "type": ["string", "null"], "format": "date-time" }
} }
}, },
"user": { "user": {
...@@ -23,12 +25,14 @@ ...@@ -23,12 +25,14 @@
"required": [ "required": [
"id", "id",
"username", "username",
"email" "email",
"created_at"
], ],
"properties": { "properties": {
"id": { "type": "integer" }, "id": { "type": "integer" },
"username": { "type": "string" }, "username": { "type": "string" },
"email": { "type": "string" } "email": { "type": "string" },
"created_at": { "type": ["string", "null"], "format": "date-time" }
} }
}, },
"pipeline": { "pipeline": {
...@@ -70,6 +74,5 @@ ...@@ -70,6 +74,5 @@
} }
} }
} }
}, }
"additionalProperties": false
} }
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do
let(:project) { create(:project) } let_it_be(:project) { create(:project) }
let(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:pipeline) { build(:ci_empty_pipeline, user: user, project: project) } let(:pipeline) { build(:ci_empty_pipeline, user: user, project: project) }
let!(:step) { described_class.new(pipeline, command) } let!(:step) { described_class.new(pipeline, command) }
...@@ -59,6 +59,14 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do ...@@ -59,6 +59,14 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do
allow(Gitlab).to receive(:com?).and_return(dot_com) allow(Gitlab).to receive(:com?).and_return(dot_com)
end end
it 'respects the defined payload schema' do
expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
expect(params[:body]).to match_schema('/external_validation')
end
perform!
end
shared_examples 'successful external authorization' do shared_examples 'successful external authorization' do
it 'does not drop the pipeline' do it 'does not drop the pipeline' do
perform! perform!
...@@ -224,16 +232,4 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do ...@@ -224,16 +232,4 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do
end end
end end
end end
describe '#validation_service_payload' do
subject(:validation_service_payload) { step.send(:validation_service_payload, pipeline, command.yaml_processor_result.stages_attributes) }
it 'respects the defined schema' do
expect(validation_service_payload).to match_schema('/external_validation')
end
it 'does not fire sql queries' do
expect { validation_service_payload }.not_to exceed_query_limit(1)
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment