Commit a36d5561 authored by Kamil Trzcinski's avatar Kamil Trzcinski

Introduce deploy command that allows to start deployment from one environment to second one

parent 1d16f137
...@@ -103,8 +103,8 @@ class Environment < ActiveRecord::Base ...@@ -103,8 +103,8 @@ class Environment < ActiveRecord::Base
def actions_for(environment) def actions_for(environment)
return [] unless manual_actions return [] unless manual_actions
manual_actions.select |action| manual_actions.select do |action|
action.expanded_environment_name = environment action.expanded_environment_name == environment
end end
end end
end end
---
title: Add deployment command to ChatOps
merge_request: 7619
author:
...@@ -4,6 +4,7 @@ module Gitlab ...@@ -4,6 +4,7 @@ module Gitlab
COMMANDS = [ COMMANDS = [
Gitlab::ChatCommands::IssueShow, Gitlab::ChatCommands::IssueShow,
Gitlab::ChatCommands::IssueCreate, Gitlab::ChatCommands::IssueCreate,
Gitlab::ChatCommands::Deploy,
].freeze ].freeze
def execute def execute
......
...@@ -9,6 +9,10 @@ module Gitlab ...@@ -9,6 +9,10 @@ module Gitlab
'deploy <environment> to <target-environment>' 'deploy <environment> to <target-environment>'
end end
def self.available?(project)
project.builds_enabled?
end
def self.allowed?(project, user) def self.allowed?(project, user)
can?(user, :create_deployment, project) can?(user, :create_deployment, project)
end end
...@@ -17,11 +21,8 @@ module Gitlab ...@@ -17,11 +21,8 @@ module Gitlab
from = match[:from] from = match[:from]
to = match[:to] to = match[:to]
environment = project.environments.find_by(name: from) actions = find_actions(from, to)
return unless environment return unless actions.present?
actions = environment.actions_for(to)
return unless actions.any?
if actions.one? if actions.one?
actions.first.play(current_user) actions.first.play(current_user)
...@@ -29,6 +30,15 @@ module Gitlab ...@@ -29,6 +30,15 @@ module Gitlab
Result.new(:error, 'Too many actions defined') Result.new(:error, 'Too many actions defined')
end end
end end
private
def find_actions(from, to)
environment = project.environments.find_by(name: from)
return unless environment
environment.actions_for(to).select(&:starts_environment?)
end
end end
end end
end end
...@@ -55,6 +55,12 @@ FactoryGirl.define do ...@@ -55,6 +55,12 @@ FactoryGirl.define do
self.when 'manual' self.when 'manual'
end end
trait :teardown_environment do
options do
{ environment: { action: 'stop' } }
end
end
trait :allowed_to_fail do trait :allowed_to_fail do
allow_failure true allow_failure true
end end
......
...@@ -4,9 +4,9 @@ describe Gitlab::ChatCommands::Command, service: true do ...@@ -4,9 +4,9 @@ describe Gitlab::ChatCommands::Command, service: true do
let(:project) { create(:empty_project) } let(:project) { create(:empty_project) }
let(:user) { create(:user) } let(:user) { create(:user) }
subject { described_class.new(project, user, params).execute }
describe '#execute' do describe '#execute' do
subject { described_class.new(project, user, params).execute }
context 'when no command is available' do context 'when no command is available' do
let(:params) { { text: 'issue show 1' } } let(:params) { { text: 'issue show 1' } }
let(:project) { create(:project, has_external_issue_tracker: true) } let(:project) { create(:project, has_external_issue_tracker: true) }
...@@ -51,5 +51,44 @@ describe Gitlab::ChatCommands::Command, service: true do ...@@ -51,5 +51,44 @@ describe Gitlab::ChatCommands::Command, service: true do
expect(subject[:text]).to match(/\/issues\/\d+/) expect(subject[:text]).to match(/\/issues\/\d+/)
end end
end end
context 'when trying to do deployment' do
let(:params) { { text: 'deploy staging to production' } }
let!(:build) { create(:ci_build, project: project) }
let!(:staging) { create(:environment, name: 'staging', project: project) }
let!(:deployment) { create(:deployment, environment: staging, deployable: build) }
let!(:manual) do
create(:ci_build, :manual, project: project, pipeline: build.pipeline, name: 'first', environment: 'production')
end
context 'and user can not create deployment' do
it 'returns action' do
expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to start_with('Whoops! That action is not allowed')
end
end
context 'and user does have deployment permission' do
before do
project.team << [user, :developer]
end
it 'returns action' do
expect(subject[:text]).to include(manual.name)
expect(subject[:response_type]).to be(:in_channel)
end
context 'when duplicate action exists' do
let!(:manual2) do
create(:ci_build, :manual, project: project, pipeline: build.pipeline, name: 'second', environment: 'production')
end
it 'returns error' do
expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to include('Too many actions defined')
end
end
end
end
end end
end end
require 'spec_helper'
describe Gitlab::ChatCommands::Deploy, service: true do
describe '#execute' do
let(:project) { create(:empty_project) }
let(:user) { create(:user) }
let(:regex_match) { described_class.match('deploy staging to production') }
before do
project.team << [user, :master]
end
subject do
described_class.new(project, user).execute(regex_match)
end
context 'if no environment is defined' do
it 'returns nil' do
expect(subject).to be_nil
end
end
context 'with environment' do
let!(:staging) { create(:environment, name: 'staging', project: project) }
let!(:build) { create(:ci_build, project: project) }
let!(:deployment) { create(:deployment, environment: staging, deployable: build) }
context 'without actions' do
it 'returns nil' do
expect(subject).to be_nil
end
end
context 'with action' do
let!(:manual1) do
create(:ci_build, :manual, project: project, pipeline: build.pipeline, name: 'first', environment: 'production')
end
it 'returns action' do
expect(subject).to eq(manual1)
end
context 'when duplicate action exists' do
let!(:manual2) do
create(:ci_build, :manual, project: project, pipeline: build.pipeline, name: 'second', environment: 'production')
end
it 'returns error' do
expect(subject.message).to eq('Too many actions defined')
end
end
context 'when teardown action exists' do
let!(:teardown) do
create(:ci_build, :manual, :teardown_environment, project: project, pipeline: build.pipeline,
name: 'teardown', environment: 'production')
end
it 'returns error' do
expect(subject).to eq(action)
end
end
end
end
end
describe 'self.match' do
it 'matches the environment' do
match = described_class.match('deploy staging to production')
expect(match[:from]).to eq('staging')
expect(match[:to]).to eq('production')
end
end
end
...@@ -9,6 +9,7 @@ describe Environment, models: true do ...@@ -9,6 +9,7 @@ describe Environment, models: true do
it { is_expected.to delegate_method(:last_deployment).to(:deployments).as(:last) } it { is_expected.to delegate_method(:last_deployment).to(:deployments).as(:last) }
it { is_expected.to delegate_method(:stop_action).to(:last_deployment) } it { is_expected.to delegate_method(:stop_action).to(:last_deployment) }
it { is_expected.to delegate_method(:manual_actions).to(:last_deployment) }
it { is_expected.to validate_presence_of(:name) } it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:project_id) } it { is_expected.to validate_uniqueness_of(:name).scoped_to(:project_id) }
...@@ -187,4 +188,15 @@ describe Environment, models: true do ...@@ -187,4 +188,15 @@ describe Environment, models: true do
it { is_expected.to be false } it { is_expected.to be false }
end end
end end
describe '#actions_for' do
let(:deployment) { create(:deployment, environment: environment) }
let(:pipeline) { deployment.deployable.pipeline }
let!(:review_action) { create(:ci_build, :manual, name: 'review-apps', pipeline: pipeline, environment: 'review/$CI_BUILD_REF_NAME' )}
let!(:production_action) { create(:ci_build, :manual, name: 'production', pipeline: pipeline, environment: 'production' )}
it 'returns a list of actions with matching environment' do
expect(environment.actions_for('review/master')).to contain_exactly(review_action)
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