fuel-astute/spec/unit/deployment_engine_spec.rb

337 lines
12 KiB
Ruby

# Copyright 2013 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
require File.join(File.dirname(__FILE__), '../spec_helper')
require 'tmpdir'
describe Astute::DeploymentEngine do
include SpecHelpers
class Engine < Astute::DeploymentEngine;
def pre_deployment_actions(deployment_info, pre_deployment)
end
def pre_node_actions(part)
end
def pre_deploy_actions(part)
end
def post_deploy_actions(part)
end
def post_deployment_actions(deployment_info, post_deployment)
end
end
let(:ctx) do
tctx = mock_ctx
tctx.stubs(:status).returns({})
tctx
end
describe '#new' do
it 'should not be avaliable to instantiation' do
expect { Astute::DeploymentEngine.new(ctx) }.to raise_exception(/Instantiation of this superclass is not allowed/)
end
it 'should be avaliable as superclass' do
expect { Engine.new(ctx) }.to be_true
end
end
let(:deployer) { Engine.new(ctx) }
describe '#deploy' do
context 'hooks' do
let(:nodes) {
[{'uid' => 1, 'priority' => 10}, {'uid' => 2, 'priority' => 0}, {'uid' => 1, 'priority' => 15}]
}
let(:pre_deployment) {
[{
"priority" => 100,
"type" => "upload_file",
"uids" => [1, 2],
"parameters" => {}
}]
}
let(:post_deployment) {
[{
"priority" => 100,
"type" => "puppet",
"uids" => [1, 2],
"parameters" => {}
}]
}
before(:each) { deployer.stubs(:deploy_piece) }
it 'should run pre deployment hooks run once for all cluster' do
deployer.expects(:pre_deployment_actions).with(nodes, []).once
deployer.deploy(nodes)
end
it 'should run post deployment hooks run once for all cluster' do
deployer.expects(:post_deployment_actions).with(nodes, []).once
deployer.deploy(nodes)
end
context 'hooks' do
it 'should run pre and post deployment nailgun hooks run once for all cluster' do
hook_order = sequence('hook_order')
deployer.expects(:pre_deployment_actions).in_sequence(hook_order)
deployer.expects(:deploy_piece).in_sequence(hook_order)
deployer.expects(:post_deployment_actions).in_sequence(hook_order)
deployer.deploy(nodes, pre_deployment, post_deployment)
end
it 'should not do additional update for node status if pre hooks failed' do
deployer.expects(:pre_deployment_actions).raises(Astute::DeploymentEngineError)
ctx.expects(:report_and_update_status).never
expect {deployer.deploy(nodes, pre_deployment, post_deployment)}.to raise_error(Astute::DeploymentEngineError)
end
end
it 'should run pre deploy hooks once for role' do
deployer.expects(:pre_deploy_actions).times(3)
deployer.deploy(nodes)
end
it 'should run post deploy hooks once for role' do
deployer.expects(:post_deploy_actions).times(3)
deployer.deploy(nodes)
end
end
it 'deploy nodes by order' do
nodes = [{'uid' => 1, 'priority' => 10}, {'uid' => 2, 'priority' => 0}, {'uid' => 1, 'priority' => 15}]
deploy_order = sequence('deploy_order')
deployer.expects(:deploy_piece).with([{'uid' => 2, 'priority' => 0}]).in_sequence(deploy_order)
deployer.expects(:deploy_piece).with([{'uid' => 1, 'priority' => 10}]).in_sequence(deploy_order)
deployer.expects(:deploy_piece).with([{'uid' => 1, 'priority' => 15}]).in_sequence(deploy_order)
deployer.deploy(nodes)
end
it 'nodes with same priority should be deploy at parallel' do
nodes = [{'uid' => 1, 'priority' => 10}, {'uid' => 2, 'priority' => 0}, {'uid' => 3, 'priority' => 10}]
deployer.expects(:deploy_piece).with([{'uid' => 2, 'priority' => 0}])
deployer.expects(:deploy_piece).with([{"uid"=>1, "priority"=>10}, {"uid"=>3, "priority"=>10}])
deployer.deploy(nodes)
end
it 'node with several roles with same priority should not run at parallel' do
nodes = [
{'uid' => 1, 'priority' => 10, 'role' => 'compute'},
{'uid' => 2, 'priority' => 0, 'role' => 'primary-controller'},
{'uid' => 1, 'priority' => 10, 'role' => 'cinder'}
]
deployer.expects(:deploy_piece).with([{'uid' => 2, 'priority' => 0, 'role' => 'primary-controller'}])
deployer.expects(:deploy_piece).with([{'uid' => 1, 'priority' => 10, 'role' => 'compute'}])
deployer.expects(:deploy_piece).with([{'uid' => 1, 'priority' => 10, 'role' => 'cinder'}])
deployer.deploy(nodes)
end
it 'node with several roles with same priority should not run at parallel, but different nodes should' do
nodes = [
{'uid' => 1, 'priority' => 10, 'role' => 'compute'},
{'uid' => 3, 'priority' => 10, 'role' => 'compute'},
{'uid' => 2, 'priority' => 0, 'role' => 'primary-controller'},
{'uid' => 1, 'priority' => 10, 'role' => 'cinder'}
]
deployer.expects(:deploy_piece).with([{'uid' => 2, 'priority' => 0, 'role' => 'primary-controller'}])
deployer.expects(:deploy_piece).with([
{'uid' => 1, 'priority' => 10, 'role' => 'compute'},
{'uid' => 3, 'priority' => 10, 'role' => 'compute'}
])
deployer.expects(:deploy_piece).with([{'uid' => 1, 'priority' => 10, 'role' => 'cinder'}])
deployer.deploy(nodes)
end
context 'critical node' do
let(:ctx) { mock_ctx }
it 'should stop deployment if critical node deployment fail' do
nodes = [
{'uid' => '1', 'priority' => 20, 'role' => 'compute', 'fail_if_error' => false},
{'uid' => '3', 'priority' => 20, 'role' => 'compute', 'fail_if_error' => false},
{'uid' => '2', 'priority' => 10, 'role' => 'primary-controller', 'fail_if_error' => true},
{'uid' => '1', 'priority' => 20, 'role' => 'cinder', 'fail_if_error' => false},
{'uid' => '2', 'priority' => 5, 'role' => 'mongo', 'fail_if_error' => false}
]
ctx.stubs(:status).returns({'2' => 'success'}).then.returns({'2' => 'error'})
deployer.expects(:deploy_piece).with([
{'uid' => '2',
'priority' => 5,
'role' => 'mongo',
'fail_if_error' => false}]
)
deployer.expects(:deploy_piece).with([
{'uid' => '2',
'priority' => 10,
'role' => 'primary-controller',
'fail_if_error' => true}]
)
ctx.stubs(:report_and_update_status)
deployer.deploy(nodes)
end
it 'should not stop deployment if fail non-critical node' do
nodes = [
{'uid' => '1', 'priority' => 20, 'role' => 'compute', 'fail_if_error' => false},
{'uid' => '2', 'priority' => 10, 'role' => 'primary-controller', 'fail_if_error' => true},
{'uid' => '1', 'priority' => 5, 'role' => 'mongo', 'fail_if_error' => false}
]
ctx.stubs(:status).returns({'1' => 'error'})
.then.returns({'2' => 'success', '1' => 'error'})
.then.returns({'1' => 'success', '2' => 'success' })
deployer.expects(:deploy_piece).with([
{'uid' => '1',
'priority' => 5,
'role' => 'mongo',
'fail_if_error' => false}]
)
deployer.expects(:deploy_piece).with([
{'uid' => '2',
'priority' => 10,
'role' => 'primary-controller',
'fail_if_error' => true}]
)
deployer.expects(:deploy_piece).with([
{'uid' => '1',
'priority' => 20,
'role' => 'compute',
'fail_if_error' => false}]
)
deployer.deploy(nodes)
end
it 'should not send status for all nodes after nodes group where critical node fail' do
nodes = [
{'uid' => '1', 'priority' => 20, 'role' => 'compute', 'fail_if_error' => false},
{'uid' => '3', 'priority' => 20, 'role' => 'compute', 'fail_if_error' => false},
{'uid' => '2', 'priority' => 10, 'role' => 'primary-controller', 'fail_if_error' => true},
{'uid' => '1', 'priority' => 20, 'role' => 'cinder', 'fail_if_error' => false},
{'uid' => '2', 'priority' => 5, 'role' => 'mongo', 'fail_if_error' => false}
]
ctx.stubs(:status).returns({'2' => 'success'}).then.returns({'2' => 'error'})
deployer.stubs(:deploy_piece).twice
ctx.expects(:report_and_update_status).never
deployer.deploy(nodes)
end
it 'should not affect parallel nodes in same running group' do
nodes = [
{'uid' => '1', 'priority' => 20, 'role' => 'compute', 'fail_if_error' => false},
{'uid' => '3', 'priority' => 20, 'role' => 'compute', 'fail_if_error' => false},
{'uid' => '2', 'priority' => 10, 'role' => 'primary-controller', 'fail_if_error' => true},
{'uid' => '2', 'priority' => 20, 'role' => 'cinder', 'fail_if_error' => false},
{'uid' => '1', 'priority' => 10, 'role' => 'mongo', 'fail_if_error' => true}
]
ctx.stubs(:status).returns({'2' => 'success', '1' => 'error'})
deployer.stubs(:deploy_piece).once
ctx.expects(:report_and_update_status).never
deployer.deploy(nodes)
end
context 'limits' do
around(:each) do |example|
old_value = Astute.config.max_nodes_per_call
example.run
Astute.config.max_nodes_per_call = old_value
end
it 'should affect nodes with same priorities in next deployment group' do
Astute.config.max_nodes_per_call = 1
nodes = [
{'uid' => '2', 'priority' => 10, 'role' => 'primary-controller', 'fail_if_error' => true},
{'uid' => '2', 'priority' => 20, 'role' => 'cinder', 'fail_if_error' => false},
{'uid' => '1', 'priority' => 10, 'role' => 'mongo', 'fail_if_error' => true}
]
ctx.stubs(:status).returns({'2' => 'error'})
deployer.stubs(:deploy_piece).once
ctx.expects(:report_and_update_status).never
deployer.deploy(nodes)
end
end # 'limits'
end
context 'limits' do
around(:each) do |example|
old_value = Astute.config.max_nodes_per_call
example.run
Astute.config.max_nodes_per_call = old_value
end
it 'number of nodes running in parallel should be limited' do
Astute.config.max_nodes_per_call = 1
nodes = [
{'uid' => 1, 'priority' => 10, 'role' => 'compute'},
{'uid' => 3, 'priority' => 10, 'role' => 'compute'},
{'uid' => 2, 'priority' => 0, 'role' => 'primary-controller'},
{'uid' => 1, 'priority' => 10, 'role' => 'cinder'}
]
deployer.expects(:deploy_piece).with([{'uid' => 2, 'priority' => 0, 'role' => 'primary-controller'}])
deployer.expects(:deploy_piece).with([{'uid' => 1, 'priority' => 10, 'role' => 'compute'}])
deployer.expects(:deploy_piece).with([{'uid' => 3, 'priority' => 10, 'role' => 'compute'}])
deployer.expects(:deploy_piece).with([{'uid' => 1, 'priority' => 10, 'role' => 'cinder'}])
deployer.deploy(nodes)
end
end
it 'should raise error if deployment list is empty' do
expect { deployer.deploy([]) }.to raise_error('Deployment info are not provided!')
end
end
end