cloudwatch_scheduler 0.1.2 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/ci.yml +49 -0
- data/.gitignore +7 -5
- data/.rubocop.yml +328 -66
- data/.travis.yml +5 -3
- data/Appraisals +7 -4
- data/Gemfile +5 -2
- data/Guardfile +2 -0
- data/README.md +43 -2
- data/Rakefile +3 -1
- data/cloudwatch_scheduler.gemspec +16 -11
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/{rails_5.0.gemfile → rails_5.2.gemfile} +5 -3
- data/gemfiles/{rails_4.2.gemfile → rails_6.0.gemfile} +5 -3
- data/lib/cloudwatch_scheduler.rb +1 -3
- data/lib/cloudwatch_scheduler/configuration.rb +5 -4
- data/lib/cloudwatch_scheduler/engine.rb +4 -0
- data/lib/cloudwatch_scheduler/job.rb +2 -1
- data/lib/cloudwatch_scheduler/provisioner.rb +42 -38
- data/lib/cloudwatch_scheduler/task.rb +11 -9
- data/lib/cloudwatch_scheduler/tasks/setup.rake +2 -4
- data/lib/cloudwatch_scheduler/version.rb +3 -1
- metadata +51 -24
- data/gemfiles/rails_4.2.gemfile.lock +0 -209
- data/gemfiles/rails_5.0.gemfile.lock +0 -213
data/.travis.yml
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
sudo: false
|
2
2
|
language: ruby
|
3
3
|
rvm:
|
4
|
-
- 2.3
|
5
|
-
|
4
|
+
- 2.5.3
|
5
|
+
- 2.6.1
|
6
|
+
before_install: gem install bundler
|
6
7
|
gemfile:
|
7
|
-
- gemfiles/rails_4.2.gemfile
|
8
8
|
- gemfiles/rails_5.0.gemfile
|
9
|
+
- gemfiles/rails_5.1.gemfile
|
10
|
+
- gemfiles/rails_5.2.gemfile
|
data/Appraisals
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
|
-
appraise "rails-
|
3
|
-
gem "
|
3
|
+
appraise "rails-5.2" do
|
4
|
+
gem "railties", "~> 5.2.0"
|
5
|
+
gem "activejob", "~> 5.2.0"
|
4
6
|
end
|
5
7
|
|
6
|
-
appraise "rails-
|
7
|
-
gem "
|
8
|
+
appraise "rails-6.1" do
|
9
|
+
gem "railties", "~> 6.1"
|
10
|
+
gem "activejob", "~> 6.1"
|
8
11
|
end
|
data/Gemfile
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
2
4
|
|
3
5
|
# Specify your gem's dependencies in cloudwatch_scheduler.gemspec
|
4
6
|
gemspec
|
@@ -8,5 +10,6 @@ group :development, :test do
|
|
8
10
|
gem "guard"
|
9
11
|
gem "guard-rspec"
|
10
12
|
|
11
|
-
gem "
|
13
|
+
gem "rubocop"
|
14
|
+
gem "rubocop-rspec"
|
12
15
|
end
|
data/Guardfile
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# CloudwatchScheduler
|
2
2
|
|
3
|
+
[![Actions Status](https://github.com/paul/cloudwatch_scheduler/workflows/CI/badge.svg)](https://github.com/paul/cloudwatch_scheduler/actions)
|
4
|
+
|
3
5
|
Are you using Rails 4.2+ and ActiveJob with the [Shoryuken
|
4
6
|
driver][shoryuken-driver] to use SQS? Do you have recurring jobs that you kick
|
5
7
|
off periodically with the [amazing Clockwork gem][clockwork]? Tired of paying
|
@@ -32,7 +34,7 @@ Or install it yourself as:
|
|
32
34
|
|
33
35
|
## Usage
|
34
36
|
|
35
|
-
Make yourself a `config/
|
37
|
+
Make yourself a `config/cloudwatch_schedule.rb` file:
|
36
38
|
|
37
39
|
```ruby
|
38
40
|
require "cloudwatch_scheduler"
|
@@ -52,10 +54,49 @@ CloudwatchScheduler do |config|
|
|
52
54
|
end
|
53
55
|
```
|
54
56
|
|
57
|
+
You'll also need to inform Shoryuken about the `cloudwatch_scheduler` queue,
|
58
|
+
either in the `config/shoryuken.yml` or with `-q cloudwatch_scheduler` on the
|
59
|
+
command line.
|
60
|
+
|
55
61
|
Then do `rake cloudwatch_scheduler:setup`, and CloudwatchScheduler will provision the events and
|
56
62
|
cloudwatch_scheduler queue. Then, start your Shoruken workers as normal, and the
|
57
63
|
`CloudwatchScheduler::Job` will get those events, and perform the tasks defined.
|
58
64
|
|
65
|
+
### IAM Permissions
|
66
|
+
|
67
|
+
The setup task requires some permissions in the AWS account to create the queue
|
68
|
+
and Cloudwatch Events. Here's a sample policy:
|
69
|
+
|
70
|
+
```json
|
71
|
+
{
|
72
|
+
"Version": "2012-10-17",
|
73
|
+
"Statement": [
|
74
|
+
{
|
75
|
+
"Effect": "Allow",
|
76
|
+
"Action": [
|
77
|
+
"sqs:CreateQueue",
|
78
|
+
"sqs:GetQueueAttributes",
|
79
|
+
"sqs:SetQueueAttributes",
|
80
|
+
],
|
81
|
+
"Resource": [
|
82
|
+
"arn:aws:sqs:REGION:AWS_ACCOUNT:cloudwatch_scheduler",
|
83
|
+
"arn:aws:sqs:REGION:AWS_ACCOUNT:cloudwatch_scheduler-failures"
|
84
|
+
]
|
85
|
+
},
|
86
|
+
{
|
87
|
+
"Effect": "Allow",
|
88
|
+
"Action": [
|
89
|
+
"events:PutRule",
|
90
|
+
"events:PutTargets"
|
91
|
+
],
|
92
|
+
"Resource": [
|
93
|
+
"*"
|
94
|
+
]
|
95
|
+
}
|
96
|
+
]
|
97
|
+
}
|
98
|
+
```
|
99
|
+
|
59
100
|
## Development
|
60
101
|
|
61
102
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
@@ -74,6 +115,6 @@ Bug reports and pull requests are welcome on GitHub at
|
|
74
115
|
https://github.com/paul/cloudwatch_scheduler.
|
75
116
|
|
76
117
|
[shoryuken-driver]: https://github.com/phstc/shoryuken/wiki/Rails-Integration-Active-Job
|
77
|
-
[clockwork]: https://
|
118
|
+
[clockwork]: https://rubygems.org/gems/clockwork
|
78
119
|
[cloudwatch-events]: http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/ScheduledEvents.html
|
79
120
|
|
data/Rakefile
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
5
|
+
require "cloudwatch_scheduler/version"
|
5
6
|
|
6
7
|
Gem::Specification.new do |spec|
|
7
8
|
spec.name = "cloudwatch_scheduler"
|
@@ -9,20 +10,24 @@ Gem::Specification.new do |spec|
|
|
9
10
|
spec.authors = ["Paul Sadauskas"]
|
10
11
|
spec.email = ["psadauskas@gmail.com"]
|
11
12
|
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
13
|
+
spec.summary = "Use AWS CloudWatch events to trigger recurring jobs."
|
14
|
+
spec.description = "Use Cloudwatch Events to kick off recurring SQS ActiveJob jobs."
|
14
15
|
spec.homepage = "https://github.com/paul/cloudwatch_scheduler"
|
15
16
|
|
16
17
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
18
|
spec.bindir = "exe"
|
18
|
-
spec.executables = spec.files.grep(%r
|
19
|
+
spec.executables = spec.files.grep(%r(^exe/)) { |f| File.basename(f) }
|
19
20
|
spec.require_paths = ["lib"]
|
20
21
|
|
21
|
-
spec.
|
22
|
-
|
23
|
-
spec.add_dependency "
|
22
|
+
spec.required_ruby_version = "~> 2.5"
|
23
|
+
|
24
|
+
spec.add_dependency "aws-sdk-cloudwatchevents", "~> 1.13"
|
25
|
+
spec.add_dependency "aws-sdk-sqs", "~> 1.10"
|
26
|
+
spec.add_dependency "rails", ">= 5.2.0"
|
27
|
+
spec.add_dependency "shoryuken", ">= 2.0"
|
24
28
|
|
25
|
-
spec.add_development_dependency "
|
26
|
-
spec.add_development_dependency "
|
29
|
+
spec.add_development_dependency "appraisal", "~> 2.2.0"
|
30
|
+
spec.add_development_dependency "bundler", ">= 1.12"
|
31
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
27
32
|
spec.add_development_dependency "rspec", "~> 3.0"
|
28
33
|
end
|
@@ -1,14 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This file was generated by Appraisal
|
2
4
|
|
3
5
|
source "https://rubygems.org"
|
4
6
|
|
5
|
-
gem "
|
7
|
+
gem "activejob", "~> 5.2.0"
|
8
|
+
gem "railties", "~> 5.2.0"
|
6
9
|
|
7
10
|
group :development, :test do
|
8
11
|
gem "awesome_print"
|
9
12
|
gem "guard"
|
10
13
|
gem "guard-rspec"
|
11
|
-
gem "appraisal"
|
12
14
|
end
|
13
15
|
|
14
|
-
gemspec :
|
16
|
+
gemspec path: "../"
|
@@ -1,14 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This file was generated by Appraisal
|
2
4
|
|
3
5
|
source "https://rubygems.org"
|
4
6
|
|
5
|
-
gem "
|
7
|
+
gem "activejob", "~> 6.0"
|
8
|
+
gem "railties", "~> 6.0"
|
6
9
|
|
7
10
|
group :development, :test do
|
8
11
|
gem "awesome_print"
|
9
12
|
gem "guard"
|
10
13
|
gem "guard-rspec"
|
11
|
-
gem "appraisal"
|
12
14
|
end
|
13
15
|
|
14
|
-
gemspec :
|
16
|
+
gemspec path: "../"
|
data/lib/cloudwatch_scheduler.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
require "cloudwatch_scheduler/configuration"
|
3
4
|
require "cloudwatch_scheduler/task"
|
@@ -10,10 +11,7 @@ def CloudwatchScheduler(&config)
|
|
10
11
|
end
|
11
12
|
|
12
13
|
module CloudwatchScheduler
|
13
|
-
|
14
14
|
def self.global
|
15
15
|
@global ||= CloudwatchScheduler::Configuration.new
|
16
16
|
end
|
17
|
-
|
18
17
|
end
|
19
|
-
|
@@ -1,10 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/core_ext/numeric/time"
|
2
4
|
|
3
5
|
module CloudwatchScheduler
|
4
6
|
class Configuration
|
5
|
-
|
6
|
-
attr_accessor :queue_name,
|
7
|
-
:queue_visibility_timeout,
|
7
|
+
attr_accessor :queue_visibility_timeout,
|
8
8
|
:queue_max_receive_count,
|
9
9
|
:use_dead_letter_queue
|
10
10
|
|
@@ -30,9 +30,10 @@ module CloudwatchScheduler
|
|
30
30
|
@use_dead_letter_queue = true
|
31
31
|
end
|
32
32
|
|
33
|
+
attr_writer :queue_name
|
34
|
+
|
33
35
|
def queue_name
|
34
36
|
@queue_name ||= CloudwatchScheduler::Job.queue_name
|
35
37
|
end
|
36
|
-
|
37
38
|
end
|
38
39
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module CloudwatchScheduler
|
3
4
|
class Engine < Rails::Engine
|
@@ -13,6 +14,9 @@ module CloudwatchScheduler
|
|
13
14
|
CloudwatchScheduler::Job.queue_name,
|
14
15
|
ActiveJob::QueueAdapters::ShoryukenAdapter::JobWrapper
|
15
16
|
)
|
17
|
+
|
18
|
+
# Load the configuration
|
19
|
+
require Rails.root.join("config/cloudwatch_schedule").to_s
|
16
20
|
end
|
17
21
|
|
18
22
|
rake_tasks do
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module CloudwatchScheduler
|
2
4
|
class Job < ::ApplicationJob
|
3
5
|
queue_as :cloudwatch_scheduler
|
@@ -9,6 +11,5 @@ module CloudwatchScheduler
|
|
9
11
|
def perform(job_to_spawn)
|
10
12
|
@config.tasks[job_to_spawn].invoke
|
11
13
|
end
|
12
|
-
|
13
14
|
end
|
14
15
|
end
|
@@ -1,14 +1,17 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "aws-sdk-sqs"
|
4
|
+
require "aws-sdk-cloudwatchevents"
|
2
5
|
|
3
6
|
module CloudwatchScheduler
|
4
7
|
class Provisioner
|
5
|
-
|
6
8
|
attr_reader :config
|
7
9
|
|
8
10
|
def initialize(config, sqs_client: Aws::SQS::Client.new,
|
9
|
-
|
11
|
+
cwe_client: Aws::CloudWatchEvents::Client.new)
|
10
12
|
@config = config
|
11
|
-
@sqs_client
|
13
|
+
@sqs_client = sqs_client
|
14
|
+
@cwe_client = cwe_client
|
12
15
|
end
|
13
16
|
|
14
17
|
def provision
|
@@ -20,37 +23,24 @@ module CloudwatchScheduler
|
|
20
23
|
attributes = { "VisibilityTimeout" => config.queue_visibility_timeout.to_s }
|
21
24
|
@queue_url = sqs.create_queue(queue_name: queue_name, attributes: attributes).queue_url
|
22
25
|
|
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
|
26
|
+
create_dead_letter_queue! if config.use_dead_letter_queue
|
37
27
|
end
|
38
28
|
|
39
29
|
def create_events!
|
40
30
|
rule_arns = config.tasks.map do |_name, task|
|
41
31
|
rule_arn = cwe.put_rule(
|
42
|
-
name:
|
32
|
+
name: task.rule_name,
|
43
33
|
schedule_expression: task.rule_schedule_expression,
|
44
|
-
state:
|
45
|
-
description:
|
34
|
+
state: "ENABLED",
|
35
|
+
description: "CloudwatchScheduler task defined at #{task.code.source_location}"
|
46
36
|
).rule_arn
|
47
37
|
|
48
38
|
cwe.put_targets(
|
49
|
-
rule:
|
39
|
+
rule: task.rule_name,
|
50
40
|
targets: [
|
51
41
|
{
|
52
|
-
id:
|
53
|
-
arn:
|
42
|
+
id: task.rule_name,
|
43
|
+
arn: queue_arn,
|
54
44
|
input: task.event_data.to_json
|
55
45
|
}
|
56
46
|
]
|
@@ -60,28 +50,28 @@ module CloudwatchScheduler
|
|
60
50
|
end
|
61
51
|
|
62
52
|
policy = {
|
63
|
-
|
64
|
-
|
65
|
-
|
53
|
+
Version: "2012-10-17",
|
54
|
+
Id: "#{queue_arn}/SQSDefaultPolicy",
|
55
|
+
Statement: rule_arns.map do |rule_arn|
|
66
56
|
{
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
57
|
+
Sid: "TrustCWESendingToSQS",
|
58
|
+
Effect: "Allow",
|
59
|
+
Principal: {
|
60
|
+
AWS: "*"
|
71
61
|
},
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
62
|
+
Action: "sqs:SendMessage",
|
63
|
+
Resource: queue_arn,
|
64
|
+
Condition: {
|
65
|
+
ArnEquals: {
|
76
66
|
"aws:SourceArn": rule_arn
|
77
67
|
}
|
78
68
|
}
|
79
69
|
}
|
80
|
-
|
70
|
+
end
|
81
71
|
}
|
82
72
|
|
83
73
|
sqs.set_queue_attributes(
|
84
|
-
queue_url:
|
74
|
+
queue_url: queue_url,
|
85
75
|
attributes: {
|
86
76
|
"Policy" => policy.to_json
|
87
77
|
}
|
@@ -90,6 +80,21 @@ module CloudwatchScheduler
|
|
90
80
|
|
91
81
|
private
|
92
82
|
|
83
|
+
def create_dead_letter_queue!
|
84
|
+
dlq_name = queue_name + "-failures"
|
85
|
+
dlq_url = sqs.create_queue(queue_name: dlq_name).queue_url
|
86
|
+
dlq_arn = sqs.get_queue_attributes(queue_url: dlq_url, attribute_names: ["QueueArn"]).attributes["QueueArn"]
|
87
|
+
|
88
|
+
redrive_attrs = {
|
89
|
+
maxReceiveCount: config.queue_max_receive_count.to_s,
|
90
|
+
deadLetterTargetArn: dlq_arn
|
91
|
+
}.to_json
|
92
|
+
|
93
|
+
attributes = { "RedrivePolicy" => redrive_attrs }
|
94
|
+
|
95
|
+
sqs.set_queue_attributes(queue_url: queue_url, attributes: attributes)
|
96
|
+
end
|
97
|
+
|
93
98
|
def queue_name
|
94
99
|
config.queue_name
|
95
100
|
end
|
@@ -112,6 +117,5 @@ module CloudwatchScheduler
|
|
112
117
|
def sqs
|
113
118
|
@sqs_client
|
114
119
|
end
|
115
|
-
|
116
120
|
end
|
117
121
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/digest/uuid"
|
2
4
|
|
3
5
|
module CloudwatchScheduler
|
4
6
|
class Task
|
@@ -8,6 +10,7 @@ module CloudwatchScheduler
|
|
8
10
|
@name = name
|
9
11
|
@every, @cron = every, cron
|
10
12
|
fail "You must specify one of every: or cron:" unless [@every, @cron].any?
|
13
|
+
|
11
14
|
@code = code
|
12
15
|
end
|
13
16
|
|
@@ -24,18 +27,18 @@ module CloudwatchScheduler
|
|
24
27
|
# "locale":"en"}
|
25
28
|
def event_data
|
26
29
|
{
|
27
|
-
job_class:
|
28
|
-
job_id:
|
30
|
+
job_class: CloudwatchScheduler::Job.name,
|
31
|
+
job_id: job_id,
|
29
32
|
queue_name: CloudwatchScheduler::Job.queue_name,
|
30
|
-
arguments:
|
31
|
-
locale:
|
32
|
-
priority:
|
33
|
+
arguments: [name],
|
34
|
+
locale: "en",
|
35
|
+
priority: nil
|
33
36
|
}
|
34
37
|
end
|
35
38
|
|
36
39
|
def rule_name
|
37
|
-
limit = 64 - CloudwatchScheduler::Job.queue_name
|
38
|
-
[name[0, limit-1], CloudwatchScheduler::Job.queue_name].join("-")
|
40
|
+
limit = 64 - CloudwatchScheduler::Job.queue_name.length
|
41
|
+
[name[0, limit - 1], CloudwatchScheduler::Job.queue_name].join("-")
|
39
42
|
end
|
40
43
|
|
41
44
|
def rule_schedule_expression
|
@@ -65,6 +68,5 @@ module CloudwatchScheduler
|
|
65
68
|
def cron_exp
|
66
69
|
"cron(#{@cron})"
|
67
70
|
end
|
68
|
-
|
69
71
|
end
|
70
72
|
end
|