conan_deploy 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +216 -0
- data/bin/conan +105 -0
- data/lib/conan/deploy.rb +46 -0
- data/lib/conan/manifest_builder.rb +494 -0
- data/lib/conan/newrelic.rb +16 -0
- data/lib/conan/options.rb +31 -0
- data/lib/conan/output.rb +169 -0
- data/lib/conan/repository.rb +95 -0
- data/lib/conan/stackato.rb +197 -0
- data/lib/conan/templates.rb +38 -0
- data/lib/conan/version.rb +3 -0
- metadata +57 -0
data/README.md
ADDED
@@ -0,0 +1,216 @@
|
|
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 `manifest.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 `manifest.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
|
+
The are also a number of shortcuts provided for shorter keys:
|
178
|
+
```
|
179
|
+
app_id => app.id
|
180
|
+
env => deploy.environment.id
|
181
|
+
org => deploy.org
|
182
|
+
ship => deploy.ship
|
183
|
+
shipcloud => deploy.shipcloud
|
184
|
+
infra => deploy.shipcloud
|
185
|
+
facility => deploy.facility_id
|
186
|
+
options => deploy.options
|
187
|
+
```
|
188
|
+
Warning: infra will be removed at some point
|
189
|
+
|
190
|
+
Here is an example Stackato template:
|
191
|
+
```erb
|
192
|
+
name: <%= @deploy_name %>
|
193
|
+
instances: 1
|
194
|
+
mem: 1G
|
195
|
+
framework:
|
196
|
+
name: standalone
|
197
|
+
runtime: java7
|
198
|
+
command: "java -Xms768m -Xmx768m -Dlog.logstash.source=\"${name}\" -Dlogback.configurationFile=logback-logstash.xml -jar poseidon.jar <%= @env %>"
|
199
|
+
url:
|
200
|
+
- <%= @env %>.poseidon.<%= @ship %>.<%= @org %>.mtnsatcloud.com
|
201
|
+
- <%= @env %>.poseidon.mtnsatcloud.com
|
202
|
+
min_version:
|
203
|
+
client: 1.4.3
|
204
|
+
services:
|
205
|
+
${name}-db: mysql
|
206
|
+
```
|
207
|
+
|
208
|
+
|
209
|
+
## Test
|
210
|
+
|
211
|
+
$ bundle install
|
212
|
+
$ bundle exec rspec
|
213
|
+
|
214
|
+
To continously run specs when files are saved (or you hit return at the prompt):
|
215
|
+
|
216
|
+
$ bundle exec guard
|
data/bin/conan
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'conan/deploy'
|
4
|
+
require 'conan/version'
|
5
|
+
require 'conan/options'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
options = {}
|
9
|
+
|
10
|
+
optparse = OptionParser.new do |o|
|
11
|
+
o.banner = "Usage: conan [options] pipeline environment"
|
12
|
+
o.separator ""
|
13
|
+
o.separator " pipeline - name of the pipeline to use"
|
14
|
+
o.separator " environment - name of a the target environment: integ,stage,prod"
|
15
|
+
o.separator ""
|
16
|
+
o.separator " Options:"
|
17
|
+
|
18
|
+
o.on( '-d', '--output-dir [DIR]', 'Generate manifest data to this directory' ) do |dir|
|
19
|
+
options[:directory] = dir
|
20
|
+
end
|
21
|
+
|
22
|
+
o.on( '-f', '--output-format [FORMAT]', [:properties, :stackato],
|
23
|
+
'The format used to write output. Either "properties" for Jenkins build properties',
|
24
|
+
' or "stackato" (default) to provision binaries and write stackato yml files',
|
25
|
+
' from project templates.' ) do |format|
|
26
|
+
if (format)
|
27
|
+
options[:format] = format
|
28
|
+
else
|
29
|
+
puts "Unsupported --format option"
|
30
|
+
puts o
|
31
|
+
exit
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
o.on('-a','--action [ACTION]', [:provision,:configure,:deploy,:bg_configure,:bg_deploy,:bg_switch,:bg_clean,:all,:bg_all],
|
36
|
+
'The action(s) to perform. Either "provision" (default), "configure","deploy" or "all".',
|
37
|
+
' Support for blue/green deployments use actions: "provision", "bg_configure", "bg_deploy",',
|
38
|
+
' "bg_switch", "bg_clean" or "bg_all".') do |action|
|
39
|
+
if (action)
|
40
|
+
options[:action] = action
|
41
|
+
else
|
42
|
+
puts "Unsupported --action option"
|
43
|
+
puts o
|
44
|
+
exit
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
o.on('-u','--paas-user [USER]', 'The stackato user name for deployments' ) do |user|
|
49
|
+
options[:'paas-user'] = user
|
50
|
+
end
|
51
|
+
|
52
|
+
o.on('-p','--paas-password [PASSWORD]', 'The stackato password name for deployments' ) do |pw|
|
53
|
+
options[:'paas-password'] = pw
|
54
|
+
end
|
55
|
+
|
56
|
+
o.on('-F','--force-deploy', 'Force deployment to bypass version checks.' ) do
|
57
|
+
options[:'force-deploy'] = true
|
58
|
+
end
|
59
|
+
|
60
|
+
o.on('-N', '--new-relic-api-key', 'New Relic API key for deployment alerts') do |api_key|
|
61
|
+
options[:'new-relic-api-key'] = api_key
|
62
|
+
end
|
63
|
+
|
64
|
+
o.on("-s", '--deploy-shipcloud [ORG-SHIP]', 'Only include deployments for the given shipcloud (org-ship)') do |shipcloud|
|
65
|
+
options[:'deploy-shipcloud'] = shipcloud
|
66
|
+
end
|
67
|
+
|
68
|
+
o.on('-D','--dry-run', 'Deployment dry-run. Prints Stackato commands' ) do
|
69
|
+
options[:'dry-run'] = true
|
70
|
+
end
|
71
|
+
|
72
|
+
o.on('-V','--verbose', 'Verbose console output' ) do
|
73
|
+
options[:verbose] = true
|
74
|
+
end
|
75
|
+
|
76
|
+
o.on("-v", "--version", "Show the software version") do
|
77
|
+
puts Conan::VERSION
|
78
|
+
exit
|
79
|
+
end
|
80
|
+
|
81
|
+
o.on_tail( '-h', '--help', 'Display this screen' ) do
|
82
|
+
puts o
|
83
|
+
exit
|
84
|
+
end
|
85
|
+
end
|
86
|
+
optparse.parse!
|
87
|
+
|
88
|
+
|
89
|
+
pl = if (ARGV.size < 1)
|
90
|
+
puts optparse
|
91
|
+
puts ''
|
92
|
+
raise ArgumentError, "No pipeline name argument was provided, e.g. nexus-apps-deploy"
|
93
|
+
else
|
94
|
+
ARGV[0].downcase.to_sym
|
95
|
+
end
|
96
|
+
|
97
|
+
env = if (ARGV.size < 2)
|
98
|
+
puts optparse
|
99
|
+
puts ''
|
100
|
+
raise ArgumentError, "No environment name argument was provided, e.g. integ"
|
101
|
+
else
|
102
|
+
ARGV[1].downcase.to_sym
|
103
|
+
end
|
104
|
+
|
105
|
+
Conan::Deploy.run(Conan::Options.new(options), pl, env)
|
data/lib/conan/deploy.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'conan/manifest_builder'
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
module Conan
|
5
|
+
class Deploy
|
6
|
+
def self.run(options, pl, env)
|
7
|
+
puts "> Pipeline: #{pl}"
|
8
|
+
puts "> Environment: #{env}"
|
9
|
+
|
10
|
+
manifest = ManifestBuilder.build(options, pl, env) {
|
11
|
+
m = File.join(options[:directory], 'manifest.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 :bg_switch
|
28
|
+
manifest.bg_switch
|
29
|
+
when :bg_clean
|
30
|
+
manifest.bg_clean
|
31
|
+
when :all
|
32
|
+
manifest.provision
|
33
|
+
manifest.configure
|
34
|
+
manifest.deploy
|
35
|
+
when :bg_all
|
36
|
+
manifest.provision
|
37
|
+
manifest.bg_configure
|
38
|
+
manifest.bg_deploy
|
39
|
+
manifest.bg_switch
|
40
|
+
manifest.bg_clean
|
41
|
+
else
|
42
|
+
raise ArgumentError.new "Invalid action: #{options[:action]}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|