conan_deploy 0.0.9 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
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