Deployment artifacts management for upgrades:
* update packages repository source(ubuntu && centos); * support custom source for manifests and modules; * tests. Implements: blueprint fuel-upgrade Change-Id: Id2d51049a79095c1dd92599dd360218ee9926e69
This commit is contained in:
parent
401bc474b1
commit
0cb0b26773
|
@ -1 +1 @@
|
|||
1.9.3
|
||||
2.1
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
require 'fileutils'
|
||||
require 'popen4'
|
||||
require 'uri'
|
||||
|
||||
KEY_DIR = "/var/lib/astute"
|
||||
|
||||
|
@ -43,6 +44,9 @@ module Astute
|
|||
# Will be used by puppet after to connect nodes between themselves.
|
||||
upload_ssh_keys(part.map{ |n| n['uid'] }, part.first['deployment_id'])
|
||||
|
||||
# Update packages source list
|
||||
update_repo_sources(part) if part.first['repo_source']
|
||||
|
||||
# Sync puppet manifests and modules to every node (emulate puppet master)
|
||||
sync_puppet_manifests(part)
|
||||
|
||||
|
@ -110,11 +114,31 @@ module Astute
|
|||
def sync_puppet_manifests(deployment_info)
|
||||
sync_mclient = MClient.new(@ctx, "puppetsync", deployment_info.map{ |n| n['uid'] }.uniq)
|
||||
master_ip = deployment_info.first['master_ip']
|
||||
# Paths /puppet/modules and /puppet/manifests/ in master node set by FUEL
|
||||
modules_source = deployment_info.first['puppet_modules_source'] || "rsync://#{master_ip}:/puppet/modules/"
|
||||
manifests_source = deployment_info.first['puppet_manifests_source'] || "rsync://#{master_ip}:/puppet/manifests/"
|
||||
# Paths to Puppet modules and manifests at the master node set by Nailgun
|
||||
# Check fuel source code /deployment/puppet/nailgun/manifests/puppetsync.pp
|
||||
sync_mclient.rsync(:modules_source => "rsync://#{master_ip}:/puppet/modules/",
|
||||
:manifests_source => "rsync://#{master_ip}:/puppet/manifests/"
|
||||
)
|
||||
schemas = [modules_source, manifests_source].map do |url|
|
||||
begin
|
||||
URI.parse(url).scheme
|
||||
rescue URI::InvalidURIError => e
|
||||
raise DeploymentEngineError, e.message
|
||||
end
|
||||
end
|
||||
|
||||
if schemas.select{ |x| x != schemas.first }.present?
|
||||
raise DeploymentEngineError, "Scheme for puppet_modules_source '#{schemas.first}' and" \
|
||||
" puppet_manifests_source '#{schemas.last}' not equivalent!"
|
||||
end
|
||||
|
||||
case schemas.first
|
||||
when 'rsync'
|
||||
sync_mclient.rsync(:modules_source => modules_source,
|
||||
:manifests_source => manifests_source
|
||||
)
|
||||
else
|
||||
raise DeploymentEngineError, "Unknown scheme '#{schemas.first}' in #{modules_source}"
|
||||
end
|
||||
end
|
||||
|
||||
def generate_ssh_keys(deployment_id, overwrite=false)
|
||||
|
@ -159,12 +183,91 @@ module Astute
|
|||
end
|
||||
end
|
||||
|
||||
def update_repo_sources(deployment_info)
|
||||
content = generate_repo_source(deployment_info)
|
||||
upload_repo_source(deployment_info, content)
|
||||
regenerate_metadata(deployment_info)
|
||||
end
|
||||
|
||||
def generate_repo_source(deployment_info)
|
||||
ubuntu_source = -> (name, url) { "deb #{url}" }
|
||||
centos_source = -> (name, url) do
|
||||
["[#{name.downcase}]", "name=#{name}", "baseurl=#{url}", "gpgcheck=0"].join("\n")
|
||||
end
|
||||
|
||||
formatter = case target_os(deployment_info)
|
||||
when 'centos' then centos_source
|
||||
when 'ubuntu' then ubuntu_source
|
||||
end
|
||||
|
||||
content = []
|
||||
# FIXME: This key 'repo_source' not approved
|
||||
deployment_info.first['repo_source'].each do |name, url|
|
||||
content << formatter.call(name,url)
|
||||
end
|
||||
content.join("\n")
|
||||
end
|
||||
|
||||
def upload_repo_source(deployment_info, content)
|
||||
upload_mclient = MClient.new(@ctx, "uploadfile", deployment_info.map{ |n| n['uid'] }.uniq)
|
||||
destination_path = case target_os(deployment_info)
|
||||
when 'centos' then '/etc/yum.repos.d/nailgun.repo'
|
||||
when 'ubuntu' then '/etc/apt/sources.list'
|
||||
end
|
||||
upload_mclient.upload(:path => destination_path,
|
||||
:content => content,
|
||||
:user_owner => 'root',
|
||||
:group_owner => 'root',
|
||||
:permissions => '0644',
|
||||
:dir_permissions => '0755',
|
||||
:overwrite => true,
|
||||
:parents => true
|
||||
)
|
||||
end
|
||||
|
||||
def regenerate_metadata(deployment_info)
|
||||
cmd = case target_os(deployment_info)
|
||||
when 'centos' then "yum clean all"
|
||||
when 'ubuntu' then "apt-get clean; apt-get update"
|
||||
end
|
||||
|
||||
run_shell_command_remotely(deployment_info.map{ |n| n['uid'] }.uniq, cmd)
|
||||
end
|
||||
|
||||
def target_os(deployment_info)
|
||||
os = deployment_info.first['cobbler']['profile']
|
||||
case os
|
||||
when 'centos-x86_64' then 'centos'
|
||||
when 'rhel-x86_64' then 'centos'
|
||||
when 'ubuntu_1204_x86_64' then 'ubuntu'
|
||||
else
|
||||
raise DeploymentEngineError, "Unknown system #{os}"
|
||||
end
|
||||
end
|
||||
|
||||
def run_system_command(cmd)
|
||||
pid, _, stdout, stderr = Open4::popen4 cmd
|
||||
_, status = Process::waitpid2 pid
|
||||
return status.exitstatus, stdout, stderr
|
||||
end
|
||||
|
||||
def run_shell_command_remotely(node_uids, cmd)
|
||||
shell = MClient.new(@ctx,
|
||||
'execute_shell_command',
|
||||
node_uids,
|
||||
check_result=true,
|
||||
timeout=60,
|
||||
retries=1)
|
||||
|
||||
#TODO: return result for all nodes not only for first
|
||||
response = shell.execute(:cmd => cmd).first
|
||||
Astute.logger.debug("#{@ctx.task_id}: cmd: #{cmd}
|
||||
stdout: #{response[:data][:stdout]}
|
||||
stderr: #{response[:data][:stderr]}
|
||||
exit code: #{response[:data][:exit_code]}")
|
||||
response
|
||||
end
|
||||
|
||||
def enable_puppet_deploy(node_uids)
|
||||
puppetd = MClient.new(@ctx, "puppetd", node_uids)
|
||||
puppetd.enable
|
||||
|
|
|
@ -41,11 +41,12 @@ describe Astute::DeploymentEngine do
|
|||
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
|
||||
|
||||
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()
|
||||
|
@ -54,9 +55,27 @@ describe Astute::DeploymentEngine do
|
|||
deployer.deploy(nodes)
|
||||
end
|
||||
|
||||
it 'should setup packages repositories' do
|
||||
nodes = [
|
||||
{'uid' => 1,
|
||||
'deployment_id' => 1,
|
||||
'repo_source' => {
|
||||
'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.stubs(:deploy_piece)
|
||||
|
||||
|
||||
deployer.expects(:enable_puppet_deploy).with([1,2]).returns()
|
||||
|
||||
|
@ -157,22 +176,221 @@ describe Astute::DeploymentEngine do
|
|||
deployer.stubs(:enable_puppet_deploy)
|
||||
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
|
||||
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'] }
|
||||
|
||||
master_ip = nodes.first['master_ip']
|
||||
mclient.expects(:rsync).with(:modules_source => "rsync://#{master_ip}:/puppet/modules/",
|
||||
:manifests_source => "rsync://#{master_ip}:/puppet/manifests/"
|
||||
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
|
||||
|
||||
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/'
|
||||
mclient.expects(:rsync).never
|
||||
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/'
|
||||
mclient.expects(:rsync).never
|
||||
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)
|
||||
end
|
||||
|
||||
let(:nodes) do
|
||||
[
|
||||
{'uid' => 1,
|
||||
'deployment_id' => 1,
|
||||
'cobbler' => {
|
||||
'profile' => 'centos-x86_64'
|
||||
},
|
||||
'repo_source' => {
|
||||
'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 rhel' do
|
||||
nodes.first['cobbler']['profile'] = 'rhel-x86_64'
|
||||
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_source']['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 rhel' do
|
||||
nodes.first['cobbler']['profile'] = 'rhel-x86_64'
|
||||
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
|
||||
|
||||
before(:each) do
|
||||
deployer.stubs(:generate_repo_source)
|
||||
deployer.stubs(:upload_repo_source)
|
||||
end
|
||||
|
||||
it 'should regenerate metadata for centos' do
|
||||
mclient.expects(:execute).with(:cmd => 'yum clean all').returns([{:data => {} }])
|
||||
deployer.deploy(nodes)
|
||||
end
|
||||
|
||||
it 'should regenerate metadata for rhel' do
|
||||
nodes.first['cobbler']['profile'] = 'rhel-x86_64'
|
||||
mclient.expects(:execute).with(:cmd => 'yum clean all').returns([{:data => {} }])
|
||||
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([{:data => {} }])
|
||||
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']
|
||||
|
@ -275,7 +493,6 @@ describe Astute::DeploymentEngine do
|
|||
|
||||
deployer.deploy(nodes)
|
||||
end
|
||||
|
||||
end # end context
|
||||
|
||||
context 'upload ssh keys' do
|
||||
|
@ -311,6 +528,5 @@ describe Astute::DeploymentEngine do
|
|||
deployer.deploy(nodes)
|
||||
end
|
||||
end # context
|
||||
|
||||
end # describe
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue