conan_deploy 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,118 @@
1
+ require 'rest_client'
2
+ require 'json'
3
+
4
+ module RestClientExt
5
+ include RestClient
6
+
7
+ RETRIES = 3
8
+
9
+ def self.handle_network_error(verb, err, url, tries)
10
+ additional_tries = tries - 1
11
+ message = "#{verb} to #{url} failed with (#{err.ai}), retrying #{additional_tries} more times"
12
+
13
+ case err
14
+ when Errno::ETIMEDOUT, Errno::ECONNREFUSED, RestClient::ServerBrokeConnection, RestClient::RequestTimeout
15
+ puts message
16
+ sleep 1
17
+ else
18
+ raise err
19
+ end
20
+
21
+ additional_tries
22
+ end
23
+
24
+ def self.request_options(verb, url, headers, payload = nil)
25
+ opts = {
26
+ :method => verb.downcase.to_sym,
27
+ :url => url,
28
+ :headers => headers.clone,
29
+ :timeout => headers[:timeout] || 10,
30
+ :open_timeout => headers[:open_timeout] || 10
31
+ }
32
+
33
+ payload ? opts.merge(:payload => payload) : opts
34
+ end
35
+
36
+ def self.execute_request(verb, url, headers, payload = nil, tries = RETRIES, &block)
37
+ Request.execute(request_options(verb, url, headers, payload),&block)
38
+ rescue SystemCallError, Exception => err
39
+ retry unless (tries = handle_network_error(verb, err, url, tries)).zero?
40
+ end
41
+
42
+ def self.get(url, headers={}, &block)
43
+ execute_request("GET", url, headers, &block)
44
+ end
45
+
46
+ def self.post(url, payload, headers={}, &block)
47
+ execute_request("POST", url, headers, payload, &block)
48
+ end
49
+
50
+ def self.put(url, payload, headers={}, &block)
51
+ execute_request("PUT", url, headers, payload, &block)
52
+ end
53
+
54
+ def self.delete(url, headers={}, &block)
55
+ execute_request("DELETE", url, headers, &block)
56
+ end
57
+ end
58
+
59
+ module ApiHelper
60
+
61
+ def self.defaultHeaders(headers = {})
62
+ default_headers = {
63
+ :user_agent => 'CONAN_DA_DEPLOYER',
64
+ :accept => :json,
65
+ :accept_encoding => :gzip,
66
+ :content_type => :json }
67
+
68
+ default_headers = default_headers.merge(headers).delete_if { |k, v| v.nil? }
69
+ return default_headers
70
+ end
71
+
72
+ def self.healthcheck(url, timeout)
73
+ RestClientExt.get(
74
+ url,
75
+ defaultHeaders(
76
+ :timeout => timeout,
77
+ :open_timeout => timeout)) do |response, request, result, &block|
78
+ return NexusResponse.new(response.code, response.body)
79
+ end
80
+ end
81
+ end
82
+
83
+ class NexusResponse
84
+ attr_accessor :code
85
+ attr_accessor :json
86
+ attr_accessor :raw_json
87
+
88
+ def initialize(code, raw_json)
89
+ @code = code
90
+
91
+ begin
92
+ @json = OpenStruct.new(JSON.parse(Zlib::GzipReader.new(StringIO.new(raw_json)).read))
93
+ @raw_json = Zlib::GzipReader.new(StringIO.new(raw_json)).read
94
+ rescue Zlib::Error
95
+ begin
96
+ @json = OpenStruct.new(JSON.parse(raw_json))
97
+ @raw_json = raw_json
98
+ rescue JSON::ParserError
99
+ puts "NexusResponse - could not parse following JSON:\n#{raw_json}"
100
+ @json = OpenStruct.new(JSON.parse("{ \"status\" : \"error\", \"errorMessage\" : \"Previous call likely returned a Stackato HTML error page\" }"))
101
+ @raw_json = raw_json
102
+ end
103
+ rescue Zlib::GzipFile::Error
104
+ begin
105
+ @json = OpenStruct.new(JSON.parse(raw_json))
106
+ @raw_json = raw_json
107
+ rescue JSON::ParserError
108
+ puts "NexusResponse - could not parse following JSON:\n#{raw_json}"
109
+ @json = OpenStruct.new(JSON.parse("{ \"status\" : \"error\", \"errorMessage\" : \"Previous call likely returned a Stackato HTML error page\" }"))
110
+ @raw_json = raw_json
111
+ end
112
+ rescue JSON::ParserError
113
+ puts "NexusResponse - could not parse following JSON:\n#{raw_json}"
114
+ @json = OpenStruct.new(JSON.parse("{ \"status\" : \"error\", \"errorMessage\" : \"Previous call likely returned a Stackato HTML error page\" }"))
115
+ @raw_json = raw_json
116
+ end
117
+ end
118
+ end
data/lib/conan/deploy.rb CHANGED
@@ -36,8 +36,13 @@ module Conan
36
36
  manifest.provision
37
37
  manifest.bg_configure
38
38
  manifest.bg_deploy
39
- manifest.bg_switch
40
- manifest.bg_clean
39
+ if(manifest.is_inactive_node_healthy?)
40
+ manifest.bg_switch
41
+ manifest.bg_clean
42
+ else
43
+ raise RuntimeError.new "Blue node is not healthy. Skipping switch and cleaning to facilitate troubleshooting."
44
+ end
45
+
41
46
  else
42
47
  raise ArgumentError.new "Invalid action: #{options[:action]}"
43
48
  end
@@ -1,6 +1,5 @@
1
1
  require 'rest_client'
2
2
  require 'rexml/document'
3
- require 'properties-ruby'
4
3
  require 'json'
5
4
  require 'securerandom'
6
5
  require 'daphne_util'
@@ -9,6 +8,7 @@ require 'conan/output'
9
8
  require 'conan/repository'
10
9
  require 'conan/stackato'
11
10
  require 'conan/newrelic'
11
+ require 'conan/application_helper'
12
12
 
13
13
  module ManifestBuilder
14
14
  def self.build(options, pipeline_id, env_id, artifact_repo=nil, outthingy=nil, &block)
@@ -119,6 +119,12 @@ class Manifest
119
119
  @current_pipeline.bg_deploy(@current_environment, paas, force)
120
120
  end
121
121
 
122
+ def is_inactive_node_healthy?
123
+ validateThatEnvironmentIsSelected
124
+ puts "Blue/green health check #{@current_environment.id} for #{@current_pipeline.id} pipeline"
125
+ @current_pipeline.is_inactive_node_healthy?(@current_environment, paas)
126
+ end
127
+
122
128
  def bg_switch
123
129
  validateThatEnvironmentIsSelected
124
130
  puts "Blue/green switch #{@current_environment.id} for #{@current_pipeline.id} pipeline"
@@ -141,6 +147,8 @@ class Manifest
141
147
  end
142
148
 
143
149
  class Pipeline
150
+ include ApiHelper
151
+
144
152
  attr_accessor :id, :apps
145
153
 
146
154
  def initialize(id, artifact_repo, output)
@@ -219,6 +227,19 @@ class Pipeline
219
227
  }
220
228
  end
221
229
 
230
+ def is_inactive_node_healthy?(environment, paas)
231
+ eachAppDeployment(environment) { |app, deploy|
232
+ response = ApiHelper.healthcheck(deploy.inactive_healthcheck_url(app.id), 60)
233
+
234
+ if(response.code != 200)
235
+ puts "Healthcheck failed for inactive (Blue) node. Here is the entire response: #{response.inspect}"
236
+ return false
237
+ else
238
+ return true
239
+ end
240
+ }
241
+ end
242
+
222
243
  def bg_switch(environment, paas)
223
244
  eachAppDeployment(environment) { |app, deploy| paas.bg_switch(app, deploy) }
224
245
  end
@@ -329,6 +350,7 @@ class Application
329
350
  res
330
351
  end
331
352
 
353
+
332
354
  def compareVersion(target_version, deployed_version)
333
355
  target = Gem::Version.new(target_version)
334
356
  begin
@@ -478,6 +500,14 @@ class Deployment < HasOptions
478
500
  "http://#{dns_name(app_id)}/status/manifest"
479
501
  end
480
502
 
503
+ def active_healthcheck_url(app_id)
504
+ "http://#{dns_name(app_id)}/status/healthcheck"
505
+ end
506
+
507
+ def inactive_healthcheck_url(app_id)
508
+ "http://inactive.#{dns_name(app_id)}/status/healthcheck"
509
+ end
510
+
481
511
  def paas_target
482
512
  "https://api.paas.#{@ship}.#{@org}.#{@@paas_domain}"
483
513
  end
data/lib/conan/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Conan
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: conan_deploy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2014-04-16 00:00:00.000000000 Z
13
+ date: 2014-04-25 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: daphne_util
@@ -36,6 +36,7 @@ extensions: []
36
36
  extra_rdoc_files: []
37
37
  files:
38
38
  - bin/conan
39
+ - lib/conan/application_helper.rb
39
40
  - lib/conan/deploy.rb
40
41
  - lib/conan/manifest_builder.rb
41
42
  - lib/conan/newrelic.rb