debloy 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +56 -0
- data/Rakefile +16 -0
- data/lib/debloy.rb +142 -0
- data/lib/debloy/apt_ftp_archive.rb +27 -0
- data/lib/debloy/apt_get.rb +17 -0
- data/lib/debloy/logger/batch.rb +22 -0
- data/lib/debloy/logger/stream.rb +16 -0
- data/lib/debloy/rsync.rb +45 -0
- data/lib/debloy/scp.rb +22 -0
- data/lib/debloy/util/collection_utils.rb +17 -0
- data/lib/debloy/util/parallel_enumerable.rb +8 -0
- metadata +56 -0
data/README.md
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
[](http://coderwall.com/jeroenr)
|
2
|
+
|
3
|
+
# debloy
|
4
|
+
|
5
|
+
debloy is a capistrano plugin to facilitate the deployment of debian packages (inspired by supply_drop). It works by simply copying (using rsync, or scp) your debian packages to your servers and installing them through the package manager
|
6
|
+
|
7
|
+
### Installation
|
8
|
+
|
9
|
+
gem install debloy
|
10
|
+
|
11
|
+
or with Bundler
|
12
|
+
|
13
|
+
gem 'debloy'
|
14
|
+
|
15
|
+
### Tasks
|
16
|
+
|
17
|
+
cap debloy:bootstrap
|
18
|
+
|
19
|
+
This sets up the environment for dpkg or apt deployment, depending on your configuration.
|
20
|
+
|
21
|
+
cap debloy
|
22
|
+
|
23
|
+
This deploys the debian packages on the target servers.
|
24
|
+
|
25
|
+
cap debloy:teardown
|
26
|
+
|
27
|
+
Cleans up the environment.
|
28
|
+
|
29
|
+
### Configuration
|
30
|
+
|
31
|
+
At the top of your deploy.rb
|
32
|
+
|
33
|
+
require 'rubygems'
|
34
|
+
require 'debloy'
|
35
|
+
|
36
|
+
then optionally set some variables
|
37
|
+
|
38
|
+
set :debian_source, '.'
|
39
|
+
|
40
|
+
the directory containing your debian packages that will be rsynced to the servers.
|
41
|
+
|
42
|
+
set :debian_target, '/tmp'
|
43
|
+
|
44
|
+
the temp directory on the target machine to hold the packages before installing.
|
45
|
+
|
46
|
+
set :debian_package_manager, 'dpkg'
|
47
|
+
|
48
|
+
the debian package manager to use (one of [dpkg, apt]).
|
49
|
+
|
50
|
+
set :debian_stream_log, false
|
51
|
+
|
52
|
+
determines whether to stream the command output.
|
53
|
+
|
54
|
+
set :debian_filter, '*'
|
55
|
+
|
56
|
+
a glob syntax filter to determine which packages to deploy. By default all will be deployed.
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
desc "clean"
|
2
|
+
task :clean do
|
3
|
+
rm_f Dir.glob("*.gem")
|
4
|
+
end
|
5
|
+
|
6
|
+
namespace :gem do
|
7
|
+
desc "build the gem"
|
8
|
+
task :build => :clean do
|
9
|
+
sh "gem build debloy.gemspec"
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "push the gem"
|
13
|
+
task :push => :build do
|
14
|
+
sh "gem push #{Dir.glob("*.gem").first}"
|
15
|
+
end
|
16
|
+
end
|
data/lib/debloy.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'debloy/rsync'
|
2
|
+
require 'debloy/apt_get'
|
3
|
+
require 'debloy/apt_ftp_archive'
|
4
|
+
require 'debloy/logger/batch'
|
5
|
+
require 'debloy/logger/stream'
|
6
|
+
require 'debloy/util/parallel_enumerable'
|
7
|
+
|
8
|
+
Capistrano::Configuration.instance.load do
|
9
|
+
namespace :debloy do
|
10
|
+
set :debian_source, '.'
|
11
|
+
set :debian_target, '/tmp'
|
12
|
+
set :debian_package_manager, 'dpkg'
|
13
|
+
set :debian_stream_log, false
|
14
|
+
set :debian_filter, '*'
|
15
|
+
|
16
|
+
RELEASE_FILE_OPTIONS = {
|
17
|
+
"Codename" => "debloy",
|
18
|
+
"Components" => "debloy",
|
19
|
+
"Origin" => "debloy",
|
20
|
+
"Label" => "Debloyed",
|
21
|
+
"Architectures" => "all",
|
22
|
+
"Suite" => "stable"
|
23
|
+
}
|
24
|
+
|
25
|
+
namespace :bootstrap do
|
26
|
+
desc "prepares remote hosts for debloyment based on selected package manager (dpkg or apt)"
|
27
|
+
task :default do
|
28
|
+
case debian_package_manager
|
29
|
+
when "dpkg"
|
30
|
+
dpkg
|
31
|
+
when "apt"
|
32
|
+
apt
|
33
|
+
else
|
34
|
+
raise "#{debian_package_manager} is an unsupported package manager. Only dpkg and apt are supported"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "creates directories and installs dependencies"
|
39
|
+
task :dpkg do
|
40
|
+
run "mkdir -p #{debian_target}/debloy"
|
41
|
+
sudo Debloy::AptGet.update_cache
|
42
|
+
sudo Debloy::AptGet.install_packages(%w(rsync))
|
43
|
+
logger.debug "Dependencies installed"
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "creates local debian repository for apt-get"
|
47
|
+
task :apt do
|
48
|
+
run "mkdir -p #{debian_target}/debloy"
|
49
|
+
|
50
|
+
sudo Debloy::AptGet.update_cache
|
51
|
+
sudo Debloy::AptGet.install_packages(%w(rsync dpkg-dev gzip))
|
52
|
+
|
53
|
+
logger.debug "Dependencies installed"
|
54
|
+
|
55
|
+
put "deb file:#{debian_target}/debloy ./", "#{debian_target}/debloy.list"
|
56
|
+
|
57
|
+
run "cd #{debian_target}/debloy && " << Debloy::AptFtpArchive.create_packages_file('.')
|
58
|
+
|
59
|
+
logger.debug "Set up local debian repository"
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
namespace :teardown do
|
65
|
+
desc "cleans up debloy files"
|
66
|
+
task :default do
|
67
|
+
sudo "rm -rf #{debian_target}/debloy"
|
68
|
+
|
69
|
+
debloy_files = %w(/etc/apt/sources.list.d/debloy.list /etc/apt/preferences.d/00debloy) << "#{debian_target}/debloy.list" << "#{debian_target}/00debloy"
|
70
|
+
debloy_files.each do |file_name|
|
71
|
+
sudo "rm -f #{file_name}"
|
72
|
+
end
|
73
|
+
logger.debug "Removed deployment directory"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
desc "copies debian packages to the server"
|
78
|
+
task :copy_packages do
|
79
|
+
targets = find_servers_for_task(current_task)
|
80
|
+
failed_targets = targets.async.map do |target|
|
81
|
+
copy_cmd = Debloy::Rsync.command(
|
82
|
+
debian_source,
|
83
|
+
Debloy::Rsync.remote_address(target.user || fetch(:user, ENV['USER']), target.host, "#{debian_target}/debloy"),
|
84
|
+
:filter => ['*/'] + debian_filter.split(',').map {|x| "#{x}.deb"},
|
85
|
+
:ssh => {
|
86
|
+
:keys => ssh_options[:keys],
|
87
|
+
:config => ssh_options[:config],
|
88
|
+
:port => fetch(:port, nil)
|
89
|
+
}
|
90
|
+
)
|
91
|
+
logger.debug copy_cmd
|
92
|
+
target.host unless system copy_cmd
|
93
|
+
end.compact
|
94
|
+
|
95
|
+
raise "rsync failed on #{failed_targets.join(',')}" if failed_targets.any?
|
96
|
+
end
|
97
|
+
|
98
|
+
task :install_packages do
|
99
|
+
log = if debian_stream_log
|
100
|
+
Debloy::Logger::Stream.new(logger)
|
101
|
+
else
|
102
|
+
Debloy::Logger::Batch.new(logger)
|
103
|
+
end
|
104
|
+
|
105
|
+
begin
|
106
|
+
case debian_package_manager
|
107
|
+
when "dpkg"
|
108
|
+
sudo "dpkg -R -i #{debian_target}/debloy" do |channel, stream, data|
|
109
|
+
log.collect(channel[:host], data)
|
110
|
+
end
|
111
|
+
when "apt"
|
112
|
+
apt_get_options = {
|
113
|
+
"Dir::Etc::SourceList" => "#{debian_target}/debloy.list"
|
114
|
+
}
|
115
|
+
|
116
|
+
list_packages_cmd = "zcat #{debian_target}/debloy/Packages.gz | grep Package | cut -d ' ' -f2 | sed ':a;N;$!ba;s/\n/ /g'"
|
117
|
+
|
118
|
+
run "cd #{debian_target}/debloy && " << Debloy::AptFtpArchive.create_packages_file('.')
|
119
|
+
run "cd #{debian_target}/debloy && " << Debloy::AptFtpArchive.create_release_file('.', Hash[RELEASE_FILE_OPTIONS.map {|k,v| ["APT::FTPArchive::Release::#{k}",v]}])
|
120
|
+
|
121
|
+
sudo Debloy::AptGet.update_cache(apt_get_options)
|
122
|
+
|
123
|
+
run "#{list_packages_cmd} | xargs #{sudo} #{Debloy::AptGet.install_packages([],apt_get_options)}" do |channel, stream, data|
|
124
|
+
log.collect(channel[:host], data)
|
125
|
+
end
|
126
|
+
else
|
127
|
+
raise "#{debian_package_manager} is an unsupported package manager. Only dpkg and apt are supported"
|
128
|
+
end
|
129
|
+
|
130
|
+
logger.debug "Package installation complete."
|
131
|
+
ensure
|
132
|
+
log.collected
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
desc "copies and installs debian packages to the server"
|
137
|
+
task :default do
|
138
|
+
copy_packages
|
139
|
+
install_packages
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Debloy
|
2
|
+
class AptFtpArchive
|
3
|
+
class << self
|
4
|
+
def command(arguments, options={})
|
5
|
+
"apt-ftparchive " << options.map{|k,v| "-o #{k}='#{v}'"}.join(' ') << " #{arguments}"
|
6
|
+
end
|
7
|
+
|
8
|
+
def create_packages_file(from, options={})
|
9
|
+
command("packages #{from} | gzip -9c > Packages.gz", options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_release_file(from, options={})
|
13
|
+
release_file_options = {
|
14
|
+
"Codename" => "debloy",
|
15
|
+
"Components" => "debloy",
|
16
|
+
"Origin" => "debloy",
|
17
|
+
"Label" => "Debloyed",
|
18
|
+
"Architectures" => "all",
|
19
|
+
"Suite" => "stable"
|
20
|
+
}
|
21
|
+
command("release #{from} > Release", options.merge(Hash[release_file_options.map {|k,v| ["APT::FTPArchive::Release::#{k}",v]}]))
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Debloy
|
2
|
+
class AptGet
|
3
|
+
class << self
|
4
|
+
def command(arguments, options={})
|
5
|
+
"apt-get -y --allow-unauthenticated " << options.map{|k,v| "-o #{k}='#{v}'"}.join(' ') << " #{arguments}"
|
6
|
+
end
|
7
|
+
|
8
|
+
def update_cache(options={})
|
9
|
+
command("update", options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def install_packages(packages, options={})
|
13
|
+
command("install #{packages.join(' ')}", options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Debloy
|
2
|
+
module Logger
|
3
|
+
class Batch
|
4
|
+
def initialize(logger)
|
5
|
+
@messages = {}
|
6
|
+
@logger = logger
|
7
|
+
end
|
8
|
+
|
9
|
+
def collect(host, data)
|
10
|
+
@messages[host] ||= ""
|
11
|
+
@messages[host] << data
|
12
|
+
end
|
13
|
+
|
14
|
+
def collected
|
15
|
+
@messages.keys.sort.each do |host|
|
16
|
+
@logger.info "Log for #{host}"
|
17
|
+
@logger.debug @messages[host], host
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/debloy/rsync.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
module Debloy
|
2
|
+
class Rsync
|
3
|
+
class << self
|
4
|
+
def command(from, to, options={})
|
5
|
+
flags = ['-az']
|
6
|
+
flags << '--delete'
|
7
|
+
flags << '--prune-empty-dirs'
|
8
|
+
flags << '--delete-excluded'
|
9
|
+
flags << includes(options[:filter])
|
10
|
+
flags << excludes(["*"])
|
11
|
+
flags << ssh_options(options[:ssh]) if options.has_key?(:ssh)
|
12
|
+
|
13
|
+
"rsync #{flags.compact.join(' ')} #{from} #{to}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def remote_address(user, host, path)
|
17
|
+
user_with_host = [user, host].compact.join('@')
|
18
|
+
[user_with_host, path].join(':')
|
19
|
+
end
|
20
|
+
|
21
|
+
def excludes(patterns)
|
22
|
+
patterns.map { |p| "--exclude=#{p}" }
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def includes(patterns)
|
27
|
+
patterns.map { |p| "--include=#{p}" }
|
28
|
+
end
|
29
|
+
|
30
|
+
def ssh_options(options)
|
31
|
+
mapped_options = options.map do |key, value|
|
32
|
+
next unless value
|
33
|
+
|
34
|
+
case key
|
35
|
+
when :keys then [value].flatten.select { |k| File.exist?(k) }.map { |k| "-i #{k}" }
|
36
|
+
when :config then "-F #{value}"
|
37
|
+
when :port then "-p #{value}"
|
38
|
+
end
|
39
|
+
end.compact
|
40
|
+
|
41
|
+
%[-e "ssh #{mapped_options.join(' ')}"] unless mapped_options.empty?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/debloy/scp.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Debloy
|
2
|
+
class Scp
|
3
|
+
class << self
|
4
|
+
def command(from, to, options={})
|
5
|
+
flags = ['-q']
|
6
|
+
flags << '-r' if options[:recurse]
|
7
|
+
flags << ssh_options(options[:ssh]) if options.has_key?(:ssh)
|
8
|
+
|
9
|
+
"scp #{flags.compact.join(' ')} #{from} #{to}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def remote_address(user, host, path)
|
13
|
+
user_with_host = [user, host].compact.join('@')
|
14
|
+
[user_with_host, path].join(':')
|
15
|
+
end
|
16
|
+
|
17
|
+
def ssh_options(options)
|
18
|
+
%[-o "#{options.join(' ')}"] unless options.empty?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Debloy
|
2
|
+
module LazyEnumerable
|
3
|
+
def each(&block)
|
4
|
+
threads = []
|
5
|
+
super do |item|
|
6
|
+
threads << Thread.new { yield item }
|
7
|
+
end
|
8
|
+
threads.each(&:join)
|
9
|
+
end
|
10
|
+
|
11
|
+
def map(&block)
|
12
|
+
super do |item|
|
13
|
+
Thread.new { Thread.current[:output] = block[item] }
|
14
|
+
end.map(&:join).map { |thread| thread[:output] }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: debloy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.3.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jeroen Rosenberg
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-06 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: See http://jeroenr.github.com/debloy/
|
15
|
+
email:
|
16
|
+
- jeroen.rosenberg@gmail.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- README.md
|
22
|
+
- Rakefile
|
23
|
+
- lib/debloy.rb
|
24
|
+
- lib/debloy/apt_get.rb
|
25
|
+
- lib/debloy/rsync.rb
|
26
|
+
- lib/debloy/apt_ftp_archive.rb
|
27
|
+
- lib/debloy/util/parallel_enumerable.rb
|
28
|
+
- lib/debloy/util/collection_utils.rb
|
29
|
+
- lib/debloy/scp.rb
|
30
|
+
- lib/debloy/logger/batch.rb
|
31
|
+
- lib/debloy/logger/stream.rb
|
32
|
+
homepage: http://github.com/jeroenr/debloy
|
33
|
+
licenses: []
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options: []
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
none: false
|
46
|
+
requirements:
|
47
|
+
- - ! '>='
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
requirements: []
|
51
|
+
rubyforge_project:
|
52
|
+
rubygems_version: 1.8.23
|
53
|
+
signing_key:
|
54
|
+
specification_version: 3
|
55
|
+
summary: Deploying debian packages with capistrano
|
56
|
+
test_files: []
|