Commit 75a244bc authored by Shinya Maeda's avatar Shinya Maeda

Merge branch 'incubation-5mp-generate-cloud-run-pipeline-service' into 'master'

GeneratePipelineService for 5mp Google Cloud

See merge request gitlab-org/gitlab!78963
parents 9186f786 355f943d
# frozen_string_literal: true
module GoogleCloud
class GeneratePipelineService < :: BaseService
ACTION_DEPLOY_TO_CLOUD_RUN = 'DEPLOY_TO_CLOUD_RUN'
ACTION_DEPLOY_TO_CLOUD_STORAGE = 'DEPLOY_TO_CLOUD_STORAGE'
def execute
commit_attributes = generate_commit_attributes
create_branch_response = ::Branches::CreateService.new(project, current_user)
.execute(commit_attributes[:branch_name], project.default_branch)
if create_branch_response[:status] == :error
return create_branch_response
end
branch = create_branch_response[:branch]
service = default_branch_gitlab_ci_yml.present? ? ::Files::UpdateService : ::Files::CreateService
commit_response = service.new(project, current_user, commit_attributes).execute
if commit_response[:status] == :error
return commit_response
end
success({ branch_name: branch.name, commit: commit_response })
end
private
def action
@params[:action]
end
def generate_commit_attributes
if action == ACTION_DEPLOY_TO_CLOUD_RUN
branch_name = "deploy-to-cloud-run-#{SecureRandom.hex(8)}"
{
commit_message: 'Enable Cloud Run deployments',
file_path: '.gitlab-ci.yml',
file_content: pipeline_content('gcp/cloud-run.gitlab-ci.yml'),
branch_name: branch_name,
start_branch: branch_name
}
elsif action == ACTION_DEPLOY_TO_CLOUD_STORAGE
branch_name = "deploy-to-cloud-storage-#{SecureRandom.hex(8)}"
{
commit_message: 'Enable Cloud Storage deployments',
file_path: '.gitlab-ci.yml',
file_content: pipeline_content('gcp/cloud-storage.gitlab-ci.yml'),
branch_name: branch_name,
start_branch: branch_name
}
end
end
def default_branch_gitlab_ci_yml
@default_branch_gitlab_ci_yml ||= project.repository.gitlab_ci_yml_for(project.default_branch)
end
def pipeline_content(include_path)
gitlab_ci_yml = Gitlab::Config::Loader::Yaml.new(default_branch_gitlab_ci_yml || '{}').load!
append_remote_include(gitlab_ci_yml, "https://gitlab.com/gitlab-org/incubation-engineering/five-minute-production/library/-/raw/main/#{include_path}")
end
def append_remote_include(gitlab_ci_yml, include_url)
stages = gitlab_ci_yml['stages'] || []
gitlab_ci_yml['stages'] = (stages + %w[build test deploy]).uniq
includes = gitlab_ci_yml['include'] || []
includes = Array.wrap(includes)
includes << { 'remote' => include_url }
gitlab_ci_yml['include'] = includes.uniq
gitlab_ci_yml.to_yaml
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GoogleCloud::GeneratePipelineService do
describe 'for cloud-run' do
describe 'when there is no existing pipeline' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:service_params) { { action: described_class::ACTION_DEPLOY_TO_CLOUD_RUN } }
let_it_be(:service) { described_class.new(project, maintainer, service_params) }
before do
project.add_maintainer(maintainer)
end
it 'creates a new branch with commit for cloud-run deployment' do
response = service.execute
branch_name = response[:branch_name]
commit = response[:commit]
local_branches = project.repository.local_branches
created_branch = local_branches.find { |branch| branch.name == branch_name }
expect(response[:status]).to eq(:success)
expect(branch_name).to start_with('deploy-to-cloud-run-')
expect(created_branch).to be_present
expect(created_branch.target).to eq(commit[:result])
end
it 'generated pipeline includes cloud-run deployment' do
response = service.execute
ref = response[:commit][:result]
gitlab_ci_yml = project.repository.gitlab_ci_yml_for(ref)
expect(response[:status]).to eq(:success)
expect(gitlab_ci_yml).to include('https://gitlab.com/gitlab-org/incubation-engineering/five-minute-production/library/-/raw/main/gcp/cloud-run.gitlab-ci.yml')
end
context 'simulate errors' do
it 'fails to create branch' do
allow_next_instance_of(Branches::CreateService) do |create_service|
allow(create_service).to receive(:execute)
.and_return({ status: :error })
end
response = service.execute
expect(response[:status]).to eq(:error)
end
it 'fails to commit changes' do
allow_next_instance_of(Files::CreateService) do |create_service|
allow(create_service).to receive(:execute)
.and_return({ status: :error })
end
response = service.execute
expect(response[:status]).to eq(:error)
end
end
end
describe 'when there is an existing pipeline without `deploy` stage' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:service_params) { { action: GoogleCloud::GeneratePipelineService::ACTION_DEPLOY_TO_CLOUD_RUN } }
let_it_be(:service) { described_class.new(project, maintainer, service_params) }
before do
project.add_maintainer(maintainer)
file_name = '.gitlab-ci.yml'
file_content = <<EOF
stages:
- build
- test
build-java:
stage: build
script: mvn clean install
test-java:
stage: test
script: mvn clean test
EOF
project.repository.create_file(maintainer,
file_name,
file_content,
message: 'Pipeline with three stages and two jobs',
branch_name: project.default_branch)
end
it 'introduces a `deploy` stage and includes the deploy-to-cloud-run job' do
response = service.execute
branch_name = response[:branch_name]
gitlab_ci_yml = project.repository.gitlab_ci_yml_for(branch_name)
pipeline = Gitlab::Config::Loader::Yaml.new(gitlab_ci_yml).load!
expect(response[:status]).to eq(:success)
expect(pipeline[:stages]).to eq(%w[build test deploy])
expect(pipeline[:include]).to be_present
expect(gitlab_ci_yml).to include('https://gitlab.com/gitlab-org/incubation-engineering/five-minute-production/library/-/raw/main/gcp/cloud-run.gitlab-ci.yml')
end
end
describe 'when there is an existing pipeline with `deploy` stage' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:service_params) { { action: GoogleCloud::GeneratePipelineService::ACTION_DEPLOY_TO_CLOUD_RUN } }
let_it_be(:service) { described_class.new(project, maintainer, service_params) }
before do
project.add_maintainer(maintainer)
file_name = '.gitlab-ci.yml'
file_content = <<EOF
stages:
- build
- test
- deploy
build-java:
stage: build
script: mvn clean install
test-java:
stage: test
script: mvn clean test
EOF
project.repository.create_file(maintainer,
file_name,
file_content,
message: 'Pipeline with three stages and two jobs',
branch_name: project.default_branch)
end
it 'includes the deploy-to-cloud-run job' do
response = service.execute
branch_name = response[:branch_name]
gitlab_ci_yml = project.repository.gitlab_ci_yml_for(branch_name)
pipeline = Gitlab::Config::Loader::Yaml.new(gitlab_ci_yml).load!
expect(response[:status]).to eq(:success)
expect(pipeline[:stages]).to eq(%w[build test deploy])
expect(pipeline[:include]).to be_present
expect(gitlab_ci_yml).to include('https://gitlab.com/gitlab-org/incubation-engineering/five-minute-production/library/-/raw/main/gcp/cloud-run.gitlab-ci.yml')
end
end
describe 'when there is an existing pipeline with `includes`' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:service_params) { { action: GoogleCloud::GeneratePipelineService::ACTION_DEPLOY_TO_CLOUD_RUN } }
let_it_be(:service) { described_class.new(project, maintainer, service_params) }
before do
project.add_maintainer(maintainer)
file_name = '.gitlab-ci.yml'
file_content = <<EOF
stages:
- build
- test
- deploy
include:
local: 'some-pipeline.yml'
EOF
project.repository.create_file(maintainer,
file_name,
file_content,
message: 'Pipeline with three stages and two jobs',
branch_name: project.default_branch)
end
it 'includes the deploy-to-cloud-run job' do
response = service.execute
branch_name = response[:branch_name]
gitlab_ci_yml = project.repository.gitlab_ci_yml_for(branch_name)
pipeline = Gitlab::Config::Loader::Yaml.new(gitlab_ci_yml).load!
expect(response[:status]).to eq(:success)
expect(pipeline[:stages]).to eq(%w[build test deploy])
expect(pipeline[:include]).to be_present
expect(gitlab_ci_yml).to include('https://gitlab.com/gitlab-org/incubation-engineering/five-minute-production/library/-/raw/main/gcp/cloud-run.gitlab-ci.yml')
end
end
end
describe 'for cloud-storage' do
describe 'when there is no existing pipeline' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:service_params) { { action: GoogleCloud::GeneratePipelineService::ACTION_DEPLOY_TO_CLOUD_STORAGE } }
let_it_be(:service) { described_class.new(project, maintainer, service_params) }
before do
project.add_maintainer(maintainer)
end
it 'creates a new branch with commit for cloud-storage deployment' do
response = service.execute
branch_name = response[:branch_name]
commit = response[:commit]
local_branches = project.repository.local_branches
search_for_created_branch = local_branches.find { |branch| branch.name == branch_name }
expect(response[:status]).to eq(:success)
expect(branch_name).to start_with('deploy-to-cloud-storage-')
expect(search_for_created_branch).to be_present
expect(search_for_created_branch.target).to eq(commit[:result])
end
it 'generated pipeline includes cloud-storage deployment' do
response = service.execute
ref = response[:commit][:result]
gitlab_ci_yml = project.repository.gitlab_ci_yml_for(ref)
expect(response[:status]).to eq(:success)
expect(gitlab_ci_yml).to include('https://gitlab.com/gitlab-org/incubation-engineering/five-minute-production/library/-/raw/main/gcp/cloud-storage.gitlab-ci.yml')
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