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.
@@ -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: []