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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4322cf9bfdbb4500943c035883b9c3f09a4d4775
4
- data.tar.gz: c7aaf5c377edcbb73e6a4ab822c05cfcc5be2db7
3
+ metadata.gz: e6dd186b31072e4741042bc7d9c5a4388a7c391d
4
+ data.tar.gz: 4c56ecf06781010d285322daaf5d65c77a09b061
5
5
  SHA512:
6
- metadata.gz: ad0e271bf16392fbad91dc42e4ff064a7166d2c2d95dcea22ef1b80eceb236eae757bae18f0e4f7167a1eef2e7950ef45324a0019a5dae5ab3e93adb8dda3158
7
- data.tar.gz: fec5d0e2e380ca032370fe983f16672230563e31b5fa6ffd041286ab56caebae01df00832382a58a45bc77e729aa06c0fe06e2991023e9107b81cedf34e9b480
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
- - 2.1.2
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 => 'm3.large'
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")
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Gantree
2
- VERSION = "0.0.9"
2
+ VERSION = "0.1.0"
3
3
  end
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-br-s1"
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 "gantree" do
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
@@ -19,6 +19,7 @@ module Helpers
19
19
  def execute(cmd)
20
20
  puts "Running: #{cmd}" if ENV['DEBUG']
21
21
  out = `#{cmd}`
22
+ raise "Stack Trance Found: \n #{out}" if out.include? "Error"
22
23
  puts out if ENV['DEBUG']
23
24
  out
24
25
  end
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.9
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-09-26 00:00:00.000000000 Z
11
+ date: 2014-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor