cloud-utils 1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +1 -0
- data/LICENSE +15 -0
- data/Manifest +14 -0
- data/README.markdown +53 -0
- data/Rakefile +9 -0
- data/bin/deploy +50 -0
- data/bin/monitor +76 -0
- data/cloud-utils.gemspec +34 -0
- data/cloud-utils.tmproj +48 -0
- data/etc/deploy.yml +12 -0
- data/etc/monitor.yml +10 -0
- data/lib/cloud/deploy.rb +63 -0
- data/lib/cloud/deploy/artifact.rb +124 -0
- data/lib/cloud/deploy/monitor.rb +57 -0
- data/lib/cloud/logger.rb +18 -0
- metadata +110 -0
data/CHANGELOG
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
v1.0. Initial release.
|
data/LICENSE
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
13
|
+
# or implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#
|
data/Manifest
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
Ruby Cloud Artifact Deployer
|
2
|
+
=====
|
3
|
+
|
4
|
+
The purpose of this utility is to aid the developer in deploying artifacts into a hybrid cloud environment. If you have several instances of an application server running on different virtual machines, then you can use these utilities to facilitate that deployment. It respects ETags and won't download a resource it's already deployed before (unless you give it the "-f" options, which forces a deployment). It also respects MD5 sums and won't redeploy a resource it's downloaded if it has already deployed it before.
|
5
|
+
|
6
|
+
Config Files
|
7
|
+
-----
|
8
|
+
|
9
|
+
In order to be a little more secure, this system doesn't deploy arbitrary artifacts. You have to explicitly configure what you want downloaded and where you want it to go. There are two main configuration files: the "monitor.yml" file, which controls how the monitor watches for deployment notifications, and the "deploy.yml" file, which configures the deployer. They are both YAML files. An example config for the deployment monitor would be:
|
10
|
+
|
11
|
+
<pre><code>
|
12
|
+
default:
|
13
|
+
host: localhost
|
14
|
+
port: 5672
|
15
|
+
user: guest
|
16
|
+
password: guest
|
17
|
+
virtual_host: /
|
18
|
+
exchange: vcloud.deployment.events
|
19
|
+
|
20
|
+
myapp.war:
|
21
|
+
:deploy: deploy -e %s
|
22
|
+
</code></pre>
|
23
|
+
|
24
|
+
The "default" section handles the RabbitMQ connection info, as well as what exchange the artifacts will be publishing deployment notifications to (more on that when I get that part uploaded). Every other section is considered to be a deployment artifact. The name should correspond to the queue name being used to publish the notifications. In this example, your continuous integration server would publish a message with the MD5 hash of the file being deployed as the "correlation_id" and the artifact name (as configured in deploy.yml) as the body of the message. The "%s" in the deploy line is where the artifact name that comes from the AMQP message. That's optional. You could specify the artifact name (as configured in deploy.yml).
|
25
|
+
|
26
|
+
The deploy.yml file configures the deployer:
|
27
|
+
|
28
|
+
<pre><code>
|
29
|
+
myapp.war:
|
30
|
+
source: http://development/artifacts/myapp.war
|
31
|
+
destination:
|
32
|
+
- /opt/tcServer/tcServer-6.0/tc1/webapps
|
33
|
+
- /opt/tcServer/tcServer-6.0/tc2/webapps
|
34
|
+
- /opt/tcServer/tcServer-6.0/tc3/webapps
|
35
|
+
|
36
|
+
myapp.html:
|
37
|
+
unzip: true
|
38
|
+
source: http://development/artifacts/myapp-html.tar.gz
|
39
|
+
destination:
|
40
|
+
- /var/www/www.mycloud.mycompany.com/public
|
41
|
+
</code></pre>
|
42
|
+
|
43
|
+
Invoking the Deployment Mechanism
|
44
|
+
-----
|
45
|
+
|
46
|
+
To deploy a file (maybe, depending on MD5 sums and ETags), you first run the "monitor" script, which checks whether or not anything needs to be deployed. It will then execute whatever is defined in the YAML file's ":deploy" section. In this case, we're calling the other Ruby script in this package: "deploy", passing it the "-e" switch, which tells it to respect ETags and not download anything if there has been no change since the last deployment, and using the "%s" replacement character, where the artifact name will be passed when the message is processed.
|
47
|
+
|
48
|
+
When the deploy script is called by the monitor script, it downloads the artifact from the URL you configured in deploy.yml and either copies it directly to the list of destinations you give it, or unzips the file (if the artifact ends in ".tar.gz", it will be untarred, otherwise it will be unzipped) if you have configured "unzip: true".
|
49
|
+
|
50
|
+
License
|
51
|
+
-----
|
52
|
+
|
53
|
+
Apache 2.0 licensed. Just like the other cloud utilities.
|
data/Rakefile
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'echoe'
|
2
|
+
|
3
|
+
Echoe.new('cloud-utils') do |pkg|
|
4
|
+
pkg.author = 'J. Brisbin'
|
5
|
+
pkg.email = 'jon@jbrisbin.com'
|
6
|
+
pkg.summary = 'Deploy artifacts in a cloud environment.'
|
7
|
+
pkg.url = 'http://github.com/jbrisbin/cloud-utils-deployer'
|
8
|
+
pkg.runtime_dependencies = ['amqp >=0.6.7']
|
9
|
+
end
|
data/bin/deploy
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$:.unshift File.expand_path("../../lib", __FILE__)
|
3
|
+
# puts "#{$:}"
|
4
|
+
|
5
|
+
require "optparse"
|
6
|
+
require "yaml"
|
7
|
+
require "cloud/logger"
|
8
|
+
require "cloud/deploy/artifact"
|
9
|
+
|
10
|
+
# Set command-line options
|
11
|
+
options = {}
|
12
|
+
optparse = OptionParser.new do |opts|
|
13
|
+
opts.banner = "Usage: deploy [options] artifact1 [artifact2] [...]"
|
14
|
+
|
15
|
+
options[:config] = '/etc/cloud/deploy.yml'
|
16
|
+
opts.on('-c', '--config CONFIG', 'Config file to read') do |config|
|
17
|
+
options[:config] = config
|
18
|
+
end
|
19
|
+
|
20
|
+
options[:etags] = false
|
21
|
+
opts.on('-e', '--etags', 'Respect ETag headers when downloading resources.') do
|
22
|
+
options[:etags] = true
|
23
|
+
end
|
24
|
+
|
25
|
+
options[:force] = false
|
26
|
+
opts.on('-f', '--force', 'Download and deploy this file irregardless of ETags and MD5 sums.') do
|
27
|
+
options[:force] = true
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on('-h', '--help', 'Display this help') do
|
31
|
+
puts opts
|
32
|
+
exit
|
33
|
+
end
|
34
|
+
end
|
35
|
+
optparse.parse!
|
36
|
+
|
37
|
+
config = YAML.load_file(options[:config])
|
38
|
+
|
39
|
+
ARGV.each do |arg|
|
40
|
+
artifact = Cloud::Deploy::Artifact.new
|
41
|
+
artifact.name = arg
|
42
|
+
artifact.uri = config[arg]['source']
|
43
|
+
artifact.local_paths = config[arg]['destination']
|
44
|
+
artifact.unzip = config[arg]['unzip']
|
45
|
+
artifact.force = options[:force]
|
46
|
+
artifact.use_etags = options[:etags]
|
47
|
+
if artifact.outdated?
|
48
|
+
artifact.deploy!
|
49
|
+
end
|
50
|
+
end
|
data/bin/monitor
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$:.unshift File.expand_path("../../lib", __FILE__)
|
3
|
+
|
4
|
+
require "optparse"
|
5
|
+
require "yaml"
|
6
|
+
require "cloud/logger"
|
7
|
+
require "cloud/deploy"
|
8
|
+
require "cloud/deploy/artifact"
|
9
|
+
require "cloud/deploy/monitor"
|
10
|
+
require "rubygems"
|
11
|
+
require "mq"
|
12
|
+
|
13
|
+
# Set command-line options
|
14
|
+
options = {}
|
15
|
+
optparse = OptionParser.new do |opts|
|
16
|
+
opts.banner = "Usage: monitor [options] [artifact1] [artifact...]"
|
17
|
+
|
18
|
+
options[:config] = '/etc/cloud/monitor.yml'
|
19
|
+
opts.on('-c', '--config CONFIG', 'Config file to read') do |config|
|
20
|
+
options[:config] = config
|
21
|
+
end
|
22
|
+
|
23
|
+
options[:force] = false
|
24
|
+
opts.on('-f', '--force', 'Disregard MD5 sums and force deployment') do
|
25
|
+
options[:force] = true
|
26
|
+
end
|
27
|
+
|
28
|
+
options[:timeout] = 5
|
29
|
+
opts.on('-t', '--timeout TIMEOUT', 'How many seconds to wait for subscriber threads to capture all messages') do |timeout|
|
30
|
+
options[:timeout] = timeout
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on('-h', '--help', 'Display this help') do
|
34
|
+
puts opts
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
end
|
38
|
+
optparse.parse!
|
39
|
+
|
40
|
+
config = YAML.load_file(options[:config])
|
41
|
+
em_thread = Thread.new { EM.run }
|
42
|
+
|
43
|
+
# Use the name coming in on the command line or all of the configured names
|
44
|
+
names = Hash.new
|
45
|
+
if ARGV.size > 0
|
46
|
+
$log.info('deployer') { "Was passed artifacts to deploy, so using those: #{ARGV.join(', ')}" }
|
47
|
+
names.replace(ARGV)
|
48
|
+
else
|
49
|
+
$log.info('deployer') { "Deploying all configured artifacts" }
|
50
|
+
names.replace(config)
|
51
|
+
names.delete_if { |k, v| k == "default" }
|
52
|
+
end
|
53
|
+
|
54
|
+
monitors = []
|
55
|
+
names.each do |k, v|
|
56
|
+
mon = Cloud::Deploy::Monitor.new(config, k)
|
57
|
+
mon.deploy!
|
58
|
+
monitors << mon
|
59
|
+
end
|
60
|
+
em_thread.join(options[:timeout])
|
61
|
+
|
62
|
+
# Finish processing
|
63
|
+
Cloud::Deploy.load_md5sums do |md5s|
|
64
|
+
monitors.each do |mon|
|
65
|
+
if !mon.artifact.nil?
|
66
|
+
deploy_cmd = config[mon.name][:deploy] % mon.artifact
|
67
|
+
already_seen = md5s[mon.md5sum].nil?
|
68
|
+
if !options[:force] and already_seen
|
69
|
+
$log.info(mon.name) { "MD5 sums match, so not deploying again" }
|
70
|
+
else
|
71
|
+
$log.info(mon.name) { "Deploying: #{mon.artifact} with hash: #{mon.md5sum} using: #{deploy_cmd}" }
|
72
|
+
`#{deploy_cmd}`
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/cloud-utils.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{cloud-utils}
|
5
|
+
s.version = "1.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["J. Brisbin"]
|
9
|
+
s.date = %q{2010-06-14}
|
10
|
+
s.description = %q{Deploy artifacts in a cloud environment.}
|
11
|
+
s.email = %q{jon@jbrisbin.com}
|
12
|
+
s.executables = ["deploy", "monitor"]
|
13
|
+
s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README.markdown", "bin/deploy", "bin/monitor", "lib/cloud/deploy.rb", "lib/cloud/deploy/artifact.rb", "lib/cloud/deploy/monitor.rb", "lib/cloud/logger.rb"]
|
14
|
+
s.files = ["CHANGELOG", "LICENSE", "README.markdown", "Rakefile", "bin/deploy", "bin/monitor", "cloud-utils.tmproj", "etc/deploy.yml", "etc/monitor.yml", "lib/cloud/deploy.rb", "lib/cloud/deploy/artifact.rb", "lib/cloud/deploy/monitor.rb", "lib/cloud/logger.rb", "Manifest", "cloud-utils.gemspec"]
|
15
|
+
s.homepage = %q{http://github.com/jbrisbin/cloud-utils-deployer}
|
16
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Cloud-utils", "--main", "README.markdown"]
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.rubyforge_project = %q{cloud-utils}
|
19
|
+
s.rubygems_version = %q{1.3.7}
|
20
|
+
s.summary = %q{Deploy artifacts in a cloud environment.}
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 3
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
27
|
+
s.add_runtime_dependency(%q<amqp>, [">= 0.6.7"])
|
28
|
+
else
|
29
|
+
s.add_dependency(%q<amqp>, [">= 0.6.7"])
|
30
|
+
end
|
31
|
+
else
|
32
|
+
s.add_dependency(%q<amqp>, [">= 0.6.7"])
|
33
|
+
end
|
34
|
+
end
|
data/cloud-utils.tmproj
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
|
+
<plist version="1.0">
|
4
|
+
<dict>
|
5
|
+
<key>currentDocument</key>
|
6
|
+
<string>lib/cloud/deploy/artifact.rb</string>
|
7
|
+
<key>documents</key>
|
8
|
+
<array>
|
9
|
+
<dict>
|
10
|
+
<key>expanded</key>
|
11
|
+
<true/>
|
12
|
+
<key>name</key>
|
13
|
+
<string>cloud-utils</string>
|
14
|
+
<key>regexFolderFilter</key>
|
15
|
+
<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
|
16
|
+
<key>sourceDirectory</key>
|
17
|
+
<string></string>
|
18
|
+
</dict>
|
19
|
+
</array>
|
20
|
+
<key>fileHierarchyDrawerWidth</key>
|
21
|
+
<integer>260</integer>
|
22
|
+
<key>metaData</key>
|
23
|
+
<dict>
|
24
|
+
<key>lib/cloud/deploy/artifact.rb</key>
|
25
|
+
<dict>
|
26
|
+
<key>caret</key>
|
27
|
+
<dict>
|
28
|
+
<key>column</key>
|
29
|
+
<integer>61</integer>
|
30
|
+
<key>line</key>
|
31
|
+
<integer>125</integer>
|
32
|
+
</dict>
|
33
|
+
<key>firstVisibleColumn</key>
|
34
|
+
<integer>0</integer>
|
35
|
+
<key>firstVisibleLine</key>
|
36
|
+
<integer>0</integer>
|
37
|
+
</dict>
|
38
|
+
</dict>
|
39
|
+
<key>openDocuments</key>
|
40
|
+
<array>
|
41
|
+
<string>lib/cloud/deploy/artifact.rb</string>
|
42
|
+
</array>
|
43
|
+
<key>showFileHierarchyDrawer</key>
|
44
|
+
<true/>
|
45
|
+
<key>windowFrame</key>
|
46
|
+
<string>{{270, 4}, {1410, 1024}}</string>
|
47
|
+
</dict>
|
48
|
+
</plist>
|
data/etc/deploy.yml
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
myapp.war:
|
2
|
+
source: http://development/artifacts/myapp.war
|
3
|
+
destination:
|
4
|
+
- /opt/tcServer/tcServer-6.0/tc1/webapps
|
5
|
+
- /opt/tcServer/tcServer-6.0/tc2/webapps
|
6
|
+
- /opt/tcServer/tcServer-6.0/tc3/webapps
|
7
|
+
|
8
|
+
myapp.html:
|
9
|
+
unzip: true
|
10
|
+
source: http://development/artifacts/myapp-html.tar.gz
|
11
|
+
destination:
|
12
|
+
- /var/www/www.mycloud.mycompany.com/public
|
data/etc/monitor.yml
ADDED
data/lib/cloud/deploy.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
13
|
+
# or implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#
|
16
|
+
require "cloud/logger"
|
17
|
+
|
18
|
+
module Cloud
|
19
|
+
module Deploy
|
20
|
+
|
21
|
+
ETAG_FILE = '/var/lib/cloud/deploy.etags'
|
22
|
+
MD5SUM_FILE = '/var/lib/cloud/deploy.md5sums'
|
23
|
+
|
24
|
+
def load_etags
|
25
|
+
etags = {}
|
26
|
+
if @use_etags
|
27
|
+
begin
|
28
|
+
File.open(ETAG_FILE, 'r') do |f|
|
29
|
+
etags = Marshal.load(f)
|
30
|
+
end
|
31
|
+
rescue => err
|
32
|
+
$log.fatal('etags') { err }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
yield etags
|
36
|
+
end
|
37
|
+
|
38
|
+
def save_etags(etags)
|
39
|
+
File.open(ETAG_FILE, 'w') do |f|
|
40
|
+
f.write(Marshal.dump(etags))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def load_md5sums
|
45
|
+
md5sums = {}
|
46
|
+
begin
|
47
|
+
File.open(MD5SUM_FILE, 'r') do |f|
|
48
|
+
md5sums = Marshal.load(f)
|
49
|
+
end
|
50
|
+
rescue => err
|
51
|
+
$log.fatal('md5sums') { err }
|
52
|
+
end
|
53
|
+
yield md5sums
|
54
|
+
end
|
55
|
+
|
56
|
+
def save_md5sums(md5sums)
|
57
|
+
File.open(MD5SUM_FILE, 'w') do |f|
|
58
|
+
f.write(Marshal.dump(md5sums))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
13
|
+
# or implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#
|
16
|
+
require "cloud/logger"
|
17
|
+
require "cloud/deploy"
|
18
|
+
require "net/http"
|
19
|
+
require "uri"
|
20
|
+
require "fileutils"
|
21
|
+
include Cloud::Deploy
|
22
|
+
|
23
|
+
module Cloud
|
24
|
+
module Deploy
|
25
|
+
|
26
|
+
class Artifact
|
27
|
+
|
28
|
+
attr_reader :uri
|
29
|
+
attr_accessor :name, :local_paths, :unzip, :force, :use_etags
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
@use_etags = true
|
33
|
+
end
|
34
|
+
|
35
|
+
def uri=(remote_uri)
|
36
|
+
@uri = URI.parse(remote_uri)
|
37
|
+
@http = Net::HTTP.new(@uri.host, @uri.port)
|
38
|
+
end
|
39
|
+
|
40
|
+
def outdated?
|
41
|
+
download
|
42
|
+
end
|
43
|
+
|
44
|
+
def deploy!
|
45
|
+
load_md5sums do |md5sums|
|
46
|
+
hash = md5sums[@name]
|
47
|
+
if !@force and !hash.nil? and hash == @hash
|
48
|
+
$log.info(@name) { "MD5 sums match. No need to deploy." }
|
49
|
+
else
|
50
|
+
@local_paths.each do |path|
|
51
|
+
FileUtils.mkdir_p(path)
|
52
|
+
if @unzip
|
53
|
+
$log.info(@name) { "Unzipping #{@name} to #{path}" }
|
54
|
+
if @uri.request_uri[-6..-1] == "tar.gz"
|
55
|
+
`tar -zxf #{@temp_file} -C #{path}`
|
56
|
+
else
|
57
|
+
`unzip -d #{path} #{@temp_file}`
|
58
|
+
end
|
59
|
+
else
|
60
|
+
$log.info(@name) { "Copying #{@name} to #{path}" }
|
61
|
+
FileUtils.cp(@temp_file, path)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
md5sums[@name] = @hash
|
65
|
+
save_md5sums(md5sums)
|
66
|
+
end
|
67
|
+
# Clean up temp file
|
68
|
+
if !@temp_file.nil?
|
69
|
+
FileUtils.rm(@temp_file)
|
70
|
+
end
|
71
|
+
# We did a deployment
|
72
|
+
return true
|
73
|
+
end
|
74
|
+
# Default return
|
75
|
+
return false
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def download
|
80
|
+
outdated = false
|
81
|
+
|
82
|
+
request = Net::HTTP::Get.new(@uri.request_uri)
|
83
|
+
load_etags do |etags|
|
84
|
+
etag = etags[@name]
|
85
|
+
if !@force and !etag.nil?
|
86
|
+
request.initialize_http_header({
|
87
|
+
'If-None-Match' => etag
|
88
|
+
})
|
89
|
+
end
|
90
|
+
|
91
|
+
response = @http.request(request)
|
92
|
+
case response
|
93
|
+
when Net::HTTPSuccess
|
94
|
+
# Continue to download file...
|
95
|
+
$log.info(@name) { "Downloading: #{@uri.to_s}..." }
|
96
|
+
bytes = response.body
|
97
|
+
require "md5"
|
98
|
+
@hash = MD5.new.update(bytes).hexdigest
|
99
|
+
# Write to temp file, ready to deploy
|
100
|
+
@temp_file = "/tmp/#{@name}"
|
101
|
+
File.open(@temp_file, "w") { |f| f.write(bytes) }
|
102
|
+
# Update ETags
|
103
|
+
etags[@name] = response['etag']
|
104
|
+
|
105
|
+
outdated = true
|
106
|
+
when Net::HTTPNotModified
|
107
|
+
# No need to download it again
|
108
|
+
$log.info(@name) { "ETag matched, not downloading: #{@uri.to_s}" }
|
109
|
+
else
|
110
|
+
$log.fatal(@name) { "Error HTTP status code received: #{response['code']}" }
|
111
|
+
end
|
112
|
+
|
113
|
+
if @use_etags
|
114
|
+
save_etags(etags)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
return outdated
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
13
|
+
# or implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#
|
16
|
+
require "logger"
|
17
|
+
require "rubygems"
|
18
|
+
require "mq"
|
19
|
+
require "cloud/logger"
|
20
|
+
require "cloud/deploy"
|
21
|
+
|
22
|
+
module Cloud
|
23
|
+
module Deploy
|
24
|
+
class Monitor
|
25
|
+
|
26
|
+
attr_reader :md5sum, :artifact
|
27
|
+
attr_accessor :config, :name
|
28
|
+
|
29
|
+
def initialize(config, name)
|
30
|
+
@config = config
|
31
|
+
@name = name
|
32
|
+
@exchange = @config['default']['exchange']
|
33
|
+
end
|
34
|
+
|
35
|
+
def deploy!
|
36
|
+
defaults = @config['default']
|
37
|
+
|
38
|
+
# Connect to RabbitMQ...
|
39
|
+
host = defaults['host']
|
40
|
+
port = defaults['port']
|
41
|
+
user = defaults['user']
|
42
|
+
pass = defaults['password']
|
43
|
+
vhost = defaults['virtual_host']
|
44
|
+
exchange = defaults['exchange']
|
45
|
+
$log.info(@name) { "Connecting to MQ h: #{host}, p: #{port}, u: #{user}, pw: #{pass}, v: #{vhost}, x: #{exchange}" }
|
46
|
+
|
47
|
+
AMQP.start(:host => host, :port => port, :user => user, :pass => pass, :vhost => vhost) do
|
48
|
+
MQ.queue(@name).bind(@exchange).subscribe(:ack => true) do |headers, body|
|
49
|
+
@md5sum = headers.properties[:correlation_id]
|
50
|
+
@artifact = body
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/cloud/logger.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
13
|
+
# or implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#
|
16
|
+
require "logger"
|
17
|
+
|
18
|
+
$log = Logger.new('/var/log/cloud/deployer.log')
|
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cloud-utils
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 15
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: "1.0"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- J. Brisbin
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-06-14 00:00:00 -05:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: amqp
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 9
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
- 6
|
32
|
+
- 7
|
33
|
+
version: 0.6.7
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
description: Deploy artifacts in a cloud environment.
|
37
|
+
email: jon@jbrisbin.com
|
38
|
+
executables:
|
39
|
+
- deploy
|
40
|
+
- monitor
|
41
|
+
extensions: []
|
42
|
+
|
43
|
+
extra_rdoc_files:
|
44
|
+
- CHANGELOG
|
45
|
+
- LICENSE
|
46
|
+
- README.markdown
|
47
|
+
- bin/deploy
|
48
|
+
- bin/monitor
|
49
|
+
- lib/cloud/deploy.rb
|
50
|
+
- lib/cloud/deploy/artifact.rb
|
51
|
+
- lib/cloud/deploy/monitor.rb
|
52
|
+
- lib/cloud/logger.rb
|
53
|
+
files:
|
54
|
+
- CHANGELOG
|
55
|
+
- LICENSE
|
56
|
+
- README.markdown
|
57
|
+
- Rakefile
|
58
|
+
- bin/deploy
|
59
|
+
- bin/monitor
|
60
|
+
- cloud-utils.tmproj
|
61
|
+
- etc/deploy.yml
|
62
|
+
- etc/monitor.yml
|
63
|
+
- lib/cloud/deploy.rb
|
64
|
+
- lib/cloud/deploy/artifact.rb
|
65
|
+
- lib/cloud/deploy/monitor.rb
|
66
|
+
- lib/cloud/logger.rb
|
67
|
+
- Manifest
|
68
|
+
- cloud-utils.gemspec
|
69
|
+
has_rdoc: true
|
70
|
+
homepage: http://github.com/jbrisbin/cloud-utils-deployer
|
71
|
+
licenses: []
|
72
|
+
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options:
|
75
|
+
- --line-numbers
|
76
|
+
- --inline-source
|
77
|
+
- --title
|
78
|
+
- Cloud-utils
|
79
|
+
- --main
|
80
|
+
- README.markdown
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
hash: 3
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
version: "0"
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
hash: 11
|
98
|
+
segments:
|
99
|
+
- 1
|
100
|
+
- 2
|
101
|
+
version: "1.2"
|
102
|
+
requirements: []
|
103
|
+
|
104
|
+
rubyforge_project: cloud-utils
|
105
|
+
rubygems_version: 1.3.7
|
106
|
+
signing_key:
|
107
|
+
specification_version: 3
|
108
|
+
summary: Deploy artifacts in a cloud environment.
|
109
|
+
test_files: []
|
110
|
+
|