1045 lines
31 KiB
Ruby
1045 lines
31 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')
|
|
|
|
include Astute
|
|
|
|
describe Astute::NailgunHooks do
|
|
include SpecHelpers
|
|
|
|
let(:ctx) { mock_ctx }
|
|
|
|
around(:each) do |example|
|
|
old_value = Astute.config.max_nodes_per_call
|
|
example.run
|
|
Astute.config.max_nodes_per_call = old_value
|
|
end
|
|
|
|
let(:upload_file_hook) do
|
|
{
|
|
"priority" => 100,
|
|
"type" => "upload_file",
|
|
"fail_on_error" => false,
|
|
"diagnostic_name" => "upload-example-1.0",
|
|
"uids" => ['2', '3'],
|
|
"parameters" => {
|
|
"path" => "/etc/yum.repos.d/fuel_awesome_plugin-0.1.0.repo",
|
|
"data" => "[fuel_awesome_plugin-0.1.0]\\nname=Plugin fuel_awesome_plugin-0.1.0 repository\\nbaseurl=http => //10.20.0.2 => 8080/plugins/fuel_awesome_plugin-0.1.0/repositories/centos\\ngpgcheck=0"
|
|
}
|
|
}
|
|
end
|
|
|
|
let(:sync_hook) do
|
|
{
|
|
"priority" => 200,
|
|
"type" => "sync",
|
|
"fail_on_error" => false,
|
|
"diagnostic_name" => "sync-example-1.0",
|
|
"uids" => ['1', '2'],
|
|
"parameters" => {
|
|
"src" => "rsync://10.20.0.2/plugins/fuel_awesome_plugin-0.1.0/deployment_scripts/",
|
|
"dst" => "/etc/fuel/plugins/fuel_awesome_plugin-0.1.0/"
|
|
}
|
|
}
|
|
end
|
|
|
|
let(:shell_hook) do
|
|
{
|
|
"priority" => 100,
|
|
"type" => "shell",
|
|
"fail_on_error" => false,
|
|
"id" => "shell-example-1.0",
|
|
"uids" => ['1','2','3'],
|
|
"parameters" => {
|
|
"cmd" => "./deploy.sh",
|
|
"cwd" => "/etc/fuel/plugins/fuel_awesome_plugin-0.1.0/",
|
|
"timeout" => 60
|
|
}
|
|
}
|
|
end
|
|
|
|
let(:puppet_hook) do
|
|
{
|
|
"priority" => 300,
|
|
"type" => "puppet",
|
|
"fail_on_error" => false,
|
|
"diagnostic_name" => "puppet-example-1.0",
|
|
"uids" => ['1', '3'],
|
|
"parameters" => {
|
|
"puppet_manifest" => "cinder_glusterfs.pp",
|
|
"puppet_modules" => "modules",
|
|
"cwd" => "/etc/fuel/plugins/plugin_name-1.0",
|
|
"timeout" => 42
|
|
}
|
|
}
|
|
end
|
|
|
|
let(:reboot_hook) do
|
|
{
|
|
"priority" => 600,
|
|
"type" => "reboot",
|
|
"fail_on_error" => false,
|
|
"diagnostic_name" => "reboot-example-1.0",
|
|
"uids" => ['2', '3'],
|
|
"parameters" => {
|
|
"timeout" => 42
|
|
}
|
|
}
|
|
end
|
|
|
|
let(:copy_files_hook) do
|
|
{
|
|
"priority" => 100,
|
|
"type" => "copy_files",
|
|
"fail_on_error" => false,
|
|
"diagnostic_name" => "copy-example-1.0",
|
|
"uids" => ['2', '3'],
|
|
"parameters" => {
|
|
"files" => [{
|
|
"src" => "/etc/fuel/nova.key",
|
|
"dst" => "/etc/astute/nova.key"}],
|
|
"permissions" => "0600",
|
|
"dir_permissions" => "0700"
|
|
}
|
|
}
|
|
end
|
|
|
|
let(:hooks_data) do
|
|
[
|
|
upload_file_hook,
|
|
sync_hook,
|
|
shell_hook,
|
|
puppet_hook,
|
|
copy_files_hook,
|
|
reboot_hook
|
|
]
|
|
end
|
|
|
|
context '#new' do
|
|
it 'should use default run type if no type setting' do
|
|
hooks = Astute::NailgunHooks.new(hooks_data, ctx)
|
|
expect(hooks.instance_variable_get("@type")).to eql('deploy')
|
|
end
|
|
|
|
it 'should use type if it set' do
|
|
hooks = Astute::NailgunHooks.new(hooks_data, ctx, 'execute_tasks')
|
|
expect(hooks.instance_variable_get("@type")).to eql('execute_tasks')
|
|
end
|
|
end
|
|
|
|
context "#process" do
|
|
it 'should process known hook type' do
|
|
hooks = Astute::NailgunHooks.new(hooks_data, ctx)
|
|
|
|
hooks.expects(:upload_file_hook)
|
|
hooks.expects(:puppet_hook)
|
|
hooks.expects(:shell_hook)
|
|
hooks.expects(:sync_hook)
|
|
hooks.expects(:reboot_hook)
|
|
hooks.expects(:copy_files_hook)
|
|
|
|
hooks.process
|
|
end
|
|
|
|
it 'should raise exception if hook type is unknown' do
|
|
wrong_hook = [{
|
|
"priority" => 300,
|
|
"type" => "unknown",
|
|
"uids" => ['1', '3'],
|
|
"parameters" => {
|
|
"parameter" => "1"
|
|
}
|
|
}]
|
|
hooks = Astute::NailgunHooks.new(wrong_hook, ctx)
|
|
|
|
expect {hooks.process}.to raise_error(StandardError, /Unknown hook type/)
|
|
end
|
|
|
|
it 'should run hooks by priority order' do
|
|
File.stubs(:file?).returns(true)
|
|
File.stubs(:readable?).returns(true)
|
|
File.stubs(:read).returns('')
|
|
hooks = Astute::NailgunHooks.new(hooks_data, ctx)
|
|
|
|
hook_order = sequence('hook_order')
|
|
hooks.expects(:upload_file_hook).in_sequence(hook_order)
|
|
hooks.expects(:copy_files_hook).in_sequence(hook_order)
|
|
hooks.expects(:shell_hook).in_sequence(hook_order)
|
|
hooks.expects(:sync_hook).in_sequence(hook_order)
|
|
hooks.expects(:puppet_hook).in_sequence(hook_order)
|
|
hooks.expects(:reboot_hook).in_sequence(hook_order)
|
|
|
|
hooks.process
|
|
end
|
|
|
|
context 'critical hook' do
|
|
|
|
before(:each) do
|
|
hooks_data[2]['fail_on_error'] = true
|
|
ctx.stubs(:report_and_update_status)
|
|
end
|
|
|
|
it 'should raise exception if critical hook failed' do
|
|
hooks = Astute::NailgunHooks.new(hooks_data, ctx)
|
|
hooks.expects(:copy_files_hook).returns(true)
|
|
hooks.expects(:upload_file_hook).returns(true)
|
|
hooks.expects(:shell_hook).returns(false)
|
|
|
|
expect {hooks.process}.to raise_error(Astute::DeploymentEngineError, /Failed to execute hook shell-example-1.0/)
|
|
end
|
|
|
|
it 'should not process next hooks if critical hook failed' do
|
|
hooks = Astute::NailgunHooks.new(hooks_data, ctx)
|
|
hooks.expects(:upload_file_hook).returns(true)
|
|
hooks.expects(:shell_hook).returns(false)
|
|
hooks.expects(:sync_hook).never
|
|
hooks.expects(:puppet_hook).never
|
|
hooks.expects(:reboot_hook).never
|
|
|
|
hooks.process rescue nil
|
|
end
|
|
|
|
it 'should process next hooks if non critical hook failed' do
|
|
hooks = Astute::NailgunHooks.new(hooks_data, ctx)
|
|
hooks.expects(:upload_file_hook).returns(false)
|
|
hooks.expects(:shell_hook).returns(true)
|
|
hooks.expects(:sync_hook).returns(false)
|
|
hooks.expects(:puppet_hook).returns(true)
|
|
hooks.expects(:reboot_hook).returns(true)
|
|
|
|
hooks.process
|
|
end
|
|
|
|
it 'should report error node status if critical hook failed' do
|
|
hooks = Astute::NailgunHooks.new(hooks_data, ctx)
|
|
hooks.expects(:upload_file_hook).returns(true)
|
|
hooks.expects(:shell_hook).returns(false)
|
|
|
|
ctx.expects(:report_and_update_status).with(
|
|
{'nodes' =>
|
|
[
|
|
{ 'uid' => '1',
|
|
'status' => 'error',
|
|
'error_type' => 'deploy',
|
|
'role' => 'hook',
|
|
'hook' => "shell-example-1.0"
|
|
},
|
|
{ 'uid' => '2',
|
|
'status' => 'error',
|
|
'error_type' => 'deploy',
|
|
'role' => 'hook',
|
|
'hook' => "shell-example-1.0"
|
|
},
|
|
{ 'uid' => '3',
|
|
'status' => 'error',
|
|
'error_type' => 'deploy',
|
|
'role' => 'hook',
|
|
'hook' => "shell-example-1.0"
|
|
},
|
|
]
|
|
}
|
|
)
|
|
|
|
hooks.process rescue nil
|
|
end
|
|
|
|
it 'should not send report if non critical hook failed' do
|
|
hooks = Astute::NailgunHooks.new(hooks_data, ctx)
|
|
hooks.expects(:upload_file_hook).returns(false)
|
|
hooks.expects(:shell_hook).returns(true)
|
|
hooks.expects(:sync_hook).returns(false)
|
|
hooks.expects(:puppet_hook).returns(true)
|
|
hooks.expects(:reboot_hook).returns(true)
|
|
|
|
ctx.expects(:report_and_update_status).never
|
|
|
|
hooks.process
|
|
end
|
|
|
|
end #hook
|
|
|
|
end #process
|
|
|
|
context '#copy_files_hook' do
|
|
|
|
it 'should validate presence of node uids' do
|
|
copy_files_hook['uids'] = []
|
|
hooks = Astute::NailgunHooks.new([copy_files_hook], ctx)
|
|
|
|
expect {hooks.process}.to raise_error(StandardError, /Missing a required parameter/)
|
|
end
|
|
|
|
it 'should validate presence files' do
|
|
copy_files_hook['parameters'].delete('files')
|
|
hooks = Astute::NailgunHooks.new([copy_files_hook], ctx)
|
|
|
|
expect {hooks.process}.to raise_error(StandardError, /Missing a required parameter/)
|
|
end
|
|
|
|
it 'should upload file' do
|
|
File.stubs(:file?).returns(true)
|
|
File.stubs(:readable?).returns(true)
|
|
File.stubs(:read).returns("")
|
|
hooks = Astute::NailgunHooks.new([copy_files_hook], ctx)
|
|
|
|
hooks.expects(:upload_file).once.with(
|
|
ctx,
|
|
['2', '3'],
|
|
has_entries(
|
|
'content' => "",
|
|
'path' => copy_files_hook['parameters']['files'][0]['dst']
|
|
)
|
|
)
|
|
|
|
hooks.process
|
|
end
|
|
|
|
it 'should limit nodes processing in parallel' do
|
|
Astute.config.max_nodes_per_call = 1
|
|
File.stubs(:file?).returns(true)
|
|
File.stubs(:readable?).returns(true)
|
|
File.stubs(:read).returns("")
|
|
hooks = Astute::NailgunHooks.new([copy_files_hook], ctx)
|
|
|
|
hooks.expects(:upload_file).once.with(
|
|
ctx,
|
|
['2'],
|
|
has_entries(
|
|
'content' => "",
|
|
'path' => copy_files_hook['parameters']['files'][0]['dst']
|
|
)
|
|
)
|
|
hooks.expects(:upload_file).once.with(
|
|
ctx,
|
|
['3'],
|
|
has_entries(
|
|
'content' => "",
|
|
'path' => copy_files_hook['parameters']['files'][0]['dst']
|
|
)
|
|
)
|
|
hooks.process
|
|
end
|
|
|
|
context 'process data from mcagent in case of critical hook' do
|
|
before(:each) do
|
|
copy_files_hook['fail_on_error'] = true
|
|
ctx.stubs(:report_and_update_status)
|
|
end
|
|
|
|
it 'mcagent success' do
|
|
File.stubs(:file?).returns(true)
|
|
File.stubs(:readable?).returns(true)
|
|
File.stubs(:read).returns("")
|
|
hooks = Astute::NailgunHooks.new([copy_files_hook], ctx)
|
|
hooks.expects(:upload_file).returns(true).once
|
|
|
|
expect {hooks.process}.to_not raise_error
|
|
end
|
|
|
|
it 'mcagent fail' do
|
|
File.stubs(:file?).returns(true)
|
|
File.stubs(:readable?).returns(true)
|
|
File.stubs(:read).returns("")
|
|
hooks = Astute::NailgunHooks.new([copy_files_hook], ctx)
|
|
hooks.expects(:upload_file).returns(false).once
|
|
|
|
expect {hooks.process}.to raise_error(Astute::DeploymentEngineError, /Failed to execute hook/)
|
|
end
|
|
end #context
|
|
end#copy_files_hook
|
|
|
|
context '#shell_hook' do
|
|
|
|
it 'should validate presence of node uids' do
|
|
shell_hook['uids'] = []
|
|
hooks = Astute::NailgunHooks.new([shell_hook], ctx)
|
|
|
|
expect {hooks.process}.to raise_error(StandardError, /Missing a required parameter/)
|
|
end
|
|
|
|
it 'should validate presence cmd hooks' do
|
|
shell_hook['parameters'].delete('cmd')
|
|
hooks = Astute::NailgunHooks.new([shell_hook], ctx)
|
|
|
|
expect {hooks.process}.to raise_error(StandardError, /Missing a required parameter/)
|
|
end
|
|
|
|
it 'should run shell command with timeout' do
|
|
hooks = Astute::NailgunHooks.new([shell_hook], ctx)
|
|
hooks.expects(:run_shell_command).once.with(
|
|
ctx,
|
|
['1','2','3'],
|
|
regexp_matches(/deploy/),
|
|
shell_hook['parameters']['timeout'],
|
|
shell_hook['parameters']['cwd']
|
|
)
|
|
.returns(:data => {:exit_code => 0})
|
|
|
|
hooks.process
|
|
end
|
|
|
|
it 'should use default timeout if it does not set' do
|
|
shell_hook['parameters'].delete('timeout')
|
|
hooks = Astute::NailgunHooks.new([shell_hook], ctx)
|
|
hooks.expects(:run_shell_command).once.with(
|
|
ctx,
|
|
['1','2','3'],
|
|
regexp_matches(/deploy/),
|
|
300,
|
|
shell_hook['parameters']['cwd']
|
|
)
|
|
.returns(:data => {:exit_code => 0})
|
|
|
|
hooks.process
|
|
end
|
|
|
|
it 'should limit nodes processing in parallel' do
|
|
Astute.config.max_nodes_per_call = 2
|
|
|
|
hooks = Astute::NailgunHooks.new([shell_hook], ctx)
|
|
hooks.expects(:run_shell_command).once.with(
|
|
ctx,
|
|
['1', '2'],
|
|
regexp_matches(/deploy/),
|
|
shell_hook['parameters']['timeout'],
|
|
shell_hook['parameters']['cwd']
|
|
)
|
|
.returns(:data => {:exit_code => 0})
|
|
|
|
hooks.expects(:run_shell_command).once.with(
|
|
ctx,
|
|
['3'],
|
|
regexp_matches(/deploy/),
|
|
shell_hook['parameters']['timeout'],
|
|
shell_hook['parameters']['cwd']
|
|
)
|
|
.returns(:data => {:exit_code => 0})
|
|
|
|
hooks.process
|
|
end
|
|
|
|
context 'process data from mcagent in case of critical hook' do
|
|
before(:each) do
|
|
shell_hook['fail_on_error'] = true
|
|
ctx.stubs(:report_and_update_status)
|
|
end
|
|
|
|
it 'if exit code eql 0 -> do not raise error' do
|
|
hooks = Astute::NailgunHooks.new([shell_hook], ctx)
|
|
hooks.expects(:run_shell_command).returns({:data => {:exit_code => 0}}).once
|
|
|
|
expect {hooks.process}.to_not raise_error
|
|
end
|
|
|
|
it 'if exit code not eql 0 -> raise error' do
|
|
hooks = Astute::NailgunHooks.new([shell_hook], ctx)
|
|
hooks.expects(:run_shell_command).returns({:data => {:exit_code => 1}}).times(Astute.config.mc_retries)
|
|
|
|
expect {hooks.process}.to raise_error(Astute::DeploymentEngineError, /Failed to execute hook/)
|
|
end
|
|
|
|
it 'if exit code not presence -> raise error' do
|
|
hooks = Astute::NailgunHooks.new([shell_hook], ctx)
|
|
hooks.expects(:run_shell_command).returns({:data => {}}).times(Astute.config.mc_retries)
|
|
|
|
expect {hooks.process}.to raise_error(Astute::DeploymentEngineError, /Failed to execute hook/)
|
|
end
|
|
end #context
|
|
|
|
end #shell_hook
|
|
|
|
context '#upload_file_hook' do
|
|
it 'should validate presence of node uids' do
|
|
upload_file_hook['uids'] = []
|
|
hooks = Astute::NailgunHooks.new([upload_file_hook], ctx)
|
|
|
|
expect {hooks.process}.to raise_error(StandardError, /Missing a required parameter/)
|
|
end
|
|
|
|
it 'should validate presence of file destination' do
|
|
upload_file_hook['parameters'].delete('path')
|
|
hooks = Astute::NailgunHooks.new([upload_file_hook], ctx)
|
|
|
|
expect {hooks.process}.to raise_error(StandardError, /Missing a required parameter/)
|
|
end
|
|
|
|
it 'should validate presence of file source' do
|
|
upload_file_hook['parameters'].delete('data')
|
|
hooks = Astute::NailgunHooks.new([upload_file_hook], ctx)
|
|
|
|
expect {hooks.process}.to raise_error(StandardError, /Missing a required parameter/)
|
|
end
|
|
|
|
it 'should upload file' do
|
|
hooks = Astute::NailgunHooks.new([upload_file_hook], ctx)
|
|
|
|
hooks.expects(:upload_file).once.with(
|
|
ctx,
|
|
['2', '3'],
|
|
has_entries(
|
|
'content' => upload_file_hook['parameters']['data'],
|
|
'path' => upload_file_hook['parameters']['path']
|
|
)
|
|
)
|
|
|
|
hooks.process
|
|
end
|
|
|
|
it 'should limit nodes processing in parallel' do
|
|
Astute.config.max_nodes_per_call = 1
|
|
hooks = Astute::NailgunHooks.new([upload_file_hook], ctx)
|
|
|
|
hooks.expects(:upload_file).once.with(
|
|
ctx,
|
|
['2'],
|
|
has_entries(
|
|
'content' => upload_file_hook['parameters']['data'],
|
|
'path' => upload_file_hook['parameters']['path']
|
|
)
|
|
)
|
|
|
|
hooks.expects(:upload_file).once.with(
|
|
ctx,
|
|
['3'],
|
|
has_entries(
|
|
'content' => upload_file_hook['parameters']['data'],
|
|
'path' => upload_file_hook['parameters']['path']
|
|
)
|
|
)
|
|
|
|
hooks.process
|
|
end
|
|
|
|
context 'process data from mcagent in case of critical hook' do
|
|
before(:each) do
|
|
upload_file_hook['fail_on_error'] = true
|
|
ctx.stubs(:report_and_update_status)
|
|
end
|
|
|
|
it 'mcagent success' do
|
|
hooks = Astute::NailgunHooks.new([upload_file_hook], ctx)
|
|
hooks.expects(:upload_file).returns(true).once
|
|
|
|
expect {hooks.process}.to_not raise_error
|
|
end
|
|
|
|
it 'mcagent fail' do
|
|
hooks = Astute::NailgunHooks.new([upload_file_hook], ctx)
|
|
hooks.expects(:upload_file).returns(false).once
|
|
|
|
expect {hooks.process}.to raise_error(Astute::DeploymentEngineError, /Failed to execute hook/)
|
|
end
|
|
end #context
|
|
|
|
end #upload_file_hook
|
|
|
|
context '#sync_hook' do
|
|
it 'should validate presence of node uids' do
|
|
sync_hook['uids'] = []
|
|
hooks = Astute::NailgunHooks.new([sync_hook], ctx)
|
|
|
|
expect {hooks.process}.to raise_error(StandardError, /Missing a required parameter/)
|
|
end
|
|
|
|
it 'should validate presence of destination' do
|
|
sync_hook['parameters'].delete('dst')
|
|
hooks = Astute::NailgunHooks.new([sync_hook], ctx)
|
|
|
|
expect {hooks.process}.to raise_error(StandardError, /Missing a required parameter/)
|
|
end
|
|
|
|
it 'should validate presence of source' do
|
|
sync_hook['parameters'].delete('src')
|
|
hooks = Astute::NailgunHooks.new([sync_hook], ctx)
|
|
|
|
expect {hooks.process}.to raise_error(StandardError, /Missing a required parameter/)
|
|
end
|
|
|
|
it 'should run sync command with timeout' do
|
|
sync_hook['parameters']['timeout'] = '60'
|
|
hooks = Astute::NailgunHooks.new([sync_hook], ctx)
|
|
hooks.expects(:run_shell_command).once.with(
|
|
ctx,
|
|
['1','2'],
|
|
regexp_matches(/deploy/),
|
|
sync_hook['parameters']['timeout']
|
|
)
|
|
.returns(:data => {:exit_code => 0})
|
|
|
|
hooks.process
|
|
end
|
|
|
|
it 'should use default timeout if it does not set' do
|
|
shell_hook['parameters'].delete('timeout')
|
|
hooks = Astute::NailgunHooks.new([sync_hook], ctx)
|
|
hooks.expects(:run_shell_command).once.with(
|
|
ctx,
|
|
['1','2'],
|
|
regexp_matches(/rsync/),
|
|
300
|
|
)
|
|
.returns(:data => {:exit_code => 0})
|
|
|
|
hooks.process
|
|
end
|
|
|
|
it 'should limit nodes processing in parallel' do
|
|
Astute.config.max_nodes_per_call = 1
|
|
|
|
hooks = Astute::NailgunHooks.new([sync_hook], ctx)
|
|
hooks.expects(:run_shell_command).once.with(
|
|
ctx,
|
|
['1'],
|
|
regexp_matches(/rsync/),
|
|
is_a(Integer)
|
|
)
|
|
.returns(:data => {:exit_code => 0})
|
|
|
|
hooks.expects(:run_shell_command).once.with(
|
|
ctx,
|
|
['2'],
|
|
regexp_matches(/rsync/),
|
|
is_a(Integer)
|
|
)
|
|
.returns(:data => {:exit_code => 0})
|
|
|
|
hooks.process
|
|
end
|
|
|
|
context 'process data from mcagent in case of critical hook' do
|
|
before(:each) do
|
|
sync_hook['fail_on_error'] = true
|
|
ctx.stubs(:report_and_update_status)
|
|
end
|
|
|
|
it 'if exit code eql 0 -> do not raise error' do
|
|
hooks = Astute::NailgunHooks.new([sync_hook], ctx)
|
|
hooks.expects(:run_shell_command).returns({:data => {:exit_code => 0}}).once
|
|
|
|
expect {hooks.process}.to_not raise_error
|
|
end
|
|
|
|
it 'if exit code not eql 0 -> raise error' do
|
|
hooks = Astute::NailgunHooks.new([sync_hook], ctx)
|
|
hooks.expects(:run_shell_command).returns({:data => {:exit_code => 1}}).times(10)
|
|
|
|
expect {hooks.process}.to raise_error(Astute::DeploymentEngineError, /Failed to execute hook/)
|
|
end
|
|
|
|
it 'if exit code not presence -> raise error' do
|
|
hooks = Astute::NailgunHooks.new([sync_hook], ctx)
|
|
hooks.expects(:run_shell_command).returns({:data => {}}).times(10)
|
|
|
|
expect {hooks.process}.to raise_error(Astute::DeploymentEngineError, /Failed to execute hook/)
|
|
end
|
|
end #context
|
|
|
|
end #sync_hook
|
|
|
|
context '#puppet_hook' do
|
|
it 'should validate presence of node uids' do
|
|
puppet_hook['uids'] = []
|
|
hooks = Astute::NailgunHooks.new([puppet_hook], ctx)
|
|
|
|
expect {hooks.process}.to raise_error(StandardError, /Missing a required parameter/)
|
|
end
|
|
|
|
it 'should validate presence of manifest parameter' do
|
|
puppet_hook['parameters'].delete('puppet_manifest')
|
|
hooks = Astute::NailgunHooks.new([puppet_hook], ctx)
|
|
|
|
expect {hooks.process}.to raise_error(StandardError, /Missing a required parameter/)
|
|
end
|
|
|
|
it 'should validate presence of modules parameter' do
|
|
puppet_hook['parameters'].delete('puppet_modules')
|
|
hooks = Astute::NailgunHooks.new([puppet_hook], ctx)
|
|
|
|
expect {hooks.process}.to raise_error(StandardError, /Missing a required parameter/)
|
|
end
|
|
|
|
it 'should validate presence of cwd parameter' do
|
|
puppet_hook['parameters'].delete('cwd')
|
|
hooks = Astute::NailgunHooks.new([puppet_hook], ctx)
|
|
|
|
expect {hooks.process}.to raise_error(StandardError, /Missing a required parameter/)
|
|
end
|
|
|
|
it 'should run puppet command using main mechanism' do
|
|
hooks = Astute::NailgunHooks.new([puppet_hook], ctx)
|
|
PuppetdDeployer.expects(:deploy).once.with(
|
|
instance_of(Astute::Context),
|
|
[
|
|
{'uid' => '1', 'role' => 'hook'},
|
|
{'uid' => '3', 'role' => 'hook'}
|
|
],
|
|
retries=2,
|
|
puppet_hook['parameters']['puppet_manifest'],
|
|
puppet_hook['parameters']['puppet_modules'],
|
|
puppet_hook['parameters']['cwd']
|
|
)
|
|
|
|
Astute::Context.any_instance.stubs(:status).returns({'1' => 'success', '3' => 'success'})
|
|
hooks.process
|
|
end
|
|
|
|
it 'should run puppet command with timeout' do
|
|
hooks = Astute::NailgunHooks.new([puppet_hook], ctx)
|
|
hooks.expects(:run_puppet).once.with(
|
|
ctx,
|
|
['1','3'],
|
|
puppet_hook['parameters']['puppet_manifest'],
|
|
puppet_hook['parameters']['puppet_modules'],
|
|
puppet_hook['parameters']['cwd'],
|
|
puppet_hook['parameters']['timeout']
|
|
).returns(true)
|
|
Astute::Context.any_instance.stubs(:status).returns({'1' => 'success', '3' => 'success'})
|
|
|
|
hooks.process
|
|
end
|
|
|
|
it 'should use default timeout if it does not set' do
|
|
puppet_hook['parameters'].delete('timeout')
|
|
hooks = Astute::NailgunHooks.new([puppet_hook], ctx)
|
|
hooks.expects(:run_puppet).once.with(
|
|
ctx,
|
|
['1','3'],
|
|
puppet_hook['parameters']['puppet_manifest'],
|
|
puppet_hook['parameters']['puppet_modules'],
|
|
puppet_hook['parameters']['cwd'],
|
|
300
|
|
).returns(true)
|
|
Astute::Context.any_instance.stubs(:status).returns({'1' => 'success', '3' => 'success'})
|
|
|
|
hooks.process
|
|
end
|
|
|
|
it 'should limit nodes processing in parallel' do
|
|
Astute.config.max_nodes_per_call = 1
|
|
|
|
hooks = Astute::NailgunHooks.new([puppet_hook], ctx)
|
|
hooks.expects(:run_puppet).once.with(
|
|
ctx,
|
|
['1'],
|
|
puppet_hook['parameters']['puppet_manifest'],
|
|
puppet_hook['parameters']['puppet_modules'],
|
|
puppet_hook['parameters']['cwd'],
|
|
puppet_hook['parameters']['timeout']
|
|
).returns(true)
|
|
|
|
hooks.expects(:run_puppet).once.with(
|
|
ctx,
|
|
['3'],
|
|
puppet_hook['parameters']['puppet_manifest'],
|
|
puppet_hook['parameters']['puppet_modules'],
|
|
puppet_hook['parameters']['cwd'],
|
|
puppet_hook['parameters']['timeout']
|
|
).returns(true)
|
|
|
|
Astute::Context.any_instance.stubs(:status).returns({'1' => 'success', '3' => 'success'})
|
|
hooks.process
|
|
end
|
|
|
|
it 'if mclient failed and task is not critical -> do not raise error' do
|
|
hooks = Astute::NailgunHooks.new([puppet_hook], ctx)
|
|
PuppetdDeployer.expects(:deploy).once.raises(Astute::MClientError)
|
|
|
|
expect {hooks.process}.to_not raise_error
|
|
end
|
|
|
|
context 'process data from mcagent in case of critical hook' do
|
|
before(:each) do
|
|
puppet_hook['fail_on_error'] = true
|
|
ctx.stubs(:report_and_update_status)
|
|
end
|
|
|
|
it 'if puppet success do not raise error' do
|
|
hooks = Astute::NailgunHooks.new([puppet_hook], ctx)
|
|
PuppetdDeployer.expects(:deploy).once
|
|
Astute::Context.any_instance.stubs(:status).returns({'1' => 'success', '3' => 'success'})
|
|
|
|
expect {hooks.process}.to_not raise_error
|
|
end
|
|
|
|
it 'if puppet fail -> raise error' do
|
|
hooks = Astute::NailgunHooks.new([puppet_hook], ctx)
|
|
PuppetdDeployer.expects(:deploy).once
|
|
Astute::Context.any_instance.stubs(:status).returns({'1' => 'error', '3' => 'success'})
|
|
|
|
expect {hooks.process}.to raise_error(Astute::DeploymentEngineError, /Failed to execute hook/)
|
|
end
|
|
|
|
it 'if mclient failed -> raise error' do
|
|
hooks = Astute::NailgunHooks.new([puppet_hook], ctx)
|
|
PuppetdDeployer.expects(:deploy).once.raises(Astute::MClientError)
|
|
|
|
expect {hooks.process}.to raise_error(Astute::DeploymentEngineError, /Failed to execute hook/)
|
|
end
|
|
end #context
|
|
end # puppet_hook
|
|
|
|
context '#reboot_hook' do
|
|
|
|
it 'should validate presence of node uids' do
|
|
reboot_hook['uids'] = []
|
|
hooks = Astute::NailgunHooks.new([reboot_hook], ctx)
|
|
|
|
expect {hooks.process}.to raise_error(StandardError, /Missing a required parameter/)
|
|
end
|
|
|
|
it 'should run reboot command with timeout - 10 sec' do
|
|
hooks = Astute::NailgunHooks.new([reboot_hook], ctx)
|
|
|
|
time = Time.now.to_i
|
|
hooks.stubs(:run_shell_without_check).twice.with(
|
|
ctx,
|
|
['2','3'],
|
|
regexp_matches(/stat/),
|
|
10,
|
|
)
|
|
.returns('2' => (time - 5).to_s, '3' => (time - 5).to_s).then
|
|
.returns('2' => time.to_s, '3' => time.to_s)
|
|
|
|
hooks.expects(:run_shell_without_check).once.with(
|
|
ctx,
|
|
['2','3'],
|
|
"reboot",
|
|
10,
|
|
)
|
|
.returns('2' => '', '3' => '')
|
|
|
|
hooks.stubs(:sleep)
|
|
|
|
hooks.process
|
|
end
|
|
|
|
it 'should run reboot validation command with timeout - 10 sec' do
|
|
hooks = Astute::NailgunHooks.new([reboot_hook], ctx)
|
|
|
|
time = Time.now.to_i
|
|
hooks.stubs(:run_shell_without_check).twice.with(
|
|
ctx,
|
|
['2','3'],
|
|
regexp_matches(/stat/),
|
|
10,
|
|
)
|
|
.returns('2' => (time - 5).to_s, '3' => (time - 5).to_s).then
|
|
.returns('2' => time.to_s, '3' => time.to_s)
|
|
|
|
hooks.stubs(:run_shell_without_check).once.with(
|
|
ctx,
|
|
['2','3'],
|
|
regexp_matches(/reboot/),
|
|
10,
|
|
)
|
|
.returns('2' => '', '3' => '')
|
|
|
|
hooks.stubs(:sleep)
|
|
|
|
hooks.process
|
|
end
|
|
|
|
it 'should sleep between checks for one-tenth of timeout' do
|
|
hooks = Astute::NailgunHooks.new([reboot_hook], ctx)
|
|
|
|
time = Time.now.to_i
|
|
hooks.stubs(:run_shell_without_check).twice.with(
|
|
ctx,
|
|
['2','3'],
|
|
regexp_matches(/stat/),
|
|
10,
|
|
)
|
|
.returns('2' => (time - 5).to_s, '3' => (time - 5).to_s).then
|
|
.returns('2' => time.to_s, '3' => time.to_s)
|
|
|
|
hooks.stubs(:run_shell_without_check).once.with(
|
|
ctx,
|
|
['2','3'],
|
|
regexp_matches(/reboot/),
|
|
10,
|
|
)
|
|
.returns('2' => '', '3' => '')
|
|
|
|
hooks.expects(:sleep).with(reboot_hook['parameters']['timeout']/10)
|
|
|
|
hooks.process
|
|
end
|
|
|
|
it 'should use default timeout if it does not set' do
|
|
reboot_hook['parameters'].delete('timeout')
|
|
hooks = Astute::NailgunHooks.new([reboot_hook], ctx)
|
|
|
|
time = Time.now.to_i
|
|
hooks.stubs(:run_shell_without_check).twice.with(
|
|
ctx,
|
|
['2','3'],
|
|
regexp_matches(/stat/),
|
|
10,
|
|
)
|
|
.returns('2' => (time - 5).to_s, '3' => (time - 5).to_s).then
|
|
.returns('2' => time.to_s, '3' => time.to_s)
|
|
|
|
hooks.stubs(:run_shell_without_check).once.with(
|
|
ctx,
|
|
['2','3'],
|
|
regexp_matches(/reboot/),
|
|
10,
|
|
)
|
|
.returns('2' => '', '3' => '')
|
|
|
|
hooks.expects(:sleep).with(300/10)
|
|
|
|
hooks.process
|
|
end
|
|
|
|
it 'should limit nodes processing in parallel' do
|
|
Astute.config.max_nodes_per_call = 1
|
|
|
|
hooks = Astute::NailgunHooks.new([reboot_hook], ctx)
|
|
|
|
time = Time.now.to_i
|
|
hooks.stubs(:run_shell_without_check).once.with(
|
|
ctx,
|
|
['2'],
|
|
regexp_matches(/stat/),
|
|
10,
|
|
)
|
|
.returns('2' => time.to_s)
|
|
|
|
hooks.stubs(:run_shell_without_check).once.with(
|
|
ctx,
|
|
['3'],
|
|
regexp_matches(/stat/),
|
|
10,
|
|
)
|
|
.returns('3' => time.to_s)
|
|
|
|
hooks.expects(:run_shell_without_check).once.with(
|
|
ctx,
|
|
['2'],
|
|
regexp_matches(/reboot/),
|
|
10,
|
|
)
|
|
.returns('2' => '')
|
|
|
|
hooks.expects(:run_shell_without_check).once.with(
|
|
ctx,
|
|
['3'],
|
|
regexp_matches(/reboot/),
|
|
10,
|
|
)
|
|
.returns('3' => '')
|
|
|
|
hooks.stubs(:sleep)
|
|
|
|
time = Time.now.to_i + 100
|
|
hooks.stubs(:run_shell_without_check).once.with(
|
|
ctx,
|
|
['2','3'],
|
|
"stat --printf='%Y' /proc/1",
|
|
10,
|
|
)
|
|
.returns('2' => time.to_s, '3' => time.to_s)
|
|
|
|
hooks.process
|
|
end
|
|
|
|
context 'process data from mcagent in case of critical hook' do
|
|
|
|
let(:hooks) do
|
|
reboot_hook['fail_on_error'] = true
|
|
reboot_hook['parameters']['timeout'] = 1
|
|
|
|
hooks = Astute::NailgunHooks.new([reboot_hook], ctx)
|
|
|
|
hooks.stubs(:run_shell_without_check).once.with(
|
|
ctx,
|
|
['2','3'],
|
|
regexp_matches(/reboot/),
|
|
10,
|
|
)
|
|
.returns('2' => '', '3' => '')
|
|
|
|
hooks.stubs(:sleep)
|
|
|
|
hooks
|
|
end
|
|
|
|
before(:each) do
|
|
ctx.stubs(:report_and_update_status)
|
|
end
|
|
|
|
it 'if reboot succeed -> do not raise error' do
|
|
time = Time.now.to_i
|
|
hooks.stubs(:run_shell_without_check).twice.with(
|
|
ctx,
|
|
['2','3'],
|
|
regexp_matches(/stat/),
|
|
10,
|
|
)
|
|
.returns('2' => (time - 5).to_s, '3' => (time - 5).to_s).then
|
|
.returns('2' => time.to_s, '3' => time.to_s)
|
|
expect {hooks.process}.to_not raise_error
|
|
end
|
|
|
|
it 'if reboot failed -> raise error' do
|
|
time = Time.now.to_i
|
|
hooks.stubs(:run_shell_without_check).with(
|
|
ctx,
|
|
['2','3'],
|
|
regexp_matches(/stat/),
|
|
10,
|
|
)
|
|
.returns('2' => time.to_s, '3' => (time - 5).to_s).then
|
|
.returns('2' => (time - 5).to_s, '3' => time.to_s)
|
|
|
|
hooks.stubs(:run_shell_without_check).with(
|
|
ctx,
|
|
['2'],
|
|
regexp_matches(/stat/),
|
|
10,
|
|
)
|
|
.returns('2' => (time - 5).to_s, '3' => time.to_s)
|
|
|
|
expect {hooks.process}.to raise_error(Astute::DeploymentEngineError, /Failed to execute hook/)
|
|
end
|
|
|
|
it 'if reboot validate info not presence -> raise error' do
|
|
time = Time.now.to_i
|
|
hooks.stubs(:run_shell_without_check).with(
|
|
ctx,
|
|
['2','3'],
|
|
regexp_matches(/stat/),
|
|
10,
|
|
)
|
|
.returns('2' => time.to_s, '3' => (time - 5).to_s).then
|
|
.returns('3' => time.to_s)
|
|
|
|
hooks.stubs(:run_shell_without_check).with(
|
|
ctx,
|
|
['2'],
|
|
regexp_matches(/stat/),
|
|
10,
|
|
).returns({})
|
|
|
|
expect {hooks.process}.to raise_error(Astute::DeploymentEngineError, /Failed to execute hook/)
|
|
end
|
|
end #context
|
|
|
|
end #reboot_hook
|
|
|
|
end # 'describe'
|