fuel-astute/spec/unit/deployment_engine_spec.rb

276 lines
11 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) { mock_ctx }
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
deployer.stubs(:generate_ssh_keys)
deployer.stubs(:upload_ssh_keys)
deployer.stubs(:sync_puppet_manifests)
end
it 'should generate and upload ssh keys' do
nodes = [{'uid' => 1, 'deployment_id' => 1}, {'uid' => 2}, {'uid' => 1}]
deployer.stubs(:deploy_piece)
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 '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 diffirent 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 '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 limit' 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)
end
let(:nodes) { [{'uid' => 1, 'deployment_id' => 1, 'master_ip' => '10.20.0.2'}, {'uid' => 2}] }
it "should sync puppet modules and manifests mcollective client 'puppetsync'" 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)
master_ip = nodes.first['master_ip']
mclient.expects(:rsync).with(:modules_source => "rsync://#{master_ip}:/puppet/modules/",
:manifests_source => "rsync://#{master_ip}:/puppet/manifests/"
)
deployer.deploy(nodes)
end
end
describe '#generation and uploading of ssh keys' do
before(:each) do
Astute.config.PUPPET_SSH_KEYS = ['nova']
deployer.stubs(:deploy_piece)
deployer.stubs(:sync_puppet_manifests)
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(:system).returns(true)
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 ssh key generation fail' do
FileUtils.stubs(:mkdir_p).returns(true)
Engine.any_instance.stubs(:system).returns(false)
expect { deployer.deploy(nodes) }.to raise_error('Could not generate ssh key!')
end
it 'should raise error if ssh key generation command not find' do
FileUtils.stubs(:mkdir_p).returns(true)
Engine.any_instance.stubs(:system).returns(nil)
expect { deployer.deploy(nodes) }.to raise_error('Could not generate ssh key!')
end
it 'should run ssh key generation with correct command' do
FileUtils.stubs(:mkdir_p).returns(true)
key_path = File.join(Engine::KEY_DIR, nodes.first['deployment_id'].to_s, 'nova', 'nova')
Engine.any_instance.expects(:system).with("ssh-keygen -b 2048 -t rsa -N '' -f #{key_path}").returns(true)
deployer.deploy(nodes)
end
it 'should not overwrite files' do
Engine.any_instance.stubs(:system).returns(true)
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
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
end