gantree 0.0.9 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +5 -1
- data/README.md +64 -0
- data/lib/gantree/cfn.rb +17 -2
- data/lib/gantree/cfn/beanstalk.rb +29 -1
- data/lib/gantree/cfn/master.rb +13 -5
- data/lib/gantree/cfn/resources.rb +45 -0
- data/lib/gantree/cli.rb +3 -0
- data/lib/gantree/init.rb +10 -1
- data/lib/gantree/version.rb +1 -1
- data/spec/lib/cli_spec.rb +17 -5
- data/spec/spec_helper.rb +1 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e6dd186b31072e4741042bc7d9c5a4388a7c391d
|
4
|
+
data.tar.gz: 4c56ecf06781010d285322daaf5d65c77a09b061
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d082baaffe38b7741065e1839a11e120f3ed1813fec06be5dd62bb5498c978ec9035414077276fb2d103f88cfdbd0c1c0882282c6b3323f2b7bdf0c6c5a2852
|
7
|
+
data.tar.gz: 8a8c83091488b7799f148bcb5388e8b56fead36d20f4136a97154723d6e38e731fd0d2b03775c4e297701ebb1b1b7ae882e90fc38ef1c819107937c777db69f3
|
data/.travis.yml
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
language: ruby
|
2
2
|
cache: bundler
|
3
3
|
rvm:
|
4
|
-
|
4
|
+
- 2.1.2
|
5
|
+
env:
|
6
|
+
global:
|
7
|
+
- secure: OoLMnBOcoXTPOimdyh4ju8yW2YpLBXEOdljN1gVQzjIIv24cQ0BxWAAktjExntkYAjnRpxzTKK5ZkWadXm8NK7TtzFU80padk+WYVzuE1Xngxw/OhS1Z6NYL8+D3ortSANqoOB6r/Z13Sb5XClYDWlj5/GGzf4AerY5xgh2nJQc=
|
8
|
+
- secure: MCPnXXEMJkamb3HuYS0H+YeMDKORYAekCfB8YMrtUeMR3QTSe2yQ6eMaNC/I2D5g8cNw60mRagaMyDMadZ4aVQ4sUK1YOHexs/vPJrVrIJU8ZGFsueQyboJun1NZLGvKAIAok7yAEEfwftCd+svYrtrTtV88fk9deOnBLs4o3gM=
|
data/README.md
CHANGED
@@ -65,3 +65,67 @@ You can also specify a new image tag to use for the deploy
|
|
65
65
|
```
|
66
66
|
gantree deploy -t latest stag-cauldon-app-s1
|
67
67
|
```
|
68
|
+
|
69
|
+
### Create Stacks
|
70
|
+
|
71
|
+
Gantree allows you to leverage the power of aws cloud formation to create your entire elastic beanstalk stack, rds, caching layer etc all while maintaining a set naming convention. This does the following:
|
72
|
+
* uses the ruby-cloudformation-dsl to generate nested cloud formation templates inside a cfn folder in your repo
|
73
|
+
* uploads them to an s3 bucket
|
74
|
+
* uses aws-sdk to communicate with cfn and initiate the stack creation
|
75
|
+
|
76
|
+
To generate a basic staging cluster for linguist we would do:
|
77
|
+
```
|
78
|
+
gantree create stag-linguist-app-s1
|
79
|
+
```
|
80
|
+
|
81
|
+
In the elastic beanstalk console you will now see an application called
|
82
|
+
**linguist-stag-s1** with an environment called **stag-linguist-app-s1**
|
83
|
+
|
84
|
+
You can modify the name of the environment if this does not fit your naming convention:
|
85
|
+
```
|
86
|
+
gantre create your_app_name -e your_env_name
|
87
|
+
```
|
88
|
+
|
89
|
+
## TODO:
|
90
|
+
|
91
|
+
### .gantreecfg
|
92
|
+
Allow defaults for commands to be set in a json file
|
93
|
+
```json
|
94
|
+
{
|
95
|
+
"ebextensions" : "configs/.ebextensions",
|
96
|
+
"default_instance_size" : "m3.medium"
|
97
|
+
}
|
98
|
+
```
|
99
|
+
|
100
|
+
#### .ebextension Support
|
101
|
+
Elastic Beanstalk cli allows you to create a .ebextension folder that you can package with your deploy to control the host/environment of your application. Deploying only a docker container image referenced in Dockerrun.aws.json has the unfortunate side effect of losing this extreamly powerful feature. To allow this feature to be included in gantree and make it even better you can select either to package a local .ebextension folder with your deploy, package a remote .ebextension folder hosted in github (with branch support) or even create a .gantreecfg file to make either of these type of deploys a default.
|
102
|
+
|
103
|
+
```
|
104
|
+
gantree deploy --ext "git:br/ebextensions:master" stag-cauldron-app-s1
|
105
|
+
```
|
106
|
+
|
107
|
+
By default your application will be created on a t1.micro unless you specify otherwise:
|
108
|
+
```
|
109
|
+
gantree ceate your_app_name -i m3.medium
|
110
|
+
```
|
111
|
+
|
112
|
+
#### What if you need a database? Here enters the beauty of RDS
|
113
|
+
|
114
|
+
PostgreSQL: ```gantree create your_app_name --rds pg```
|
115
|
+
|
116
|
+
Mysql: ```gantree create your_app_name --rds msql```
|
117
|
+
|
118
|
+
#### What if you want a cdn behind each of your generated applications
|
119
|
+
|
120
|
+
Fastly: ```gantree create your_app_name --cdn fastly```
|
121
|
+
|
122
|
+
CloudFront: ```gantree create yoour_app_name --cdn cloudfront```
|
123
|
+
|
124
|
+
#### Autogenerated Release Notes
|
125
|
+
|
126
|
+
I would like have built in integration with opbeat configurable thorugh the .gantreecfg located in the applications repository.
|
127
|
+
|
128
|
+
|
129
|
+
***Notes:***
|
130
|
+
|
131
|
+
Also if the cloud formation template that is generated doesn't match your needs (which it might now) you can edit the .rb files in the repo's cfn folder, build your own gem and use it how you like.
|
data/lib/gantree/cfn.rb
CHANGED
@@ -6,11 +6,14 @@ require_relative 'cfn/resources'
|
|
6
6
|
module Gantree
|
7
7
|
class Stack
|
8
8
|
def initialize stack_name,options
|
9
|
+
@options = options
|
10
|
+
check_credentials
|
9
11
|
AWS.config(
|
10
12
|
:access_key_id => ENV['AWS_ACCESS_KEY_ID'],
|
11
13
|
:secret_access_key => ENV['AWS_SECRET_ACCES_KEY'])
|
12
14
|
@s3 = AWS::S3.new
|
13
15
|
@cfm = AWS::CloudFormation.new
|
16
|
+
@size = options[:instance_size] ||= "t1.micro"
|
14
17
|
@requirements = "#!/usr/bin/env ruby
|
15
18
|
require 'bundler/setup'
|
16
19
|
require 'cloudformation-ruby-dsl/cfntemplate'
|
@@ -19,20 +22,32 @@ module Gantree
|
|
19
22
|
@stack_name = stack_name
|
20
23
|
@env = options[:env] || stack_name.match(/^[a-zA-Z]*\-([a-zA-Z]*)\-[a-zA-Z]*\-([a-zA-Z]*\d*)/)[1] + "-" + stack_name.match(/^([a-zA-Z]*)\-([a-zA-Z]*)\-[a-zA-Z]*\-([a-zA-Z]*\d*)/)[1] + '-' + stack_name.match(/^([a-zA-Z]*)\-([a-zA-Z]*)\-[a-zA-Z]*\-([a-zA-Z]*\d*)/)[3]
|
21
24
|
@params = {
|
25
|
+
instance_size: @size,
|
26
|
+
rds: options[:rds],
|
22
27
|
stack_name: @stack_name,
|
23
28
|
requirements: @requirements,
|
24
29
|
cfn_bucket: "br-templates",
|
25
30
|
env: @env,
|
26
31
|
stag_domain: "sbleacherreport.com",
|
27
|
-
prod_domain: "bleacherreport.com"
|
32
|
+
prod_domain: "bleacherreport.com",
|
28
33
|
}
|
29
34
|
end
|
30
35
|
|
36
|
+
def check_credentials
|
37
|
+
raise "Please set your AWS Environment Variables" if ENV['AWS_SECRET_ACCESS_KEY'] == nil
|
38
|
+
raise "Please set your AWS Environment Variables" if ENV['AWS_ACCESS_KEY_ID'] == nil
|
39
|
+
end
|
40
|
+
|
31
41
|
def create
|
42
|
+
create_cfn_if_needed
|
32
43
|
generate("master", MasterTemplate.new(@params).create)
|
33
44
|
generate("beanstalk", BeanstalkTemplate.new(@params).create)
|
34
45
|
generate("resources", ResourcesTemplate.new(@params).create)
|
35
|
-
create_aws_cfn_stack
|
46
|
+
create_aws_cfn_stack unless @options[:dry_run]
|
47
|
+
end
|
48
|
+
|
49
|
+
def create_cfn_if_needed
|
50
|
+
Dir.mkdir 'cfn' unless File.directory?("cfn")
|
36
51
|
end
|
37
52
|
|
38
53
|
def generate(template_name, template)
|
@@ -2,6 +2,8 @@ class BeanstalkTemplate
|
|
2
2
|
|
3
3
|
def initialize params
|
4
4
|
@stack_name = params[:stack_name]
|
5
|
+
@size = params[:instance_size]
|
6
|
+
@rds = params[:rds]
|
5
7
|
@env = params[:env]
|
6
8
|
@prod_domain = params[:prod_domain]
|
7
9
|
@stag_domain = params[:stag_domain]
|
@@ -36,7 +38,7 @@ class BeanstalkTemplate
|
|
36
38
|
:Description => 'EC2 Instance Type',
|
37
39
|
:Type => 'String',
|
38
40
|
:AllowedValues => %w(t1.micro m1.small m3.medium m3.large m3.xlarge m3.2xlarge c3.large c3.xlarge c3.2xlarge c3.4xlarge c3.8xlarge),
|
39
|
-
:Default => '
|
41
|
+
:Default => '#{@size}'
|
40
42
|
|
41
43
|
parameter 'ApplicationName',
|
42
44
|
:Description => 'The name of the Elastic Beanstalk Application',
|
@@ -51,6 +53,8 @@ class BeanstalkTemplate
|
|
51
53
|
:Type => 'String',
|
52
54
|
:Default => 'EbApp'
|
53
55
|
|
56
|
+
#{"parameter 'RDSHostURLPass', :Type => 'String'" if rds_enabled? }
|
57
|
+
|
54
58
|
resource 'Application', :Type => 'AWS::ElasticBeanstalk::Application', :Properties => {
|
55
59
|
:Description => '#{@env}',
|
56
60
|
:ApplicationName => '#{@env}',
|
@@ -104,6 +108,7 @@ class BeanstalkTemplate
|
|
104
108
|
{ :Namespace => 'aws:autoscaling:updatepolicy:rollingupdate', :OptionName => 'MaxBatchSize', :Value => '1' },
|
105
109
|
{ :Namespace => 'aws:autoscaling:updatepolicy:rollingupdate', :OptionName => 'MinInstancesInService', :Value => '2' },
|
106
110
|
{ :Namespace => 'aws:elasticbeanstalk:hostmanager', :OptionName => 'LogPublicationControl', :Value => 'true' },
|
111
|
+
#{set_rds_parameters}
|
107
112
|
],
|
108
113
|
}
|
109
114
|
|
@@ -132,6 +137,29 @@ class BeanstalkTemplate
|
|
132
137
|
end.exec!
|
133
138
|
"
|
134
139
|
end
|
140
|
+
def set_rds_parameters
|
141
|
+
if rds_enabled?
|
142
|
+
"{
|
143
|
+
:Namespace => 'aws:elasticbeanstalk:application:environment',
|
144
|
+
:OptionName => 'DB_HostURL',
|
145
|
+
:Value => ref('RDSHostURLPass'),
|
146
|
+
},"
|
147
|
+
else
|
148
|
+
nil
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def rds_enabled?
|
153
|
+
if @rds == nil
|
154
|
+
puts "RDS is not enabled, no DB created"
|
155
|
+
false
|
156
|
+
elsif @rds == "pg" || @rds == "mysql"
|
157
|
+
puts "RDS is enabled, creating DB"
|
158
|
+
true
|
159
|
+
else
|
160
|
+
raise "The --rds option you passed is not supported please use 'pg' or 'mysql'"
|
161
|
+
end
|
162
|
+
end
|
135
163
|
|
136
164
|
def env_type
|
137
165
|
if @env.include?("prod")
|
data/lib/gantree/cfn/master.rb
CHANGED
@@ -2,6 +2,7 @@ class MasterTemplate
|
|
2
2
|
|
3
3
|
def initialize params
|
4
4
|
@stack_name = params[:stack_name]
|
5
|
+
@rds = params[:rds]
|
5
6
|
@env = params[:env]
|
6
7
|
@bucket = params[:cfn_bucket]
|
7
8
|
@requirements = params[:requirements]
|
@@ -27,10 +28,6 @@ class MasterTemplate
|
|
27
28
|
:Type => 'String',
|
28
29
|
:Default => 'default'
|
29
30
|
|
30
|
-
parameter 'InstanceType',
|
31
|
-
:Type => 'String',
|
32
|
-
:Default => 't1.micro'
|
33
|
-
|
34
31
|
parameter 'ApplicationName',
|
35
32
|
:Type => 'String',
|
36
33
|
:Default => '#{@env}'
|
@@ -53,10 +50,10 @@ class MasterTemplate
|
|
53
50
|
:Parameters => {
|
54
51
|
:KeyName => ref('KeyName'),
|
55
52
|
:InstanceSecurityGroup => get_att('AppResources', 'Outputs.InstanceSecurityGroup'),
|
56
|
-
:InstanceType => ref('InstanceType'),
|
57
53
|
:ApplicationName => ref('ApplicationName'),
|
58
54
|
:Environment => ref('Environment'),
|
59
55
|
:IamInstanceProfile => ref('IamInstanceProfile'),
|
56
|
+
#{":RDSHostURLPass => get_att('AppResources','Outputs.RDSHostURL')," if rds_enabled?}
|
60
57
|
},
|
61
58
|
}
|
62
59
|
|
@@ -66,6 +63,17 @@ class MasterTemplate
|
|
66
63
|
|
67
64
|
end.exec!"
|
68
65
|
end
|
66
|
+
def rds_enabled?
|
67
|
+
if @rds == nil
|
68
|
+
puts "RDS is not enabled, no DB created"
|
69
|
+
false
|
70
|
+
elsif @rds == "pg" || @rds == "mysql"
|
71
|
+
puts "RDS is enabled, creating DB"
|
72
|
+
true
|
73
|
+
else
|
74
|
+
raise "The --rds option you passed is not supported please use 'pg' or 'mysql'"
|
75
|
+
end
|
76
|
+
end
|
69
77
|
|
70
78
|
def env_type
|
71
79
|
if @env.include?("prod")
|
@@ -2,6 +2,7 @@ class ResourcesTemplate
|
|
2
2
|
|
3
3
|
def initialize params
|
4
4
|
@stack_name = params[:stack_name]
|
5
|
+
@rds = params[:rds]
|
5
6
|
@env = params[:env]
|
6
7
|
@requirements = params[:requirements]
|
7
8
|
end
|
@@ -25,7 +26,51 @@ class ResourcesTemplate
|
|
25
26
|
output 'InstanceSecurityGroup',
|
26
27
|
:Value => ref('InstanceSecurityGroup')
|
27
28
|
|
29
|
+
#{rds}
|
30
|
+
|
28
31
|
end.exec!
|
29
32
|
"
|
30
33
|
end
|
34
|
+
|
35
|
+
def rds
|
36
|
+
if rds_enabled?
|
37
|
+
"
|
38
|
+
resource 'sampleDB', :Type => 'AWS::RDS::DBInstance', :DeletionPolicy => 'Snapshot', :Properties => {
|
39
|
+
:DBName => 'sampledb',
|
40
|
+
:AllocatedStorage => '10',
|
41
|
+
:DBInstanceClass => 'db.m3.large',
|
42
|
+
:DBSecurityGroups => [ ref('DBSecurityGroup') ],
|
43
|
+
:Engine => 'postgres',
|
44
|
+
:EngineVersion => '9.3',
|
45
|
+
:MasterUsername => 'masterUser',
|
46
|
+
:MasterUserPassword => 'masterpassword',
|
47
|
+
}
|
48
|
+
|
49
|
+
resource 'DBSecurityGroup', :Type => 'AWS::RDS::DBSecurityGroup', :Properties => {
|
50
|
+
:DBSecurityGroupIngress => [
|
51
|
+
{ :EC2SecurityGroupName => ref('InstanceSecurityGroup') },
|
52
|
+
],
|
53
|
+
:GroupDescription => 'Allow Beanstalk Instances Access',
|
54
|
+
}
|
55
|
+
|
56
|
+
output 'RDSHostURL',
|
57
|
+
:Value => get_att('sampleDB', 'Endpoint.Address')
|
58
|
+
"
|
59
|
+
else
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def rds_enabled?
|
65
|
+
if @rds == nil
|
66
|
+
puts "RDS is not enabled, no DB created"
|
67
|
+
false
|
68
|
+
elsif @rds == "pg" || @rds == "mysql"
|
69
|
+
puts "RDS is enabled, creating DB"
|
70
|
+
true
|
71
|
+
else
|
72
|
+
raise "The --rds option you passed is not supported please use 'pg' or 'mysql'"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
31
76
|
end
|
data/lib/gantree/cli.rb
CHANGED
@@ -22,6 +22,9 @@ module Gantree
|
|
22
22
|
|
23
23
|
desc "create APP", "create or update a cfn stack"
|
24
24
|
method_option :env, :aliases => "-e", :desc => "(optional) environment name"
|
25
|
+
method_option :instance_size, :aliases => "-i", :desc => "(optional) set instance size"
|
26
|
+
method_option :rds, :aliases => "-r", :desc => "(optional) set database type [pg,mysql]"
|
27
|
+
method_option :dry_run, :desc => "do not actually create the stack"
|
25
28
|
def create app
|
26
29
|
Gantree::Stack.new(app, options).create
|
27
30
|
end
|
data/lib/gantree/init.rb
CHANGED
@@ -6,7 +6,7 @@ module Gantree
|
|
6
6
|
class Init
|
7
7
|
def initialize image,options
|
8
8
|
@image = image
|
9
|
-
@options = options
|
9
|
+
@options = merge_defaults(options)
|
10
10
|
AWS.config(
|
11
11
|
:access_key_id => ENV['AWS_ACCESS_KEY_ID'],
|
12
12
|
:secret_access_key => ENV['AWS_SECRET_ACCESS_KEY'])
|
@@ -54,11 +54,20 @@ module Gantree
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def upload_docker_config
|
57
|
+
raise "You need to run 'docker login' to generate a .dockercfg file" if File.exist?("#{ENV['HOME']}/.dockercfg") != true
|
57
58
|
filename = "#{ENV['HOME']}/#{@options.user}.dockercfg"
|
58
59
|
FileUtils.cp("#{ENV['HOME']}/.dockercfg", "#{ENV['HOME']}/#{@options.user}.dockercfg")
|
59
60
|
key = File.basename(filename)
|
60
61
|
@s3.buckets["docker-cfgs"].objects[key].write(:file => filename)
|
61
62
|
end
|
62
63
|
|
64
|
+
def merge_defaults(options)
|
65
|
+
if File.exist?(".gantreecfg")
|
66
|
+
defaults = JSON.parse(File.open(".gantreecfg").read)
|
67
|
+
defaults.merge(options)
|
68
|
+
else
|
69
|
+
options
|
70
|
+
end
|
71
|
+
end
|
63
72
|
end
|
64
73
|
end
|
data/lib/gantree/version.rb
CHANGED
data/spec/lib/cli_spec.rb
CHANGED
@@ -7,27 +7,39 @@ require 'spec_helper'
|
|
7
7
|
# $ rake clean:vcr ; time rake
|
8
8
|
describe Gantree::CLI do
|
9
9
|
before(:all) do
|
10
|
-
@env = "stag-app-
|
10
|
+
@env = "stag-app-knarr-s1"
|
11
11
|
@owner = "bleacher"
|
12
12
|
@repo = "cauldron"
|
13
13
|
@tag = "master"
|
14
14
|
@user = "feelobot"
|
15
|
-
end
|
15
|
+
end
|
16
16
|
|
17
|
-
describe "
|
17
|
+
describe "init" do
|
18
18
|
it "should create a new dockerrun for a private repo" do
|
19
19
|
out = execute("bin/gantree init -u #{@user} #{@owner}/#{@repo}:#{@tag}")
|
20
|
-
expect(out).to include ""
|
20
|
+
expect(out).to include "initialize image"
|
21
|
+
expect(File.exist?("Dockerrun.aws.json")).to be true
|
22
|
+
expect(IO.read("Dockerrun.aws.json").include? @user)
|
21
23
|
end
|
22
24
|
|
23
25
|
it "should create a new dockerrun for a public repo" do
|
24
26
|
out = execute("bin/gantree init #{@owner}/#{@repo}:#{@tag}")
|
25
|
-
expect(out).to include ""
|
27
|
+
expect(out).to include "initialize image"
|
28
|
+
expect(File.exist?("Dockerrun.aws.json")).to be true
|
26
29
|
end
|
30
|
+
end
|
27
31
|
|
32
|
+
describe "deploy" do
|
28
33
|
it "should deploy images" do
|
29
34
|
out = execute("bin/gantree deploy #{@env}")
|
30
35
|
expect(out).to include("Deploying")
|
31
36
|
end
|
32
37
|
end
|
38
|
+
|
39
|
+
describe "create" do
|
40
|
+
it "should create clusters" do
|
41
|
+
#out = execute("bin/gantree create #{@env}")
|
42
|
+
#expect(out).to include "Created"
|
43
|
+
end
|
44
|
+
end
|
33
45
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gantree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Felix
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-10-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|