704 lines
27 KiB
Ruby
704 lines
27 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; 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
|
|
|
|
before(:each) do
|
|
Astute::PreDeployActions.any_instance.stubs(:process).returns(nil)
|
|
Astute::PostDeployActions.any_instance.stubs(:process).returns(nil)
|
|
Astute::PreNodeActions.any_instance.stubs(:process).returns(nil)
|
|
deployer.stubs(:generate_ssh_keys)
|
|
deployer.stubs(:upload_ssh_keys)
|
|
deployer.stubs(:sync_puppet_manifests)
|
|
deployer.stubs(:enable_puppet_deploy)
|
|
deployer.stubs(:update_repo_sources)
|
|
deployer.stubs(:deploy_piece)
|
|
deployer.stubs(:sync_time)
|
|
end
|
|
|
|
it 'should generate and upload ssh keys' do
|
|
nodes = [{'uid' => 1, 'deployment_id' => 1}, {'uid' => 2}, {'uid' => 1}]
|
|
|
|
deployer.expects(:generate_ssh_keys).with(nodes.first['deployment_id'])
|
|
deployer.expects(:upload_ssh_keys).with([1,2], nodes.first['deployment_id']).returns()
|
|
deployer.expects(:sync_puppet_manifests).with([{'uid' => 1, 'deployment_id' => 1}, {'uid' => 2}])
|
|
|
|
deployer.deploy(nodes)
|
|
end
|
|
|
|
it 'should setup packages repositories' do
|
|
nodes = [
|
|
{'uid' => 1,
|
|
'deployment_id' => 1,
|
|
'repo_metadata' => {
|
|
'Nailgun' => 'http://10.20.0.2:8080/centos/fuelweb/x86_64/'
|
|
}
|
|
},
|
|
{'uid' => 2},
|
|
{'uid' => 1}
|
|
]
|
|
uniq_nodes = nodes[0..-2]
|
|
|
|
deployer.expects(:update_repo_sources).with(uniq_nodes)
|
|
|
|
deployer.deploy(nodes)
|
|
end
|
|
|
|
it 'should enable puppet for all nodes' do
|
|
nodes = [{'uid' => 1, 'deployment_id' => 1}, {'uid' => 2}, {'uid' => 1}]
|
|
|
|
|
|
deployer.expects(:enable_puppet_deploy).with([1,2]).returns()
|
|
|
|
deployer.deploy(nodes)
|
|
end
|
|
|
|
it 'deploy nodes by order' do
|
|
nodes = [{'uid' => 1, 'priority' => 10}, {'uid' => 2, 'priority' => 0}, {'uid' => 1, 'priority' => 15}]
|
|
|
|
deployer.expects(:deploy_piece).with([{'uid' => 2, 'priority' => 0}])
|
|
deployer.expects(:deploy_piece).with([{'uid' => 1, 'priority' => 10}])
|
|
deployer.expects(:deploy_piece).with([{'uid' => 1, 'priority' => 15}])
|
|
|
|
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
|
|
deployer.stubs(:generate_ssh_keys)
|
|
deployer.stubs(:upload_ssh_keys)
|
|
deployer.stubs(:sync_puppet_manifests)
|
|
|
|
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
|
|
|
|
describe '#sync_puppet_manifests' do
|
|
before(:each) do
|
|
deployer.stubs(:deploy_piece)
|
|
deployer.stubs(:generate_ssh_keys)
|
|
deployer.stubs(:upload_ssh_keys)
|
|
deployer.stubs(:enable_puppet_deploy)
|
|
deployer.stubs(:sync_time)
|
|
end
|
|
|
|
let(:nodes) { [
|
|
{'uid' => 1,
|
|
'deployment_id' => 1,
|
|
'master_ip' => '10.20.0.2',
|
|
'puppet_modules_source' => 'rsync://10.20.0.2:/puppet/modules/',
|
|
'puppet_manifests_source' => 'rsync://10.20.0.2:/puppet/manifests/'
|
|
},
|
|
{'uid' => 2}
|
|
]
|
|
}
|
|
let(:mclient) do
|
|
mclient = mock_rpcclient(nodes)
|
|
Astute::MClient.any_instance.stubs(:rpcclient).returns(mclient)
|
|
Astute::MClient.any_instance.stubs(:log_result).returns(mclient)
|
|
Astute::MClient.any_instance.stubs(:check_results_with_retries).returns(mclient)
|
|
mclient
|
|
end
|
|
let(:master_ip) { nodes.first['master_ip'] }
|
|
|
|
it "should sync puppet modules and manifests mcollective client 'puppetsync'" do
|
|
mclient.expects(:rsync).with(:modules_source => "rsync://10.20.0.2:/puppet/modules/",
|
|
:manifests_source => "rsync://10.20.0.2:/puppet/manifests/"
|
|
)
|
|
deployer.deploy(nodes)
|
|
end
|
|
|
|
it 'should able to customize path for puppet modules and manifests' do
|
|
modules_source = 'rsync://10.20.0.2:/puppet/vX/modules/'
|
|
manifests_source = 'rsync://10.20.0.2:/puppet/vX/manifests/'
|
|
nodes.first['puppet_modules_source'] = modules_source
|
|
nodes.first['puppet_manifests_source'] = manifests_source
|
|
mclient.expects(:rsync).with(:modules_source => modules_source,
|
|
:manifests_source => manifests_source
|
|
)
|
|
deployer.deploy(nodes)
|
|
end
|
|
|
|
|
|
context 'retry sync if mcollective raise error and' do
|
|
it 'raise error if retry fail SYNC_RETRIES times' do
|
|
mclient.stubs(:rsync)
|
|
Astute::MClient.any_instance.stubs(:check_results_with_retries)
|
|
.raises(Astute::MClientError)
|
|
.times(Astute::DeploymentEngine::SYNC_RETRIES)
|
|
expect { deployer.deploy(nodes) }.to raise_error(Astute::MClientError)
|
|
end
|
|
|
|
it 'not raise error if mcollective return success less than SYNC_RETRIES attempts' do
|
|
mclient.stubs(:rsync)
|
|
Astute::MClient.any_instance.stubs(:check_results_with_retries)
|
|
.raises(Astute::MClientError)
|
|
.then.returns("")
|
|
expect { deployer.deploy(nodes) }.to_not raise_error(Astute::MClientError)
|
|
end
|
|
end
|
|
|
|
it 'should raise exception if modules/manifests schema of uri is not equal' do
|
|
nodes.first['puppet_manifests_source'] = 'rsync://10.20.0.2:/puppet/vX/modules/'
|
|
nodes.first['puppet_manifests_source'] = 'http://10.20.0.2:/puppet/vX/manifests/'
|
|
expect { deployer.deploy(nodes) }.to raise_error(Astute::DeploymentEngineError,
|
|
/Scheme for puppet_modules_source 'rsync' and puppet_manifests_source/)
|
|
end
|
|
|
|
it 'should raise exception if modules/manifests source uri is incorrect' do
|
|
nodes.first['puppet_manifests_source'] = ':/puppet/modules/'
|
|
expect { deployer.deploy(nodes) }.to raise_error(Astute::DeploymentEngineError,
|
|
/bad URI/)
|
|
end
|
|
|
|
it 'should raise exception if schema of uri is incorrect' do
|
|
nodes.first['puppet_modules_source'] = 'http2://localhost/puppet/modules/'
|
|
nodes.first['puppet_manifests_source'] = 'http2://localhost/puppet/manifests/'
|
|
mclient.expects(:rsync).never
|
|
expect { deployer.deploy(nodes) }.to raise_error(Astute::DeploymentEngineError,
|
|
/Unknown scheme /)
|
|
end
|
|
end
|
|
|
|
describe '#update_repo_sources' do
|
|
before(:each) do
|
|
deployer.stubs(:generate_ssh_keys)
|
|
deployer.stubs(:upload_ssh_keys)
|
|
deployer.stubs(:sync_puppet_manifests)
|
|
deployer.stubs(:enable_puppet_deploy)
|
|
deployer.stubs(:deploy_piece)
|
|
deployer.stubs(:sync_time)
|
|
end
|
|
|
|
let(:nodes) do
|
|
[
|
|
{'uid' => 1,
|
|
'deployment_id' => 1,
|
|
'cobbler' => {
|
|
'profile' => 'centos-x86_64'
|
|
},
|
|
'repo_metadata' => {
|
|
'Nailgun' => 'http://10.20.0.2:8080/centos/fuelweb/x86_64/',
|
|
}
|
|
},
|
|
{'uid' => 2}
|
|
]
|
|
end
|
|
|
|
let(:mclient) do
|
|
mclient = mock_rpcclient(nodes)
|
|
Astute::MClient.any_instance.stubs(:rpcclient).returns(mclient)
|
|
Astute::MClient.any_instance.stubs(:log_result).returns(mclient)
|
|
Astute::MClient.any_instance.stubs(:check_results_with_retries).returns(mclient)
|
|
mclient
|
|
end
|
|
|
|
context 'source configuration generation' do
|
|
before(:each) do
|
|
deployer.stubs(:regenerate_metadata)
|
|
end
|
|
|
|
it 'should generate correct config for centos' do
|
|
content = ["[nailgun]",
|
|
"name=Nailgun",
|
|
"baseurl=http://10.20.0.2:8080/centos/fuelweb/x86_64/",
|
|
"gpgcheck=0"].join("\n")
|
|
|
|
deployer.expects(:upload_repo_source).with(nodes, content)
|
|
deployer.deploy(nodes)
|
|
end
|
|
|
|
it 'should generate correct config for ubuntu' do
|
|
nodes.first['cobbler']['profile'] = 'ubuntu_1204_x86_64'
|
|
nodes.first['repo_metadata']['Nailgun'] =
|
|
'http://10.20.0.2:8080/ubuntu/fuelweb/x86_64 precise main'
|
|
content = "deb http://10.20.0.2:8080/ubuntu/fuelweb/x86_64 precise main"
|
|
|
|
deployer.expects(:upload_repo_source).with(nodes, content)
|
|
deployer.deploy(nodes)
|
|
end
|
|
|
|
it 'should raise error if os not recognized' do
|
|
nodes.first['cobbler']['profile'] = 'unknown'
|
|
|
|
expect {deployer.deploy(nodes)}.to raise_error(Astute::DeploymentEngineError,
|
|
/Unknown system/)
|
|
end
|
|
end # source configuration generation
|
|
|
|
context 'new source configuration uploading' do
|
|
|
|
let(:repo_content) { "repo conf" }
|
|
|
|
before(:each) do
|
|
deployer.stubs(:generate_repo_source).returns(repo_content)
|
|
deployer.stubs(:regenerate_metadata)
|
|
end
|
|
|
|
it 'should upload config in correct place for centos' do
|
|
mclient.expects(:upload).with(:path => '/etc/yum.repos.d/nailgun.repo',
|
|
:content => repo_content,
|
|
:user_owner => 'root',
|
|
:group_owner => 'root',
|
|
:permissions => '0644',
|
|
:dir_permissions => '0755',
|
|
:overwrite => true,
|
|
:parents => true
|
|
)
|
|
deployer.deploy(nodes)
|
|
end
|
|
|
|
it 'should upload config in correct place for ubuntu' do
|
|
nodes.first['cobbler']['profile'] = 'ubuntu_1204_x86_64'
|
|
|
|
mclient.expects(:upload).with(:path => '/etc/apt/sources.list',
|
|
:content => repo_content,
|
|
:user_owner => 'root',
|
|
:group_owner => 'root',
|
|
:permissions => '0644',
|
|
:dir_permissions => '0755',
|
|
:overwrite => true,
|
|
:parents => true
|
|
)
|
|
deployer.deploy(nodes)
|
|
end
|
|
end #new source configuration uploading
|
|
|
|
context 'metadata regeneration' do
|
|
|
|
let(:fail_return) { [{:data => {:exit_code => 1}}] }
|
|
|
|
before(:each) do
|
|
deployer.stubs(:sync_time)
|
|
deployer.stubs(:generate_repo_source)
|
|
deployer.stubs(:upload_repo_source)
|
|
end
|
|
|
|
let(:success_return) { [{:data => {:exit_code => 0}}] }
|
|
|
|
it 'should regenerate metadata for centos' do
|
|
mclient.expects(:execute).with(:cmd => 'yum clean all').returns(success_return)
|
|
deployer.deploy(nodes)
|
|
end
|
|
|
|
it 'should regenerate metadata for ubuntu' do
|
|
nodes.first['cobbler']['profile'] = 'ubuntu_1204_x86_64'
|
|
mclient.expects(:execute).with(:cmd => 'apt-get clean; apt-get update').returns(success_return)
|
|
deployer.deploy(nodes)
|
|
end
|
|
|
|
it 'should raise error if metadata not updated' do
|
|
nodes.first['cobbler']['profile'] = 'ubuntu_1204_x86_64'
|
|
mclient.expects(:execute).with(:cmd => 'apt-get clean; apt-get update').returns(fail_return).times(Astute.config[:MC_RETRIES])
|
|
expect { deployer.deploy(nodes) }.to raise_error(Astute::DeploymentEngineError,
|
|
/Run command:/)
|
|
end
|
|
|
|
it 'should retry metadata update several time if get error' do
|
|
nodes.first['cobbler']['profile'] = 'ubuntu_1204_x86_64'
|
|
mclient.expects(:execute).with(:cmd => 'apt-get clean; apt-get update').returns(fail_return)
|
|
.then.returns(success_return).twice
|
|
deployer.deploy(nodes)
|
|
end
|
|
end #'metadata regeneration'
|
|
end # update_repo_sources
|
|
|
|
describe '#generation and uploading of ssh keys' do
|
|
before(:each) do
|
|
Astute.config.PUPPET_SSH_KEYS = ['nova']
|
|
deployer.stubs(:sync_time)
|
|
deployer.stubs(:deploy_piece)
|
|
deployer.stubs(:sync_puppet_manifests)
|
|
deployer.stubs(:enable_puppet_deploy)
|
|
end
|
|
|
|
let(:nodes) { [{'uid' => 1, 'deployment_id' => 1}, {'uid' => 2}] }
|
|
|
|
it 'should use Astute.config to get the ssh names that need to generate' do
|
|
deployer.expects(:generate_ssh_keys).with(nodes.first['deployment_id'])
|
|
deployer.expects(:upload_ssh_keys).with([1, 2], nodes.first['deployment_id'])
|
|
deployer.deploy(nodes)
|
|
end
|
|
|
|
it 'should raise error if deployment_id is not set' do
|
|
nodes = [{'uid' => 1}, {'uid' => 2}]
|
|
expect { deployer.deploy(nodes) }.to raise_error('Deployment_id is missing')
|
|
end
|
|
|
|
context 'generation of ssh keys' do
|
|
before(:each) do
|
|
deployer.stubs(:upload_ssh_keys).with([1, 2], nodes.first['deployment_id'])
|
|
end
|
|
|
|
it 'should save files in correct place: KEY_DIR/<name of key>/' do
|
|
Engine.any_instance.stubs(:run_system_command).returns([0, "", ""])
|
|
|
|
Dir.mktmpdir do |temp_dir|
|
|
Astute::DeploymentEngine.const_set 'KEY_DIR', temp_dir
|
|
deployer.deploy(nodes)
|
|
|
|
expect { File.directory? File.join(temp_dir, 'nova') }.to be_true
|
|
end
|
|
end
|
|
|
|
it 'should raise error if directory for key was not created' do
|
|
FileUtils.stubs(:mkdir_p).returns(false)
|
|
File.stubs(:directory?).returns(false)
|
|
|
|
expect { deployer.deploy(nodes) }.to raise_error(Astute::DeploymentEngineError,
|
|
/Could not create directory/)
|
|
end
|
|
|
|
it 'should raise error if ssh key generation fail' do
|
|
FileUtils.stubs(:mkdir_p).returns(true)
|
|
File.stubs(:directory?).returns(true)
|
|
Engine.any_instance.stubs(:run_system_command).returns([1, "", ""])
|
|
|
|
expect { deployer.deploy(nodes) }.to raise_error(Astute::DeploymentEngineError,
|
|
/Could not generate ssh key! Command:/)
|
|
end
|
|
|
|
it 'should raise error if ssh key generation command not find' do
|
|
FileUtils.stubs(:mkdir_p).returns(true)
|
|
File.stubs(:directory?).returns(true)
|
|
Engine.any_instance.stubs(:run_system_command).returns([127, "Command not found", ""])
|
|
|
|
expect { deployer.deploy(nodes) }.to raise_error(Astute::DeploymentEngineError,
|
|
/Command not found/)
|
|
end
|
|
|
|
it 'should run ssh key generation with correct command' do
|
|
FileUtils.stubs(:mkdir_p).returns(true)
|
|
File.stubs(:directory?).returns(true)
|
|
|
|
key_path = File.join(Engine::KEY_DIR, nodes.first['deployment_id'].to_s, 'nova', 'nova')
|
|
cmd = "ssh-keygen -b 2048 -t rsa -N '' -f #{key_path} 2>&1"
|
|
Engine.any_instance.expects(:run_system_command).with(cmd).returns([0, "", ""])
|
|
|
|
deployer.deploy(nodes)
|
|
end
|
|
|
|
it 'should not overwrite files' do
|
|
Dir.mktmpdir do |temp_dir|
|
|
Astute::DeploymentEngine.const_set 'KEY_DIR', temp_dir
|
|
key_path = File.join(temp_dir,'nova', 'nova')
|
|
FileUtils.mkdir_p File.join(temp_dir,'nova')
|
|
File.open(key_path, 'w') { |file| file.write("say no overwrite") }
|
|
deployer.deploy(nodes)
|
|
|
|
expect { File.exist? File.join(key_path, 'nova', 'nova') }.to be_true
|
|
expect { File.read File.join(key_path, 'nova', 'nova') == "say no overwrite" }.to be_true
|
|
end
|
|
end
|
|
|
|
it 'should check next key if find existing' do
|
|
Astute.config.PUPPET_SSH_KEYS = ['nova', 'test']
|
|
nova_key_path = File.join(Engine::KEY_DIR, nodes.first['deployment_id'].to_s, 'nova', 'nova')
|
|
test_key_path = File.join(Engine::KEY_DIR, nodes.first['deployment_id'].to_s, 'test', 'test')
|
|
|
|
FileUtils.stubs(:mkdir_p).returns(true).twice
|
|
File.stubs(:directory?).returns(true).twice
|
|
|
|
File.stubs(:exist?).with(nova_key_path).returns(true)
|
|
File.stubs(:exist?).with(test_key_path).returns(false)
|
|
|
|
Engine.any_instance.expects(:run_system_command).returns([0, "", ""])
|
|
|
|
deployer.deploy(nodes)
|
|
end
|
|
end # end context
|
|
|
|
context 'upload ssh keys' do
|
|
before(:each) do
|
|
deployer.stubs(:generate_ssh_keys)
|
|
end
|
|
|
|
it "should upload ssh keys using mcollective client 'uploadfile'" do
|
|
mclient = mock_rpcclient(nodes)
|
|
Astute::MClient.any_instance.stubs(:rpcclient).returns(mclient)
|
|
Astute::MClient.any_instance.stubs(:log_result).returns(mclient)
|
|
Astute::MClient.any_instance.stubs(:check_results_with_retries).returns(mclient)
|
|
|
|
File.stubs(:read).returns("private key").then.returns("public key")
|
|
mclient.expects(:upload).with(:path => File.join(Engine::KEY_DIR, 'nova', 'nova'),
|
|
:content => "private key",
|
|
:user_owner => 'root',
|
|
:group_owner => 'root',
|
|
:permissions => '0600',
|
|
:dir_permissions => '0700',
|
|
:overwrite => true,
|
|
:parents => true
|
|
)
|
|
mclient.expects(:upload).with(:path => File.join(Engine::KEY_DIR, 'nova', 'nova.pub'),
|
|
:content => "public key",
|
|
:user_owner => 'root',
|
|
:group_owner => 'root',
|
|
:permissions => '0600',
|
|
:dir_permissions => '0700',
|
|
:overwrite => true,
|
|
:parents => true
|
|
)
|
|
deployer.deploy(nodes)
|
|
end
|
|
end # context
|
|
end # describe
|
|
|
|
describe '#sync_time' do
|
|
before(:each) do
|
|
deployer.stubs(:generate_ssh_keys)
|
|
deployer.stubs(:upload_ssh_keys)
|
|
deployer.stubs(:sync_puppet_manifests)
|
|
deployer.stubs(:enable_puppet_deploy)
|
|
deployer.stubs(:update_repo_sources)
|
|
deployer.stubs(:deploy_piece)
|
|
end
|
|
|
|
let(:nodes) { [{'uid' => 1, 'deployment_id' => 1}, {'uid' => 2}] }
|
|
|
|
it 'should sync time between cluster nodes' do
|
|
deployer.expects(:sync_time).with([1,2])
|
|
deployer.deploy(nodes)
|
|
end
|
|
|
|
it 'should not raise exception if fail' do
|
|
deployer.stubs(:run_shell_command_remotely).returns(false)
|
|
expect {deployer.deploy(nodes)}.to_not raise_error
|
|
end
|
|
|
|
it 'should try to sync several times if fail' do
|
|
deployer.stubs(:run_shell_command_remotely).returns(false)
|
|
.then.returns(true).twice
|
|
|
|
deployer.deploy(nodes)
|
|
end
|
|
|
|
end #sync_time
|
|
end
|