cloudwatch_scheduler 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.
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: []