beanstalkify 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ credentials.yml
2
+ config.yml
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :rubygems
2
+
3
+ gem 'rake'
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,34 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ beanstalkify (0.0.1)
5
+ aws-sdk
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ aws-sdk (1.14.1)
11
+ json (~> 1.4)
12
+ nokogiri (< 1.6.0)
13
+ uuidtools (~> 2.1)
14
+ diff-lcs (1.2.4)
15
+ json (1.8.0)
16
+ nokogiri (1.5.10)
17
+ rake (10.0.3)
18
+ rspec (2.14.1)
19
+ rspec-core (~> 2.14.0)
20
+ rspec-expectations (~> 2.14.0)
21
+ rspec-mocks (~> 2.14.0)
22
+ rspec-core (2.14.4)
23
+ rspec-expectations (2.14.0)
24
+ diff-lcs (>= 1.1.3, < 2.0)
25
+ rspec-mocks (2.14.2)
26
+ uuidtools (2.1.4)
27
+
28
+ PLATFORMS
29
+ ruby
30
+
31
+ DEPENDENCIES
32
+ beanstalkify!
33
+ rake
34
+ rspec
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2013 Pranav Raja.
2
+
3
+ Permission to use, copy, modify, and/or distribute this software for any
4
+ purpose with or without fee is hereby granted, provided that the above
5
+ copyright notice and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,58 @@
1
+
2
+ [AWS Elastic Beanstalk](http://aws.amazon.com/elasticbeanstalk/) automation. A work in progress.
3
+
4
+ # Prerequisites
5
+
6
+ - Ruby 1.9
7
+
8
+ # Installation
9
+
10
+ gem install beanstalkify
11
+
12
+ Create a file called `credentials.yml`, with the following format:
13
+
14
+ access_key_id: A98KJHBLABLABLA
15
+ secret_access_key: dkuU90kmySuperSecretKey
16
+ region: us-east-1
17
+
18
+ # Running
19
+
20
+ beanstalkify -k credentials.yml -a app-version.zip -s "64bit Amazon Linux running Node.js" -e env [-c config.yml]
21
+
22
+ Should do the following
23
+
24
+ - Connect to aws using the credentials in `credentials.yml`
25
+ - Publish `app-version.zip` to s3
26
+ - Ensure that a beanstalk application called 'app' exists, and add a version called 'version', linked to the archive in that s3 bucket
27
+ - Ensure that the environment `env` exists (with optional settings overrides in `config.yml` - see below), running the specified stack.
28
+ - Deploy the provided version of the application into `env`
29
+ - Report progress. When complete report the URL where the app can be hit (note: this will be the URL of the ELB).
30
+
31
+ # Settings overrides
32
+
33
+ An example `config.yml`:
34
+
35
+ ```yaml
36
+ -
37
+ namespace: 'aws:autoscaling:asg'
38
+ option_name: Availability Zones
39
+ value: Any 2
40
+ -
41
+ namespace: 'aws:autoscaling:launchconfiguration'
42
+ option_name: InstanceType
43
+ value: m1.small
44
+ -
45
+ namespace: 'aws:autoscaling:launchconfiguration'
46
+ option_name: EC2KeyName
47
+ value: MyAwesomeEC2-dev
48
+ ```
49
+
50
+ # Running the somewhat useless tests
51
+
52
+ bundle exec rspec test
53
+
54
+ # TODO
55
+
56
+ - Tests around error handling
57
+ - Provide some blue-green deployment integrated with health checks, using the `swap_environment_cnames` feature of the AWS SDK.
58
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require 'beanstalkify/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'beanstalkify'
7
+ s.version = Beanstalkify::VERSION
8
+ s.summary = "Beanstalk automation for dummies"
9
+ s.description = "Create Amazon Elastic Beanstalk apps and deploy versions from the command line"
10
+ s.authors = ["Pranav Raja"]
11
+ s.email = 'rickdangerous1@gmail.com'
12
+ s.files = `git ls-files`.split("\n")
13
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
15
+ s.require_paths = ["lib"]
16
+ s.homepage = 'https://github.com/pranavraja/beanstalkify'
17
+ s.license = 'MIT'
18
+
19
+ s.add_dependency 'aws-sdk'
20
+ s.add_development_dependency "rspec"
21
+ end
data/bin/beanstalkify ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require 'yaml'
4
+ require 'beanstalkify/beanstalk'
5
+ require 'beanstalkify/application'
6
+
7
+ options = {}
8
+ OptionParser.new do |opts|
9
+ opts.on("-k", "--keyfile [file]", "Load credentials from yaml file") do |v|
10
+ options[:credentials] = YAML.load_file(v)
11
+ end
12
+ opts.on("-a", "--archive [file]", "Archive to deploy (e.g. AppName-version.zip)") do |v|
13
+ options[:archive] = v
14
+ end
15
+ opts.on("-e", "--environment [env]", "Environment to provision (e.g. AppName-test)") do |v|
16
+ options[:environment] = v
17
+ end
18
+ opts.on("-s", "--stack [stack]", "Stack to provision (e.g. '64bit Amazon Linux running Node.js')") do |v|
19
+ options[:stack] = v
20
+ end
21
+ opts.on("-c", "--config [file]", "Configuration overrides for the environment") do |v|
22
+ options[:config] = YAML.load_file(v)
23
+ end
24
+ end.parse!
25
+
26
+ required_params = [:credentials, :archive, :environment, :stack]
27
+ unless (required_params - options.keys).empty?
28
+ puts "Example usage: beanstalkify -k credentials.yml -a AppName-version.zip -e AppName-test -s '64bit Amazon Linux running Node.js' -c config.yml"
29
+ exit
30
+ end
31
+
32
+ Beanstalkify::Beanstalk.configure! options[:credentials]
33
+ app = Beanstalkify::Application.new(options[:stack], options[:config] || [])
34
+ app.deploy!(options[:archive], options[:environment])
35
+
@@ -0,0 +1,38 @@
1
+ require 'beanstalkify/environment'
2
+ require 'beanstalkify/deploy'
3
+
4
+ module Beanstalkify
5
+ class Application
6
+ attr_accessor :stack, :config
7
+
8
+ # config is an array of hashes:
9
+ # :namespace, :option_name, :value
10
+ def initialize(stack, config)
11
+ @stack = stack
12
+ @config = config.map { |c| Hash[c.map { |k, v| [k.to_sym,v]}] }
13
+ end
14
+
15
+ # Deploy an archive to an environment.
16
+ # If the environment doesn't exist, it will be created.
17
+ def deploy!(archive, environment_name)
18
+ deployment = Deploy.new(archive)
19
+ env = Environment.new(environment_name)
20
+ if deployment.deployed?
21
+ puts "#{deployment.application.version} is already uploaded."
22
+ else
23
+ deployment.upload!
24
+ deployment.wait!
25
+ end
26
+ if env.status.empty?
27
+ puts "Creating stack '#{@stack}' for #{deployment.application.name}-#{deployment.application.version}..."
28
+ env.create!(deployment.application, @stack, @config)
29
+ env.wait!("Launching")
30
+ else
31
+ puts "Deploying #{deployment.application.version} to #{environment_name}..."
32
+ env.deploy!(deployment.application, @config)
33
+ env.wait!("Updating")
34
+ end
35
+ puts "Done. Visit http://#{env.url} in your browser."
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+
2
+ module Beanstalkify
3
+ class Archive
4
+ attr_accessor :path
5
+
6
+ def initialize(path)
7
+ @path = path
8
+ end
9
+
10
+ def name
11
+ filename.split('-')[0]
12
+ end
13
+
14
+ def version
15
+ File.basename(@path, '.*').split('-')[-1]
16
+ end
17
+
18
+ def filename
19
+ File.basename(@path)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ require 'aws-sdk'
2
+
3
+ module Beanstalkify
4
+ class Beanstalk
5
+ @@config = {}
6
+ def self.configure!(config={})
7
+ # Convert string keys to symbols
8
+ @@config = Hash[config.map{|(k,v)| [k.to_sym,v]}]
9
+ end
10
+ def self.api
11
+ AWS.config(@@config)
12
+ AWS::ElasticBeanstalk.new.client
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,49 @@
1
+ require 'beanstalkify/archive'
2
+ require 'beanstalkify/beanstalk'
3
+ require 'aws-sdk'
4
+
5
+ module Beanstalkify
6
+ class Deploy
7
+ attr_accessor :beanstalk, :application
8
+
9
+ def initialize(path)
10
+ @application = Archive.new(path)
11
+ @beanstalk = Beanstalk.api
12
+ end
13
+
14
+ def bucket
15
+ @beanstalk.create_storage_location.data[:s3_bucket]
16
+ end
17
+
18
+ def upload!
19
+ client = AWS::S3.new.client
20
+ bucket = self.bucket
21
+ puts "Uploading #{@application.filename} to bucket #{bucket}..."
22
+ client.put_object({ :bucket_name => bucket, :key => @application.filename, :data => File.open(@application.path) })
23
+ puts "Creating #{@application.name} - #{@application.version} in beanstalk"
24
+ @beanstalk.create_application_version({
25
+ :application_name => @application.name,
26
+ :version_label => @application.version,
27
+ :source_bundle => {
28
+ :s3_bucket => bucket,
29
+ :s3_key => @application.filename
30
+ },
31
+ :auto_create_application => true
32
+ })
33
+ end
34
+
35
+ def deployed?
36
+ @beanstalk.describe_application_versions({
37
+ :application_name => @application.name,
38
+ :version_labels => [@application.version]
39
+ }).data[:application_versions].count > 0
40
+ end
41
+
42
+ def wait!
43
+ while not self.deployed?
44
+ puts "Waiting for #{@application.version} to be available..."
45
+ sleep 10
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,60 @@
1
+ require 'beanstalkify/beanstalk'
2
+
3
+ module Beanstalkify
4
+ class Environment
5
+ attr_accessor :name
6
+
7
+ def initialize(name)
8
+ @name = name
9
+ @beanstalk = Beanstalk.api
10
+ end
11
+
12
+ # Assuming the provided app has already been uploaded,
13
+ # update this environment to the app's version
14
+ # Optionally pass in a bunch of settings to override
15
+ def deploy!(app, settings=[])
16
+ @beanstalk.update_environment({
17
+ :version_label => app.version,
18
+ :environment_name => self.name,
19
+ :option_settings => settings
20
+ })
21
+ end
22
+
23
+ # Assuming the app has already been uploaded,
24
+ # create a new environment with the app deployed onto the provided stack.
25
+ def create!(app, stack, settings=[])
26
+ @beanstalk.create_environment({
27
+ :application_name => app.name,
28
+ :version_label => app.version,
29
+ :environment_name => self.name,
30
+ :solution_stack_name => stack,
31
+ :option_settings => settings
32
+ })
33
+ end
34
+
35
+ def status
36
+ envs = @beanstalk.describe_environments({
37
+ :environment_names => [self.name],
38
+ :include_deleted => false
39
+ }).data[:environments]
40
+ e = envs.first
41
+ e ? e[:status] : ""
42
+ end
43
+
44
+ def url
45
+ envs = @beanstalk.describe_environments({
46
+ :environment_names => [self.name]
47
+ }).data[:environments]
48
+ e = envs.first
49
+ e ? e[:endpoint_url] : ""
50
+ end
51
+
52
+ # Wait for the status to change from `old_status` to something else
53
+ def wait!(old_status)
54
+ while self.status == old_status
55
+ puts "#{self.name} is still #{old_status}..."
56
+ sleep 20
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,3 @@
1
+ module Beanstalkify
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,9 @@
1
+ require './application'
2
+ require './environment'
3
+
4
+ describe Application do
5
+ it 'should deploy an application' do
6
+ #application = Application.new('64bit Amazon Linux running Node.js', [])
7
+ #application.deploy!('/Users/pmoney/tmp/App-64fc96e.zip', 'test-env')
8
+ end
9
+ end
@@ -0,0 +1,26 @@
1
+
2
+ require './archive'
3
+
4
+ describe Archive do
5
+ archive = nil
6
+ before do
7
+ archive = Archive.new '/path/to/my/archive/appname-version.zip'
8
+ end
9
+
10
+ it 'should store the path' do
11
+ archive.path.should eq('/path/to/my/archive/appname-version.zip')
12
+ end
13
+
14
+ it 'extracts the application name from the path' do
15
+ archive.name.should eq('appname')
16
+ end
17
+
18
+ it 'extracts the version from the path' do
19
+ archive.version.should eq('version')
20
+ end
21
+
22
+ it 'returns the filename of the archive' do
23
+ archive.filename.should eq('appname-version.zip')
24
+ end
25
+ end
26
+
@@ -0,0 +1,24 @@
1
+ require './deploy'
2
+
3
+ describe Deploy do
4
+ deploy = nil
5
+ before do
6
+ deploy = Deploy.new('/Users/pmoney/temp/foo-1.0.zip')
7
+ end
8
+
9
+ it 'should get the s3 bucket' do
10
+ #puts deploy.bucket
11
+ end
12
+
13
+ it 'should upload the application' do
14
+ #puts deploy.upload!
15
+ end
16
+
17
+ it 'should create a new environment with the application' do
18
+ #puts deploy.create!("test3", "64bit Amazon Linux running Node.js").data
19
+ end
20
+
21
+ it 'should get the status of an environment' do
22
+ #puts deploy.status("test3")
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ require './environment'
2
+
3
+ describe Environment do
4
+ environment = Environment.new("test")
5
+
6
+ it 'should get the status' do
7
+ #environment.status.should eq("Ready")
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: beanstalkify
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Pranav Raja
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-08-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: aws-sdk
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Create Amazon Elastic Beanstalk apps and deploy versions from the command
47
+ line
48
+ email: rickdangerous1@gmail.com
49
+ executables:
50
+ - beanstalkify
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - Gemfile
56
+ - Gemfile.lock
57
+ - LICENSE
58
+ - README.md
59
+ - Rakefile
60
+ - beanstalkify.gemspec
61
+ - bin/beanstalkify
62
+ - lib/beanstalkify/application.rb
63
+ - lib/beanstalkify/archive.rb
64
+ - lib/beanstalkify/beanstalk.rb
65
+ - lib/beanstalkify/deploy.rb
66
+ - lib/beanstalkify/environment.rb
67
+ - lib/beanstalkify/version.rb
68
+ - test/application_spec.rb
69
+ - test/archive_spec.rb
70
+ - test/deploy_spec.rb
71
+ - test/environment_spec.rb
72
+ homepage: https://github.com/pranavraja/beanstalkify
73
+ licenses:
74
+ - MIT
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ! '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 1.8.24
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: Beanstalk automation for dummies
97
+ test_files:
98
+ - test/application_spec.rb
99
+ - test/archive_spec.rb
100
+ - test/deploy_spec.rb
101
+ - test/environment_spec.rb