conan_deploy_stackato3 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MTg4MDU5ODY5Y2YyMjQ3YmQ3YzU0M2E5ODc4MzUwZDY1NDQzMGVmNw==
5
+ data.tar.gz: !binary |-
6
+ OWM2YjNjMGYwYWMxYzE2NGU1NzZlZDJmZTM2NWE1YWE1ZDA5ZGIxNw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YWQwNjA0OWNjMzJlYmFhYjNiZDM4ZTY3M2VjODc4M2FmYTU0ZmY1ZGY4MWQ3
10
+ MDE1NDJkZDc3Njc1Njk5NTAwNDg1NWEwYWY3ODg4M2VkYWUwNzUxNWZlYTI4
11
+ YWExNzQ5YmY2MmM2NWUzMDQxOGNiMzFhZTdkNmFhOTdjYzQzOTE=
12
+ data.tar.gz: !binary |-
13
+ NTE2NDg0N2I0NzY3YmNjMTZhMjM1NmQxZTg2YzJmOTlmNjJjNjdlN2FkOWFh
14
+ YWQ1NzkyMjQxMDNhZDNmN2E0MzQxZGNlNmQ4Y2YyOGYzYjJkZjRhZjE5Nzk5
15
+ NjlkOTJkOWQxNTM1MWJjZWUxNTQzMThkZjE2MGJhY2I2MjhlOTQ=
data/README.md ADDED
@@ -0,0 +1,223 @@
1
+ conan
2
+ =====
3
+
4
+ Conan da Deployer
5
+
6
+ This project provides automation for a [deployment pipeline](http://martinfowler.com/bliki/DeploymentPipeline.html) (ala 'Continous Delivery') which provisions, configures and deploys applications (Java and Ruby) to "ship-clouds". Configuration is modeled through a DSL as a list of logical target environments, each with a number of ship-clouds. Endpoints for web applications and Stackato PaaS APIs are created based on conventions, including support for [Blue/Green deployments](http://martinfowler.com/bliki/BlueGreenDeployment.html).
7
+
8
+ Applications are deployed from a Sonotype Nexus artifact repository, and not from the source repository. Java applications must be executable jars, and must the provide deployment resources needed in the jar. Ruby apps (Rails or otherwise) must be vendored and packaged as a tar ball, with a compatible structrue to provide the deployment resources.
9
+
10
+ Application artifacts are idenitifed and provisioned by Maven coordinates - group id, artifact id, packaging and version. The most recent version available on the repository will be used, or a promotion workflow along the pipeline(s) can be implemented by pinning the versions. Artifacts are pinned by keeping the build meta-data in an `environments` directory structure that mirrors the configuration model, which should be commited to version control after successful deployment and testing. _At this time, the `environments` directory and the `environments.rb` are located from the execution directory, which is assumed to be the [nexus-apps-manifest](https://github.com/MTNSatelliteComm/nexus-app-manifest) project._
11
+
12
+ Conan is also designed to be idempotent. It can be executed against a target deployment over and over, and it should not change an application unless it out of alignment with the configuration model. Applications are also assumed to support standard set of managments APIs which provide build metadata, and health to support this.
13
+
14
+
15
+ #Configuration
16
+
17
+ Configuration for the deployment pipline is loaded through a DSL which models a target environment(s). The DSL is loaded from a `environments.rb` file that should be provided in the execution directory.
18
+
19
+ ```ruby
20
+ pipeline(:'team1') {
21
+ app(:foo, 'com.mtnsat:foo', :jvm)
22
+ app(:bar, 'com.mtnsat:bar', :rails_zip)
23
+ }
24
+ pipeline(:'team2')
25
+ app(:baz, 'com.mtnsat:baz-web', :rails_zip)
26
+ app(:bang, 'com.mtnsat:bang', :rails_zip)
27
+ }
28
+
29
+ environment(:integ) {
30
+ org('mtn') {
31
+ deploy('aws') {
32
+ apps :foo
33
+ }
34
+ deploy('sea1') {
35
+ apps :foo, :bar
36
+ facility '999'
37
+ }
38
+ deploy('mia1') {
39
+ apps :foo, :bar, :baz
40
+ facility '888'
41
+ }
42
+ }
43
+ }
44
+
45
+ environment(:prod) {
46
+ promotes_from :integ
47
+ org('mtn') {
48
+ deploy('aws') {
49
+ apps :foo
50
+ }
51
+ }
52
+ org('acme') {
53
+ deploy('minnow1') {
54
+ enabled false
55
+ apps :foo, :bar, :baz
56
+ facility '42'
57
+ }
58
+ deploy('minnow1') {
59
+ apps :bang
60
+ option 'mapping', 'acme-stuff.com'
61
+ option 'do-dad', 'flim-flam'
62
+ }
63
+ deploy('bounty1') {
64
+ apps :foo, :bar, :baz
65
+ facility '100'
66
+ }
67
+ deploy('bounty1') {
68
+ apps :bang
69
+ option 'mapping', 'acme-stuff.com'
70
+ option 'do-dad', 'woo-woo'
71
+ }
72
+ }
73
+ }
74
+ ```
75
+
76
+ <dl>
77
+ <dt>pipeline</dt>
78
+ <dd>any number of application pipelines. Takes a symbol for the pipeline, and defines the apps that are delivered by that pipeline</dd>
79
+
80
+ <dt>app</dt>
81
+ <dd>any number of applications. Takes a symbol for the app, a maven group_id:artifact_id and the type of platform the app uses (:jvm or :rails_zip)</dd>
82
+
83
+ <dt>environment</dt>
84
+ <dd>any number of environments. takes a symbol for the environment, and any number of `deploy`s. NOTE: the order is _not_ significant.</dd>
85
+
86
+ <dt>promotes_from</dt>
87
+ <dd>a declaration to control what environment the provisioning uses to check for new versions to promote. The absence of this declartation means that the artifact repository will be checked for the latest version</dd>
88
+
89
+ <dt>org</dt>
90
+ <dd>scopes the deployment to the organization served by those deployments.</dd>
91
+
92
+ <dt>deploy</dt>
93
+ <dd>describes a deployment to the given ship-cloud. It takes a key for the ship, and some attributes</dd>
94
+
95
+ <dt>endabled</dt>
96
+ <dd>deployments will be avoided if not 'true', default value is 'true' if absent</dd>
97
+
98
+ <dt>apps</dt>
99
+ <dd>a list of symbols for applications (defined in `pipeline/app` above) which should be deployed to the environment</dd>
100
+
101
+ <dt>facility</dt>
102
+ <dd>the facitility id</dd>
103
+
104
+ <dt>option</dt>
105
+ <dd>a way to define a key/value pair for deploy/app specific configurations (can be used multiple times to define N number key/value pairs)</dd>
106
+ <dl>
107
+
108
+ ## Usage
109
+
110
+ ```
111
+ $ conan [options] pipeline environment"
112
+
113
+ pipeline - name of the pipeline to use
114
+ environment - name of a the target environment: integ,stage,prod
115
+
116
+ Options:
117
+ -d, --output-dir [DIR] Generate manifest data to this directory
118
+ -f, --output-format [FORMAT] The format used to write output. Either "properties" for Jenkins build properties
119
+ or "stackato" (default) to provision binaries and write stackato yml files
120
+ from project templates.
121
+ -a, --action [ACTION] The action(s) to perform. Either "provision" (default), "configure","deploy" or "all".
122
+ Support for blue/green deployments use actions: "provision", "bg_configure", "bg_deploy",
123
+ "bg_switch", "bg_clean" or "bg_all".
124
+ -u, --paas-user [USER] The stackato user name for deployments
125
+ -p, --paas-password [PASSWORD] The stackato password name for deployments
126
+ -F, --force-deploy Force deployment to bypass version checks.
127
+ -N, --new-relic-api-key New Relic API key for deployment alerts
128
+ -s [ORG-SHIP], Only include deployments for the given shipcloud (org-ship)
129
+ --deploy-shipcloud
130
+ -D, --dry-run Deployment dry-run. Prints Stackato commands
131
+ -V, --verbose Verbose console output
132
+ -v, --version Show the software version
133
+ -h, --help Display this screen
134
+
135
+
136
+ ```
137
+
138
+
139
+ There are three phases... provision, configure, deploy, The current pipeline only preforms the provision phase.
140
+
141
+ 1. provision: Locate the binaries to deploy, download them from the artifact repository, and persist version information used to pin artifact versions and provides a workflow/promotion process. Version information (now really just the artifact meta-data from the artifact repository, instead of a properties file) is persisted in the `environments` directory tree. Artifacts that are downloaded are staged for configuration and deployment in `tmp/apps`. Artifacts which are tar-balls are extracted in the staging directory. Projects should provide configuration templates in a `deploy-templates` directory (assuming that get's approval from the Rails crowd). For jar artifacts, it attempts to extract configuration templates from the jar, at `META-INF/deploy/templates` and puts them in the staging directory at the same `deploy-templates` location.
142
+
143
+ 2. configure: Generate deployment-time configuration files from the model. It uses any ERB templates found at `deploy-templates` in the staging directory. The templates generate files in the staging root directory, with the same name as the template, but the extension is removed. e.g. `stackato.yml.erb` -> `stackato.yml`
144
+
145
+ 3. deploy: Run Stackato deployment of the application staging directory. It will avoid doing the deployment unless no service is found at the target hostname, the version check shows an older version running at the target hostname, or the `-F` flag forces deployment.
146
+
147
+ _TBD: B/G actions_
148
+
149
+ ## Templates
150
+
151
+ Templates for deloyment artifacts are .erb files (Erubis user guide: http://www.kuwata-lab.com/erubis/users-guide.html).
152
+
153
+ The templates are given a Context when it is evaluated. The Context provides the template with a set of key/values. The expected set of
154
+ these is still maturing but here is an example of what is currently provided for a seanetsevice deployment, which has options defined:
155
+
156
+ ```
157
+ app.artifact_id = "seanetservice"
158
+ app.extension =
159
+ app.group_id = "com.mtnsat"
160
+ app.id = foo
161
+ app.platform_type = rails_zip
162
+ app.sha1 =
163
+ app.version =
164
+ deploy.app_ids = [:foo]
165
+ deploy.apps = []
166
+ deploy.enabled? = true
167
+ deploy.environment.deployments = [mtn-central, mtn-minnow1, mtn-minnow1, mtn-minnow1]
168
+ deploy.environment.id = integ
169
+ deploy.environment.upstream_env =
170
+ deploy.facility_id = "10007"
171
+ deploy.shipcloud = "mtn-minnow1"
172
+ deploy.options = {"mapping"=>["integ.example.com"], "network"=>["guest=1.1.1.10", "crew=1.1.1.91", "cloud=1.1.1.91"]}
173
+ deploy.org = "mtn"
174
+ deploy.ship = "minnow1"
175
+ deploy_name = "foo-dev-minnow1"
176
+ ```
177
+
178
+ App tokens for oauth are a special case, and assigned to special keys that will look something like:
179
+ ```
180
+ oauth_id = application-gs8f8ae6-64a9-c987-c470-9c18b6fb918d
181
+ oauth_secret = "4z3r6e779455d89db8896b3f4d8c2369ae71e9ca662abb366eea905a41de1dcf"
182
+ ```
183
+
184
+ The are also a number of shortcuts provided for shorter keys:
185
+ ```
186
+ app_id => app.id
187
+ env => deploy.environment.id
188
+ org => deploy.org
189
+ ship => deploy.ship
190
+ shipcloud => deploy.shipcloud
191
+ infra => deploy.shipcloud
192
+ facility => deploy.facility_id
193
+ options => deploy.options
194
+ ```
195
+ Warning: infra will be removed at some point
196
+
197
+ Here is an example Stackato template:
198
+ ```erb
199
+ name: <%= @deploy_name %>
200
+ instances: 1
201
+ mem: 1G
202
+ framework:
203
+ name: standalone
204
+ runtime: java7
205
+ command: "java -Xms768m -Xmx768m -Dlog.logstash.source=\"${name}\" -Dlogback.configurationFile=logback-logstash.xml -jar poseidon.jar <%= @env %>"
206
+ url:
207
+ - <%= @env %>.poseidon.<%= @ship %>.<%= @org %>.mtnsatcloud.com
208
+ - <%= @env %>.poseidon.mtnsatcloud.com
209
+ min_version:
210
+ client: 1.4.3
211
+ services:
212
+ ${name}-db: mysql
213
+ ```
214
+
215
+
216
+ ## Test
217
+
218
+ $ bundle install
219
+ $ bundle exec rspec
220
+
221
+ To continously run specs when files are saved (or you hit return at the prompt):
222
+
223
+ $ bundle exec guard
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env ruby
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'
10
+ require 'conan/deploy'
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'
18
+ require 'conan/options'
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'
26
+ options = {}
27
+
28
+ optparse = OptionParser.new do |o|
29
+ o.banner = "Usage: conan [options] pipeline environment"
30
+ o.separator ""
31
+ o.separator " pipeline - name of the pipeline to use"
32
+ o.separator " environment - name of a the target environment: integ,stage,prod"
33
+ o.separator ""
34
+ o.separator " Options:"
35
+
36
+ o.on( '-d', '--output-dir [DIR]', 'Generate manifest data to this directory' ) do |dir|
37
+ options[:directory] = dir
38
+ end
39
+
40
+ o.on( '-f', '--output-format [FORMAT]', [:properties, :stackato],
41
+ 'The format used to write output. Either "properties" for Jenkins build properties',
42
+ ' or "stackato" (default) to provision binaries and write stackato yml files',
43
+ ' from project templates.' ) do |format|
44
+ if (format)
45
+ options[:format] = format
46
+ else
47
+ puts "Unsupported --format option"
48
+ puts o
49
+ exit
50
+ end
51
+ end
52
+
53
+ o.on('-a','--action [ACTION]', [:provision,:configure,:deploy,:bg_configure,:bg_deploy,:all,:bg_all],
54
+ 'The action(s) to perform. Either "provision" (default), "configure","deploy" or "all".',
55
+ ' Support for blue/green deployments use actions: "provision", "bg_configure", "bg_deploy",',
56
+ ' or "bg_all".') do |action|
57
+ if (action)
58
+ options[:action] = action
59
+ else
60
+ puts "Unsupported --action option"
61
+ puts o
62
+ exit
63
+ end
64
+ end
65
+
66
+ o.on('-u','--paas-user [USER]', 'The stackato user name for deployments' ) do |user|
67
+ options[:'paas-user'] = user
68
+ end
69
+
70
+ o.on('-p','--paas-password [PASSWORD]', 'The stackato password name for deployments' ) do |pw|
71
+ options[:'paas-password'] = pw
72
+ end
73
+
74
+ o.on('-S','--paas-space [SPACE]', 'The stackato space for deployments' ) do |space|
75
+ options[:'paas-space'] = space
76
+ end
77
+
78
+ o.on('-F','--force-deploy', 'Force deployment to bypass version checks.' ) do
79
+ options[:'force-deploy'] = true
80
+ end
81
+
82
+ o.on('-N', '--new-relic-api-key', 'New Relic API key for deployment alerts') do |api_key|
83
+ options[:'new-relic-api-key'] = api_key
84
+ end
85
+
86
+ o.on("-s", '--deploy-shipcloud [ORG-SHIP]', 'Only include deployments for the given shipcloud (org-ship)') do |shipcloud|
87
+ options[:'deploy-shipcloud'] = shipcloud
88
+ end
89
+
90
+ o.on("-n", '--deploy-app-name [APPNAME]', 'Only include deployments for the given application name') do |appname|
91
+ options[:'deploy-app-name'] = appname
92
+ end
93
+
94
+ o.on("-r", '--deploy-app-version [APPVERSION]', 'Only include deployments for the given application version') do |appversion|
95
+ options[:'deploy-app-version'] = appversion
96
+ end
97
+
98
+ o.on('-D','--dry-run', 'Deployment dry-run. Prints Stackato commands' ) do
99
+ options[:'dry-run'] = true
100
+ end
101
+
102
+ o.on("-v", "--version", "Show the software version") do
103
+ puts Conan::VERSION
104
+ exit
105
+ end
106
+
107
+ o.on_tail( '-h', '--help', 'Display this screen' ) do
108
+ puts o
109
+ exit
110
+ end
111
+ end
112
+ optparse.parse!
113
+
114
+
115
+ if (ARGV.size < 1)
116
+ puts optparse
117
+ puts ''
118
+ raise ArgumentError, "No pipeline name argument was provided, e.g. nexus-apps-deploy"
119
+ else
120
+ options[:pipeline] = ARGV[0].downcase.to_sym
121
+ end
122
+
123
+ if (ARGV.size < 2)
124
+ puts optparse
125
+ puts ''
126
+ raise ArgumentError, "No environment name argument was provided, e.g. integ"
127
+ else
128
+ options[:environment] = ARGV[1].downcase.to_sym
129
+ end
130
+
131
+ Conan::Deploy.run(Conan::Options.new(options))
@@ -0,0 +1,113 @@
1
+
2
+ class Application
3
+
4
+ attr_accessor :id, :platform_type, :group_id, :artifact_id, :version, :extension, :sha1, :artifact_meta_data, :url_segment,
5
+ :org, :infra
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
+ end
19
+
20
+ def findRequestedVersion(requested_version='LATEST')
21
+ puts("Finding requested version: #{requested_version}")
22
+ extension = case platform_type
23
+ when :jvm
24
+ "jar"
25
+ when :rails_zip
26
+ "tar.gz"
27
+ else
28
+ raise "Unsupported platform type: #{platform_type}"
29
+ end
30
+ readArtifactMetadata(@artifact_repo.resolveArtifact(group_id, artifact_id, extension, requested_version))
31
+ puts "- Found version #{version} #{@id} artifact\n"+
32
+ " Artifact: #{self}\n" +
33
+ " Checksum: #{@sha1}"
34
+ end
35
+
36
+ def readArtifactMetadata(xml)
37
+ @artifact_meta_data = xml
38
+ begin
39
+ doc = REXML::Document.new(xml)
40
+ rescue Exception => e
41
+ puts "Failed to parse meta-data: #{e}"
42
+ puts "--------------------------------"
43
+ puts xml
44
+ puts "--------------------------------"
45
+ raise "Unreadable artifact meta-data for #{self}"
46
+ end
47
+
48
+ meta_data = doc.elements['/artifact-resolution/data']
49
+ @version = meta_data.elements['version'].text
50
+ @sha1 = meta_data.elements['sha1'].text
51
+ @extension = meta_data.elements['extension'].text
52
+ end
53
+
54
+ def download(location)
55
+ puts "- Provision #{group_id}:#{artifact_id}:#{extension}:#{version}"
56
+ @artifact_repo.downloadArtifact(location, group_id, artifact_id, extension, version, sha1)
57
+ end
58
+
59
+ def isDeployedAt(url)
60
+ # only deploy if an older version is found, or no app found
61
+ res = false
62
+ begin
63
+ response = RestClient::Request.new(
64
+ :method => :get,
65
+ :url => url,
66
+ :headers => {
67
+ 'accept' => :json,
68
+ 'content_type' => :json
69
+ },
70
+ :timeout => 5,
71
+ :open_timeout => 5
72
+ ).execute
73
+ if (response.code == 200)
74
+ build_metadata = JSON.parse(response.to_str)
75
+ puts ''
76
+ puts "+- Currently at #{url}:"
77
+ printM = ->(s) {
78
+ puts " | #{s}: #{build_metadata[s]}"
79
+ }
80
+ printM.call 'Implementation-Title'
81
+ printM.call 'Build-Version'
82
+ printM.call 'Build-Url'
83
+ printM.call 'Git-Commit'
84
+ current_ver = build_metadata["Build-Version"]
85
+ end
86
+ #TODO make sure the artifact id matches too, currently finding "Implementation-Title": "apollo" in build metadata
87
+ if (compareVersion(@version, current_ver))
88
+ puts "#{@artifact_id} #{@version} is newer than version found at #{url}: #{current_ver}"
89
+ else
90
+ puts "#{@artifact_id} #{@version} found at #{url}. (skipping deployment)"
91
+ res = true
92
+ end
93
+ rescue => e
94
+ puts "#{@artifact_id} not found at #{url}: #{e}"
95
+ end
96
+ res
97
+ end
98
+
99
+ def compareVersion(target_version, deployed_version)
100
+ target = Gem::Version.new(target_version)
101
+ begin
102
+ found = Gem::Version.new(deployed_version)
103
+ target > found
104
+ rescue => e
105
+ puts "Error parsing version number: #{e}"
106
+ false
107
+ end
108
+ end
109
+
110
+ def to_s
111
+ "#{artifact_id}-#{version}.#{extension}"
112
+ end
113
+ end
@@ -0,0 +1,120 @@
1
+ require 'rest_client'
2
+ require 'json'
3
+ require 'ostruct'
4
+
5
+ module RestClientExt
6
+ include RestClient
7
+
8
+ RETRIES = 3
9
+
10
+ def self.handle_network_error(verb, err, url, tries)
11
+ additional_tries = tries - 1
12
+ message = "#{verb} to #{url} failed with (#{err.ai}), retrying #{additional_tries} more times"
13
+
14
+ case err
15
+ when Errno::ETIMEDOUT, Errno::ECONNREFUSED, RestClient::ServerBrokeConnection, RestClient::RequestTimeout
16
+ puts message
17
+ sleep 1
18
+ else
19
+ raise err
20
+ end
21
+
22
+ additional_tries
23
+ end
24
+
25
+ def self.request_options(verb, url, headers, payload = nil)
26
+ opts = {
27
+ :method => verb.downcase.to_sym,
28
+ :url => url,
29
+ :headers => headers.clone,
30
+ :timeout => headers[:timeout] || 10,
31
+ :open_timeout => headers[:open_timeout] || 10
32
+ }
33
+
34
+ payload ? opts.merge(:payload => payload) : opts
35
+ end
36
+
37
+ def self.execute_request(verb, url, headers, payload = nil, tries = RETRIES, &block)
38
+ Request.execute(request_options(verb, url, headers, payload),&block)
39
+ rescue SystemCallError, Exception => err
40
+ retry unless (tries = handle_network_error(verb, err, url, tries)).zero?
41
+ end
42
+
43
+ def self.get(url, headers={}, &block)
44
+ execute_request("GET", url, headers, &block)
45
+ end
46
+
47
+ def self.post(url, payload, headers={}, &block)
48
+ execute_request("POST", url, headers, payload, &block)
49
+ end
50
+
51
+ def self.put(url, payload, headers={}, &block)
52
+ execute_request("PUT", url, headers, payload, &block)
53
+ end
54
+
55
+ def self.delete(url, headers={}, &block)
56
+ execute_request("DELETE", url, headers, &block)
57
+ end
58
+ end
59
+
60
+ module ApiHelper
61
+
62
+ def self.defaultHeaders(headers = {})
63
+ default_headers = {
64
+ :user_agent => 'CONAN_DA_DEPLOYER',
65
+ :accept => :json,
66
+ :accept_encoding => :gzip,
67
+ :content_type => :json }
68
+
69
+ default_headers = default_headers.merge(headers).delete_if { |k, v| v.nil? }
70
+ return default_headers
71
+ end
72
+
73
+ def self.smoke_test(url, timeout)
74
+ puts "Smoke Testing #{url}"
75
+ RestClientExt.get(
76
+ url,
77
+ defaultHeaders(
78
+ :timeout => timeout,
79
+ :open_timeout => timeout)) do |response, request, result, &block|
80
+ return NexusResponse.new(response.code, response.body)
81
+ end
82
+ end
83
+ end
84
+
85
+ class NexusResponse
86
+ attr_accessor :code
87
+ attr_accessor :json
88
+ attr_accessor :raw_json
89
+
90
+ def initialize(code, raw_json)
91
+ @code = code
92
+
93
+ begin
94
+ @json = OpenStruct.new(JSON.parse(Zlib::GzipReader.new(StringIO.new(raw_json)).read))
95
+ @raw_json = Zlib::GzipReader.new(StringIO.new(raw_json)).read
96
+ rescue Zlib::Error
97
+ begin
98
+ @json = OpenStruct.new(JSON.parse(raw_json))
99
+ @raw_json = raw_json
100
+ rescue JSON::ParserError
101
+ puts "NexusResponse - could not parse following JSON:\n#{raw_json}"
102
+ @json = OpenStruct.new(JSON.parse("{ \"status\" : \"error\", \"errorMessage\" : \"Previous call likely returned a Stackato HTML error page\" }"))
103
+ @raw_json = raw_json
104
+ end
105
+ rescue Zlib::GzipFile::Error
106
+ begin
107
+ @json = OpenStruct.new(JSON.parse(raw_json))
108
+ @raw_json = raw_json
109
+ rescue JSON::ParserError
110
+ puts "NexusResponse - could not parse following JSON:\n#{raw_json}"
111
+ @json = OpenStruct.new(JSON.parse("{ \"status\" : \"error\", \"errorMessage\" : \"Previous call likely returned a Stackato HTML error page\" }"))
112
+ @raw_json = raw_json
113
+ end
114
+ rescue JSON::ParserError
115
+ puts "NexusResponse - could not parse following JSON:\n#{raw_json}"
116
+ @json = OpenStruct.new(JSON.parse("{ \"status\" : \"error\", \"errorMessage\" : \"Previous call likely returned a Stackato HTML error page\" }"))
117
+ @raw_json = raw_json
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,41 @@
1
+ require 'conan/manifest_builder'
2
+ require 'optparse'
3
+
4
+ module Conan
5
+ class Deploy
6
+ def self.run(options)
7
+ puts "> Pipeline: #{options[:pipeline]}"
8
+ puts "> Environment: #{options[:environment]}"
9
+
10
+ manifest = ManifestBuilder.build(options) {
11
+ m = File.join(options[:directory], 'environments.rb')
12
+ puts "> Manifest: #{m}"
13
+ instance_eval(File.read(m), m)
14
+ }
15
+
16
+ case options[:action]
17
+ when :provision
18
+ manifest.provision
19
+ when :configure
20
+ manifest.configure
21
+ when :deploy
22
+ manifest.deploy
23
+ when :bg_configure
24
+ manifest.bg_configure
25
+ when :bg_deploy
26
+ manifest.bg_deploy
27
+ when :all
28
+ manifest.provision
29
+ manifest.configure
30
+ manifest.deploy
31
+ when :bg_all
32
+ manifest.provision
33
+ manifest.bg_configure
34
+ manifest.bg_deploy
35
+
36
+ else
37
+ raise ArgumentError.new "Invalid action: #{options[:action]}"
38
+ end
39
+ end
40
+ end
41
+ end