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)) attrs[:tags] = JSON.parse(File.read(tags_filepath))
end end
json = JSON.pretty_generate( attrs.merge!(
launch_options: {
without_bootstrap: options[:without_bootstrap] || false, without_bootstrap: options[:without_bootstrap] || false,
skip_rollback: options[:skip_rollback] || false, skip_rollback: options[:skip_rollback] || false,
stack_attributes: attrs }
) )
json = JSON.pretty_generate(attrs)
if question(I18n.t("handler.stack.question.create")) {puts json} if question(I18n.t("handler.stack.question.create")) {puts json}
job_ids = post_body "/stack", json job_ids = post_body "/stack", json
reports_urls(job_ids) reports_urls(job_ids)

View File

@ -20,18 +20,13 @@ module Devops
end end
def create_stack def create_stack
object = parser.create stack_attrs = parser.create
stack_attrs = object['stack_attributes']
project = Devops::Db.connector.project(stack_attrs['project']) project = Devops::Db.connector.project(stack_attrs['project'])
env = project.deploy_env(stack_attrs['deploy_env']) 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? 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) add_stack_attributes(stack_attrs, env, parser)
jid = Worker.start_async(StackBootstrapWorker, jid = Worker.start_async(StackBootstrapWorker, stack_attributes: stack_attrs)
stack_attributes: stack_attrs,
without_bootstrap: object['without_bootstrap'],
skip_rollback: object['skip_rollback']
)
[jid] [jid]
end end

View File

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

View File

@ -30,7 +30,6 @@ module Devops
# - Content-Type: application/json # - Content-Type: application/json
# - body : # - body :
# { # {
# "stack_attributes": {
# "project": "project_name", # "project": "project_name",
# "deploy_env": "test", # "deploy_env": "test",
# "provider": "ec2", # "provider": "ec2",
@ -39,11 +38,12 @@ module Devops
# }, # },
# "parameters": { # "parameters": {
# "KeyName": "Value" # "KeyName": "Value"
# } # },
# } # "launch_options": {
# "without_bootstrap": false, # "without_bootstrap": false,
# "skip_rollback": false # "skip_rollback": false
# } # }
# }
# #
# * *Returns* : # * *Returns* :
# [report_id] # [report_id]

View File

@ -8,6 +8,7 @@ module Devops
include ModelWithProvider include ModelWithProvider
attr_accessor :parameters, :events, :stack_status, :persisting_is_locked 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, set_field_validators :id, [::Validators::FieldValidator::NotNil,
::Validators::FieldValidator::FieldType::String, ::Validators::FieldValidator::FieldType::String,
@ -57,6 +58,7 @@ module Devops
self.tags = attrs['tags'] || {} self.tags = attrs['tags'] || {}
self.stack_status = attrs['stack_status'] self.stack_status = attrs['stack_status']
self.persisting_is_locked = attrs['persisting_is_locked'] self.persisting_is_locked = attrs['persisting_is_locked']
self.launch_options = attrs['launch_options'] || {}
self self
end end
@ -72,7 +74,8 @@ module Devops
owner: owner, owner: owner,
run_list: run_list, run_list: run_list,
tags: tags, tags: tags,
persisting_is_locked: persisting_is_locked persisting_is_locked: persisting_is_locked,
launch_options: launch_options
}.merge(provider_hash) }.merge(provider_hash)
end end
@ -114,6 +117,14 @@ module Devops
Devops::Db.connector.unlock_persisting_stack(id) Devops::Db.connector.unlock_persisting_stack(id)
end end
def without_bootstrap?
launch_options['without_bootstrap'] || false
end
def skip_rollback?
launch_options['skip_rollback'] || false
end
class << self class << self
# attrs should include: # 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})." 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_unknown_error: "Unknown error occured during server '%{server_id}' deploy (job %{job_id})."
deploy_failed: "Server '%{server_id}' deploy failed (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})." unknown_error: "Unknown error occured during server '%{server_id}' bootstrap or deploy (job %{job_id})."
creating_server_in_cloud_failed: "Server '%{server_id}' creation in cloud failed (job %{job_id})."
stack_creation_waiter: stack_creation_waiter:
result: result:
ok: | 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' name 'iamstack'
owner 'root' owner 'root'
run_list [] run_list []
launch_options({})
initialize_with { new(attributes.stringify_keys) } 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 describe '#to_hash_without_id' do
it 'returns hash with several fields' 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
end end
@ -52,6 +52,37 @@ RSpec.describe Devops::Model::StackEc2, type: :model do
end end
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 describe '#create_stack_in_cloud!' do
it 'calls create_stack method of provider instance' 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(:stack_attrs) { attributes_for(:stack_ec2).stringify_keys }
let(:worker) { described_class.new } let(:worker) { described_class.new }
let(:perform_with_bootstrap) { worker.perform('stack_attributes' => stack_attrs) } 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) { let(:executor) {
instance_double(Devops::Executor::StackExecutor, instance_double(Devops::Executor::StackExecutor,
wait_till_stack_is_created: true, wait_till_stack_is_created: true,
create_stack: Devops::Model::StackEc2.new(stack_attrs),
persist_new_servers: nil, persist_new_servers: nil,
delete_stack: nil, delete_stack: nil,
bootstrap_just_persisted: bootstrap_result(:ok) 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) Devops::Executor::StackExecutor::PrioritizedGroupsBootstrapper::Result.from_reason(reason)
end 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 before do
allow(worker).to receive(:update_report) allow(worker).to receive(:update_report)
allow(worker).to receive(:executor) { executor } allow(worker).to receive(:executor) { executor }
allow(stubbed_connector).to receive(:unlock_persisting_stack) 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 end
@ -64,29 +76,55 @@ RSpec.describe StackBootstrapWorker, type: :worker, stubbed_connector: true, ini
end end
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 it 'returns 0 when bootstraping servers was successful' do
expect(perform_with_bootstrap).to eq 0 expect(perform_with_bootstrap).to eq 0
end end
it 'rollbacks stack and returns 1 when a known error occured during servers bootstrap' do context "when a known error occured during servers bootstrap" do
before do
allow(executor).to receive(:bootstrap_just_persisted) { bootstrap_result(:bootstrap_error) } 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) expect(executor).to receive(:delete_stack)
perform_with_bootstrap perform_with_bootstrap
expect(perform_with_bootstrap).to eq 1 expect(perform_with_bootstrap).to eq 1
end 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 it "doesn't rollback stack and returns 2 when a known error occured during servers deploy" do
allow(executor).to receive(:bootstrap_just_persisted) { bootstrap_result(:deploy_error) } allow(executor).to receive(:bootstrap_just_persisted) { bootstrap_result(:deploy_error) }
expect(worker).not_to receive(:rollback_stack!) expect(worker).not_to receive(:rollback_stack!)
expect(perform_with_bootstrap).to eq 2 expect(perform_with_bootstrap).to eq 2
end end
it 'rollbacks stack and reraises that error when an unknown error occured during servers bootsrap and deploy' do context "when an unknown error occured during servers bootsrap and deploy" do
error = StandardError.new let(:error) { StandardError.new }
before do
allow(executor).to receive(:bootstrap_just_persisted) { raise error } 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(worker).to receive(:rollback_stack!)
expect{perform_with_bootstrap}.to raise_error(error) expect{perform_with_bootstrap}.to raise_error(error)
end 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
end end

View File

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