conan_deploy 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,3 @@
1
+ module Conan
2
+ VERSION = "0.0.2"
3
+ 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: []