fum 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ .idea
4
+ Gemfile.lock
5
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in fum.gemspec
4
+ gemspec
5
+
6
+ #gem "fog", :git => "git://github.com/fog/fog.git"
7
+ gem "fog", :path => "~/ruby/fog"
data/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ Copyright (c) 2012 George Scott, RumbleWare Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation
5
+ the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
6
+ and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7
+
8
+ The above copyright notice and this permission notice shall be included in all copies or substantial
9
+ portions of the Software.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
12
+ TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
13
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
14
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
15
+ IN THE SOFTWARE.
@@ -0,0 +1,219 @@
1
+
2
+ fum is a tool for managing Amazon Web Services Elastic beanstalk lifecycle stages. fum assists with the following:
3
+
4
+ * Managing DNS changes associated with deployments
5
+ * Application version management and deployment
6
+ * Maintaing multiple lifecycle stages (test/staging/production) of an application.
7
+
8
+ == Getting started
9
+
10
+ fum is a Ruby DSL (Domain Specific Language) similar to rake, chef, and other tools. You declare application lifecycle
11
+ stages using the fum language, and then use the fum command to manage those stages.
12
+
13
+ Installing fum is simple:
14
+
15
+ sudo gem install fum
16
+
17
+ Create a file named +hello.fum+ with the following:
18
+
19
+ application_name 'HelloWorld'
20
+
21
+ stage :hello do
22
+ solution_stack "32bit Amazon Linux running Tomcat 7"
23
+ end
24
+
25
+ Tell fum about your credentials by creating a file named +.fum+ in your home directory with your AWS credentials (Note,
26
+ we recommend running these examples only in a development account until you are comfortable using fum):
27
+
28
+ access_key_id: <YOUR_ACCESS_KEY>
29
+ secret_access_key: <YOUR_SECRET_KEY>
30
+
31
+ Now, let's go ahead and launch it:
32
+
33
+ fum hello.fum launch --create hello
34
+
35
+ This command tells fum to launch a new environment using the "hello" stage definition you have provided in +hello.fum+.
36
+ By default fum will not create new applications or versions, but if you specify the +--create+ flag, then fum will
37
+ create the application and a sample version for you if none exists. In this example, fum will create an application
38
+ named "HelloWorld". It will also create an environment named "hello" corresponding to the stage you specified.
39
+
40
+ == Adding DNS support.
41
+
42
+ The previous examle was simple, and possibly a time saver, but is not particularly interesting. If you are developing
43
+ an app on elastic beanstalk, you most likely don't want your end users to access your app using
44
+ "example.elasticbeantalk.com" as the URL. Instead, you most likely want them to access your app using your own
45
+ hostname such as "www.example.com" or even "example.com" directly. The most flexible way to accomplish this is to use
46
+ AWS Route 53 which supports alias records that map a domain name to an elastic load balancer. The example below shows
47
+ how one can map a few different records types to an AWS Route 53 zone name 'example.com':
48
+
49
+ stage :hello do
50
+ solution_stack "32bit Amazon Linux running Tomcat 7"
51
+
52
+ zone 'example.com'
53
+ elb_alias :apex # Create an alias entry to the load balancer at the zone apex 'myapp.com.'
54
+ elb_alias 'www' # Create an alias entry to the load balancer at 'www.myapp.com.'
55
+ elb_cname 'www2' # Create a CNAME to the load balancer rather than an alias record.
56
+ cname 'www3' # Create a CNAME to the elastic beanstalk CNAME (useful when using swap cname feature)
57
+ end
58
+
59
+ end
60
+
61
+ Each stage declaration can have multiple zones and each zone can declare multiple records to create/update when a stage
62
+ is launched. The +elb_alias+ directive provides a domain name that should be mapped, you can specify +:apex+ if you
63
+ would like to map an alias to the zone apex, in this case 'example.com'. The +elb_cname+ creates a CNAME record to
64
+ the load balancer's DNS name, while the +cname+ record creates a CNAME record pointing to the elastic beanstalk
65
+ environment's cname.
66
+
67
+ To run this configuration create a zone named 'example.com' using the AWS Managmenent Console and run the following
68
+ command:
69
+
70
+ fum hello_r53.fum launch hello
71
+
72
+ When you run the command, you will notice a few differences in the output from before. First, the newly created
73
+ environment is
74
+
75
+ As an alternative to using AWS Route 53, you can use elastic beanstalk's cname prefix and swapping features to manage
76
+ your environments. This is configured as follows:
77
+
78
+ stage :hello do
79
+ solution_stack "32bit Amazon Linux running Tomcat 7"
80
+
81
+ cname_prefix "example", :swap => true
82
+ end
83
+
84
+ The +cname_prefix+ declaration tells fum what prefix to request for the beanstalk environment when it is launched.
85
+ By default, fum does not specify a prefix, and lets AWS assign a name dynamically. If you specify a cname prefix,
86
+ then you must ensure that the name is unique across all AWS customers. You can also specify an option to +cname_prefix+
87
+ to turn on cname swapping. By specifying +:swap => true+ as an option, a newly launched environment will always try
88
+ to take over the CNAME from any currently running environments.
89
+
90
+ Let's try it (Note, if you want to run this sample, you will most likely need to pick a cname prefix other than
91
+ "example" as it likely not available.):
92
+
93
+ fum hello_cname.fum launch hello
94
+
95
+ Login to the AWS Management Console and observe your environment is launched correctly and has the correct CNAME, take
96
+ note of the environment name created, and then launch another:
97
+
98
+ fum hello_cname.fum launch hello
99
+
100
+ Afer the command is complete, login to the AWS Management Console and observe the newly created environemnt now
101
+ is using the specified CNAME, while the previously created environment is using a dynamically assigned CNAME.
102
+
103
+
104
+ == Multiple environments per stage
105
+
106
+ Stages typically have only one elastic beanstalk environment active at any given time. However, fum uses multiple
107
+ environments per stage combined with DNS to mask lifecycle changes from the end users of the application. When
108
+ deploying a new version of your application, or updaing an environment configuration, fum manages the environments
109
+ and DNS records to provide a seamless rollout for your users.
110
+
111
+ stage :multi
112
+ solution_stack "32bit Amazon Linux running Tomcat 7"
113
+ name timestamp_name('multi')
114
+ matcher timestamp_name_matcher('multi')
115
+ description "Environment multi launched on #{Time.now}."
116
+
117
+ end
118
+
119
+ The name directive tells fum what name to give an environment upon launch. You can provide a string as an argument
120
+ to the +name+ directive, but then fum will only be able to launch.
121
+
122
+ Launch two instances:
123
+
124
+ fum multi.fum launch multi
125
+
126
+ And the launch another:
127
+
128
+ fum multi.fum launch multi
129
+
130
+ You can login to the AWS Managment Console to view them, but you can also use the fum list command as follow:
131
+
132
+ fum multi.fum status multi
133
+
134
+ You will notice that the status command displays the name of each environment, followed by
135
+
136
+ fum multi.fm terminate --all multi
137
+
138
+ This will terminate all environments that match the
139
+
140
+ == Configuration templates
141
+
142
+ stage :production
143
+ template "production-config"
144
+ end
145
+
146
+ stage :latest
147
+ template "latest-config"
148
+ end
149
+
150
+ == Version promotion
151
+
152
+ Up to this point, we haven't talked about how fum handles application versions. You can specify what version a
153
+ particular stage uses using the +version+ directive as follows:
154
+
155
+ stage :last_release
156
+ version "last-release-label"
157
+ end
158
+
159
+ If you run:
160
+
161
+ fum launch last_release
162
+
163
+ It will launch an environment.
164
+ In a typical application environment, you will
165
+
166
+ # An environment that whose version is automatically updated via a build server.
167
+ stage :build do
168
+ template 'build-config'
169
+
170
+ zone 'example.com'
171
+ elb_alas 'build' # build.myapp.com will point to this environment.
172
+ end
173
+ end
174
+
175
+ stage :staging do
176
+ template 'staging-config'
177
+
178
+ version :from_stage => :build
179
+
180
+ zone 'example.com'
181
+ elb_alias 'staging'
182
+ end
183
+ end
184
+
185
+ stage :production do
186
+ template 'produciton-config'
187
+
188
+ version :from_stage => :staging
189
+
190
+ zone 'example.com'
191
+ elb_alias :apex
192
+ elb_alias 'www'
193
+ end
194
+ end
195
+
196
+ Let's launch it:
197
+
198
+
199
+ In this example, the "build" stage is
200
+
201
+ == Utility commands
202
+
203
+ Fum also provides a number of utility commands to aid in manging elastic beanstalk.
204
+
205
+ The *list* command allows you to list various beanstalk components. For example:
206
+
207
+ fum list stacks # List all solutions stacks
208
+ fum list env # Lists all environments
209
+
210
+ The *tail* command allows you to tail the event log
211
+
212
+ fum tail
213
+
214
+ The *template* command allows for manipulating configuration templates
215
+
216
+ == Why is the tool called "fum"?
217
+
218
+ Because fum is the {final word}[http://en.wikipedia.org/wiki/Fee-fi-fo-fum] in managing your
219
+ {AWS Elastic Beanstalk}[http://aws.amazon.com/elasticbeanstalk/] infrastructure.
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/fum ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'fum'
4
+
5
+ Fum::Application.new.start
@@ -0,0 +1,6 @@
1
+
2
+ application_name 'HelloWorld'
3
+
4
+ stage :hello do
5
+ solution_stack "32bit Amazon Linux running Tomcat 7"
6
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "fum/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "fum"
7
+ s.version = Fum::VERSION
8
+ s.authors = ["George Scott", "RumbleWare Inc."]
9
+ s.email = ["foss@rumbleware.com"]
10
+ s.homepage = "http://github.com/rumbleware/fum"
11
+ s.summary = "Management tool for AWS Elastic Beanstalk"
12
+ s.description = "fum helps you manage your AWS Elastic Beanstalk environments"
13
+
14
+ s.rubyforge_project = "fum"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ s.add_runtime_dependency "fog", "~>1.3.1"
24
+ s.add_runtime_dependency "trollop", "~>1.16.2"
25
+ s.add_runtime_dependency "json", "~>1.6.5"
26
+ #s.add_runtime_dependency "highline", "~>1.6.0"
27
+ end
@@ -0,0 +1,12 @@
1
+
2
+ require 'fum/version'
3
+
4
+ require 'fum/util'
5
+ require 'fum/command_manager'
6
+ require 'fum/command'
7
+ require 'fum/dns'
8
+ require 'fum/stage_analyzer'
9
+
10
+ require 'fum/application'
11
+
12
+ require 'pp'
@@ -0,0 +1,117 @@
1
+
2
+ require 'rubygems'
3
+ require 'fum/lang/fum_file'
4
+ require 'trollop'
5
+ require 'fog'
6
+
7
+
8
+ module Fum
9
+
10
+ def die(msg)
11
+ puts msg
12
+ exit 1
13
+ end
14
+
15
+ class Application
16
+ include CommandManager
17
+
18
+ attr_accessor :main_decl
19
+
20
+ command :events
21
+ command :launch
22
+ command :list
23
+ command :repair
24
+ command :status
25
+ command :tail
26
+ command :template
27
+ command :terminate
28
+
29
+ def start
30
+ global_options = process_command_line
31
+
32
+ @beanstalk = Fog::AWS[:beanstalk]
33
+
34
+ command = commands[@command_name.to_sym]
35
+
36
+ if global_options[:verbose]
37
+ puts "Loading definition file #{@filename}"
38
+ end
39
+
40
+ @main_decl = load_file(@filename)
41
+
42
+ if command
43
+ options = parse_command_options(@command_name.to_sym)
44
+ options.merge!(global_options)
45
+ run_command(@command_name.to_sym, options)
46
+ else
47
+ parser.die "Unknown command '#{@command_name}'", nil
48
+ end
49
+
50
+ end
51
+
52
+ def process_command_line
53
+ #global_options = Trollop::options do
54
+
55
+ sub_commands = commands.keys.map { |c| c.to_s }
56
+
57
+ parser = Trollop::Parser.new do
58
+ version "fum #{VERSION}"
59
+ banner <<-EOS
60
+ usage: fum <filename> [options] <command> [command-options]
61
+
62
+ Commands are (use --help after command to see additional usage):
63
+ launch
64
+ list
65
+
66
+ where [options] are:
67
+ EOS
68
+
69
+ opt :credentials, "File containing AWS credentials to use", :type => :string
70
+ opt :noop, "Do nothing, print steps to be executed.", :default => false
71
+ opt :verbose, "Print verbose output.", :default => false
72
+ stop_on sub_commands #commands.keys.map { |c| c.to_s }
73
+ end
74
+
75
+ global_options = Trollop::with_standard_exception_handling parser do
76
+ o = parser.parse ARGV
77
+ raise Trollop::HelpNeeded if ARGV.empty?
78
+ @filename = ARGV.shift
79
+ raise Trollop::HelpNeeded if ARGV.empty?
80
+ @command_name = ARGV.shift
81
+ o
82
+ end
83
+
84
+ config = load_credentials
85
+
86
+ Fog.credentials = {
87
+ :aws_access_key_id => config[:access_key_id],
88
+ :aws_secret_access_key => config[:secret_access_key]
89
+ }
90
+
91
+ global_options
92
+ end
93
+
94
+
95
+ def load_file(filename)
96
+ Fum::Lang::FumFile.load(filename)
97
+ end
98
+
99
+ def load_credentials
100
+ require 'yaml'
101
+
102
+ config_file = File.expand_path('~/.fum')
103
+
104
+ options = YAML.load(File.read(config_file))
105
+
106
+ # Convert to symbols
107
+ options.each { |key, value|
108
+ options.delete(key)
109
+ options[key.to_sym] = value
110
+ }
111
+ options
112
+ end
113
+
114
+ end
115
+
116
+ end
117
+
@@ -0,0 +1,21 @@
1
+ module Fum
2
+
3
+ class Command
4
+ include Fum::Util
5
+
6
+ def initialize(application)
7
+ @application = application
8
+ end
9
+
10
+ def parse_options
11
+ {}
12
+ end
13
+
14
+ # Return the stage declaration given app and name.
15
+ def stage(app, name)
16
+ app.stages[name.to_s] || die("Unknown stage '#{name}'")
17
+ end
18
+
19
+ end
20
+
21
+ end