CID-472: introduce launch_options stack attribute

This commit is contained in:
Anton Chuchkalov 2016-04-20 14:48:33 +03:00
parent c77b382c33
commit 3e832a45d5
11 changed files with 139 additions and 60 deletions

View File

@ -61,11 +61,14 @@ class Stack < Handler
attrs[:tags] = JSON.parse(File.read(tags_filepath))
end
json = JSON.pretty_generate(
without_bootstrap: options[:without_bootstrap] || false,
skip_rollback: options[:skip_rollback] || false,
stack_attributes: attrs
attrs.merge!(
launch_options: {
without_bootstrap: options[:without_bootstrap] || false,
skip_rollback: options[:skip_rollback] || false,
}
)
json = JSON.pretty_generate(attrs)
if question(I18n.t("handler.stack.question.create")) {puts json}
job_ids = post_body "/stack", json
reports_urls(job_ids)

View File

@ -20,18 +20,13 @@ module Devops
end
def create_stack
object = parser.create
stack_attrs = object['stack_attributes']
stack_attrs = parser.create
project = Devops::Db.connector.project(stack_attrs['project'])
env = project.deploy_env(stack_attrs['deploy_env'])
raise InvalidRecord.new("Environment '#{env.identifier}' of project '#{project.id}' has no stack template") if env.stack_template.nil?
add_stack_attributes(stack_attrs, env, parser)
jid = Worker.start_async(StackBootstrapWorker,
stack_attributes: stack_attrs,
without_bootstrap: object['without_bootstrap'],
skip_rollback: object['skip_rollback']
)
jid = Worker.start_async(StackBootstrapWorker, stack_attributes: stack_attrs)
[jid]
end

View File

@ -7,21 +7,10 @@ module Devops
def create
@body ||= create_object_from_json_body
# temp fix to work on qa:
unless @body['stack_attributes']
@body = {
'stack_attributes' => @body.dup,
'without_bootstrap' => true,
'skip_rollback' => true
}
end
stack_attributes = @body.fetch('stack_attributes')
project_name = check_string(stack_attributes["project"], "Parameter 'project' must be a not empty string")
env_name = check_string(stack_attributes["deploy_env"], "Parameter 'deploy_env' must be a not empty string")
check_string(stack_attributes["name"], "Parameter 'name' must be a not empty string", true, false)
list = check_array(stack_attributes["run_list"], "Parameter 'run_list' is invalid, it should be not empty array of strings", String, true, true)
project_name = check_string(@body["project"], "Parameter 'project' must be a not empty string")
env_name = check_string(@body["deploy_env"], "Parameter 'deploy_env' must be a not empty string")
check_string(@body["name"], "Parameter 'name' must be a not empty string", true, false)
list = check_array(@body["run_list"], "Parameter 'run_list' is invalid, it should be not empty array of strings", String, true, true)
Validators::Helpers::RunList.new(list).validate! unless list.nil?
@body
end

View File

@ -30,19 +30,19 @@ module Devops
# - Content-Type: application/json
# - body :
# {
# "stack_attributes": {
# "project": "project_name",
# "deploy_env": "test",
# "provider": "ec2",
# "tags": {
# "tagName": "tagValue"
# },
# "parameters": {
# "KeyName": "Value"
# }
# "project": "project_name",
# "deploy_env": "test",
# "provider": "ec2",
# "tags": {
# "tagName": "tagValue"
# },
# "parameters": {
# "KeyName": "Value"
# },
# "launch_options": {
# "without_bootstrap": false,
# "skip_rollback": false
# }
# "without_bootstrap": false,
# "skip_rollback": false
# }
#
# * *Returns* :

View File

@ -8,6 +8,7 @@ module Devops
include ModelWithProvider
attr_accessor :parameters, :events, :stack_status, :persisting_is_locked
attr_accessor :launch_options # {'without_bootstrap' => false, 'skip_rollback' => true}
set_field_validators :id, [::Validators::FieldValidator::NotNil,
::Validators::FieldValidator::FieldType::String,
@ -57,6 +58,7 @@ module Devops
self.tags = attrs['tags'] || {}
self.stack_status = attrs['stack_status']
self.persisting_is_locked = attrs['persisting_is_locked']
self.launch_options = attrs['launch_options'] || {}
self
end
@ -72,7 +74,8 @@ module Devops
owner: owner,
run_list: run_list,
tags: tags,
persisting_is_locked: persisting_is_locked
persisting_is_locked: persisting_is_locked,
launch_options: launch_options
}.merge(provider_hash)
end
@ -114,6 +117,14 @@ module Devops
Devops::Db.connector.unlock_persisting_stack(id)
end
def without_bootstrap?
launch_options['without_bootstrap'] || false
end
def skip_rollback?
launch_options['skip_rollback'] || false
end
class << self
# attrs should include:

View File

@ -31,8 +31,7 @@ en:
server_bootstrap_private_ip_unset: "Server '%{server_id}' deploy failed: private ip is unset (job %{job_id})."
deploy_unknown_error: "Unknown error occured during server '%{server_id}' deploy (job %{job_id})."
deploy_failed: "Server '%{server_id}' deploy failed (job %{job_id})."
creating_server_unknown_error: "Unknown error occured during server '%{server_id}' creation (job %{job_id})."
creating_server_in_cloud_failed: "Server '%{server_id}' creation in cloud failed (job %{job_id})."
unknown_error: "Unknown error occured during server '%{server_id}' bootstrap or deploy (job %{job_id})."
stack_creation_waiter:
result:
ok: |

View File

@ -0,0 +1,14 @@
db.stacks.update(
{},
{
$set: {
launch_options: {
without_bootstrap: false,
skip_rollback: false
}
}
},
{
multi: true
}
)

View File

@ -10,6 +10,7 @@ FactoryGirl.define do
name 'iamstack'
owner 'root'
run_list []
launch_options({})
initialize_with { new(attributes.stringify_keys) }

View File

@ -34,7 +34,7 @@ RSpec.describe Devops::Model::StackEc2, type: :model do
describe '#to_hash_without_id' do
it 'returns hash with several fields' do
expect(stack.to_hash_without_id.keys).to include('provider', :project, :deploy_env, :stack_template, :name, :owner, :run_list, :tags)
expect(stack.to_hash_without_id.keys).to include('provider', :project, :deploy_env, :stack_template, :name, :owner, :run_list, :tags, :launch_options)
end
end
@ -52,6 +52,37 @@ RSpec.describe Devops::Model::StackEc2, type: :model do
end
end
describe 'without_bootstrap?' do
it 'returns set falsey value' do
stack = described_class.new('launch_options' => {'without_bootstrap' => false})
expect(stack.without_bootstrap?).to be false
end
it 'returns set truthy value' do
stack = described_class.new('launch_options' => {'without_bootstrap' => true})
expect(stack.without_bootstrap?).to be true
end
it 'returns false by default' do
expect(described_class.new.without_bootstrap?).to be false
end
end
describe 'skip_rollback?' do
it 'returns set falsey value' do
stack = described_class.new('launch_options' => {'skip_rollback' => false})
expect(stack.skip_rollback?).to be false
end
it 'returns set truthy value' do
stack = described_class.new('launch_options' => {'skip_rollback' => true})
expect(stack.skip_rollback?).to be true
end
it 'returns false by default' do
expect(described_class.new.skip_rollback?).to be false
end
end
describe '#create_stack_in_cloud!' do
it 'calls create_stack method of provider instance' do

View File

@ -5,11 +5,13 @@ RSpec.describe StackBootstrapWorker, type: :worker, stubbed_connector: true, ini
let(:stack_attrs) { attributes_for(:stack_ec2).stringify_keys }
let(:worker) { described_class.new }
let(:perform_with_bootstrap) { worker.perform('stack_attributes' => stack_attrs) }
let(:perform_without_bootstrap) { worker.perform('stack_attributes' => stack_attrs, 'without_bootstrap' => true) }
let(:perform_without_bootstrap) {
set_without_bootstrap_to(true)
worker.perform('stack_attributes' => stack_attrs)
}
let(:executor) {
instance_double(Devops::Executor::StackExecutor,
wait_till_stack_is_created: true,
create_stack: Devops::Model::StackEc2.new(stack_attrs),
persist_new_servers: nil,
delete_stack: nil,
bootstrap_just_persisted: bootstrap_result(:ok)
@ -20,10 +22,20 @@ RSpec.describe StackBootstrapWorker, type: :worker, stubbed_connector: true, ini
Devops::Executor::StackExecutor::PrioritizedGroupsBootstrapper::Result.from_reason(reason)
end
def set_without_bootstrap_to(value)
stack_attrs.merge!('launch_options' => {'without_bootstrap' => value})
end
def set_skip_rollback_to(value)
stack_attrs.merge!('launch_options' => {'skip_rollback' => value})
end
before do
allow(worker).to receive(:update_report)
allow(worker).to receive(:executor) { executor }
allow(stubbed_connector).to receive(:unlock_persisting_stack)
# #create_stack should be lazy evaluated because stack_attrs may change
allow(executor).to receive(:create_stack) { Devops::Model::StackEc2.new(stack_attrs) }
end
@ -64,16 +76,29 @@ RSpec.describe StackBootstrapWorker, type: :worker, stubbed_connector: true, ini
end
end
context 'if without_bootstrap is false or not set' do
context 'if without_bootstrap is false' do
it 'returns 0 when bootstraping servers was successful' do
expect(perform_with_bootstrap).to eq 0
end
it 'rollbacks stack and returns 1 when a known error occured during servers bootstrap' do
allow(executor).to receive(:bootstrap_just_persisted) { bootstrap_result(:bootstrap_error) }
expect(executor).to receive(:delete_stack)
perform_with_bootstrap
expect(perform_with_bootstrap).to eq 1
context "when a known error occured during servers bootstrap" do
before do
allow(executor).to receive(:bootstrap_just_persisted) { bootstrap_result(:bootstrap_error) }
end
it 'rollbacks stack and returns 1 if skip_rollback is false' do
set_skip_rollback_to(false)
expect(executor).to receive(:delete_stack)
perform_with_bootstrap
expect(perform_with_bootstrap).to eq 1
end
it "doesn't rollback stack, but returns 1 if skip_rollback is true" do
set_skip_rollback_to(true)
expect(executor).not_to receive(:delete_stack)
perform_with_bootstrap
expect(perform_with_bootstrap).to eq 1
end
end
it "doesn't rollback stack and returns 2 when a known error occured during servers deploy" do
@ -82,11 +107,24 @@ RSpec.describe StackBootstrapWorker, type: :worker, stubbed_connector: true, ini
expect(perform_with_bootstrap).to eq 2
end
it 'rollbacks stack and reraises that error when an unknown error occured during servers bootsrap and deploy' do
error = StandardError.new
allow(executor).to receive(:bootstrap_just_persisted) { raise error }
expect(worker).to receive(:rollback_stack!)
expect{perform_with_bootstrap}.to raise_error(error)
context "when an unknown error occured during servers bootsrap and deploy" do
let(:error) { StandardError.new }
before do
allow(executor).to receive(:bootstrap_just_persisted) { raise error }
end
it 'rollbacks stack and reraises that error if skip_rollback is false' do
set_skip_rollback_to(false)
expect(worker).to receive(:rollback_stack!)
expect{perform_with_bootstrap}.to raise_error(error)
end
it "doesn't rollback stack, but reraises that error if skip_rollback is true" do
set_skip_rollback_to(true)
expect(worker).not_to receive(:rollback_stack!)
expect{perform_with_bootstrap}.to raise_error(error)
end
end
end
end

View File

@ -4,18 +4,16 @@ class StackBootstrapWorker < Worker
# @options:
# 'stack_attributes', required
# 'without_bootstrap', optional. false by default
# 'skip_rollback', optional. false by default
def perform(options)
call do
puts_and_flush JSON.pretty_generate(options)
stack_attrs = options.fetch('stack_attributes')
without_bootstrap = options['without_bootstrap'] || false
skip_rollback = options['skip_rollback'] || false
save_report(stack_attrs)
@stack = executor.create_stack(stack_attrs)
without_bootstrap = @stack.without_bootstrap?
skip_rollback = @stack.skip_rollback?
if !executor.wait_till_stack_is_created
puts_and_flush "Stack creating error"
return 1