elastic_whenever 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b08fd8f4fb4826af5a1863a42cb29c9ece9ec66a
4
- data.tar.gz: d3030170100ddaa8d00480e0985e14853503ef78
3
+ metadata.gz: 279fa293930adedd6b35d4fbf629c8b647c09b35
4
+ data.tar.gz: d2949e35f69c75489f38fc9e9b45a655c06de5b4
5
5
  SHA512:
6
- metadata.gz: c9e571272b10b3f871773d3326a1bfc16a9fb0fb96a14a2316bfa635840189b176d58244020f696daff20004079f4c00686671b1cdca71e256f4392f4a814370
7
- data.tar.gz: 593d852b48bd44d977379750c9154e65e88218c58ca7fecd28a2701c6e428a01dc82d490a7349fb94c28ef46a8e630dd5186b2748c315c257e08968734789524
6
+ metadata.gz: bdd1cf72a17f318c78ad1e27fb6340761e064ace66c4a45b8942437dc302a8a06b984a0cf16b95afad9199c13b9d650c8d56be5fbb29471d7afc33d2d5599301
7
+ data.tar.gz: 74528044ab14b01dfdf3c40910b2049ffae4b06c56476033d2374910e3a5c58e45793bb7a5f06f70f41a314d1dd6ecd2b2dc56414a5a8500489c30080c10ae6b
data/.travis.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.3.0
4
+ - 2.4.2
5
5
  before_install: gem install bundler -v 1.15.4
data/README.md CHANGED
@@ -39,7 +39,23 @@ Usage: elastic_whenever [options]
39
39
  -v, --version Print version
40
40
  ```
41
41
 
42
- However, please be aware that you must specify an identifier.
42
+ However, please be aware that you must specify an identifier. Also, you must specify the cluster, task definition and container name in schedule file.
43
+
44
+ ```ruby
45
+ set :cluster, 'ecs-test' # ECS cluster name
46
+ set :task_definition, 'oneoff-application:2' # Task definition name, If omit the revision, use the latest revision of family automatically.
47
+ set :container, 'oneoff' # Container name of task definition
48
+
49
+ every :day, at: '03:00am' do
50
+ runner 'Hoge.run'
51
+ end
52
+ ```
53
+
54
+ If you do not write it in the schedule file, specify it with arguments.
55
+
56
+ ```
57
+ $ elastic_whenever -i test --set 'environment=staging&cluster=ecs-test&task_definition=oneoff-application:2&container=oneoff'
58
+ ```
43
59
 
44
60
  NOTE: Currently, it supports only the syntax of whenever partially. We recommend to check what happens beforehand with the `elastic_whenever` command.
45
61
 
@@ -28,10 +28,15 @@ module ElasticWhenever
28
28
  end
29
29
 
30
30
  SUCCESS_EXIT_CODE
31
+ rescue Aws::Errors::MissingRegionError
32
+ Logger.instance.fail("missing region error occurred; please use `--region` option or export `AWS_REGION` environment variable.")
33
+ ERROR_EXIT_CODE
34
+ rescue Aws::Errors::MissingCredentialsError
35
+ Logger.instance.fail("missing credential error occurred; please specify it with arguments, use shared credentials, or export `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variable.")
36
+ ERROR_EXIT_CODE
31
37
  rescue OptionParser::MissingArgument,
32
38
  Option::InvalidOptionException,
33
- Schedule::InvalidScheduleException,
34
- Aws::Errors::MissingCredentialsError => exn
39
+ Schedule::InvalidScheduleException => exn
35
40
 
36
41
  Logger.instance.fail(exn.message)
37
42
  ERROR_EXIT_CODE
@@ -40,10 +45,7 @@ module ElasticWhenever
40
45
  private
41
46
 
42
47
  def update_tasks(option, dry_run:)
43
- schedule = Schedule.new(option.schedule_file)
44
- option.variables.each do |var|
45
- schedule.set(var[:key], var[:value])
46
- end
48
+ schedule = Schedule.new(option.schedule_file, option.variables)
47
49
  schedule.validate!
48
50
 
49
51
  cluster = Task::Cluster.new(option, schedule.cluster)
@@ -55,7 +57,12 @@ module ElasticWhenever
55
57
 
56
58
  clear_tasks(option) unless dry_run
57
59
  schedule.tasks.each do |task|
58
- rule = Task::Rule.convert(option, task)
60
+ begin
61
+ rule = Task::Rule.convert(option, task, schedule.chronic_options)
62
+ rescue Task::Rule::UnsupportedOptionException => exn
63
+ Logger.instance.warn(exn.message)
64
+ next
65
+ end
59
66
  targets = task.commands.map do |command|
60
67
  Task::Target.new(
61
68
  option,
@@ -71,7 +78,12 @@ module ElasticWhenever
71
78
  if dry_run
72
79
  print_task(rule, targets)
73
80
  else
74
- rule.create
81
+ begin
82
+ rule.create
83
+ rescue Aws::CloudWatchEvents::Errors::ValidationException => exn
84
+ Logger.instance.warn("#{exn.message} Ignore this task: name=#{rule.name} expression=#{rule.expression}")
85
+ next
86
+ end
75
87
  targets.each(&:create)
76
88
  end
77
89
  end
@@ -83,8 +95,8 @@ module ElasticWhenever
83
95
 
84
96
  def list_tasks(option)
85
97
  Task::Rule.fetch(option).each do |rule|
86
- target = Task::Target.fetch(option, rule)
87
- print_task(rule, target)
98
+ targets = Task::Target.fetch(option, rule)
99
+ print_task(rule, targets)
88
100
  end
89
101
  end
90
102
 
@@ -3,11 +3,11 @@ module ElasticWhenever
3
3
  include Singleton
4
4
 
5
5
  def fail(message)
6
- puts "[fail] #{message}"
6
+ STDERR.puts "[fail] #{message}"
7
7
  end
8
8
 
9
9
  def warn(message)
10
- puts "[warn] #{message}"
10
+ STDERR.puts "[warn] #{message}"
11
11
  end
12
12
 
13
13
  def log(event, message)
@@ -18,4 +18,4 @@ module ElasticWhenever
18
18
  puts "## [message] #{message}"
19
19
  end
20
20
  end
21
- end
21
+ end
@@ -4,15 +4,21 @@ module ElasticWhenever
4
4
  attr_reader :cluster
5
5
  attr_reader :task_definition
6
6
  attr_reader :container
7
+ attr_reader :chronic_options
8
+ attr_reader :bundle_command
7
9
 
8
10
  class InvalidScheduleException < StandardError; end
9
11
 
10
- def initialize(file, environment: "production")
11
- @environment = environment
12
+ def initialize(file, variables)
13
+ @environment = "production"
12
14
  @tasks = []
13
15
  @cluster = nil
14
16
  @task_definition = nil
15
17
  @container = nil
18
+ @chronic_options = {}
19
+ @bundle_command = "bundle exec"
20
+
21
+ variables.each { |var| set(var[:key], var[:value]) }
16
22
  instance_eval(File.read(file), file)
17
23
  end
18
24
 
@@ -21,7 +27,7 @@ module ElasticWhenever
21
27
  end
22
28
 
23
29
  def every(frequency, options = {}, &block)
24
- @tasks << Task.new(@environment, frequency, options).tap do |task|
30
+ @tasks << Task.new(@environment, @bundle_command, frequency, options).tap do |task|
25
31
  task.instance_eval(&block)
26
32
  end
27
33
  end
@@ -31,5 +37,9 @@ module ElasticWhenever
31
37
  raise InvalidScheduleException.new("You must set task definition") unless task_definition
32
38
  raise InvalidScheduleException.new("You must set container") unless container
33
39
  end
40
+
41
+ def method_missing(name, *args)
42
+ Logger.instance.warn("Skipping unsupported method: #{name}")
43
+ end
34
44
  end
35
45
  end
@@ -4,33 +4,32 @@ module ElasticWhenever
4
4
  attr_reader :frequency
5
5
  attr_reader :options
6
6
 
7
- def initialize(environment, frequency, options = {})
7
+ def initialize(environment, bundle_command, frequency, options = {})
8
8
  @environment = environment
9
+ @bundle_command = bundle_command.split(" ")
9
10
  @frequency = frequency
10
11
  @options = options
11
12
  @commands = []
12
13
  end
13
14
 
14
- def runner(src)
15
- @commands << [
16
- "bundle",
17
- "exec",
18
- "bin/rails",
19
- "runner",
20
- "-e",
21
- @environment,
22
- src
23
- ]
15
+ def command(task)
16
+ @commands << task.split(" ")
24
17
  end
25
18
 
26
19
  def rake(task)
27
- @commands << [
28
- "bundle",
29
- "exec",
30
- "rake",
31
- task,
32
- "--silent"
33
- ]
20
+ @commands << [@bundle_command, "rake", task, "--silent"].flatten
21
+ end
22
+
23
+ def runner(src)
24
+ @commands << [@bundle_command, "bin/rails", "runner", "-e", @environment, src].flatten
25
+ end
26
+
27
+ def script(script)
28
+ @commands << [@bundle_command, "script/#{script}"].flatten
29
+ end
30
+
31
+ def method_missing(name, *args)
32
+ Logger.instance.warn("Skipping unsupported method: #{name}")
34
33
  end
35
34
  end
36
35
  end
@@ -4,6 +4,8 @@ module ElasticWhenever
4
4
  attr_reader :name
5
5
  attr_reader :expression
6
6
 
7
+ class UnsupportedOptionException < StandardError; end
8
+
7
9
  def self.fetch(option)
8
10
  client = Aws::CloudWatchEvents::Client.new(option.aws_config)
9
11
  client.list_rules(name_prefix: option.identifier).rules.map do |rule|
@@ -15,11 +17,11 @@ module ElasticWhenever
15
17
  end
16
18
  end
17
19
 
18
- def self.convert(option, task)
20
+ def self.convert(option, task, chronic_options)
19
21
  self.new(
20
22
  option,
21
23
  name: rule_name(option.identifier, task.commands),
22
- expression: schedule_expression(task.frequency, task.options)
24
+ expression: schedule_expression(task.frequency, task.options, chronic_options)
23
25
  )
24
26
  end
25
27
 
@@ -39,7 +41,7 @@ module ElasticWhenever
39
41
 
40
42
  def delete
41
43
  targets = client.list_targets_by_rule(rule: name).targets
42
- client.remove_targets(rule: name, ids: targets.map(&:id))
44
+ client.remove_targets(rule: name, ids: targets.map(&:id)) unless targets.empty?
43
45
  client.delete_rule(name: name)
44
46
  end
45
47
 
@@ -49,19 +51,53 @@ module ElasticWhenever
49
51
  "#{identifier}_#{Digest::SHA1.hexdigest(commands.map { |command| command.join("-") }.join("-"))}"
50
52
  end
51
53
 
52
- def self.schedule_expression(frequency, options)
53
- time = Chronic.parse(options[:at]) || Time.new(2017, 9, 9, 0, 0, 0)
54
+ def self.schedule_expression(frequency, options, chronic_options)
55
+ time = Chronic.parse(options[:at], chronic_options) || Time.new(2017, 12, 1, 0, 0, 0)
54
56
 
55
57
  case frequency
56
58
  when :hour
57
- "cron(#{time.hour} * * * ? *)"
59
+ "cron(#{time.min} * * * ? *)"
58
60
  when :day
59
61
  "cron(#{time.min} #{time.hour} * * ? *)"
60
- else
62
+ when :month
63
+ "cron(#{time.min} #{time.hour} #{time.day} * ? *)"
64
+ when :year
65
+ "cron(#{time.min} #{time.hour} #{time.day} #{time.month} ? *)"
66
+ when :sunday
67
+ "cron(#{time.min} #{time.hour} ? * 1 *)"
68
+ when :monday
69
+ "cron(#{time.min} #{time.hour} ? * 2 *)"
70
+ when :tuesday
71
+ "cron(#{time.min} #{time.hour} ? * 3 *)"
72
+ when :wednesday
73
+ "cron(#{time.min} #{time.hour} ? * 4 *)"
74
+ when :thursday
75
+ "cron(#{time.min} #{time.hour} ? * 5 *)"
76
+ when :friday
77
+ "cron(#{time.min} #{time.hour} ? * 6 *)"
78
+ when :saturday
79
+ "cron(#{time.min} #{time.hour} ? * 7 *)"
80
+ when :weekend
81
+ "cron(#{time.min} #{time.hour} ? * 1,7 *)"
82
+ when :weekday
83
+ "cron(#{time.min} #{time.hour} ? * 2-6 *)"
84
+ # cron syntax
85
+ when /^((\*?[\d\/,\-]*)\s*){5}$/
61
86
  min, hour, day, mon, week, year = frequency.split(" ")
62
- week.gsub!("*", "?")
87
+ # You can't specify the Day-of-month and Day-of-week fields in the same Cron expression.
88
+ # If you specify a value in one of the fields, you must use a ? (question mark) in the other.
89
+ week.gsub!("*", "?") if day != "?"
90
+ day.gsub!("*", "?") if week != "?"
91
+ # cron syntax: sunday -> 0
92
+ # scheduled expression: sunday -> 1
93
+ week.gsub!(/(\d)/) { (Integer($1) + 1) % 7 }
63
94
  year = year || "*"
64
95
  "cron(#{min} #{hour} #{day} #{mon} #{week} #{year})"
96
+ # schedule expression syntax
97
+ when /^((\*?\??L?W?[\d\/,\-]*)\s*){6}$/
98
+ "cron(#{frequency})"
99
+ else
100
+ raise UnsupportedOptionException.new("`#{frequency}` is not supported option. Ignore this task.")
65
101
  end
66
102
  end
67
103
 
@@ -1,3 +1,3 @@
1
1
  module ElasticWhenever
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elastic_whenever
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kazuma Watanabe
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-09-12 00:00:00.000000000 Z
11
+ date: 2017-09-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -157,9 +157,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
157
157
  version: '0'
158
158
  requirements: []
159
159
  rubyforge_project:
160
- rubygems_version: 2.6.11
160
+ rubygems_version: 2.6.13
161
161
  signing_key:
162
162
  specification_version: 4
163
163
  summary: Manage ECS Scheduled Tasks like Whenever
164
164
  test_files: []
165
- has_rdoc: