cloudwatch_scheduler 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2a274f2f21104f2b1be5dc1ef1bfc663a97d4641
4
+ data.tar.gz: 45e256394378397e208f67fe6bd3091a366cf838
5
+ SHA512:
6
+ metadata.gz: 160a54b0c376e707fe3458aaa0568a66c57cbb5e89d0621bfc31f3bd9f14380684fc6ee5d37cd4d572e2e2efa2961af47e59b83fc9d927fd1c4dc04fdca35fbf
7
+ data.tar.gz: c0b463e6b2040da8fbcb004dbd6cc9cc8fee8612953925bc603fcf5ef81fd87286e9fde249c0f2f1f3cba43d09a4063053760133889927495b4a06f55c4bd55a
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ test/dummy-*/db/*.sqlite3
12
+ test/dummy-*/db/*.sqlite3-journal
13
+ test/dummy-*/log/*.log
14
+ test/dummy-*/tmp/
15
+ test/dummy-*/.sass-cache
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,89 @@
1
+ Rails:
2
+ Enabled: true
3
+
4
+ AllCops:
5
+ DisplayCopNames: true
6
+ DisplayStyleGuide: true
7
+
8
+ Include:
9
+ - "**/Rakefile"
10
+ - "**/config.ru"
11
+ Exclude:
12
+ - "vendor/**/*"
13
+ - "spec/fixtures/**/*"
14
+ - "bin/**/*"
15
+ - "script/**/*"
16
+ Metrics/LineLength:
17
+ Max: 120
18
+ Rails/Date:
19
+ Enabled: false
20
+ Rails/TimeZone:
21
+ Enabled: false
22
+ AllCops:
23
+ TargetRubyVersion: 2.3
24
+ Style/AndOr:
25
+ EnforcedStyle: conditionals
26
+ Style/CaseIndentation:
27
+ IndentOneStep: true
28
+ Style/Documentation:
29
+ Enabled: false
30
+ Style/EachWithObject:
31
+ Enabled: false
32
+ Style/ExtraSpacing:
33
+ Exclude:
34
+ - "config/routes.rb"
35
+ Style/HashSyntax:
36
+ Exclude:
37
+ - "lib/tasks/**/*"
38
+ Style/MultilineOperationIndentation:
39
+ EnforcedStyle: indented
40
+ Style/NumericLiterals:
41
+ Enabled: false
42
+ Style/PercentLiteralDelimiters:
43
+ PreferredDelimiters:
44
+ "%w": "[]"
45
+ "%W": "[]"
46
+ "%i": "[]"
47
+ "%I": "[]"
48
+ "%r": "()"
49
+ Style/SignalException:
50
+ EnforcedStyle: semantic
51
+ Style/SingleLineBlockParams:
52
+ Enabled: false
53
+ Style/StringLiterals:
54
+ EnforcedStyle: double_quotes
55
+ Style/MultilineMethodCallIndentation:
56
+ Exclude:
57
+ - "spec/**/*.rb"
58
+
59
+
60
+ # Rubocop's global exclude seems to fail to exclude the bin/ dir, so set all
61
+ # the cops that are failing manually, since most of these files are
62
+ # auto-generated anyways. Also, if the rules also appear above, we need to copy
63
+ # the same attrs, because YAML won't merge, only overwrite.
64
+
65
+ Style/StringLiterals:
66
+ EnforcedStyle: double_quotes
67
+ Exclude:
68
+ - "bin/**/*"
69
+ Style/FrozenStringLiteralComment:
70
+ Exclude:
71
+ - "bin/**/*"
72
+ Style/LeadingCommentSpace:
73
+ Exclude:
74
+ - "bin/**/*"
75
+ Style/SpaceInsideParens:
76
+ Exclude:
77
+ - "bin/**/*"
78
+ Style/AlignParameters:
79
+ Exclude:
80
+ - "bin/**/*"
81
+ Style/ExtraSpacing:
82
+ Exclude:
83
+ - "bin/**/*"
84
+ Exclude:
85
+ - "config/routes.rb"
86
+ Lint/PercentStringArray:
87
+ Exclude:
88
+ # SecureHeaders needs the single quotes in `%w[https: 'self']`
89
+ - config/initializers/secure_headers.rb
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.12.5
6
+ gemfile:
7
+ - gemfiles/rails_4.2.gemfile
8
+ - gemfiles/rails_5.0.gemfile
data/Appraisals ADDED
@@ -0,0 +1,8 @@
1
+
2
+ appraise "rails-4.2" do
3
+ gem "rails", "~> 4.2.0"
4
+ end
5
+
6
+ appraise "rails-5.0" do
7
+ gem "rails", "~> 5.0.0"
8
+ end
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cloudwatch_scheduler.gemspec
4
+ gemspec
5
+
6
+ group :development, :test do
7
+ gem "awesome_print"
8
+ gem "guard"
9
+ gem "guard-rspec"
10
+
11
+ gem "appraisal"
12
+ end
data/Guardfile ADDED
@@ -0,0 +1,70 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ # Note: The cmd option is now required due to the increasing number of ways
19
+ # rspec may be run, below are examples of the most common uses.
20
+ # * bundler: 'bundle exec rspec'
21
+ # * bundler binstubs: 'bin/rspec'
22
+ # * spring: 'bin/rspec' (This will use spring if running and you have
23
+ # installed the spring binstubs per the docs)
24
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
25
+ # * 'just' rspec: 'rspec'
26
+
27
+ guard :rspec, cmd: "bundle exec rspec", failed_mode: :focus do
28
+ require "guard/rspec/dsl"
29
+ dsl = Guard::RSpec::Dsl.new(self)
30
+
31
+ # Feel free to open issues for suggestions and improvements
32
+
33
+ # RSpec files
34
+ rspec = dsl.rspec
35
+ watch(rspec.spec_helper) { rspec.spec_dir }
36
+ watch(rspec.spec_support) { rspec.spec_dir }
37
+ watch(rspec.spec_files)
38
+
39
+ # Ruby files
40
+ ruby = dsl.ruby
41
+ dsl.watch_spec_files_for(ruby.lib_files)
42
+
43
+ # Rails files
44
+ rails = dsl.rails(view_extensions: %w(erb haml slim))
45
+ dsl.watch_spec_files_for(rails.app_files)
46
+ dsl.watch_spec_files_for(rails.views)
47
+
48
+ watch(rails.controllers) do |m|
49
+ [
50
+ rspec.spec.call("routing/#{m[1]}_routing"),
51
+ rspec.spec.call("controllers/#{m[1]}_controller"),
52
+ rspec.spec.call("acceptance/#{m[1]}")
53
+ ]
54
+ end
55
+
56
+ # Rails config changes
57
+ watch(rails.spec_helper) { rspec.spec_dir }
58
+ watch(rails.routes) { "#{rspec.spec_dir}/routing" }
59
+ watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
60
+
61
+ # Capybara features specs
62
+ watch(rails.view_dirs) { |m| rspec.spec.call("features/#{m[1]}") }
63
+ watch(rails.layouts) { |m| rspec.spec.call("features/#{m[1]}") }
64
+
65
+ # Turnip features and steps
66
+ watch(%r{^spec/acceptance/(.+)\.feature$})
67
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
68
+ Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
69
+ end
70
+ end
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # CloudwatchScheduler
2
+
3
+ Are you using Rails 4.2+ and ActiveJob with the [Shoryuken
4
+ driver][shoryuken-driver] to use SQS? Do you have recurring jobs that you kick
5
+ off periodically with the [amazing Clockwork gem][clockwork]? Tired of paying
6
+ for a Heroku dyno just to run the clockwork instance? Then *CloudwatchScheduler* is just
7
+ the gem for you!
8
+
9
+ CloudwatchScheduler uses [AWS Cloudwatch scheduled event rules][cloudwatch-events] to
10
+ push a message on the cloudwatch_scheduler queue according to the schedule you provide,
11
+ using a simple DSL similar to the Clockwork DSL you're already familiar with.
12
+ The rules are free, and the messages cost a few billionths of a cent each,
13
+ saving you over $25/mo in Heroku dyno costs! Wow!!
14
+
15
+ And thats not all! It will automatically provision the Cloudwatch Events and Queues via a simple rake task! Amazing!
16
+
17
+ ## Installation
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ ```ruby
22
+ gem 'cloudwatch_scheduler'
23
+ ```
24
+
25
+ And then execute:
26
+
27
+ $ bundle
28
+
29
+ Or install it yourself as:
30
+
31
+ $ gem install cloudwatch_scheduler
32
+
33
+ ## Usage
34
+
35
+ Make yourself a `config/cloudwatch_scheduler.rb` file:
36
+
37
+ ```ruby
38
+ require "cloudwatch_scheduler"
39
+
40
+ CloudwatchScheduler do |config|
41
+
42
+ # 4am every day
43
+ task "spawn_analytics_jobs", cron: "0 4 * * ? *" do
44
+ return if Rails.application.config.deploy_env.sandbox?
45
+ AnalyticsSpawnerJob.perform_later
46
+ end
47
+
48
+ task "capture_pg_stats", every: 5.minutes do
49
+ PgHero.capture_query_stats
50
+ end
51
+
52
+ end
53
+ ```
54
+
55
+ Then do `rake cloudwatch_scheduler:setup`, and CloudwatchScheduler will provision the events and
56
+ cloudwatch_scheduler queue. Then, start your Shoruken workers as normal, and the
57
+ `CloudwatchScheduler::Job` will get those events, and perform the tasks defined.
58
+
59
+ ## Development
60
+
61
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
62
+ `rake spec` to run the tests. You can also run `bin/console` for an interactive
63
+ prompt that will allow you to experiment.
64
+
65
+ To install this gem onto your local machine, run `bundle exec rake install`. To
66
+ release a new version, update the version number in `version.rb`, and then run
67
+ `bundle exec rake release`, which will create a git tag for the version, push
68
+ git commits and tags, and push the `.gem` file to
69
+ [rubygems.org](https://rubygems.org).
70
+
71
+ ## Contributing
72
+
73
+ Bug reports and pull requests are welcome on GitHub at
74
+ https://github.com/paul/cloudwatch_scheduler.
75
+
76
+ [shoryuken-driver]: https://github.com/phstc/shoryuken/wiki/Rails-Integration-Active-Job
77
+ [clockwork]: https://github.com/tomykaira/clockwork
78
+ [cloudwatch-events]: http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/ScheduledEvents.html
79
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "cloudwatch_scheduler"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cloudwatch_scheduler/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "cloudwatch_scheduler"
8
+ spec.version = CloudwatchScheduler::VERSION
9
+ spec.authors = ["Paul Sadauskas"]
10
+ spec.email = ["psadauskas@gmail.com"]
11
+
12
+ spec.summary = %q{Use AWS CloudWatch events to trigger recurring jobs.}
13
+ spec.description = %q{Use Cloudwatch Events to kick off recurring SQS ActiveJob jobs.}
14
+ spec.homepage = "https://github.com/paul/cloudwatch_scheduler"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "aws-sdk", "~> 2.2"
22
+ spec.add_dependency "rails", ">= 4.2.0"
23
+ spec.add_dependency "shoryuken", "~> 2.0"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.12"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "rspec", "~> 3.0"
28
+ end
@@ -0,0 +1,14 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 4.2.0"
6
+
7
+ group :development, :test do
8
+ gem "awesome_print"
9
+ gem "guard"
10
+ gem "guard-rspec"
11
+ gem "appraisal"
12
+ end
13
+
14
+ gemspec :path => "../"
@@ -0,0 +1,209 @@
1
+ PATH
2
+ remote: ../
3
+ specs:
4
+ cloudwatch_scheduler (0.1.0)
5
+ aws-sdk (~> 2.2)
6
+ rails (>= 4.2.0)
7
+ shoryuken (~> 2.0)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ actionmailer (4.2.7)
13
+ actionpack (= 4.2.7)
14
+ actionview (= 4.2.7)
15
+ activejob (= 4.2.7)
16
+ mail (~> 2.5, >= 2.5.4)
17
+ rails-dom-testing (~> 1.0, >= 1.0.5)
18
+ actionpack (4.2.7)
19
+ actionview (= 4.2.7)
20
+ activesupport (= 4.2.7)
21
+ rack (~> 1.6)
22
+ rack-test (~> 0.6.2)
23
+ rails-dom-testing (~> 1.0, >= 1.0.5)
24
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
25
+ actionview (4.2.7)
26
+ activesupport (= 4.2.7)
27
+ builder (~> 3.1)
28
+ erubis (~> 2.7.0)
29
+ rails-dom-testing (~> 1.0, >= 1.0.5)
30
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
31
+ activejob (4.2.7)
32
+ activesupport (= 4.2.7)
33
+ globalid (>= 0.3.0)
34
+ activemodel (4.2.7)
35
+ activesupport (= 4.2.7)
36
+ builder (~> 3.1)
37
+ activerecord (4.2.7)
38
+ activemodel (= 4.2.7)
39
+ activesupport (= 4.2.7)
40
+ arel (~> 6.0)
41
+ activesupport (4.2.7)
42
+ i18n (~> 0.7)
43
+ json (~> 1.7, >= 1.7.7)
44
+ minitest (~> 5.1)
45
+ thread_safe (~> 0.3, >= 0.3.4)
46
+ tzinfo (~> 1.1)
47
+ appraisal (2.1.0)
48
+ bundler
49
+ rake
50
+ thor (>= 0.14.0)
51
+ arel (6.0.3)
52
+ awesome_print (1.7.0)
53
+ aws-sdk (2.4.4)
54
+ aws-sdk-resources (= 2.4.4)
55
+ aws-sdk-core (2.4.4)
56
+ jmespath (~> 1.0)
57
+ aws-sdk-resources (2.4.4)
58
+ aws-sdk-core (= 2.4.4)
59
+ builder (3.2.2)
60
+ celluloid (0.17.3)
61
+ celluloid-essentials
62
+ celluloid-extras
63
+ celluloid-fsm
64
+ celluloid-pool
65
+ celluloid-supervision
66
+ timers (>= 4.1.1)
67
+ celluloid-essentials (0.20.5)
68
+ timers (>= 4.1.1)
69
+ celluloid-extras (0.20.5)
70
+ timers (>= 4.1.1)
71
+ celluloid-fsm (0.20.5)
72
+ timers (>= 4.1.1)
73
+ celluloid-pool (0.20.5)
74
+ timers (>= 4.1.1)
75
+ celluloid-supervision (0.20.6)
76
+ timers (>= 4.1.1)
77
+ coderay (1.1.1)
78
+ concurrent-ruby (1.0.2)
79
+ diff-lcs (1.2.5)
80
+ erubis (2.7.0)
81
+ ffi (1.9.14)
82
+ formatador (0.2.5)
83
+ globalid (0.3.7)
84
+ activesupport (>= 4.1.0)
85
+ guard (2.14.0)
86
+ formatador (>= 0.2.4)
87
+ listen (>= 2.7, < 4.0)
88
+ lumberjack (~> 1.0)
89
+ nenv (~> 0.1)
90
+ notiffany (~> 0.0)
91
+ pry (>= 0.9.12)
92
+ shellany (~> 0.0)
93
+ thor (>= 0.18.1)
94
+ guard-compat (1.2.1)
95
+ guard-rspec (4.7.3)
96
+ guard (~> 2.1)
97
+ guard-compat (~> 1.1)
98
+ rspec (>= 2.99.0, < 4.0)
99
+ hitimes (1.2.4)
100
+ i18n (0.7.0)
101
+ jmespath (1.3.1)
102
+ json (1.8.3)
103
+ listen (3.1.5)
104
+ rb-fsevent (~> 0.9, >= 0.9.4)
105
+ rb-inotify (~> 0.9, >= 0.9.7)
106
+ ruby_dep (~> 1.2)
107
+ loofah (2.0.3)
108
+ nokogiri (>= 1.5.9)
109
+ lumberjack (1.0.10)
110
+ mail (2.6.4)
111
+ mime-types (>= 1.16, < 4)
112
+ method_source (0.8.2)
113
+ mime-types (3.1)
114
+ mime-types-data (~> 3.2015)
115
+ mime-types-data (3.2016.0521)
116
+ mini_portile2 (2.1.0)
117
+ minitest (5.9.0)
118
+ nenv (0.3.0)
119
+ nokogiri (1.6.8)
120
+ mini_portile2 (~> 2.1.0)
121
+ pkg-config (~> 1.1.7)
122
+ notiffany (0.1.1)
123
+ nenv (~> 0.1)
124
+ shellany (~> 0.0)
125
+ pkg-config (1.1.7)
126
+ pry (0.10.4)
127
+ coderay (~> 1.1.0)
128
+ method_source (~> 0.8.1)
129
+ slop (~> 3.4)
130
+ rack (1.6.4)
131
+ rack-test (0.6.3)
132
+ rack (>= 1.0)
133
+ rails (4.2.7)
134
+ actionmailer (= 4.2.7)
135
+ actionpack (= 4.2.7)
136
+ actionview (= 4.2.7)
137
+ activejob (= 4.2.7)
138
+ activemodel (= 4.2.7)
139
+ activerecord (= 4.2.7)
140
+ activesupport (= 4.2.7)
141
+ bundler (>= 1.3.0, < 2.0)
142
+ railties (= 4.2.7)
143
+ sprockets-rails
144
+ rails-deprecated_sanitizer (1.0.3)
145
+ activesupport (>= 4.2.0.alpha)
146
+ rails-dom-testing (1.0.7)
147
+ activesupport (>= 4.2.0.beta, < 5.0)
148
+ nokogiri (~> 1.6.0)
149
+ rails-deprecated_sanitizer (>= 1.0.1)
150
+ rails-html-sanitizer (1.0.3)
151
+ loofah (~> 2.0)
152
+ railties (4.2.7)
153
+ actionpack (= 4.2.7)
154
+ activesupport (= 4.2.7)
155
+ rake (>= 0.8.7)
156
+ thor (>= 0.18.1, < 2.0)
157
+ rake (10.5.0)
158
+ rb-fsevent (0.9.7)
159
+ rb-inotify (0.9.7)
160
+ ffi (>= 0.5.0)
161
+ rspec (3.5.0)
162
+ rspec-core (~> 3.5.0)
163
+ rspec-expectations (~> 3.5.0)
164
+ rspec-mocks (~> 3.5.0)
165
+ rspec-core (3.5.2)
166
+ rspec-support (~> 3.5.0)
167
+ rspec-expectations (3.5.0)
168
+ diff-lcs (>= 1.2.0, < 2.0)
169
+ rspec-support (~> 3.5.0)
170
+ rspec-mocks (3.5.0)
171
+ diff-lcs (>= 1.2.0, < 2.0)
172
+ rspec-support (~> 3.5.0)
173
+ rspec-support (3.5.0)
174
+ ruby_dep (1.3.1)
175
+ shellany (0.0.1)
176
+ shoryuken (2.0.11)
177
+ aws-sdk-core (~> 2)
178
+ celluloid (~> 0.16)
179
+ slop (3.6.0)
180
+ sprockets (3.7.0)
181
+ concurrent-ruby (~> 1.0)
182
+ rack (> 1, < 3)
183
+ sprockets-rails (3.1.1)
184
+ actionpack (>= 4.0)
185
+ activesupport (>= 4.0)
186
+ sprockets (>= 3.0.0)
187
+ thor (0.19.1)
188
+ thread_safe (0.3.5)
189
+ timers (4.1.1)
190
+ hitimes
191
+ tzinfo (1.2.2)
192
+ thread_safe (~> 0.1)
193
+
194
+ PLATFORMS
195
+ ruby
196
+
197
+ DEPENDENCIES
198
+ appraisal
199
+ awesome_print
200
+ bundler (~> 1.12)
201
+ guard
202
+ guard-rspec
203
+ cloudwatch_scheduler!
204
+ rails (~> 4.2.0)
205
+ rake (~> 10.0)
206
+ rspec (~> 3.0)
207
+
208
+ BUNDLED WITH
209
+ 1.12.5
@@ -0,0 +1,14 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 5.0.0"
6
+
7
+ group :development, :test do
8
+ gem "awesome_print"
9
+ gem "guard"
10
+ gem "guard-rspec"
11
+ gem "appraisal"
12
+ end
13
+
14
+ gemspec :path => "../"
@@ -0,0 +1,213 @@
1
+ PATH
2
+ remote: ../
3
+ specs:
4
+ cloudwatch_scheduler (0.1.0)
5
+ aws-sdk (~> 2.2)
6
+ rails (>= 4.2.0)
7
+ shoryuken (~> 2.0)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ actioncable (5.0.0)
13
+ actionpack (= 5.0.0)
14
+ nio4r (~> 1.2)
15
+ websocket-driver (~> 0.6.1)
16
+ actionmailer (5.0.0)
17
+ actionpack (= 5.0.0)
18
+ actionview (= 5.0.0)
19
+ activejob (= 5.0.0)
20
+ mail (~> 2.5, >= 2.5.4)
21
+ rails-dom-testing (~> 2.0)
22
+ actionpack (5.0.0)
23
+ actionview (= 5.0.0)
24
+ activesupport (= 5.0.0)
25
+ rack (~> 2.0)
26
+ rack-test (~> 0.6.3)
27
+ rails-dom-testing (~> 2.0)
28
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
29
+ actionview (5.0.0)
30
+ activesupport (= 5.0.0)
31
+ builder (~> 3.1)
32
+ erubis (~> 2.7.0)
33
+ rails-dom-testing (~> 2.0)
34
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
35
+ activejob (5.0.0)
36
+ activesupport (= 5.0.0)
37
+ globalid (>= 0.3.6)
38
+ activemodel (5.0.0)
39
+ activesupport (= 5.0.0)
40
+ activerecord (5.0.0)
41
+ activemodel (= 5.0.0)
42
+ activesupport (= 5.0.0)
43
+ arel (~> 7.0)
44
+ activesupport (5.0.0)
45
+ concurrent-ruby (~> 1.0, >= 1.0.2)
46
+ i18n (~> 0.7)
47
+ minitest (~> 5.1)
48
+ tzinfo (~> 1.1)
49
+ appraisal (2.1.0)
50
+ bundler
51
+ rake
52
+ thor (>= 0.14.0)
53
+ arel (7.1.1)
54
+ awesome_print (1.7.0)
55
+ aws-sdk (2.4.4)
56
+ aws-sdk-resources (= 2.4.4)
57
+ aws-sdk-core (2.4.4)
58
+ jmespath (~> 1.0)
59
+ aws-sdk-resources (2.4.4)
60
+ aws-sdk-core (= 2.4.4)
61
+ builder (3.2.2)
62
+ celluloid (0.17.3)
63
+ celluloid-essentials
64
+ celluloid-extras
65
+ celluloid-fsm
66
+ celluloid-pool
67
+ celluloid-supervision
68
+ timers (>= 4.1.1)
69
+ celluloid-essentials (0.20.5)
70
+ timers (>= 4.1.1)
71
+ celluloid-extras (0.20.5)
72
+ timers (>= 4.1.1)
73
+ celluloid-fsm (0.20.5)
74
+ timers (>= 4.1.1)
75
+ celluloid-pool (0.20.5)
76
+ timers (>= 4.1.1)
77
+ celluloid-supervision (0.20.6)
78
+ timers (>= 4.1.1)
79
+ coderay (1.1.1)
80
+ concurrent-ruby (1.0.2)
81
+ diff-lcs (1.2.5)
82
+ erubis (2.7.0)
83
+ ffi (1.9.14)
84
+ formatador (0.2.5)
85
+ globalid (0.3.7)
86
+ activesupport (>= 4.1.0)
87
+ guard (2.14.0)
88
+ formatador (>= 0.2.4)
89
+ listen (>= 2.7, < 4.0)
90
+ lumberjack (~> 1.0)
91
+ nenv (~> 0.1)
92
+ notiffany (~> 0.0)
93
+ pry (>= 0.9.12)
94
+ shellany (~> 0.0)
95
+ thor (>= 0.18.1)
96
+ guard-compat (1.2.1)
97
+ guard-rspec (4.7.3)
98
+ guard (~> 2.1)
99
+ guard-compat (~> 1.1)
100
+ rspec (>= 2.99.0, < 4.0)
101
+ hitimes (1.2.4)
102
+ i18n (0.7.0)
103
+ jmespath (1.3.1)
104
+ listen (3.1.5)
105
+ rb-fsevent (~> 0.9, >= 0.9.4)
106
+ rb-inotify (~> 0.9, >= 0.9.7)
107
+ ruby_dep (~> 1.2)
108
+ loofah (2.0.3)
109
+ nokogiri (>= 1.5.9)
110
+ lumberjack (1.0.10)
111
+ mail (2.6.4)
112
+ mime-types (>= 1.16, < 4)
113
+ method_source (0.8.2)
114
+ mime-types (3.1)
115
+ mime-types-data (~> 3.2015)
116
+ mime-types-data (3.2016.0521)
117
+ mini_portile2 (2.1.0)
118
+ minitest (5.9.0)
119
+ nenv (0.3.0)
120
+ nio4r (1.2.1)
121
+ nokogiri (1.6.8)
122
+ mini_portile2 (~> 2.1.0)
123
+ pkg-config (~> 1.1.7)
124
+ notiffany (0.1.1)
125
+ nenv (~> 0.1)
126
+ shellany (~> 0.0)
127
+ pkg-config (1.1.7)
128
+ pry (0.10.4)
129
+ coderay (~> 1.1.0)
130
+ method_source (~> 0.8.1)
131
+ slop (~> 3.4)
132
+ rack (2.0.1)
133
+ rack-test (0.6.3)
134
+ rack (>= 1.0)
135
+ rails (5.0.0)
136
+ actioncable (= 5.0.0)
137
+ actionmailer (= 5.0.0)
138
+ actionpack (= 5.0.0)
139
+ actionview (= 5.0.0)
140
+ activejob (= 5.0.0)
141
+ activemodel (= 5.0.0)
142
+ activerecord (= 5.0.0)
143
+ activesupport (= 5.0.0)
144
+ bundler (>= 1.3.0, < 2.0)
145
+ railties (= 5.0.0)
146
+ sprockets-rails (>= 2.0.0)
147
+ rails-dom-testing (2.0.1)
148
+ activesupport (>= 4.2.0, < 6.0)
149
+ nokogiri (~> 1.6.0)
150
+ rails-html-sanitizer (1.0.3)
151
+ loofah (~> 2.0)
152
+ railties (5.0.0)
153
+ actionpack (= 5.0.0)
154
+ activesupport (= 5.0.0)
155
+ method_source
156
+ rake (>= 0.8.7)
157
+ thor (>= 0.18.1, < 2.0)
158
+ rake (10.5.0)
159
+ rb-fsevent (0.9.7)
160
+ rb-inotify (0.9.7)
161
+ ffi (>= 0.5.0)
162
+ rspec (3.5.0)
163
+ rspec-core (~> 3.5.0)
164
+ rspec-expectations (~> 3.5.0)
165
+ rspec-mocks (~> 3.5.0)
166
+ rspec-core (3.5.2)
167
+ rspec-support (~> 3.5.0)
168
+ rspec-expectations (3.5.0)
169
+ diff-lcs (>= 1.2.0, < 2.0)
170
+ rspec-support (~> 3.5.0)
171
+ rspec-mocks (3.5.0)
172
+ diff-lcs (>= 1.2.0, < 2.0)
173
+ rspec-support (~> 3.5.0)
174
+ rspec-support (3.5.0)
175
+ ruby_dep (1.3.1)
176
+ shellany (0.0.1)
177
+ shoryuken (2.0.11)
178
+ aws-sdk-core (~> 2)
179
+ celluloid (~> 0.16)
180
+ slop (3.6.0)
181
+ sprockets (3.7.0)
182
+ concurrent-ruby (~> 1.0)
183
+ rack (> 1, < 3)
184
+ sprockets-rails (3.1.1)
185
+ actionpack (>= 4.0)
186
+ activesupport (>= 4.0)
187
+ sprockets (>= 3.0.0)
188
+ thor (0.19.1)
189
+ thread_safe (0.3.5)
190
+ timers (4.1.1)
191
+ hitimes
192
+ tzinfo (1.2.2)
193
+ thread_safe (~> 0.1)
194
+ websocket-driver (0.6.4)
195
+ websocket-extensions (>= 0.1.0)
196
+ websocket-extensions (0.1.2)
197
+
198
+ PLATFORMS
199
+ ruby
200
+
201
+ DEPENDENCIES
202
+ appraisal
203
+ awesome_print
204
+ bundler (~> 1.12)
205
+ guard
206
+ guard-rspec
207
+ cloudwatch_scheduler!
208
+ rails (~> 5.0.0)
209
+ rake (~> 10.0)
210
+ rspec (~> 3.0)
211
+
212
+ BUNDLED WITH
213
+ 1.12.5
@@ -0,0 +1,38 @@
1
+ require "active_support/core_ext/numeric/time"
2
+
3
+ module CloudwatchScheduler
4
+ class Configuration
5
+
6
+ attr_accessor :queue_name,
7
+ :queue_visibility_timeout,
8
+ :queue_max_receive_count,
9
+ :use_dead_letter_queue
10
+
11
+ attr_reader :tasks
12
+
13
+ def initialize
14
+ @tasks = {}
15
+ set_defaults
16
+ end
17
+
18
+ def configure(&config)
19
+ instance_exec(self, &config)
20
+ self
21
+ end
22
+
23
+ def task(name, **kwargs, &task)
24
+ @tasks[name] = Task.new(name, **kwargs, &task)
25
+ end
26
+
27
+ def set_defaults
28
+ @queue_visibility_timeout = 1.minute
29
+ @queue_max_receive_count = 2
30
+ @use_dead_letter_queue = true
31
+ end
32
+
33
+ def queue_name
34
+ @queue_name ||= CloudwatchScheduler::Job.queue_name
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+
2
+ module CloudwatchScheduler
3
+ class Engine < Rails::Engine
4
+ initializer "cloudwatch_scheduler.setup_job" do
5
+ # Have to do this in initializer rather than require time because it
6
+ # inherits from ApplicationJob
7
+ require "cloudwatch_scheduler/job"
8
+
9
+ # Explicitly register this worker, because Shoryuken expects the message
10
+ # attributes to specify the job class, and these jobs are produced by
11
+ # Cloudwatch Events, which provide no way to set message attributes
12
+ Shoryuken.worker_registry.register_worker(
13
+ CloudwatchScheduler::Job.queue_name,
14
+ ActiveJob::QueueAdapters::ShoryukenAdapter::JobWrapper
15
+ )
16
+ end
17
+
18
+ rake_tasks do
19
+ load "cloudwatch_scheduler/tasks/setup.rake"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ module CloudwatchScheduler
2
+ class Job < ::ApplicationJob
3
+ queue_as :cloudwatch_scheduler
4
+
5
+ def initialize(config: CloudwatchScheduler.global)
6
+ @config = config
7
+ end
8
+
9
+ def perform(job_to_spawn)
10
+ @config.tasks[job_to_spawn].invoke
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,117 @@
1
+ require "aws-sdk"
2
+
3
+ module CloudwatchScheduler
4
+ class Provisioner
5
+
6
+ attr_reader :config
7
+
8
+ def initialize(config, sqs_client: Aws::SQS::Client.new,
9
+ cwe_client: Aws::CloudWatchEvents::Client.new)
10
+ @config = config
11
+ @sqs_client, @cwe_client = sqs_client, cwe_client
12
+ end
13
+
14
+ def provision
15
+ create_queue!
16
+ create_events!
17
+ end
18
+
19
+ def create_queue!
20
+ attributes = { "VisibilityTimeout" => config.queue_visibility_timeout.to_s }
21
+ @queue_url = sqs.create_queue(queue_name: queue_name, attributes: attributes).queue_url
22
+
23
+ if config.use_dead_letter_queue
24
+ dlq_name = queue_name + "-failures"
25
+ dlq_url = sqs.create_queue(queue_name: dlq_name).queue_url
26
+ dlq_arn = sqs.get_queue_attributes(queue_url: dlq_url, attribute_names: ["QueueArn"]).attributes["QueueArn"]
27
+
28
+ redrive_attrs = {
29
+ maxReceiveCount: config.queue_max_receive_count.to_s,
30
+ deadLetterTargetArn: dlq_arn
31
+ }.to_json
32
+
33
+ attributes = {"RedrivePolicy" => redrive_attrs}
34
+
35
+ sqs.set_queue_attributes(queue_url: queue_url, attributes: attributes)
36
+ end
37
+ end
38
+
39
+ def create_events!
40
+ rule_arns = config.tasks.map do |_name, task|
41
+ rule_arn = cwe.put_rule(
42
+ name: task.rule_name,
43
+ schedule_expression: task.rule_schedule_expression,
44
+ state: "ENABLED",
45
+ description: "CloudwatchScheduler task defined at #{task.code.source_location}"
46
+ ).rule_arn
47
+
48
+ cwe.put_targets(
49
+ rule: task.rule_name,
50
+ targets: [
51
+ {
52
+ id: task.rule_name + "Target",
53
+ arn: queue_arn,
54
+ input: task.event_data.to_json
55
+ }
56
+ ]
57
+ )
58
+
59
+ rule_arn
60
+ end
61
+
62
+ policy = {
63
+ "Version": "2012-10-17",
64
+ "Id": "#{queue_arn}/SQSDefaultPolicy",
65
+ "Statement": rule_arns.map { |rule_arn|
66
+ {
67
+ "Sid": "TrustCWESendingToSQS",
68
+ "Effect": "Allow",
69
+ "Principal": {
70
+ "AWS": "*"
71
+ },
72
+ "Action": "sqs:SendMessage",
73
+ "Resource": queue_arn,
74
+ "Condition": {
75
+ "ArnEquals": {
76
+ "aws:SourceArn": rule_arn
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+
83
+ sqs.set_queue_attributes(
84
+ queue_url: queue_url,
85
+ attributes: {
86
+ "Policy" => policy.to_json
87
+ }
88
+ )
89
+ end
90
+
91
+ private
92
+
93
+ def queue_name
94
+ config.queue_name
95
+ end
96
+
97
+ def queue_url
98
+ @queue_url ||= sqs.get_queue_url(queue_name: queue_name).queue_url
99
+ end
100
+
101
+ def queue_arn
102
+ @queue_arn ||=
103
+ sqs
104
+ .get_queue_attributes(queue_url: queue_url, attribute_names: ["QueueArn"])
105
+ .attributes["QueueArn"]
106
+ end
107
+
108
+ def cwe
109
+ @cwe_client
110
+ end
111
+
112
+ def sqs
113
+ @sqs_client
114
+ end
115
+
116
+ end
117
+ end
@@ -0,0 +1,69 @@
1
+ require 'active_support/core_ext/digest/uuid'
2
+
3
+ module CloudwatchScheduler
4
+ class Task
5
+ attr_reader :name, :code
6
+
7
+ def initialize(name, every: nil, cron: nil, &code)
8
+ @name = name
9
+ @every, @cron = every, cron
10
+ fail "You must specify one of every: or cron:" unless [@every, @cron].any?
11
+ @code = code
12
+ end
13
+
14
+ def invoke
15
+ code.call
16
+ end
17
+
18
+ def job_id
19
+ Digest::UUID.uuid_v5(Digest::UUID::DNS_NAMESPACE, name)
20
+ end
21
+
22
+ # {"job_class":"PollActionJob","job_id":"d319ca2e-235f-492b-ab9d-a76d35490ae9",
23
+ # "queue_name":"scalar-production-poller","priority":null,"arguments":[433],
24
+ # "locale":"en"}
25
+ def event_data
26
+ {
27
+ job_class: CloudwatchScheduler::Job.name,
28
+ job_id: job_id,
29
+ queue_name: CloudwatchScheduler::Job.queue_name,
30
+ arguments: [name],
31
+ locale: "en",
32
+ priority: nil
33
+ }
34
+ end
35
+
36
+ def rule_name
37
+ [name, CloudwatchScheduler::Job.queue_name].join("-")
38
+ end
39
+
40
+ def rule_schedule_expression
41
+ if @every
42
+ rate_exp
43
+ else
44
+ cron_exp
45
+ end
46
+ end
47
+
48
+ def rate_exp
49
+ units = if @every % 1.day == 0
50
+ "day"
51
+ elsif @every % 1.hour == 0
52
+ "hour"
53
+ elsif @every % 1.minute == 0
54
+ "minute"
55
+ else
56
+ fail "Intervals less than 1 minute are not allowed by Cloudwatch Events."
57
+ end
58
+
59
+ qty = @every.to_i / 1.send(units)
60
+
61
+ "rate(#{qty.to_i} #{units.pluralize(qty)})"
62
+ end
63
+
64
+ def cron_exp
65
+ "cron(#{@cron})"
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,13 @@
1
+
2
+
3
+ namespace :cloudwatch_scheduler do
4
+
5
+ desc "Create AWS Cloudwatch Event Rules for all defined tasks"
6
+ task :setup => :environment do
7
+ Aws.config[:logger] = Logger.new(STDOUT)
8
+ require Rails.root.join("config/cloudwatch_scheduler")
9
+ config = CloudwatchScheduler.global
10
+ CloudwatchScheduler::Provisioner.new(config).provision
11
+ end
12
+
13
+ end
@@ -0,0 +1,3 @@
1
+ module CloudwatchScheduler
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,19 @@
1
+
2
+ require "cloudwatch_scheduler/configuration"
3
+ require "cloudwatch_scheduler/task"
4
+ require "cloudwatch_scheduler/provisioner"
5
+
6
+ require "cloudwatch_scheduler/engine" if defined?(Rails)
7
+
8
+ def CloudwatchScheduler(&config)
9
+ CloudwatchScheduler.global.tap { |c| c.configure(&config) }
10
+ end
11
+
12
+ module CloudwatchScheduler
13
+
14
+ def self.global
15
+ @global ||= CloudwatchScheduler::Configuration.new
16
+ end
17
+
18
+ end
19
+
metadata ADDED
@@ -0,0 +1,151 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cloudwatch_scheduler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Paul Sadauskas
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-08-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 4.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 4.2.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: shoryuken
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.12'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.12'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ description: Use Cloudwatch Events to kick off recurring SQS ActiveJob jobs.
98
+ email:
99
+ - psadauskas@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".rspec"
106
+ - ".rubocop.yml"
107
+ - ".travis.yml"
108
+ - Appraisals
109
+ - Gemfile
110
+ - Guardfile
111
+ - README.md
112
+ - Rakefile
113
+ - bin/console
114
+ - bin/setup
115
+ - cloudwatch_scheduler.gemspec
116
+ - gemfiles/rails_4.2.gemfile
117
+ - gemfiles/rails_4.2.gemfile.lock
118
+ - gemfiles/rails_5.0.gemfile
119
+ - gemfiles/rails_5.0.gemfile.lock
120
+ - lib/cloudwatch_scheduler.rb
121
+ - lib/cloudwatch_scheduler/configuration.rb
122
+ - lib/cloudwatch_scheduler/engine.rb
123
+ - lib/cloudwatch_scheduler/job.rb
124
+ - lib/cloudwatch_scheduler/provisioner.rb
125
+ - lib/cloudwatch_scheduler/task.rb
126
+ - lib/cloudwatch_scheduler/tasks/setup.rake
127
+ - lib/cloudwatch_scheduler/version.rb
128
+ homepage: https://github.com/paul/cloudwatch_scheduler
129
+ licenses: []
130
+ metadata: {}
131
+ post_install_message:
132
+ rdoc_options: []
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ requirements: []
146
+ rubyforge_project:
147
+ rubygems_version: 2.5.1
148
+ signing_key:
149
+ specification_version: 4
150
+ summary: Use AWS CloudWatch events to trigger recurring jobs.
151
+ test_files: []