hudson_deployer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Collin VanDyck
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest ADDED
@@ -0,0 +1,5 @@
1
+ LICENSE
2
+ Manifest
3
+ README.md
4
+ Rakefile
5
+ lib/hudson_deployer.rb
data/README.md ADDED
@@ -0,0 +1,100 @@
1
+ ## Overview
2
+
3
+ This gem was written to help simplify Java deployments using fat-jar hudson builds.
4
+
5
+ ## Example Capfile
6
+
7
+ require 'hudson_deployer'
8
+
9
+ set :hudson, "build.company.com"
10
+ set :application, "project"
11
+ set :build, "project-release"
12
+ set :version, "2.0.0"
13
+ set :deployer, "deploy"
14
+
15
+ config[:default] = {
16
+ :jdbc_url => "jdbc:mysql://localhost:3306/project"
17
+ }
18
+
19
+ config[:staging] = {
20
+ :user => "cvandyck",
21
+ :roles => {
22
+ :app => "192.168.185.132"
23
+ }
24
+ }
25
+
26
+ config[:production] = {
27
+ :user => "admin",
28
+ :roles => {
29
+ :app => "production-001"
30
+ }
31
+ }
32
+
33
+ before :deploy do
34
+ if @env == :production
35
+ run "#{sudo} apt-get install sun-java6-jdk dbus hal -y"
36
+ else
37
+ run "#{sudo} apt-get install openjdk-6-jdk dbus hal -y"
38
+ end
39
+ create_user deployer, :homedir => "/opt/project", :shell => "/bin/sh"
40
+ create_directory "/var/log/project"
41
+ touch "/var/log/project/sysout.log"
42
+ end
43
+
44
+ after :deploy do
45
+ render_template "/etc/project.conf", :template => "project.conf.erb", :mode => "0600"
46
+ render_template "/etc/project.jvm.conf", :template => "#{@env}/project.jvm.conf.erb", :mode => "0644"
47
+ upstart "/etc/init/project.conf", :template => "upstart.conf.erb"
48
+ run "#{sudo} stop project || true"
49
+ sleep 5
50
+ run "#{sudo} start project"
51
+ end
52
+
53
+ ## Templates
54
+
55
+ Templates are kept in a folder called "templates". They can be named anything, but to make your life easier suffix them with .erb. Render templates with
56
+
57
+ render_template(/etc/broccoli, :template => "broccoli.erb")
58
+
59
+ Note that the :template parameter assumes a path relative to the templates folder.
60
+
61
+ Templates are rendered using the same scope binding as the gem itself. They therefore can access configuration (described below).
62
+
63
+ ## Upstart
64
+
65
+ Servers are bounced using upstart. In the above example, we use upstart to render an upstart configuration file for our project. Actual upstart commands are done using the Capistrano run command
66
+
67
+ ## Staging vs Production
68
+
69
+ It is assumed that unless otherwise specified, it will be run in staging mode. To use production,
70
+
71
+ cap production deploy
72
+
73
+ ## Configuration
74
+
75
+ Configuration can be set on a per-environment basis or for all environments. Set configuration variables using the config hash:
76
+
77
+ config[:default] = {
78
+ :jdbc_url => "jdbc:mysql://localhost:3306/project"
79
+ }
80
+
81
+ Set per-environment configuration:
82
+
83
+ config[:production] = {
84
+ :user => "admin",
85
+ :roles => {
86
+ :app => "production-001"
87
+ }
88
+ }
89
+
90
+ The :user and :roles keys are special and change the way that Capistrano works, as expected. You may also set any other key/value pair you wish. The combined configuration is available through the config_h parameter:
91
+
92
+ "database": {
93
+ "jdbc_url": "<%= config_h[:jdbc_url] %>",
94
+ "driver_class": "com.vertica.Driver"
95
+ }
96
+
97
+ ## Other Stuff
98
+
99
+ &copy; Collin VanDyck 2011. Distributed under the MIT license.
100
+
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('hudson_deployer', '0.0.1') do |p|
6
+ p.description = "Unmagical Capistrano deployment using Hudson"
7
+ p.url = "https://github.com/collinvandyck/hudson_deployer"
8
+ p.author = "Collin VanDyck"
9
+ p.email = "collinvandyck @nospam@ gmail.com"
10
+ p.ignore_pattern = ["tmp/*", "script/*"]
11
+ p.development_dependencies = []
12
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{hudson_deployer}
5
+ s.version = "0.0.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Collin VanDyck"]
9
+ s.date = %q{2011-03-16}
10
+ s.description = %q{Unmagical Capistrano deployment using Hudson}
11
+ s.email = %q{collinvandyck @nospam@ gmail.com}
12
+ s.extra_rdoc_files = ["LICENSE", "README.md", "lib/hudson_deployer.rb"]
13
+ s.files = ["LICENSE", "Manifest", "README.md", "Rakefile", "lib/hudson_deployer.rb", "hudson_deployer.gemspec"]
14
+ s.homepage = %q{https://github.com/collinvandyck/hudson_deployer}
15
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Hudson_deployer", "--main", "README.md"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = %q{hudson_deployer}
18
+ s.rubygems_version = %q{1.5.2}
19
+ s.summary = %q{Unmagical Capistrano deployment using Hudson}
20
+
21
+ if s.respond_to? :specification_version then
22
+ s.specification_version = 3
23
+
24
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
25
+ else
26
+ end
27
+ else
28
+ end
29
+ end
@@ -0,0 +1,289 @@
1
+ require 'capistrano'
2
+ require 'rest_client'
3
+ require 'json'
4
+ require 'erb'
5
+
6
+ class Deploy
7
+ attr_accessor :application,
8
+ :version,
9
+ :user,
10
+ :build_num,
11
+ :artifact_url
12
+
13
+ def initialize(&block)
14
+ if block_given?
15
+ yield self
16
+ end
17
+ end
18
+
19
+ def debug
20
+ puts "Deployment Configuration:"
21
+ [:application, :version, :user, :build_num].each do |f|
22
+ puts " #{f}: #{send(f)}"
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ Capistrano::Configuration.instance(:must_exist).load do
29
+
30
+ def _cset(name, *args, &block)
31
+ unless exists?(name)
32
+ set(name, *args, &block)
33
+ end
34
+ end
35
+
36
+ def hud_api(url)
37
+ JSON.parse(RestClient.get(url + "/api/json"))
38
+ end
39
+
40
+ # environment configuration
41
+ ######################################################################
42
+
43
+ # by default we are in the staging environment
44
+ @env = :staging
45
+
46
+ task :staging do
47
+ @env = :staging
48
+ end
49
+
50
+ task :production do
51
+ @env = :production
52
+ end
53
+
54
+ set(:config) do
55
+ {
56
+ :default => {},
57
+ :staging => {},
58
+ :production => {}
59
+ }
60
+ end
61
+
62
+ def config_h
63
+ config[:default].merge(config[@env])
64
+ end
65
+
66
+ # required variables
67
+ ######################################################################
68
+
69
+ _cset(:hudson) { abort ":hudson must be set (e.g. build.yammer.com)" }
70
+ _cset(:application) { abort ":application must be set" }
71
+ _cset(:build) { abort ":build must be set (e.g. app-release)" }
72
+ _cset(:version) { abort ":version must be set (e.g. 2.0.0)" }
73
+ _cset(:user) { abort ":user must be set" }
74
+ _cset(:launch_command) { abort ":launch_command must be set" }
75
+
76
+ # derived variables
77
+ ######################################################################
78
+
79
+ set(:directory) { "/opt/#{application}" }
80
+
81
+ set(:current_release) { "#{directory}/releases/#{Time.now.to_i}" }
82
+
83
+ set(:job) do
84
+ record = hud_api(hudson)["jobs"].find { |j| j["name"] == build }
85
+ abort("No such hudson build could be found") if !record
86
+ hud_api(record["url"])
87
+ end
88
+
89
+ set(:current_build) do
90
+ builds = job["builds"]
91
+ abort("There are no existing builds for this project") if builds.empty?
92
+ url = builds.first["url"]
93
+ hud_api(url).tap do |b|
94
+ if b["result"] != "SUCCESS"
95
+ abort("The last build was not successful: #{b["result"]}")
96
+ end
97
+ if b["building"] == true
98
+ abort("Project is currently building. You must wait until it finishes.")
99
+ end
100
+ end
101
+ end
102
+
103
+ set(:artifact_url) do
104
+ artifacts = current_build["artifacts"]
105
+ if artifacts.empty?
106
+ abort("No artifacts exist for this project!")
107
+ end
108
+ artifact = if artifacts.length > 1
109
+ puts "More than one artifact was found. Please choose:"
110
+ artifacts.each_with_index do |artifact, index|
111
+ puts "#{index}: #{artifact["relativePath"]}"
112
+ end
113
+ print "? "
114
+ artifact_index = Capistrano::CLI.ui.ask("choice: ").to_i
115
+ artifacts[artifact_index]
116
+ else
117
+ artifacts.first
118
+ end
119
+ current_build["url"] + "artifact/" + artifact["relativePath"]
120
+ end
121
+
122
+ set(:artifact_filename) do
123
+ artifact_url.split("/").last
124
+ end
125
+
126
+ set(:tmpdir) do
127
+ "/tmp/deployer-#{Time.now.to_i}".tap do |tmp|
128
+ FileUtils.mkdir_p(tmp)
129
+ end
130
+ end
131
+
132
+ set(:local_entries) do
133
+ Dir.entries(File.expand_path(File.dirname(__FILE__) + "/" + application)).reject do |name|
134
+ name =~ /^\./ || name == "Capfile"
135
+ end
136
+ end
137
+
138
+ set(:template_names) do
139
+ Dir.entries(File.expand_path(File.dirname(__FILE__) + "/" + application + "/templates")).reject do |name|
140
+ name =~ /^\./
141
+ end
142
+ end
143
+
144
+ def build_deployment
145
+ Deploy.new do |d|
146
+ d.application = application
147
+ d.version = version
148
+ d.user = user
149
+ d.build_num = current_build["number"]
150
+ d.artifact_url = artifact_url
151
+ end
152
+ end
153
+
154
+ def make_release_directory
155
+ run "#{sudo} mkdir -p #{current_release}", :roles => "app"
156
+ run "#{sudo} chown -R #{deployer} #{current_release}", :roles => "app"
157
+ end
158
+
159
+ def create_local_build
160
+ from = File.expand_path(File.dirname(__FILE__) + "/" + application)
161
+ local_entries.each { |f| FileUtils.cp_r f, tmpdir, :verbose => true }
162
+ end
163
+
164
+ def verify_plan
165
+ puts "*" * 80
166
+ puts "Deployment plan:"
167
+ @deploy.debug
168
+ puts "Copying assets:"
169
+ puts `find #{tmpdir}`
170
+ puts "*" * 80
171
+ unless Capistrano::CLI.ui.ask("Is this what you want?") =~ /^y.*/i
172
+ exit
173
+ end
174
+ end
175
+
176
+ def download_artifact
177
+ puts "Downloading artifact..."
178
+ `wget #{@deploy.artifact_url} -O #{tmpdir}/#{artifact_filename}`
179
+ end
180
+
181
+ def execute_actions
182
+ @rendered_actions.each do |action|
183
+ run "cd #{current_release} && sh #{action}"
184
+ end
185
+ end
186
+
187
+ def transfer_build
188
+ puts "Transferring build..."
189
+ Dir.entries(tmpdir).each do |e|
190
+ unless e =~ /^\./
191
+ tmpfile = "/tmp/#{Time.now.to_i}"
192
+ upload "#{tmpdir}/#{e}", tmpfile
193
+ run "#{sudo} mv #{tmpfile} #{current_release}/#{e}"
194
+ run "#{sudo} chown -R #{deployer}:#{deployer} #{current_release}/#{e}"
195
+ end
196
+ end
197
+ end
198
+
199
+ def symlink
200
+ link = "#{current_release}/../current"
201
+ run "#{sudo} rm #{link} || true"
202
+ run "#{sudo} ln -sF #{current_release} #{link}"
203
+ end
204
+
205
+ def cleanup
206
+ FileUtils.rm_r tmpdir
207
+ end
208
+
209
+ def create_user(username, opts={})
210
+ create_directory(directory, :owner => deployer, :group => deployer) do
211
+ run "if ! id #{username} > /dev/null 2>&1; then #{sudo} useradd --no-create-home --home-dir #{opts[:homedir]} --shell #{opts[:shell]} #{username}; fi", :roles => "app"
212
+ end
213
+ end
214
+
215
+ def create_directory(path, opts={})
216
+ opts = { :owner => deployer, :group => deployer, :mode => "0755" }.merge(opts)
217
+ run "#{sudo} mkdir -p #{path}", :roles => "app"
218
+ yield if block_given?
219
+ run "#{sudo} chmod #{opts[:mode]} #{path}"
220
+ run "#{sudo} chown -R #{opts[:owner]}:#{opts[:group]} #{path}", :roles => "app"
221
+ end
222
+
223
+ def touch(path, opts={})
224
+ opts = { :owner => deployer, :group => deployer, :mode => "0755" }.merge(opts)
225
+ run "#{sudo} touch #{path}"
226
+ run "#{sudo} chown #{opts[:owner]}:#{opts[:group]} #{path}", :roles => "app"
227
+ end
228
+
229
+ def render_template(path, opts={})
230
+ opts = { :owner => deployer, :group => deployer, :mode => "0644" }.merge(opts)
231
+ data = erb(File.read("templates/#{opts[:template]}"))
232
+ filename = "/tmp/file-#{Time.now.to_i}"
233
+ put data, filename
234
+ run "#{sudo} mv #{filename} #{path}"
235
+ run "#{sudo} chmod #{opts[:mode]} #{path}"
236
+ run "#{sudo} chown -R #{opts[:owner]}:#{opts[:group]} #{path}", :roles => "app"
237
+ end
238
+
239
+ def upstart(path, opts={})
240
+ render_template path, :template => opts[:template], :owner => "root", :group => "root"
241
+ end
242
+
243
+ def erb(text)
244
+ ERB.new(text).result(binding)
245
+ end
246
+
247
+ def render_scripts
248
+ template_names.each do |script_name|
249
+ filename = tmpdir + "/scripts/" + script_name
250
+ template = ERB.new(File.read(filename))
251
+ rendered = template.result(binding)
252
+ File.open(filename, "w") { |f| f.puts(rendered) }
253
+ end
254
+ end
255
+
256
+ task :init do
257
+ puts "Setting user and roles from config"
258
+ if u = config_h[:user]
259
+ puts "Setting user to #{u}"
260
+ set :user, u
261
+ end
262
+ if roles = config_h[:roles]
263
+ roles.each do |h,v|
264
+ puts "Setting role #{h} => #{v}"
265
+ role h, v
266
+ end
267
+ end
268
+ end
269
+
270
+ before :deploy, [:init]
271
+
272
+ task :deploy do
273
+ @deploy = build_deployment
274
+ create_local_build
275
+ download_artifact
276
+ verify_plan
277
+ make_release_directory
278
+ transfer_build
279
+ symlink
280
+ end
281
+
282
+ end
283
+
284
+
285
+
286
+
287
+
288
+
289
+
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hudson_deployer
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Collin VanDyck
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-03-16 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Unmagical Capistrano deployment using Hudson
23
+ email: collinvandyck @nospam@ gmail.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - LICENSE
30
+ - README.md
31
+ - lib/hudson_deployer.rb
32
+ files:
33
+ - LICENSE
34
+ - Manifest
35
+ - README.md
36
+ - Rakefile
37
+ - lib/hudson_deployer.rb
38
+ - hudson_deployer.gemspec
39
+ has_rdoc: true
40
+ homepage: https://github.com/collinvandyck/hudson_deployer
41
+ licenses: []
42
+
43
+ post_install_message:
44
+ rdoc_options:
45
+ - --line-numbers
46
+ - --inline-source
47
+ - --title
48
+ - Hudson_deployer
49
+ - --main
50
+ - README.md
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ hash: 11
68
+ segments:
69
+ - 1
70
+ - 2
71
+ version: "1.2"
72
+ requirements: []
73
+
74
+ rubyforge_project: hudson_deployer
75
+ rubygems_version: 1.5.2
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Unmagical Capistrano deployment using Hudson
79
+ test_files: []
80
+