Commit 14acddd8 authored by Fabio Pitino's avatar Fabio Pitino

Merge branch 'evaluate-group-pipeline-config' into 'master'

Evaluate group pipeline configuration [RUN ALL RSPEC] [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!52629
parents c6c1dd31 f201a735
...@@ -74,7 +74,8 @@ module Enums ...@@ -74,7 +74,8 @@ module Enums
remote_source: 4, remote_source: 4,
external_project_source: 5, external_project_source: 5,
bridge_source: 6, bridge_source: 6,
parameter_source: 7 parameter_source: 7,
compliance_source: 8
} }
end end
end end
......
...@@ -3973,7 +3973,7 @@ type ComplianceFramework { ...@@ -3973,7 +3973,7 @@ type ComplianceFramework {
""" """
Full path of the compliance pipeline configuration stored in a project Full path of the compliance pipeline configuration stored in a project
repository, such as `.gitlab/compliance/soc2/.gitlab-ci.yml`. repository, such as `.gitlab/.compliance-gitlab-ci.yml@compliance/hippa`.
""" """
pipelineConfigurationFullPath: String pipelineConfigurationFullPath: String
} }
...@@ -4031,7 +4031,7 @@ input ComplianceFrameworkInput { ...@@ -4031,7 +4031,7 @@ input ComplianceFrameworkInput {
""" """
Full path of the compliance pipeline configuration stored in a project Full path of the compliance pipeline configuration stored in a project
repository, such as `.gitlab/compliance/soc2/.gitlab-ci.yml`. repository, such as `.gitlab/.compliance-gitlab-ci.yml@compliance/hippa`.
""" """
pipelineConfigurationFullPath: String pipelineConfigurationFullPath: String
} }
...@@ -18383,7 +18383,7 @@ type Pipeline { ...@@ -18383,7 +18383,7 @@ type Pipeline {
""" """
Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE,
AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE,
BRIDGE_SOURCE, PARAMETER_SOURCE) BRIDGE_SOURCE, PARAMETER_SOURCE, COMPLIANCE_SOURCE)
""" """
configSource: PipelineConfigSourceEnum configSource: PipelineConfigSourceEnum
...@@ -18654,6 +18654,7 @@ type PipelineCancelPayload { ...@@ -18654,6 +18654,7 @@ type PipelineCancelPayload {
enum PipelineConfigSourceEnum { enum PipelineConfigSourceEnum {
AUTO_DEVOPS_SOURCE AUTO_DEVOPS_SOURCE
BRIDGE_SOURCE BRIDGE_SOURCE
COMPLIANCE_SOURCE
EXTERNAL_PROJECT_SOURCE EXTERNAL_PROJECT_SOURCE
PARAMETER_SOURCE PARAMETER_SOURCE
REMOTE_SOURCE REMOTE_SOURCE
......
...@@ -10821,7 +10821,7 @@ ...@@ -10821,7 +10821,7 @@
}, },
{ {
"name": "pipelineConfigurationFullPath", "name": "pipelineConfigurationFullPath",
"description": "Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/compliance/soc2/.gitlab-ci.yml`.", "description": "Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/.compliance-gitlab-ci.yml@compliance/hippa`.",
"args": [ "args": [
], ],
...@@ -10991,7 +10991,7 @@ ...@@ -10991,7 +10991,7 @@
}, },
{ {
"name": "pipelineConfigurationFullPath", "name": "pipelineConfigurationFullPath",
"description": "Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/compliance/soc2/.gitlab-ci.yml`.", "description": "Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/.compliance-gitlab-ci.yml@compliance/hippa`.",
"type": { "type": {
"kind": "SCALAR", "kind": "SCALAR",
"name": "String", "name": "String",
...@@ -53950,7 +53950,7 @@ ...@@ -53950,7 +53950,7 @@
}, },
{ {
"name": "configSource", "name": "configSource",
"description": "Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE)", "description": "Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE, COMPLIANCE_SOURCE)",
"args": [ "args": [
], ],
...@@ -54847,6 +54847,12 @@ ...@@ -54847,6 +54847,12 @@
"description": null, "description": null,
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
},
{
"name": "COMPLIANCE_SOURCE",
"description": null,
"isDeprecated": false,
"deprecationReason": null
} }
], ],
"possibleTypes": null "possibleTypes": null
...@@ -630,7 +630,7 @@ Represents a ComplianceFramework associated with a Project. ...@@ -630,7 +630,7 @@ Represents a ComplianceFramework associated with a Project.
| `description` | String! | Description of the compliance framework. | | `description` | String! | Description of the compliance framework. |
| `id` | ID! | Compliance framework ID. | | `id` | ID! | Compliance framework ID. |
| `name` | String! | Name of the compliance framework. | | `name` | String! | Name of the compliance framework. |
| `pipelineConfigurationFullPath` | String | Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/compliance/soc2/.gitlab-ci.yml`. | | `pipelineConfigurationFullPath` | String | Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/.compliance-gitlab-ci.yml@compliance/hippa`. |
### ComposerMetadata ### ComposerMetadata
...@@ -2785,7 +2785,7 @@ Information about pagination in a connection.. ...@@ -2785,7 +2785,7 @@ Information about pagination in a connection..
| `beforeSha` | String | Base SHA of the source branch. | | `beforeSha` | String | Base SHA of the source branch. |
| `cancelable` | Boolean! | Specifies if a pipeline can be canceled. | | `cancelable` | Boolean! | Specifies if a pipeline can be canceled. |
| `committedAt` | Time | Timestamp of the pipeline's commit. | | `committedAt` | Time | Timestamp of the pipeline's commit. |
| `configSource` | PipelineConfigSourceEnum | Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE) | | `configSource` | PipelineConfigSourceEnum | Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE, COMPLIANCE_SOURCE) |
| `coverage` | Float | Coverage percentage. | | `coverage` | Float | Coverage percentage. |
| `createdAt` | Time! | Timestamp of the pipeline's creation. | | `createdAt` | Time! | Timestamp of the pipeline's creation. |
| `detailedStatus` | DetailedStatus! | Detailed status of the pipeline. | | `detailedStatus` | DetailedStatus! | Detailed status of the pipeline. |
...@@ -5214,6 +5214,7 @@ Rotation length unit of an on-call rotation. ...@@ -5214,6 +5214,7 @@ Rotation length unit of an on-call rotation.
| ----- | ----------- | | ----- | ----------- |
| `AUTO_DEVOPS_SOURCE` | | | `AUTO_DEVOPS_SOURCE` | |
| `BRIDGE_SOURCE` | | | `BRIDGE_SOURCE` | |
| `COMPLIANCE_SOURCE` | |
| `EXTERNAL_PROJECT_SOURCE` | | | `EXTERNAL_PROJECT_SOURCE` | |
| `PARAMETER_SOURCE` | | | `PARAMETER_SOURCE` | |
| `REMOTE_SOURCE` | | | `REMOTE_SOURCE` | |
......
...@@ -24,7 +24,7 @@ module Types ...@@ -24,7 +24,7 @@ module Types
argument :pipeline_configuration_full_path, argument :pipeline_configuration_full_path,
GraphQL::STRING_TYPE, GraphQL::STRING_TYPE,
required: false, required: false,
description: 'Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/compliance/soc2/.gitlab-ci.yml`.' description: 'Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/.compliance-gitlab-ci.yml@compliance/hippa`.'
end end
end end
end end
...@@ -25,7 +25,7 @@ module Types ...@@ -25,7 +25,7 @@ module Types
field :pipeline_configuration_full_path, GraphQL::STRING_TYPE, field :pipeline_configuration_full_path, GraphQL::STRING_TYPE,
null: true, null: true,
description: 'Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/compliance/soc2/.gitlab-ci.yml`.' description: 'Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/.compliance-gitlab-ci.yml@compliance/hippa`.'
end end
end end
end end
...@@ -173,6 +173,7 @@ class License < ApplicationRecord ...@@ -173,6 +173,7 @@ class License < ApplicationRecord
subepics subepics
threat_monitoring threat_monitoring
vulnerability_auto_fix vulnerability_auto_fix
evaluate_group_level_compliance_pipeline
] ]
EEU_FEATURES.freeze EEU_FEATURES.freeze
......
---
name: ff_evaluate_group_level_compliance_pipeline
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52629
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/300324
milestone: '13.9'
type: development
group: group::compliance
default_enabled: false
# frozen_string_literal: true
module EE
module Gitlab
module Ci
module Pipeline
module Chain
module Config
module Content
extend ::Gitlab::Utils::Override
EE_SOURCES = [::Gitlab::Ci::Pipeline::Chain::Config::Content::Compliance].freeze
private
override :sources
def sources
EE_SOURCES + super
end
end
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module Ci
module Pipeline
module Chain
module Config
class Content
class Compliance < Source
def content
strong_memoize(:content) do
next unless available?
next unless pipeline_configuration_full_path
path_file, path_project = pipeline_configuration_full_path.split('@', 2)
YAML.dump('include' => [{ 'project' => path_project, 'file' => path_file }])
end
end
def source
:compliance_source
end
private
def pipeline_configuration_full_path
strong_memoize(:pipeline_configuration_full_path) do
next unless project
project.compliance_pipeline_configuration_full_path
end
end
def available?
project.feature_available?(:evaluate_group_level_compliance_pipeline) &&
::Feature.enabled?(:ff_evaluate_group_level_compliance_pipeline, project, default_enabled: :yaml)
end
end
end
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ::Gitlab::Ci::Pipeline::Chain::Config::Content do
let(:ci_config_path) { nil }
let(:pipeline) { build(:ci_pipeline, project: project) }
let(:content) { nil }
let(:source) { :push }
let(:command) { Gitlab::Ci::Pipeline::Chain::Command.new(project: project, content: content, source: source) }
subject { described_class.new(pipeline, command) }
let(:content_result) do
<<~EOY
---
include:
- project: compliance/hippa
file: ".compliance-gitlab-ci.yml"
EOY
end
shared_examples 'does not include compliance pipeline configuration content' do
it do
subject.perform!
expect(pipeline.config_source).not_to eq 'compliance_source'
expect(pipeline.pipeline_config.content).not_to eq(content_result)
expect(command.config_content).not_to eq(content_result)
end
end
context 'when project has compliance pipeline configuration defined' do
let(:project) { create(:project, ci_config_path: ci_config_path) }
let(:compliance_group) { create(:group, :private, name: "compliance") }
let(:compliance_project) { create(:project, namespace: compliance_group, name: "hippa") }
let(:framework) { create(:compliance_framework, namespace_id: compliance_group.id, pipeline_configuration_full_path: ".compliance-gitlab-ci.yml@compliance/hippa") }
let!(:framework_project_setting) { create(:compliance_framework_project_setting, project: project, framework_id: framework.id) }
context 'when feature is available' do
before do
stub_feature_flags(ff_evaluate_group_level_compliance_pipeline: true)
stub_licensed_features(evaluate_group_level_compliance_pipeline: true)
end
it 'includes compliance pipeline configuration content' do
subject.perform!
expect(pipeline.config_source).to eq 'compliance_source'
expect(pipeline.pipeline_config.content).to eq(content_result)
expect(command.config_content).to eq(content_result)
end
end
context 'when feature is not available' do
using RSpec::Parameterized::TableSyntax
where(:licensed, :feature_flag) do
true | false
false | true
false | false
end
with_them do
before do
stub_feature_flags(ff_evaluate_group_level_compliance_pipeline: licensed)
stub_licensed_features(evaluate_group_level_compliance_pipeline: feature_flag)
end
it_behaves_like 'does not include compliance pipeline configuration content'
end
end
end
context 'when project does not have compliance label defined' do
let(:project) { create(:project, ci_config_path: ci_config_path) }
context 'when feature is available' do
before do
stub_feature_flags(ff_evaluate_group_level_compliance_pipeline: true)
stub_licensed_features(evaluate_group_level_compliance_pipeline: true)
end
it_behaves_like 'does not include compliance pipeline configuration content'
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::CreatePipelineService do
include AfterNextHelpers
subject(:execute) { service.execute(:push) }
let(:project) { create(:project, :repository, name: 'website') }
let(:user) { project.owner }
let(:compliance_group) { create(:group, :private, name: "compliance") }
let(:compliance_project) { create(:project, :repository, namespace: compliance_group, name: "hippa") }
let(:framework) { create(:compliance_framework, namespace_id: compliance_group.id, pipeline_configuration_full_path: ".compliance-gitlab-ci.yml@compliance/hippa") }
let!(:framework_project_setting) { create(:compliance_framework_project_setting, project: project, framework_id: framework.id) }
let!(:ref_sha) { compliance_project.commit('HEAD').sha }
let(:compliance_config) do
<<~EOY
---
compliance_build:
stage: build
script:
- echo 'hello from compliance build'
compliance_test:
stage: test
script:
- echo 'hello from compliance test'
EOY
end
let(:service) { described_class.new(project, user, { ref: 'master' }) }
before do
stub_feature_flags(ff_evaluate_group_level_compliance_pipeline: true)
stub_licensed_features(evaluate_group_level_compliance_pipeline: true)
allow_next(Repository).to receive(:blob_data_at).with(ref_sha, '.compliance-gitlab-ci.yml').and_return(compliance_config)
end
context 'when user has access to compliance project' do
before do
compliance_project.add_maintainer(project.owner)
end
it 'persists pipeline' do
is_expected.to be_persisted
end
it 'sets the correct source' do
expect(execute.config_source).to eq("compliance_source")
end
it 'persists jobs' do
expect { execute }.to change(Ci::Build, :count).from(0).to(2)
end
it do
expect(execute.processables.map(&:name)).to eq(%w(compliance_build compliance_test))
end
end
context 'when user does not have access to compliance project' do
it 'includes access denied error' do
expect(execute.yaml_errors).to eq "Project `compliance/hippa` not found or access denied!"
end
it 'does not persist jobs' do
expect { execute }.not_to change(Ci::Build, :count).from(0)
end
end
end
...@@ -34,16 +34,22 @@ module Gitlab ...@@ -34,16 +34,22 @@ module Gitlab
private private
def find_config def find_config
SOURCES.each do |source| sources.each do |source|
config = source.new(@pipeline, @command) config = source.new(@pipeline, @command)
return config if config.exists? return config if config.exists?
end end
nil nil
end end
def sources
SOURCES
end
end end
end end
end end
end end
end end
end end
Gitlab::Ci::Pipeline::Chain::Config::Content.prepend_if_ee('EE::Gitlab::Ci::Pipeline::Chain::Config::Content')
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