conan_deploy 0.0.9 → 0.0.12

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/bin/conan CHANGED
@@ -1,10 +1,28 @@
1
1
  #!/usr/bin/env ruby
2
-
2
+ require 'rest_client'
3
+ require 'rexml/document'
4
+ require 'json'
5
+ require 'securerandom'
6
+ require 'daphne_util'
7
+ require 'optparse'
8
+ require 'conan/application'
9
+ require 'conan/application_helper'
3
10
  require 'conan/deploy'
4
- require 'conan/version'
11
+ require 'conan/deployment'
12
+ require 'conan/environment'
13
+ require 'conan/has_options'
14
+ require 'conan/jvm_app'
15
+ require 'conan/manifest'
16
+ require 'conan/manifest_builder'
17
+ require 'conan/newrelic'
5
18
  require 'conan/options'
6
- require 'optparse'
7
-
19
+ require 'conan/output'
20
+ require 'conan/pipeline'
21
+ require 'conan/rails_zip_app'
22
+ require 'conan/repository'
23
+ require 'conan/stackato'
24
+ require 'conan/templates'
25
+ require 'conan/version'
8
26
  options = {}
9
27
 
10
28
  optparse = OptionParser.new do |o|
@@ -14,12 +32,12 @@ optparse = OptionParser.new do |o|
14
32
  o.separator " environment - name of a the target environment: integ,stage,prod"
15
33
  o.separator ""
16
34
  o.separator " Options:"
17
-
35
+
18
36
  o.on( '-d', '--output-dir [DIR]', 'Generate manifest data to this directory' ) do |dir|
19
37
  options[:directory] = dir
20
38
  end
21
39
 
22
- o.on( '-f', '--output-format [FORMAT]', [:properties, :stackato],
40
+ o.on( '-f', '--output-format [FORMAT]', [:properties, :stackato],
23
41
  'The format used to write output. Either "properties" for Jenkins build properties',
24
42
  ' or "stackato" (default) to provision binaries and write stackato yml files',
25
43
  ' from project templates.' ) do |format|
@@ -29,14 +47,14 @@ optparse = OptionParser.new do |o|
29
47
  puts "Unsupported --format option"
30
48
  puts o
31
49
  exit
32
- end
50
+ end
33
51
  end
34
52
 
35
53
  o.on('-a','--action [ACTION]', [:provision,:configure,:deploy,:bg_configure,:bg_deploy,:bg_switch,:bg_clean,:all,:bg_all],
36
54
  'The action(s) to perform. Either "provision" (default), "configure","deploy" or "all".',
37
55
  ' Support for blue/green deployments use actions: "provision", "bg_configure", "bg_deploy",',
38
56
  ' "bg_switch", "bg_clean" or "bg_all".') do |action|
39
- if (action)
57
+ if (action)
40
58
  options[:action] = action
41
59
  else
42
60
  puts "Unsupported --action option"
@@ -44,19 +62,19 @@ optparse = OptionParser.new do |o|
44
62
  exit
45
63
  end
46
64
  end
47
-
65
+
48
66
  o.on('-u','--paas-user [USER]', 'The stackato user name for deployments' ) do |user|
49
67
  options[:'paas-user'] = user
50
68
  end
51
69
 
52
70
  o.on('-p','--paas-password [PASSWORD]', 'The stackato password name for deployments' ) do |pw|
53
71
  options[:'paas-password'] = pw
54
- end
72
+ end
55
73
 
56
74
  o.on('-F','--force-deploy', 'Force deployment to bypass version checks.' ) do
57
75
  options[:'force-deploy'] = true
58
- end
59
-
76
+ end
77
+
60
78
  o.on('-N', '--new-relic-api-key', 'New Relic API key for deployment alerts') do |api_key|
61
79
  options[:'new-relic-api-key'] = api_key
62
80
  end
@@ -75,7 +93,7 @@ optparse = OptionParser.new do |o|
75
93
 
76
94
  o.on('-D','--dry-run', 'Deployment dry-run. Prints Stackato commands' ) do
77
95
  options[:'dry-run'] = true
78
- end
96
+ end
79
97
 
80
98
  o.on('-V','--verbose', 'Verbose console output' ) do
81
99
  options[:verbose] = true
@@ -83,9 +101,9 @@ optparse = OptionParser.new do |o|
83
101
 
84
102
  o.on("-v", "--version", "Show the software version") do
85
103
  puts Conan::VERSION
86
- exit
104
+ exit
87
105
  end
88
-
106
+
89
107
  o.on_tail( '-h', '--help', 'Display this screen' ) do
90
108
  puts o
91
109
  exit
@@ -94,20 +112,20 @@ end
94
112
  optparse.parse!
95
113
 
96
114
 
97
- pl = if (ARGV.size < 1)
115
+ if (ARGV.size < 1)
98
116
  puts optparse
99
117
  puts ''
100
118
  raise ArgumentError, "No pipeline name argument was provided, e.g. nexus-apps-deploy"
101
119
  else
102
- ARGV[0].downcase.to_sym
120
+ options[:pipeline] = ARGV[0].downcase.to_sym
103
121
  end
104
122
 
105
- env = if (ARGV.size < 2)
123
+ if (ARGV.size < 2)
106
124
  puts optparse
107
- puts ''
125
+ puts ''
108
126
  raise ArgumentError, "No environment name argument was provided, e.g. integ"
109
127
  else
110
- ARGV[1].downcase.to_sym
128
+ options[:environment] = ARGV[1].downcase.to_sym
111
129
  end
112
130
 
113
- Conan::Deploy.run(Conan::Options.new(options), pl, env)
131
+ Conan::Deploy.run(Conan::Options.new(options))
@@ -0,0 +1,164 @@
1
+
2
+ class Application
3
+
4
+ attr_accessor :id, :platform_type, :group_id, :artifact_id, :version, :extension, :sha1, :artifact_meta_data, :url_segment,
5
+ :unique_name, :additional_mappings, :org, :infra, :smoke_test_path
6
+
7
+ def initialize(id, project_name, platform_type, options, url_segment=nil)
8
+ @id = id
9
+ @platform_type = platform_type
10
+ @url_segment = url_segment || id
11
+ mvn_id = project_name.split(':')
12
+ @options = options
13
+ @org = options[:'deploy-shipcloud'].split('-')[0]
14
+ @infra = options[:'deploy-shipcloud'].split('-')[1]
15
+ @environment = options[:environment]
16
+ @group_id = mvn_id[0]
17
+ @artifact_id = mvn_id[1]
18
+ @smoke_test_path = 'status/healthcheck'
19
+ end
20
+
21
+ def is_inactive_node_operational?
22
+ puts "Checking inactive app at #{inactive_healthcheck_url}"
23
+ !!(RestClient.get(inactive_healthcheck_url))
24
+ rescue
25
+ false
26
+ end
27
+
28
+ def is_active_node_operational?
29
+ puts "Checking active app at #{inactive_healthcheck_url}"
30
+ !!(RestClient.get(active_healthcheck_url))
31
+ rescue
32
+ false
33
+ end
34
+
35
+ def inactive_healthcheck_url
36
+ "http://inactive.#{full_backchannel_domain}/#{@smoke_test_path}"
37
+ end
38
+
39
+ def active_healthcheck_url
40
+ "http://#{full_backchannel_domain}/#{@smoke_test_path}"
41
+ end
42
+
43
+ def old_versions
44
+ @old_apps ||= Stackato.find_old_versions(self)
45
+ end
46
+
47
+ def stackato_base_name
48
+ "#{@id}-#{@environment}-#{@infra}"
49
+ end
50
+
51
+ def findRequestedVersion(requested_version='LATEST')
52
+ puts("Finding requested version: #{requested_version}")
53
+ extension = case platform_type
54
+ when :jvm
55
+ "jar"
56
+ when :rails_zip
57
+ "tar.gz"
58
+ else
59
+ raise "Unsupported platform type: #{platform_type}"
60
+ end
61
+ readArtifactMetadata(@artifact_repo.resolveArtifact(group_id, artifact_id, extension, requested_version))
62
+ puts "- Found version #{version} #{@id} artifact\n"+
63
+ " Artifact: #{self}\n" +
64
+ " Checksum: #{@sha1}"
65
+ end
66
+
67
+ def readArtifactMetadata(xml)
68
+ @artifact_meta_data = xml
69
+ begin
70
+ doc = REXML::Document.new(xml)
71
+ rescue Exception => e
72
+ puts "Failed to parse meta-data: #{e}"
73
+ puts "--------------------------------"
74
+ puts xml
75
+ puts "--------------------------------"
76
+ raise "Unreadable artifact meta-data for #{self}"
77
+ end
78
+
79
+ meta_data = doc.elements['/artifact-resolution/data']
80
+ @version = meta_data.elements['version'].text
81
+ @sha1 = meta_data.elements['sha1'].text
82
+ @extension = meta_data.elements['extension'].text
83
+ end
84
+
85
+ def download(location)
86
+ puts "- Provision #{group_id}:#{artifact_id}:#{extension}:#{version}"
87
+ @artifact_repo.downloadArtifact(location, group_id, artifact_id, extension, version, sha1)
88
+ end
89
+
90
+ def isDeployedAt(url)
91
+ # only deploy if an older version is found, or no app found
92
+ res = false
93
+ begin
94
+ response = RestClient::Request.new(
95
+ :method => :get,
96
+ :url => url,
97
+ :headers => {
98
+ 'accept' => :json,
99
+ 'content_type' => :json
100
+ },
101
+ :timeout => 5,
102
+ :open_timeout => 5
103
+ ).execute
104
+ if (response.code == 200)
105
+ build_metadata = JSON.parse(response.to_str)
106
+ puts ''
107
+ puts "+- Currently at #{url}:"
108
+ printM = ->(s) {
109
+ puts " | #{s}: #{build_metadata[s]}"
110
+ }
111
+ printM.call 'Implementation-Title'
112
+ printM.call 'Build-Version'
113
+ printM.call 'Build-Url'
114
+ printM.call 'Git-Commit'
115
+ current_ver = build_metadata["Build-Version"]
116
+ end
117
+ #TODO make sure the artifact id matches too, currently finding "Implementation-Title": "apollo" in build metadata
118
+ if (compareVersion(@version, current_ver))
119
+ puts "#{@artifact_id} #{@version} is newer than version found at #{url}: #{current_ver}"
120
+ else
121
+ puts "#{@artifact_id} #{@version} found at #{url}. (skipping deployment)"
122
+ res = true
123
+ end
124
+ rescue => e
125
+ puts "#{@artifact_id} not found at #{url}: #{e}"
126
+ end
127
+ res
128
+ end
129
+
130
+
131
+ def compareVersion(target_version, deployed_version)
132
+ target = Gem::Version.new(target_version)
133
+ begin
134
+ found = Gem::Version.new(deployed_version)
135
+ target > found
136
+ rescue => e
137
+ puts "Error parsing version number: #{e}"
138
+ false
139
+ end
140
+ end
141
+
142
+ def to_s
143
+ "#{artifact_id}-#{version}.#{extension}"
144
+ end
145
+
146
+
147
+ def active_domains
148
+ [full_backchannel_domain, infra_agnostic_backchannel_domain] + Array(@additional_mappings)
149
+ end
150
+
151
+ def inactive_domains
152
+ active_domains.map{|domain| "inactive.#{domain}"}
153
+ end
154
+
155
+ private
156
+
157
+ def full_backchannel_domain
158
+ "#{@environment}.#{@url_segment}.#{@infra}.#{@org}.mtnsatcloud.com"
159
+ end
160
+
161
+ def infra_agnostic_backchannel_domain
162
+ "#{@environment}.#{@url_segment}.mtnsatcloud.com"
163
+ end
164
+ end
@@ -71,6 +71,7 @@ module ApiHelper
71
71
  end
72
72
 
73
73
  def self.smoke_test(url, timeout)
74
+ puts "Smoke Testing #{url}"
74
75
  RestClientExt.get(
75
76
  url,
76
77
  defaultHeaders(
@@ -116,4 +117,4 @@ class NexusResponse
116
117
  @raw_json = raw_json
117
118
  end
118
119
  end
119
- end
120
+ end
data/lib/conan/deploy.rb CHANGED
@@ -3,11 +3,11 @@ require 'optparse'
3
3
 
4
4
  module Conan
5
5
  class Deploy
6
- def self.run(options, pl, env)
7
- puts "> Pipeline: #{pl}"
8
- puts "> Environment: #{env}"
6
+ def self.run(options)
7
+ puts "> Pipeline: #{options[:pipeline]}"
8
+ puts "> Environment: #{options[:environment]}"
9
9
 
10
- manifest = ManifestBuilder.build(options, pl, env) {
10
+ manifest = ManifestBuilder.build(options) {
11
11
  m = File.join(options[:directory], 'environments.rb')
12
12
  puts "> Manifest: #{m}"
13
13
  instance_eval(File.read(m), m)
@@ -33,14 +33,16 @@ module Conan
33
33
  manifest.configure
34
34
  manifest.deploy
35
35
  when :bg_all
36
+ Stackato.static_login(options)
36
37
  manifest.provision
37
38
  manifest.bg_configure
38
- manifest.bg_deploy
39
- if(manifest.is_inactive_node_operational?)
40
- manifest.bg_switch
41
- manifest.bg_clean
42
- else
43
- raise RuntimeError.new "Blue node is not operational. Skipping switch and cleaning to facilitate troubleshooting."
39
+ manifest.bg_deploy.each do |app|
40
+ if app.is_inactive_node_operational?
41
+ Stackato.bg_switch(app)
42
+ Stackato.bg_clean(app)
43
+ else
44
+ raise RuntimeError.new "#{app}'s blue node is not operational. Skipping switch and clean."
45
+ end
44
46
  end
45
47
 
46
48
  else
@@ -0,0 +1,93 @@
1
+ require 'conan/has_options'
2
+ class Deployment < HasOptions
3
+
4
+ @@paas_domain = 'mtnsatcloud.com'
5
+
6
+ attr_accessor :environment, :org, :ship, :shipcloud, :app_ids, :facility_id, :additional_mappings, :custom_smoke_test_path
7
+
8
+ def initialize(environment, org, ship)
9
+ # inherit options from the environment
10
+ #super(environment.options) # TODO: copy?!
11
+ super({})
12
+ @environment = environment
13
+ @org = org
14
+ @ship = ship
15
+ @shipcloud = "#{org}-#{ship}"
16
+ @app_ids = []
17
+ @additional_mappings = []
18
+ @facility_id = nil
19
+ @enabled = true
20
+ @randomid = SecureRandom.hex(3)
21
+ @custom_smoke_test_path = nil
22
+ end
23
+
24
+ def apps(*app_ids)
25
+ @app_ids = app_ids
26
+ end
27
+
28
+ def facility(facility_id)
29
+ @facility_id = facility_id
30
+ end
31
+
32
+ def hostnames(*hostnames)
33
+ hostnames.each do |hostname|
34
+ @additional_mappings.push(hostname) unless hostname.empty?
35
+ end
36
+ end
37
+
38
+ def enabled(b)
39
+ @enabled = b
40
+ end
41
+
42
+ def enabled?
43
+ @enabled
44
+ end
45
+
46
+ def name(app)
47
+ "#{app.id}-#{@environment.id}-#{@ship}"
48
+ end
49
+
50
+ def unique_name(app)
51
+ "#{name(app)}-#{@randomid}"
52
+ end
53
+
54
+ def dns_name(app)
55
+ "#{@environment.id}.#{app.url_segment}.#{@ship}.#{@org}.#{@@paas_domain}"
56
+ end
57
+
58
+ def agnostic_dns_name(app)
59
+ "#{@environment.id}.#{app.url_segment}.#{@@paas_domain}"
60
+ end
61
+
62
+ def active_urls(app)
63
+ [ dns_name(app), agnostic_dns_name(app) ] + @additional_mappings
64
+ end
65
+
66
+ def inactive_urls(app)
67
+ active_urls(app).map{ |url| "inactive.#{url}" }
68
+ end
69
+
70
+ def manifest_url(app)
71
+ "http://#{dns_name(app)}/status/manifest"
72
+ end
73
+
74
+ def active_smoke_test_url(app)
75
+ "http://#{dns_name(app)}/#{@custom_smoke_test_path || 'status/healthcheck'}"
76
+ end
77
+
78
+ def inactive_smoke_test_url(app)
79
+ "http://inactive.#{dns_name(app)}/#{@custom_smoke_test_path || 'status/healthcheck'}"
80
+ end
81
+
82
+ def paas_target
83
+ "https://api.paas.#{@ship}.#{@org}.#{@@paas_domain}"
84
+ end
85
+
86
+ def to_s
87
+ "#{org}-#{ship}"
88
+ end
89
+
90
+ def smoke_test_path(path=nil)
91
+ @custom_smoke_test_path = path
92
+ end
93
+ end
@@ -0,0 +1,24 @@
1
+ class Environment
2
+ attr_accessor :id, :upstream_env, :deployments
3
+
4
+ def initialize(env_id)
5
+ @id = env_id
6
+ @deployments = []
7
+ @org_holder = nil
8
+ end
9
+
10
+ def promotes_from(env_id)
11
+ @upstream_env = env_id
12
+ end
13
+
14
+ def org(org_name, &block)
15
+ @org_holder = org_name
16
+ self.instance_eval(&block)
17
+ end
18
+
19
+ def deploy(ship, &block)
20
+ d = Deployment.new(self, @org_holder, ship)
21
+ d.instance_eval(&block)
22
+ @deployments << d
23
+ end
24
+ end