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.
@@ -0,0 +1,56 @@
1
+ [![endorse](http://api.coderwall.com/jeroenr/endorsecount.png)](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.
@@ -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
@@ -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
@@ -0,0 +1,16 @@
1
+ module Debloy
2
+ module Logger
3
+ class Stream
4
+ def initialize(logger)
5
+ @logger = logger
6
+ end
7
+
8
+ def collect(host, data)
9
+ @logger.debug data, host
10
+ end
11
+
12
+ def collected
13
+ end
14
+ end
15
+ end
16
+ end
@@ -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
@@ -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
@@ -0,0 +1,8 @@
1
+ require 'debloy/util/collection_utils'
2
+
3
+ module Enumerable
4
+ def async
5
+ self.clone.extend Debloy::LazyEnumerable
6
+ end
7
+ end
8
+
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: []