fuel-astute/spec/unit/task_deployment_spec.rb

401 lines
14 KiB
Ruby

# Copyright 2015 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')
describe Astute::TaskDeployment do
include SpecHelpers
let(:ctx) do
ctx = mock('context')
ctx.stubs(:task_id)
ctx
end
let(:tasks_metadata) do
{
'fault_tolerance_groups' =>[
{"fault_tolerance"=>0, "name"=>"primary-controller", "node_ids"=>["1"]},
{"fault_tolerance"=>1, "name"=>"controller", "node_ids"=>[]},
{"fault_tolerance"=>0, "name"=>"cinder", "node_ids"=>[]},
{"fault_tolerance"=>0, "name"=>"cinder-block-device", "node_ids"=>[]},
{"fault_tolerance"=>1, "name"=>"cinder-vmware", "node_ids"=>[]},
{"fault_tolerance"=>0, "name"=>"compute", "node_ids"=>["3", "2"]},
{"fault_tolerance"=>1, "name"=>"compute-vmware", "node_ids"=>[]},
{"fault_tolerance"=>1, "name"=>"mongo", "node_ids"=>[]},
{"fault_tolerance"=>1, "name"=>"primary-mongo", "node_ids"=>[]},
{"fault_tolerance"=>1,
"name"=>"ceph-osd",
"node_ids"=>["3", "2", "5", "4"]},
{"fault_tolerance"=>1, "name"=>"base-os", "node_ids"=>[]},
{"fault_tolerance"=>1, "name"=>"virt", "node_ids"=>[]},
{"fault_tolerance"=>1, "name"=>"ironic", "node_ids"=>[]}
]
}
end
let(:tasks_graph) do
{"1"=>
[{
"type"=>"noop",
"fail_on_error"=>true,
"required_for"=>[],
"requires"=> [],
"id"=>"ironic_post_swift_key",
"parameters"=>{},
}],
"null"=> [{
"skipped"=>true,
"type"=>"skipped",
"fail_on_error"=>false,
"required_for"=>[],
"requires"=>[],
"parameters"=>{},
"id"=>"post_deployment_start"}]
}
end
let(:tasks_directory) do
{"ironic_post_swift_key"=>{
"parameters"=>{
"retries"=>3,
"cmd"=>"sh generate_keys.sh -i 1 -s 'ceph' -p /var/lib/fuel/keys/",
"cwd"=>"/",
"timeout"=>180,
"interval"=>1},
"type"=>"shell",
"id"=>"ironic_post_swift_key"},
"post_deployment_start"=>{
"parameters"=>{}
}
}
end
let(:task_deployment) { Astute::TaskDeployment.new(ctx) }
describe '#deploy' do
it 'should run deploy' do
task_deployment.stubs(:fail_offline_nodes).returns([])
task_deployment.stubs(:write_graph_to_file)
ctx.stubs(:report)
Astute::TaskCluster.any_instance.expects(:run).returns({:success => true})
task_deployment.deploy(
tasks_metadata: tasks_metadata,
tasks_graph: tasks_graph,
tasks_directory: tasks_directory)
end
it 'should not raise error if deployment info not provided' do
task_deployment.stubs(:fail_offline_nodes).returns([])
task_deployment.stubs(:write_graph_to_file)
ctx.stubs(:report)
Astute::TaskCluster.any_instance.expects(:run).returns({:success => true})
expect{task_deployment.deploy(
tasks_graph: tasks_graph,
tasks_directory: tasks_directory)}.to_not raise_error
end
it 'should raise error if tasks graph not provided' do
expect{task_deployment.deploy(
tasks_directory: tasks_directory)}.to raise_error(
Astute::DeploymentEngineError,
"Deployment graph was not provided!"
)
end
it 'should support virtual node' do
d_t = task_deployment.send(:support_virtual_node, tasks_graph)
expect(d_t.keys).to include 'virtual_sync_node'
expect(d_t.keys).not_to include 'null'
end
it 'should support critical nodes' do
critical_nodes = task_deployment.send(
:critical_node_uids,
tasks_metadata['fault_tolerance_groups']
)
expect(critical_nodes).to include '1'
expect(critical_nodes).to include '2'
expect(critical_nodes).to include '3'
expect(critical_nodes.size).to eql(3)
end
it 'should fail offline nodes' do
Astute::TaskPreDeploymentActions.any_instance.stubs(:process)
task_deployment.stubs(:write_graph_to_file)
ctx.stubs(:report)
task_deployment.expects(:fail_offline_nodes).returns([])
Astute::TaskCluster.any_instance.stubs(:run).returns({:success => true})
task_deployment.deploy(
tasks_metadata: tasks_metadata,
tasks_graph: tasks_graph,
tasks_directory: tasks_directory)
end
it 'should setup stop condition' do
Astute::TaskPreDeploymentActions.any_instance.stubs(:process)
task_deployment.stubs(:write_graph_to_file)
ctx.stubs(:report)
task_deployment.stubs(:fail_offline_nodes).returns([])
Astute::TaskCluster.any_instance.stubs(:run).returns({:success => true})
Astute::TaskCluster.any_instance.expects(:stop_condition)
task_deployment.deploy(
tasks_metadata: tasks_metadata,
tasks_graph: tasks_graph,
tasks_directory: tasks_directory)
end
it 'should setup deployment logger' do
Astute::TaskPreDeploymentActions.any_instance.stubs(:process)
task_deployment.stubs(:write_graph_to_file)
ctx.stubs(:report)
task_deployment.stubs(:fail_offline_nodes).returns([])
Astute::TaskCluster.any_instance.stubs(:run).returns({:success => true})
Deployment::Log.expects(:logger=).with(Astute.logger)
task_deployment.deploy(
tasks_metadata: tasks_metadata,
tasks_graph: tasks_graph,
tasks_directory: tasks_directory)
end
context 'task concurrency' do
let(:task_concurrency) { mock('task_concurrency') }
before(:each) do
task_deployment.stubs(:write_graph_to_file)
ctx.stubs(:report)
task_deployment.stubs(:fail_offline_nodes).returns([])
Astute::TaskCluster.any_instance.stubs(:run).returns({:success => true})
Deployment::Concurrency::Counter.any_instance
.stubs(:maximum=).with(
Astute.config.max_nodes_per_call)
end
it 'should setup 0 if no task concurrency setup' do
Deployment::Concurrency::Counter.any_instance.expects(:maximum=).with(0).times(5)
task_deployment.deploy(
tasks_metadata: tasks_metadata,
tasks_graph: tasks_graph,
tasks_directory: tasks_directory)
end
it 'it should setup 1 if task concurrency type one_by_one' do
tasks_graph['1'].first['parameters']['strategy'] =
{'type' => 'one_by_one'}
Deployment::Concurrency::Counter.any_instance.expects(:maximum=)
.with(0).times(4)
Deployment::Concurrency::Counter.any_instance.expects(:maximum=)
.with(1)
task_deployment.deploy(
tasks_metadata: tasks_metadata,
tasks_graph: tasks_graph,
tasks_directory: tasks_directory)
end
it 'should setup task concurrency as amount if type is parallel' do
tasks_graph['1'].first['parameters']['strategy'] =
{'type' => 'parallel', 'amount' => 7}
Deployment::Concurrency::Counter.any_instance.expects(:maximum=)
.with(0).times(4)
Deployment::Concurrency::Counter.any_instance.expects(:maximum=)
.with(7)
task_deployment.deploy(
tasks_metadata: tasks_metadata,
tasks_graph: tasks_graph,
tasks_directory: tasks_directory)
end
it 'should setup 0 if task strategy is parallel and amount do not set' do
tasks_graph['1'].first['parameters']['strategy'] = {'type' => 'parallel'}
Deployment::Concurrency::Counter.any_instance.expects(:maximum=)
.with(0).times(5)
task_deployment.deploy(
tasks_metadata: tasks_metadata,
tasks_graph: tasks_graph,
tasks_directory: tasks_directory)
end
it 'should raise error if amount is non-positive integer and type is parallel' do
tasks_graph['1'].first['parameters']['strategy'] =
{'type' => 'parallel', 'amount' => -4}
Deployment::Concurrency::Counter.any_instance.expects(:maximum=)
.with(0).times(2)
expect {task_deployment.deploy(
tasks_metadata: tasks_metadata,
tasks_graph: tasks_graph,
tasks_directory: tasks_directory)}.to raise_error(
Astute::DeploymentEngineError, /expect only non-negative integer, but got -4./
)
end
end
context 'dry_run' do
it 'should not run actual deployment if dry_run is set to True' do
task_deployment.stubs(:fail_offline_nodes).returns([])
task_deployment.stubs(:write_graph_to_file)
ctx.stubs(:report)
Astute::TaskCluster.any_instance.expects(:run).never
task_deployment.deploy(
tasks_metadata: tasks_metadata,
tasks_graph: tasks_graph,
tasks_directory: tasks_directory,
dry_run: true)
end
end
context 'config' do
around(:each) do |example|
max_nodes_old_value = Astute.config.max_nodes_per_call
example.run
Astute.config.max_nodes_per_call = max_nodes_old_value
end
it 'should setup max nodes per call using config' do
Astute.config.max_nodes_per_call = 33
task_deployment.stubs(:fail_offline_nodes).returns([])
task_deployment.stubs(:write_graph_to_file)
ctx.stubs(:report)
Astute::TaskCluster.any_instance
.stubs(:run)
.returns({:success => true})
node_concurrency = mock('node_concurrency')
Astute::TaskCluster.any_instance
.expects(:node_concurrency).returns(node_concurrency)
node_concurrency.expects(:maximum=).with(Astute.config.max_nodes_per_call)
task_deployment.deploy(
tasks_metadata: tasks_metadata,
tasks_graph: tasks_graph,
tasks_directory: tasks_directory)
end
end
context 'should report final status' do
it 'succeed status' do
Astute::TaskCluster.any_instance.stubs(:run).returns({:success => true})
task_deployment.stubs(:fail_offline_nodes).returns([])
task_deployment.stubs(:write_graph_to_file)
ctx.expects(:report).with({'status' => 'ready', 'progress' => 100})
task_deployment.deploy(
tasks_metadata: tasks_metadata,
tasks_graph: tasks_graph,
tasks_directory: tasks_directory)
end
it 'failed status' do
failed_node = mock('node')
failed_node.expects(:id).returns('1')
failed_task = mock('task')
failed_task.expects(:node).returns(failed_node)
failed_task.expects(:name).returns('test')
failed_task.expects(:status).returns(:failed)
Astute::TaskCluster.any_instance.stubs(:run).returns({
:success => false,
:failed_nodes => [failed_node],
:failed_tasks => [failed_task],
:status => 'Failed because of'})
task_deployment.stubs(:fail_offline_nodes).returns([])
task_deployment.stubs(:write_graph_to_file)
ctx.expects(:report).with('nodes' => [{
'uid' => '1',
'status' => 'error',
'error_type' => 'deploy',
'error_msg' => 'Failed because of',
'deployment_graph_task_name' => 'test',
'task_status' => 'failed'
}])
ctx.expects(:report).with({
'status' => 'error',
'progress' => 100,
'error' => 'Failed because of'})
task_deployment.deploy(
tasks_metadata: tasks_metadata,
tasks_graph: tasks_graph,
tasks_directory: tasks_directory)
end
end
context 'graph file' do
around(:each) do |example|
old_value = Astute.config.enable_graph_file
example.run
Astute.config.enable_graph_file = old_value
end
it 'should write if disable' do
Astute.config.enable_graph_file = false
task_deployment.stubs(:fail_offline_nodes).returns([])
ctx.stubs(:report)
Astute::TaskCluster.any_instance.stubs(:run).returns({:success => true})
file_handle = mock
file_handle.expects(:write).with(regexp_matches(/digraph/)).never
File.expects(:open).with("#{Astute.config.graph_dot_dir}/graph-#{ctx.task_id}.dot", 'w')
.yields(file_handle).never
task_deployment.deploy(
tasks_metadata: tasks_metadata,
tasks_graph: tasks_graph,
tasks_directory: tasks_directory)
end
it 'should write graph if enable' do
Astute.config.enable_graph_file = true
task_deployment.stubs(:fail_offline_nodes).returns([])
ctx.stubs(:report)
Astute::TaskCluster.any_instance.stubs(:run).returns({:success => true})
file_handle = mock
file_handle.expects(:write).with(regexp_matches(/digraph/)).once
File.expects(:open).with("#{Astute.config.graph_dot_dir}/graph-#{ctx.task_id}.dot", 'w')
.yields(file_handle).once
task_deployment.deploy(
tasks_metadata: tasks_metadata,
tasks_graph: tasks_graph,
tasks_directory: tasks_directory)
end
end # 'graph file'
end
end