conan_deploy 0.0.2
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 +216 -0
- data/bin/conan +105 -0
- data/lib/conan/deploy.rb +46 -0
- data/lib/conan/manifest_builder.rb +494 -0
- data/lib/conan/newrelic.rb +16 -0
- data/lib/conan/options.rb +31 -0
- data/lib/conan/output.rb +169 -0
- data/lib/conan/repository.rb +95 -0
- data/lib/conan/stackato.rb +197 -0
- data/lib/conan/templates.rb +38 -0
- data/lib/conan/version.rb +3 -0
- metadata +57 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
require 'digest'
|
3
|
+
|
4
|
+
class DefaultArtifactRepository
|
5
|
+
@@artifact_repository_url = 'http://nexus.mtnsatcloud.com/nexus'
|
6
|
+
@@artifact_repository_local_api_url = "#{@@artifact_repository_url}/service/local/artifact/maven"
|
7
|
+
|
8
|
+
def resolveArtifact(group_id, artifact_id, extension, version='LATEST')
|
9
|
+
# we only care about the releases repo, no snapshots please
|
10
|
+
RestClient.get("#{@@artifact_repository_local_api_url}/resolve?r=releases&g=#{group_id}&a=#{artifact_id}&v=#{version}&e=#{extension}")
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def downloadArtifact(output_dir, group_id, artifact_id, extension, version, sha1_hash)
|
15
|
+
if (extension == 'jar')
|
16
|
+
# put jars in a parent directory
|
17
|
+
output_dir = File.join(output_dir, "#{artifact_id}")
|
18
|
+
end
|
19
|
+
url="#{@@artifact_repository_local_api_url}/content?r=releases&g=#{group_id}&a=#{artifact_id}&v=#{version}&e=#{extension}"
|
20
|
+
target_file = File.join(output_dir, "#{artifact_id}.#{extension}")
|
21
|
+
HttpUtil.download(url, target_file, "#{artifact_id}-#{version}.#{extension}", sha1_hash)
|
22
|
+
|
23
|
+
# extract tar balls
|
24
|
+
if (extension == 'tar.gz')
|
25
|
+
dir = "#{output_dir}/#{artifact_id}"
|
26
|
+
FileUtils.rm_rf dir
|
27
|
+
FileUtils.mkdir_p dir
|
28
|
+
`tar -xf #{target_file} -C #{dir}`
|
29
|
+
# TODO fail if tar doesn't work
|
30
|
+
|
31
|
+
# extract deploy artifacts
|
32
|
+
elsif (extension == 'jar')
|
33
|
+
# templates
|
34
|
+
dir = "#{output_dir}/deploy-templates"
|
35
|
+
FileUtils.rm_rf dir
|
36
|
+
FileUtils.mkdir_p dir
|
37
|
+
c = "unzip -j #{target_file} 'META-INF/deploy/templates/*.erb' -d '#{dir}'"
|
38
|
+
system(c) or raise "unzip failed: #{c}"
|
39
|
+
|
40
|
+
# new relic extensions
|
41
|
+
dir = "#{output_dir}/extensions"
|
42
|
+
FileUtils.rm_rf dir
|
43
|
+
FileUtils.mkdir_p dir
|
44
|
+
c = "unzip -j #{target_file} 'META-INF/deploy/extensions/*.yml' -d '#{dir}'"
|
45
|
+
system(c) # extensions are optional, it's ok if this one fails
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def downloadNewRelicAgent(output_dir, nr_version, sha1_hash)
|
50
|
+
url = "#{@@artifact_repository_url}/content/groups/public/com/newrelic/agent/java/newrelic-agent/#{nr_version}/newrelic-agent-#{nr_version}.jar"
|
51
|
+
target_file = File.join(output_dir, "newrelic.jar")
|
52
|
+
HttpUtil.download(url, target_file, "newrelic-agent-#{nr_version}.jar", sha1_hash)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module HttpUtil
|
57
|
+
def self.download(url, target_file, download_file_name, sha1_hash)
|
58
|
+
not_shasum = ->(file) {
|
59
|
+
ret = true
|
60
|
+
if (File.exists?(file))
|
61
|
+
# verify the file
|
62
|
+
found_hash = Digest::SHA1.file(file).hexdigest
|
63
|
+
puts "Found: #{found_hash}, Want: #{sha1_hash}"
|
64
|
+
if (found_hash != sha1_hash)
|
65
|
+
puts "#{file} is not the file we're looking for. It has a SHA1 hash of #{found_hash}, but #{sha1_hash} is expected"
|
66
|
+
ret = true
|
67
|
+
else
|
68
|
+
puts "#{file} matches checksum"
|
69
|
+
ret = false
|
70
|
+
end
|
71
|
+
end
|
72
|
+
ret
|
73
|
+
}
|
74
|
+
|
75
|
+
if (not_shasum.call(target_file))
|
76
|
+
tmp = File.join(Dir.tmpdir(), "nexus-app-manifest")
|
77
|
+
FileUtils.mkdir_p tmp
|
78
|
+
download_file = File.join(tmp, download_file_name)
|
79
|
+
if (not_shasum.call(download_file))
|
80
|
+
# download it
|
81
|
+
puts "Fetching artifact #{download_file_name}"
|
82
|
+
puts " #{url}"
|
83
|
+
puts "..."
|
84
|
+
wget_cmd = "wget -nv \"#{url}\" --content-disposition --no-use-server-timestamps --output-document \"#{download_file}\""
|
85
|
+
puts wget_cmd
|
86
|
+
system( wget_cmd )
|
87
|
+
raise "Failed to download #{download_file_name}" if not_shasum.call(download_file)
|
88
|
+
end
|
89
|
+
# move download to target file
|
90
|
+
puts "cp #{download_file} to #{target_file}"
|
91
|
+
FileUtils.mkdir_p File.dirname(target_file)
|
92
|
+
FileUtils.copy(download_file, target_file)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
class Stackato
|
5
|
+
|
6
|
+
def initialize(paas_user, paas_password, trace=false, dry_run=false)
|
7
|
+
@paas_user = paas_user
|
8
|
+
@paas_password = paas_password
|
9
|
+
@doit = dry_run ? "echo " : ""
|
10
|
+
@trace = trace ? "--trace" : ""
|
11
|
+
random_str = (0...8).map { ('a'..'z').to_a[rand(26)] }.join
|
12
|
+
@paas_token_file = File.join(Dir.tmpdir(), "token_#{random_str}")
|
13
|
+
@paas_manifest = "stackato.yml"
|
14
|
+
end
|
15
|
+
|
16
|
+
def login(paas_target)
|
17
|
+
# login only one time per target, and only one target at a time
|
18
|
+
if (@session != paas_target)
|
19
|
+
@session = paas_target
|
20
|
+
@paas_cmd = "stackato #{@trace} --target #{paas_target} --token-file #{@paas_token_file}"
|
21
|
+
unless @paas_user.nil?
|
22
|
+
system( "#{@paas_cmd} login --user #{@paas_user} --password #{@paas_password}" ) or raise "Stackato login failed on #{paas_target}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def deploy(work_dir, app, deployment, force=false)
|
28
|
+
puts ''
|
29
|
+
Dir.chdir(work_dir){
|
30
|
+
login(deployment.paas_target)
|
31
|
+
|
32
|
+
paas_app_name = deployment.name(app.id)
|
33
|
+
appCreate = ->(cmd) {
|
34
|
+
c = "#{@doit} #{@paas_cmd} --manifest #{@paas_manifest} --no-prompt #{cmd} --no-start"
|
35
|
+
system(c) or raise "Stackato #{cmd} failed: #{c}"
|
36
|
+
}
|
37
|
+
appOperation = ->(cmd) {
|
38
|
+
c = "#{@doit} #{@paas_cmd} #{cmd} #{paas_app_name} --no-prompt"
|
39
|
+
system(c) or raise "Stackato #{cmd} failed: #{c}"
|
40
|
+
}
|
41
|
+
|
42
|
+
yield paas_app_name if block_given?
|
43
|
+
|
44
|
+
stats_status = `#{@paas_cmd} stats #{paas_app_name} 2>&1 >/dev/null`
|
45
|
+
puts stats_status
|
46
|
+
raise ScriptError, "Stackato authorization failure. Provide user and password" if (stats_status =~ /Not Authorized/)
|
47
|
+
|
48
|
+
# print out the stackato.yml for posterity
|
49
|
+
puts '------------------------------------------------------------------------------'
|
50
|
+
|
51
|
+
File.open(@paas_manifest, "r") do |f|
|
52
|
+
while (line = f.gets)
|
53
|
+
puts "#{line}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
puts '------------------------------------------------------------------------------'
|
57
|
+
puts ''
|
58
|
+
|
59
|
+
# A Error 301 will be returned from stackato if the app does not exist, this determins if we should push or update
|
60
|
+
if (stats_status =~ /Error 301/)
|
61
|
+
appCreate.call "push"
|
62
|
+
else
|
63
|
+
if (force)
|
64
|
+
puts "Replacing #{paas_app_name}. May be force be with you!"
|
65
|
+
appOperation.call "delete"
|
66
|
+
appCreate.call "push"
|
67
|
+
else
|
68
|
+
puts "Update #{paas_app_name}"
|
69
|
+
appOperation.call "stop"
|
70
|
+
appCreate.call "update"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
puts "Starting #{paas_app_name}"
|
75
|
+
appOperation.call "start"
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
def bg_deploy(work_dir, app, deployment)
|
80
|
+
puts ''
|
81
|
+
Dir.chdir(work_dir){
|
82
|
+
login(deployment.paas_target)
|
83
|
+
|
84
|
+
app_info = application_info(app, deployment)
|
85
|
+
raise "Please remove inactive app '#{app_info.inactive_app_name}' before proceeding." unless app_info.inactive_app_name.nil?
|
86
|
+
|
87
|
+
puts "Deploying inactive app #{deployment.name(app.id)}"
|
88
|
+
c = "#{@doit} #{@paas_cmd} --manifest #{@paas_manifest} --no-prompt push"
|
89
|
+
system(c) or raise "Stackato push failed: #{c}"
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
def bg_switch(app, deployment)
|
94
|
+
login(deployment.paas_target)
|
95
|
+
|
96
|
+
app_info = application_info(app, deployment)
|
97
|
+
|
98
|
+
if app_info.inactive_app_name.nil?
|
99
|
+
puts "No inactive app to switch to."
|
100
|
+
return
|
101
|
+
end
|
102
|
+
|
103
|
+
active_urls = deployment.active_urls(app.id)
|
104
|
+
inactive_urls = deployment.inactive_urls(app.id)
|
105
|
+
|
106
|
+
appUrlOp = ->(cmd, paas_app_name, url) {
|
107
|
+
c = "#{@doit} #{@paas_cmd} #{cmd} #{paas_app_name} #{url}"
|
108
|
+
system(c) or raise "Stackato #{cmd} failed: #{c}"
|
109
|
+
}
|
110
|
+
|
111
|
+
# For inactive app, map active urls to it and unmap inactive urls
|
112
|
+
puts "Mapping active urls"
|
113
|
+
active_urls.each {|active_url| appUrlOp.call("map", app_info.inactive_app_name, active_url)}
|
114
|
+
inactive_urls.each {|inactive_url| appUrlOp.call("unmap", app_info.inactive_app_name, inactive_url)}
|
115
|
+
|
116
|
+
# If active app exists, unmap active urls and map inactive urls to it
|
117
|
+
if app_info.active_app_name
|
118
|
+
puts "Mapping inactive urls"
|
119
|
+
inactive_urls.each {|inactive_url| appUrlOp.call("map", app_info.active_app_name, inactive_url)}
|
120
|
+
active_urls.each {|active_url| appUrlOp.call("unmap", app_info.active_app_name, active_url)}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def bg_clean(app, deployment)
|
125
|
+
login(deployment.paas_target)
|
126
|
+
|
127
|
+
app_info = application_info(app, deployment)
|
128
|
+
|
129
|
+
# Delete inactive app if it exists
|
130
|
+
if app_info.inactive_app_name
|
131
|
+
puts "Deleting inactive app."
|
132
|
+
c = "#{@doit} #{@paas_cmd} delete #{app_info.inactive_app_name} --no-prompt"
|
133
|
+
system(c) or raise "Stackato delete failed: #{c}"
|
134
|
+
else
|
135
|
+
puts "No inactive app to clean."
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def application_info(app, deployment)
|
140
|
+
list_json = `#{@paas_cmd} list --json`
|
141
|
+
raise "Stackato list failed" if !$?.success?
|
142
|
+
|
143
|
+
base_name = deployment.name(app.id)
|
144
|
+
active_urls = deployment.active_urls(app.id)
|
145
|
+
inactive_urls = deployment.inactive_urls(app.id)
|
146
|
+
|
147
|
+
StackatoApplicationInfo.new(list_json, base_name, active_urls, inactive_urls)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
class StackatoApplicationInfo
|
152
|
+
def initialize(list_json, base_name, active_urls, inactive_urls)
|
153
|
+
app_list = JSON.parse(list_json)
|
154
|
+
|
155
|
+
active_app_names = []
|
156
|
+
inactive_app_names = []
|
157
|
+
|
158
|
+
app_list.each { |app_hash|
|
159
|
+
if !(app_hash.has_key? 'name' and app_hash.has_key? 'uris')
|
160
|
+
raise "Stackato list json is malformed: " + list_json
|
161
|
+
end
|
162
|
+
|
163
|
+
app_name = app_hash['name']
|
164
|
+
app_uris = app_hash['uris']
|
165
|
+
|
166
|
+
if app_name.start_with? base_name
|
167
|
+
if app_uris.sort == active_urls.sort
|
168
|
+
active_app_names << app_name
|
169
|
+
elsif app_uris.sort == inactive_urls.sort
|
170
|
+
inactive_app_names << app_name
|
171
|
+
else
|
172
|
+
raise 'Stackato app "' + app_name +
|
173
|
+
'" needs to be mapped exclusively to either the active or inactive set of URLs. Please reconcile before continuing.'
|
174
|
+
end
|
175
|
+
end
|
176
|
+
}
|
177
|
+
|
178
|
+
if active_app_names.length > 1
|
179
|
+
raise "More than one app is mapped to the active URL(s). Please reconcile before continuing."
|
180
|
+
end
|
181
|
+
if inactive_app_names.length > 1
|
182
|
+
raise "More than one app is mapped to the inactive URL(s). Please reconcile before continuing."
|
183
|
+
end
|
184
|
+
|
185
|
+
# These will be set to nil if no app is found, which is OK
|
186
|
+
@active_app_name = active_app_names.first
|
187
|
+
@inactive_app_name = inactive_app_names.first
|
188
|
+
end
|
189
|
+
|
190
|
+
def active_app_name
|
191
|
+
@active_app_name
|
192
|
+
end
|
193
|
+
|
194
|
+
def inactive_app_name
|
195
|
+
@inactive_app_name
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'erubis'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
module Templates
|
5
|
+
|
6
|
+
# Returns the number of files changed/created
|
7
|
+
def self.evaluate(templates_dir, output_dir, context)
|
8
|
+
no_changes = 0
|
9
|
+
FileUtils.mkdir_p File.dirname(output_dir)
|
10
|
+
Dir.glob(File.join(templates_dir, '*.erb')) do |template|
|
11
|
+
target_file = File.join(output_dir, File.basename(template)[0..-5])
|
12
|
+
eruby = Erubis::Eruby.new(File.read(template))
|
13
|
+
if (File.exist? target_file)
|
14
|
+
Tempfile.open(File.basename template) { |t|
|
15
|
+
t.write(eruby.evaluate(context))
|
16
|
+
t.rewind
|
17
|
+
if (FileUtils.identical? t.path, target_file)
|
18
|
+
puts "-- No change from: #{File.basename template} => #{target_file}"
|
19
|
+
else
|
20
|
+
puts ">> Replace file from template: #{File.basename template} => #{target_file}"
|
21
|
+
puts "------------------------------"
|
22
|
+
puts `diff -y -W 120 #{t.path} #{target_file}`
|
23
|
+
puts "------------------------------"
|
24
|
+
FileUtils.cp t.path, target_file
|
25
|
+
no_changes += 1
|
26
|
+
end
|
27
|
+
}
|
28
|
+
else
|
29
|
+
File.open(target_file, 'w') { |f|
|
30
|
+
puts "++ New file from template: #{File.basename template} => #{target_file}"
|
31
|
+
f.write(eruby.evaluate(context))
|
32
|
+
no_changes += 1
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
no_changes
|
37
|
+
end
|
38
|
+
end
|
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: conan_deploy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Mike Reardon
|
9
|
+
- Michael Crane
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2014-04-09 00:00:00.000000000 Z
|
14
|
+
dependencies: []
|
15
|
+
description: Provision, configure, deploy and never hear da lamantation of da users
|
16
|
+
email: michael.reardon@mtnsat.com
|
17
|
+
executables:
|
18
|
+
- conan
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- bin/conan
|
23
|
+
- lib/conan/deploy.rb
|
24
|
+
- lib/conan/manifest_builder.rb
|
25
|
+
- lib/conan/newrelic.rb
|
26
|
+
- lib/conan/options.rb
|
27
|
+
- lib/conan/output.rb
|
28
|
+
- lib/conan/repository.rb
|
29
|
+
- lib/conan/stackato.rb
|
30
|
+
- lib/conan/templates.rb
|
31
|
+
- lib/conan/version.rb
|
32
|
+
- README.md
|
33
|
+
homepage: http://github.com/MTNSatelliteComm/conan/README.md
|
34
|
+
licenses: []
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
47
|
+
requirements:
|
48
|
+
- - ! '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
requirements: []
|
52
|
+
rubyforge_project:
|
53
|
+
rubygems_version: 1.8.23
|
54
|
+
signing_key:
|
55
|
+
specification_version: 3
|
56
|
+
summary: Conan da Deployer
|
57
|
+
test_files: []
|