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.
data/.travis.yml CHANGED
@@ -1,8 +1,10 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.3.1
5
- before_install: gem install bundler -v 1.12.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-4.2" do
3
- gem "rails", "~> 4.2.0"
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-5.0" do
7
- gem "rails", "~> 5.0.0"
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
- source 'https://rubygems.org'
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 "appraisal"
13
+ gem "rubocop"
14
+ gem "rubocop-rspec"
12
15
  end
data/Guardfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # A sample Guardfile
2
4
  # More info at https://github.com/guard/guard#readme
3
5
 
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/cloudwatch_scheduler.rb` file:
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://github.com/tomykaira/clockwork
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,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  require "rspec/core/rake_task"
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec)
5
7
 
6
- task :default => :spec
8
+ task default: :spec
@@ -1,7 +1,8 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
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 'cloudwatch_scheduler/version'
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 = %q{Use AWS CloudWatch events to trigger recurring jobs.}
13
- spec.description = %q{Use Cloudwatch Events to kick off recurring SQS ActiveJob jobs.}
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{^exe/}) { |f| File.basename(f) }
19
+ spec.executables = spec.files.grep(%r(^exe/)) { |f| File.basename(f) }
19
20
  spec.require_paths = ["lib"]
20
21
 
21
- spec.add_dependency "aws-sdk", "~> 2.2"
22
- spec.add_dependency "rails", ">= 4.2.0"
23
- spec.add_dependency "shoryuken", "~> 2.0"
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 "bundler", "~> 1.12"
26
- spec.add_development_dependency "rake", "~> 10.0"
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
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_RETRY: "1"
@@ -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 "rails", "~> 5.0.0"
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 :path => "../"
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 "rails", "~> 4.2.0"
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 :path => "../"
16
+ gemspec path: "../"
@@ -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
- require "aws-sdk"
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
- cwe_client: Aws::CloudWatchEvents::Client.new)
11
+ cwe_client: Aws::CloudWatchEvents::Client.new)
10
12
  @config = config
11
- @sqs_client, @cwe_client = sqs_client, cwe_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: task.rule_name,
32
+ name: task.rule_name,
43
33
  schedule_expression: task.rule_schedule_expression,
44
- state: "ENABLED",
45
- description: "CloudwatchScheduler task defined at #{task.code.source_location}"
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: task.rule_name,
39
+ rule: task.rule_name,
50
40
  targets: [
51
41
  {
52
- id: SecureRandom.uuid,
53
- arn: queue_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
- "Version": "2012-10-17",
64
- "Id": "#{queue_arn}/SQSDefaultPolicy",
65
- "Statement": rule_arns.map { |rule_arn|
53
+ Version: "2012-10-17",
54
+ Id: "#{queue_arn}/SQSDefaultPolicy",
55
+ Statement: rule_arns.map do |rule_arn|
66
56
  {
67
- "Sid": "TrustCWESendingToSQS",
68
- "Effect": "Allow",
69
- "Principal": {
70
- "AWS": "*"
57
+ Sid: "TrustCWESendingToSQS",
58
+ Effect: "Allow",
59
+ Principal: {
60
+ AWS: "*"
71
61
  },
72
- "Action": "sqs:SendMessage",
73
- "Resource": queue_arn,
74
- "Condition": {
75
- "ArnEquals": {
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: 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
- require 'active_support/core_ext/digest/uuid'
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: CloudwatchScheduler::Job.name,
28
- job_id: job_id,
30
+ job_class: CloudwatchScheduler::Job.name,
31
+ job_id: job_id,
29
32
  queue_name: CloudwatchScheduler::Job.queue_name,
30
- arguments: [name],
31
- locale: "en",
32
- priority: nil
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