whenever-elasticbeanstalk 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in whenever-elasticbeanstalk.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Chad McGimpsey
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # Whenever::Elasticbeanstalk
2
+
3
+ Whenever-elasticbeanstalk is an extension gem to [Whenever](https://github.com/javan/whenever) that automatically ensures that one instance in an AWS Elastic Beanstalk environment is set as leader. This allows you to run cron jobs on all instances, or just on the leader. This is required since Elastic Beanstalk may start or stop any instance as it scales up or down.
4
+
5
+ ## Installation
6
+
7
+ **Whenever-elasticbeanstalk is still under development and not packaged as a gem yet**
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'whenever-elasticbeanstalk'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ ```bash
18
+ $ bundle
19
+ ```
20
+
21
+ Or install it yourself as:
22
+
23
+ ```bash
24
+ $ gem install whenever-elasticbeanstalk
25
+ ```
26
+
27
+ ## Getting started
28
+ ```bash
29
+ $ cd /apps/my-great-project
30
+ $ wheneverize-eb .
31
+ ```
32
+
33
+ This will create an initial `config/schedule.rb` file for you with the `ensure_leader` job set to run every minute. It will also create a `.ebextensions/cron.config` file that will automatically choose a leader on environment initialization, and start up Whenever with the correct `leader` role.
34
+
35
+ ### Manually updating schedule
36
+
37
+ If you are already using Whenever, running `wheneverize-eb .` won't overwrite your `config/schedule.rb` file. You'll need to add the following lines in order for your environment to always have one leader.
38
+
39
+ ```ruby
40
+ every 1.minute do
41
+ command "cd /var/app/current && bundle exec ensure_one_cron_leader"
42
+ end
43
+ ```
44
+
45
+ ### Create AWS IAM user
46
+
47
+ In order for the scripts to work, you need to supply AWS credentials for a user with access to EC2 instances and tags. It is recommended to create a new user with limited access.
48
+
49
+ Example policy:
50
+ ```json
51
+ {
52
+ "Version": "2012-10-17",
53
+ "Statement": [
54
+ {
55
+ "Action": [
56
+ "ec2:DescribeInstanceAttribute",
57
+ "ec2:DescribeInstanceStatus",
58
+ "ec2:DescribeInstances",
59
+ "ec2:DescribeTags",
60
+ "ec2:CreateTags"
61
+ ],
62
+ "Resource": [
63
+ "*"
64
+ ],
65
+ "Effect": "Allow"
66
+ }
67
+ ]
68
+ }
69
+ ```
70
+
71
+ Then add the credentials to your `config/whenever-elasticbeanstalk.yml` file.
72
+ ```yaml
73
+ staging:
74
+ access_key_id: 'your access key'
75
+ secret_access_key: 'your secret access key'
76
+ ```
77
+
78
+ ## Usage
79
+
80
+ For `config/schedule.rb` usage, please see the documentation for the [Whenever gem](https://github.com/javan/whenever).
81
+
82
+ ### Run a task on only one instance
83
+
84
+ To run a task only on one instance, assign the task to the `leader` role.
85
+ ```ruby
86
+ every :day, :at => "12:30am", :roles => [:leader] do
87
+ runner "MyModel.task_to_run_nightly_only_on_one_instance"
88
+ end
89
+ ```
90
+
91
+ ### Run a task on all instances
92
+
93
+ To run a task on all instance, omit the `roles` option.
94
+ ```ruby
95
+ every 1.minute do
96
+ command "touch /opt/elasticbeanstalk/support/.cron_check"
97
+ end
98
+ ```
99
+
100
+ ## Contributing
101
+
102
+ 1. Fork it
103
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
104
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
105
+ 4. Push to the branch (`git push origin my-new-feature`)
106
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'optparse'
4
+ require 'rubygems'
5
+ gem 'aws-sdk'
6
+ require 'aws-sdk'
7
+
8
+ options = {}
9
+
10
+ optparse = OptionParser.new do |opts|
11
+ opts.banner = "Usage: #{File.basename($0)} [options]"
12
+
13
+ # Define the options, and what they do
14
+ options[:no_update] = false
15
+ opts.on( '--no-update', 'Do not update crontab after making leader' ) do
16
+ options[:no_update] = true
17
+ end
18
+
19
+ # This displays the help screen, all programs are
20
+ # assumed to have this option.
21
+ opts.on( '-h', '--help', 'Display this screen' ) do
22
+ puts opts
23
+ exit
24
+ end
25
+ end
26
+
27
+ optparse.parse!
28
+
29
+ this_instance_id = `/opt/aws/bin/ec2-metadata -i | awk '{print $2}'`.strip
30
+ environment = ENV["RAILS_ENV"]
31
+
32
+ AWS_CREDENTIALS = YAML.load_file("config/whenever-elasticbeanstalk.yml")[environment]
33
+ AWS.config(AWS_CREDENTIALS)
34
+ ec2 = AWS::EC2.new
35
+
36
+ environment_name = ec2.instances[this_instance_id].tags["elasticbeanstalk:environment-name"]
37
+
38
+ leader_instances = ec2.instances.inject([]) do |m, i|
39
+ m << i.id if i.tags["elasticbeanstalk:environment-name"] == environment_name &&
40
+ i.status == :running &&
41
+ i.tags["leader"] == "true"
42
+ m
43
+ end
44
+
45
+ if leader_instances.count < 1
46
+ ec2.instances[this_instance_id].tags["leader"] = "true"
47
+ end
48
+
49
+ unless options[:no_update]
50
+ `bundle exec setup_cron`
51
+ end
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/ruby
2
+ require 'rubygems'
3
+ gem 'aws-sdk'
4
+
5
+ require 'aws-sdk'
6
+
7
+ this_instance_id = `/opt/aws/bin/ec2-metadata -i | awk '{print $2}'`.strip
8
+ environment = ENV["RAILS_ENV"]
9
+
10
+ AWS_CREDENTIALS = YAML.load_file("config/whenever-elasticbeanstalk.yml")[environment]
11
+ AWS.config(AWS_CREDENTIALS)
12
+ ec2 = AWS::EC2.new
13
+
14
+ environment_name = ec2.instances[this_instance_id].tags["elasticbeanstalk:environment-name"]
15
+
16
+ leader_instances = ec2.instances.inject([]) do |m, i|
17
+ m << i.id if i.tags["elasticbeanstalk:environment-name"] == environment_name &&
18
+ i.status == :running &&
19
+ i.tags["leader"] == "true"
20
+ m
21
+ end
22
+
23
+ if leader_instances.count < 1
24
+ `bundle exec create_cron_leader`
25
+ elsif leader_instances.count > 1 && leader_instances.includes? this_instance_id
26
+ `bundle exec remove_cron_leader`
27
+ end
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/ruby
2
+ require 'rubygems'
3
+ gem 'aws-sdk'
4
+
5
+ require 'aws-sdk'
6
+
7
+ this_instance_id = `/opt/aws/bin/ec2-metadata -i | awk '{print $2}'`.strip
8
+ environment = ENV["RAILS_ENV"]
9
+
10
+ AWS_CREDENTIALS = YAML.load_file("config/whenever-elasticbeanstalk.yml")[environment]
11
+ AWS.config(AWS_CREDENTIALS)
12
+ ec2 = AWS::EC2.new
13
+
14
+ environment_name = ec2.instances[this_instance_id].tags["elasticbeanstalk:environment-name"]
15
+
16
+ leader_instances = ec2.instances.inject([]) do |m, i|
17
+ m << i.id if i.tags["elasticbeanstalk:environment-name"] == environment_name &&
18
+ i.status == :running &&
19
+ i.tags["leader"] == "true"
20
+ m
21
+ end
22
+
23
+ if leader_instances.count > 1 && leader_instances.includes? this_instance_id
24
+ ec2.instances[this_instance_id].tags["leader"] = "false"
25
+ end
26
+
27
+ `bundle exec setup_cron`
data/bin/setup_cron ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/ruby
2
+ require 'rubygems'
3
+ gem 'aws-sdk'
4
+
5
+ require 'aws-sdk'
6
+
7
+ this_instance_id = `/opt/aws/bin/ec2-metadata -i | awk '{print $2}'`.strip
8
+ environment = ENV["RAILS_ENV"]
9
+
10
+ AWS_CREDENTIALS = YAML.load_file("config/whenever-elasticbeanstalk.yml")[environment]
11
+ AWS.config(AWS_CREDENTIALS)
12
+ ec2 = AWS::EC2.new
13
+
14
+ if ec2.instances[this_instance_id].tags["leader"] == "true"
15
+ `bundle exec whenever --roles leader --set 'environment=#{environment}&path=/var/app/current' --update-crontab`
16
+ else
17
+ `bundle exec whenever --set 'environment=#{environment}&path=/var/app/current' --update-crontab`
18
+ end
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This file is based heavily on Whenever's 'wheneverize' command
4
+
5
+ require 'optparse'
6
+ require 'fileutils'
7
+
8
+ OptionParser.new do |opts|
9
+ opts.banner = "Usage: #{File.basename($0)} [path]"
10
+
11
+ begin
12
+ opts.parse!(ARGV)
13
+ rescue OptionParser::ParseError => e
14
+ warn e.message
15
+ puts opts
16
+ exit 1
17
+ end
18
+ end
19
+
20
+ unless ARGV.empty?
21
+ if !File.exists?(ARGV.first)
22
+ abort "`#{ARGV.first}' does not exist."
23
+ elsif !File.directory?(ARGV.first)
24
+ abort "`#{ARGV.first}' is not a directory."
25
+ elsif ARGV.length > 1
26
+ abort "Too many arguments; please specify only the directory to wheneverize."
27
+ end
28
+ end
29
+
30
+ base = ARGV.empty? ? '.' : ARGV.shift
31
+
32
+ schedule_content = <<-FILE
33
+ # Use this file to easily define all of your cron jobs.
34
+ #
35
+ # It's helpful, but not entirely necessary to understand cron before proceeding.
36
+ # http://en.wikipedia.org/wiki/Cron
37
+
38
+ # Example:
39
+ #
40
+ # set :output, "/path/to/my/cron_log.log"
41
+ #
42
+ # every 2.hours do
43
+ # command "/usr/bin/some_great_command"
44
+ # runner "MyModel.some_method"
45
+ # rake "some:great:rake:task"
46
+ # end
47
+ #
48
+ # every 4.days do
49
+ # runner "AnotherModel.prune_old_records"
50
+ # end
51
+
52
+ # Learn more: http://github.com/javan/whenever
53
+
54
+ every 1.minute do
55
+ command "cd /var/app/current && bundle exec ensure_one_cron_leader"
56
+ end
57
+ FILE
58
+
59
+ file = 'config/schedule.rb'
60
+ file = File.join(base, file)
61
+ if File.exists?(file)
62
+ warn "[skip] `#{file}' already exists"
63
+ elsif File.exists?(file.downcase)
64
+ warn "[skip] `#{file.downcase}' exists, which could conflict with `#{file}'"
65
+ elsif !File.exists?(File.dirname(file))
66
+ warn "[skip] directory `#{File.dirname(file)}' does not exist"
67
+ else
68
+ puts "[add] writing `#{file}'"
69
+ File.open(file, "w") { |f| f.write(schedule_content) }
70
+ end
71
+
72
+
73
+ eb_config_content = <<-FILE
74
+ files:
75
+ /opt/elasticbeanstalk/hooks/appdeploy/post/10_reload_cron.sh:
76
+ mode: "00700"
77
+ owner: root
78
+ group: root
79
+ content: |
80
+ #!/usr/bin/env bash
81
+ . /opt/elasticbeanstalk/support/envvars
82
+ cd $EB_CONFIG_APP_CURRENT
83
+ bundle exec setup_cron
84
+
85
+ container_commands:
86
+ cron_01_set_leader:
87
+ test: test ! -f /opt/elasticbeanstalk/support/.cron-setup-complete
88
+ leader_only: true
89
+ cwd: /var/app/ondeck
90
+ command: bundle exec create_cron_leader --no-update
91
+ cron_02_write_cron_setup_complete_file:
92
+ cwd: /opt/elasticbeanstalk/support
93
+ command: touch .cron-setup-complete
94
+ FILE
95
+
96
+ file = '.ebextensions/cron.config'
97
+ file = File.join(base, file)
98
+ if File.exists?(file)
99
+ warn "[skip] `#{file}' already exists"
100
+ elsif File.exists?(file.downcase)
101
+ warn "[skip] `#{file.downcase}' exists, which could conflict with `#{file}'"
102
+ elsif !File.exists?(File.dirname(file))
103
+ warn "[skip] directory `#{File.dirname(file)}' does not exist"
104
+ else
105
+ puts "[add] writing `#{file}'"
106
+ File.open(file, "w") { |f| f.write(eb_config_content) }
107
+ end
108
+
109
+
110
+ aws_credentials_content = <<-FILE
111
+ staging:
112
+ access_key_id: ''
113
+ secret_access_key: ''
114
+ production:
115
+ access_key_id: ''
116
+ secret_access_key: ''
117
+ FILE
118
+
119
+ file = 'config/whenever-elasticbeanstalk.yml'
120
+ file = File.join(base, file)
121
+ if File.exists?(file)
122
+ warn "[skip] `#{file}' already exists"
123
+ elsif File.exists?(file.downcase)
124
+ warn "[skip] `#{file.downcase}' exists, which could conflict with `#{file}'"
125
+ elsif !File.exists?(File.dirname(file))
126
+ warn "[skip] directory `#{File.dirname(file)}' does not exist"
127
+ else
128
+ puts "[add] writing `#{file}'"
129
+ File.open(file, "w") { |f| f.write(aws_credentials_content) }
130
+ end
131
+
132
+ puts "[done] wheneverized for Elastic Beanstalk!"
@@ -0,0 +1,7 @@
1
+ require "whenever-elasticbeanstalk/version"
2
+
3
+ module Whenever
4
+ module Elasticbeanstalk
5
+ # Your code goes here...
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ module Whenever
2
+ module Elasticbeanstalk
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'whenever-elasticbeanstalk/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "whenever-elasticbeanstalk"
8
+ gem.version = Whenever::Elasticbeanstalk::VERSION
9
+ gem.platform = Gem::Platform::RUBY
10
+ gem.authors = ["Chad McGimpsey"]
11
+ gem.email = ["chad.mcgimpsey@gmail.com"]
12
+ gem.description = %q{Use Whenever on AWS Elastic Beanstalk}
13
+ gem.summary = %q{Allows you to run cron jobs easily on one or all AWS Elastic Beanstalk instances.}
14
+ gem.homepage = "https://github.com/dignoe/whenever-elasticbeanstalk"
15
+
16
+ gem.add_dependency('whenever')
17
+ gem.add_dependency('aws-sdk')
18
+
19
+ gem.files = `git ls-files`.split($/)
20
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
22
+ gem.require_paths = ["lib"]
23
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: whenever-elasticbeanstalk
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Chad McGimpsey
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: whenever
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: aws-sdk
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
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: Use Whenever on AWS Elastic Beanstalk
47
+ email:
48
+ - chad.mcgimpsey@gmail.com
49
+ executables:
50
+ - create_cron_leader
51
+ - ensure_one_cron_leader
52
+ - remove_cron_leader
53
+ - setup_cron
54
+ - wheneverize-eb
55
+ extensions: []
56
+ extra_rdoc_files: []
57
+ files:
58
+ - .gitignore
59
+ - Gemfile
60
+ - LICENSE.txt
61
+ - README.md
62
+ - Rakefile
63
+ - bin/create_cron_leader
64
+ - bin/ensure_one_cron_leader
65
+ - bin/remove_cron_leader
66
+ - bin/setup_cron
67
+ - bin/wheneverize-eb
68
+ - lib/whenever-elasticbeanstalk.rb
69
+ - lib/whenever-elasticbeanstalk/version.rb
70
+ - whenever-elasticbeanstalk.gemspec
71
+ homepage: https://github.com/dignoe/whenever-elasticbeanstalk
72
+ licenses: []
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 1.8.25
92
+ signing_key:
93
+ specification_version: 3
94
+ summary: Allows you to run cron jobs easily on one or all AWS Elastic Beanstalk instances.
95
+ test_files: []