elastic_whenever 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 +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/README.md +62 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/elastic_whenever.gemspec +31 -0
- data/exe/elastic_whenever +5 -0
- data/lib/elastic_whenever.rb +19 -0
- data/lib/elastic_whenever/cli.rb +103 -0
- data/lib/elastic_whenever/logger.rb +21 -0
- data/lib/elastic_whenever/option.rb +93 -0
- data/lib/elastic_whenever/schedule.rb +35 -0
- data/lib/elastic_whenever/task.rb +36 -0
- data/lib/elastic_whenever/task/cluster.rb +27 -0
- data/lib/elastic_whenever/task/definition.rb +25 -0
- data/lib/elastic_whenever/task/role.rb +52 -0
- data/lib/elastic_whenever/task/rule.rb +71 -0
- data/lib/elastic_whenever/task/target.rb +73 -0
- data/lib/elastic_whenever/version.rb +3 -0
- metadata +164 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ad3dfc5d950eec47b72cb965c0c6c155f5fc0ed5
|
4
|
+
data.tar.gz: f35c6069ccd1514e6b62feff2bc0e3c62a00c70a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 231fccc051e1d1e9e387eae7a175d9b140e7c6c3c1fc4871110dd8449a8e8ccfc2f5b63e80ad6956d8578543ab9c4ce36cd58aef4fa22f798ccc115ba9600990
|
7
|
+
data.tar.gz: 4622de10422dbab1a665ed661dabc070a484f3a3d5e40c4fee403d2718b4f6b17ecc111d57dc53a0b3948748eb6215658e633c6c6d34f5d53673b043012b662e
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# Elastic Whenever
|
2
|
+
|
3
|
+
Manage ECS scheduled tasks like whenever gem.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'elastic_whenever'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install elastic_whenever
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
You can use it almost like whenever :)
|
24
|
+
|
25
|
+
```
|
26
|
+
$ elastic_whenever --help
|
27
|
+
Usage: elastic_whenever [options]
|
28
|
+
-i, --update identifier Clear and create scheduled tasks by schedule file
|
29
|
+
-c, --clear identifier Clear scheduled tasks
|
30
|
+
-l, --list identifier List scheduled tasks
|
31
|
+
-s, --set variables Example: --set 'environment=staging&cluster=ecs-test'
|
32
|
+
-f, --file schedule_file Default: config/schedule.rb
|
33
|
+
--profile profile_name AWS shared profile name
|
34
|
+
--access-key aws_access_key_id
|
35
|
+
AWS access key ID
|
36
|
+
--secret-key aws_secret_access_key
|
37
|
+
AWS secret access key
|
38
|
+
--region region AWS region
|
39
|
+
-v, --version Print version
|
40
|
+
```
|
41
|
+
|
42
|
+
However, please be aware that you must specify an identifier.
|
43
|
+
|
44
|
+
NOTE: Currently, it supports only the syntax of whenever partially. We recommend to check what happens beforehand with the `elastic_whenever` command.
|
45
|
+
|
46
|
+
```
|
47
|
+
$ elastic_whenever
|
48
|
+
cron(0 3 * * ? *) ecs-test example:2 cron bundle exec rake hoge:run
|
49
|
+
|
50
|
+
## [message] Above is your schedule file converted to scheduled tasks; your scheduled tasks was not updated.
|
51
|
+
## [message] Run `elastic_whenever --help' for more options.
|
52
|
+
```
|
53
|
+
|
54
|
+
## Development
|
55
|
+
|
56
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
57
|
+
|
58
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
59
|
+
|
60
|
+
## Contributing
|
61
|
+
|
62
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/wata727/elastic_whenever.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "elastic_whenever"
|
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(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "elastic_whenever/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "elastic_whenever"
|
8
|
+
spec.version = ElasticWhenever::VERSION
|
9
|
+
spec.authors = ["Kazuma Watanabe"]
|
10
|
+
spec.email = ["watassbass@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Manage ECS Scheduled Tasks like Whenever}
|
13
|
+
spec.description = %q{Manage ECS Scheduled Tasks like Whenever}
|
14
|
+
spec.homepage = "https://github.com/wata727/elastic_whenever"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
|
+
f.match(%r{^(test|spec|features)/})
|
18
|
+
end
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.15"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
26
|
+
|
27
|
+
spec.add_dependency "aws-sdk-ecs", "~> 1.0"
|
28
|
+
spec.add_dependency "aws-sdk-cloudwatchevents", "~> 1.0"
|
29
|
+
spec.add_dependency "aws-sdk-iam", "~> 1.0"
|
30
|
+
spec.add_dependency "chronic", "~> 0.10"
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "optparse"
|
2
|
+
require "aws-sdk-ecs"
|
3
|
+
require "aws-sdk-cloudwatchevents"
|
4
|
+
require "aws-sdk-iam"
|
5
|
+
require "chronic"
|
6
|
+
require "singleton"
|
7
|
+
require "json"
|
8
|
+
|
9
|
+
require "elastic_whenever/version"
|
10
|
+
require "elastic_whenever/cli"
|
11
|
+
require "elastic_whenever/logger"
|
12
|
+
require "elastic_whenever/option"
|
13
|
+
require "elastic_whenever/schedule"
|
14
|
+
require "elastic_whenever/task"
|
15
|
+
require "elastic_whenever/task/cluster"
|
16
|
+
require "elastic_whenever/task/definition"
|
17
|
+
require "elastic_whenever/task/role"
|
18
|
+
require "elastic_whenever/task/rule"
|
19
|
+
require "elastic_whenever/task/target"
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module ElasticWhenever
|
2
|
+
class CLI
|
3
|
+
SUCCESS_EXIT_CODE = 0
|
4
|
+
ERROR_EXIT_CODE = 1
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def run(args)
|
8
|
+
option = Option.new(args)
|
9
|
+
case option.mode
|
10
|
+
when Option::DRYRUN_MODE
|
11
|
+
option.validate!
|
12
|
+
update_tasks(option, dry_run: true)
|
13
|
+
Logger.instance.message("Above is your schedule file converted to scheduled tasks; your scheduled tasks was not updated.")
|
14
|
+
Logger.instance.message("Run `elastic_whenever --help' for more options.")
|
15
|
+
when Option::UPDATE_MODE
|
16
|
+
option.validate!
|
17
|
+
update_tasks(option, dry_run: false)
|
18
|
+
Logger.instance.log("write", "scheduled tasks updated")
|
19
|
+
when Option::CLEAR_MODE
|
20
|
+
clear_tasks(option)
|
21
|
+
Logger.instance.log("write", "shceduled tasks cleared")
|
22
|
+
when Option::LIST_MODE
|
23
|
+
list_tasks(option)
|
24
|
+
Logger.instance.message("Above is your scheduled tasks.")
|
25
|
+
Logger.instance.message("Run `elastic_whenever --help` for more options.")
|
26
|
+
when Option::PRINT_VERSION_MODE
|
27
|
+
print_version
|
28
|
+
end
|
29
|
+
|
30
|
+
SUCCESS_EXIT_CODE
|
31
|
+
rescue OptionParser::MissingArgument,
|
32
|
+
Option::InvalidOptionException,
|
33
|
+
Schedule::InvalidScheduleException,
|
34
|
+
Aws::Errors::MissingCredentialsError => exn
|
35
|
+
|
36
|
+
Logger.instance.fail(exn.message)
|
37
|
+
ERROR_EXIT_CODE
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
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
|
47
|
+
schedule.validate!
|
48
|
+
|
49
|
+
cluster = Task::Cluster.new(option, schedule.cluster)
|
50
|
+
definition = Task::Definition.new(option, schedule.task_definition)
|
51
|
+
role = Task::Role.new(option)
|
52
|
+
if !role.exists? && !dry_run
|
53
|
+
role.create
|
54
|
+
end
|
55
|
+
|
56
|
+
clear_tasks(option) unless dry_run
|
57
|
+
schedule.tasks.each do |task|
|
58
|
+
rule = Task::Rule.convert(option, task)
|
59
|
+
targets = task.commands.map do |command|
|
60
|
+
Task::Target.new(
|
61
|
+
option,
|
62
|
+
cluster: cluster,
|
63
|
+
definition: definition,
|
64
|
+
container: schedule.container,
|
65
|
+
commands: command,
|
66
|
+
rule: rule,
|
67
|
+
role: role,
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
if dry_run
|
72
|
+
print_task(rule, targets)
|
73
|
+
else
|
74
|
+
rule.create
|
75
|
+
targets.each(&:create)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def clear_tasks(option)
|
81
|
+
Task::Rule.fetch(option).each(&:delete)
|
82
|
+
end
|
83
|
+
|
84
|
+
def list_tasks(option)
|
85
|
+
Task::Rule.fetch(option).each do |rule|
|
86
|
+
target = Task::Target.fetch(option, rule)
|
87
|
+
print_task(rule, target)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def print_version
|
92
|
+
puts "Elastic Whenever v#{ElasticWhenever::VERSION}"
|
93
|
+
end
|
94
|
+
|
95
|
+
def print_task(rule, targets)
|
96
|
+
targets.each do |target|
|
97
|
+
puts "#{rule.expression} #{target.cluster.name} #{target.definition.name} #{target.container} #{target.commands.join(" ")}"
|
98
|
+
puts
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ElasticWhenever
|
2
|
+
class Logger
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
def fail(message)
|
6
|
+
puts "[fail] #{message}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def warn(message)
|
10
|
+
puts "[warn] #{message}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def log(event, message)
|
14
|
+
puts "[#{event}] #{message}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def message(message)
|
18
|
+
puts "## [message] #{message}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module ElasticWhenever
|
2
|
+
class Option
|
3
|
+
DRYRUN_MODE = 1
|
4
|
+
UPDATE_MODE = 2
|
5
|
+
CLEAR_MODE = 3
|
6
|
+
LIST_MODE = 4
|
7
|
+
PRINT_VERSION_MODE = 5
|
8
|
+
|
9
|
+
attr_reader :identifier
|
10
|
+
attr_reader :mode
|
11
|
+
attr_reader :variables
|
12
|
+
attr_reader :schedule_file
|
13
|
+
|
14
|
+
class InvalidOptionException < StandardError; end
|
15
|
+
|
16
|
+
def initialize(args)
|
17
|
+
@identifier = nil
|
18
|
+
@mode = DRYRUN_MODE
|
19
|
+
@variables = []
|
20
|
+
@schedule_file = 'config/schedule.rb'
|
21
|
+
@profile = nil
|
22
|
+
@access_key = nil
|
23
|
+
@secret_key = nil
|
24
|
+
@region = nil
|
25
|
+
|
26
|
+
OptionParser.new do |opts|
|
27
|
+
opts.on('-i', '--update identifier', 'Clear and create scheduled tasks by schedule file') do |identifier|
|
28
|
+
@identifier = identifier
|
29
|
+
@mode = UPDATE_MODE
|
30
|
+
end
|
31
|
+
opts.on('-c', '--clear identifier', 'Clear scheduled tasks') do |identifier|
|
32
|
+
@identifier = identifier
|
33
|
+
@mode = CLEAR_MODE
|
34
|
+
end
|
35
|
+
opts.on('-l', '--list identifier', 'List scheduled tasks') do |identifier|
|
36
|
+
@identifier = identifier
|
37
|
+
@mode = LIST_MODE
|
38
|
+
end
|
39
|
+
opts.on('-s' ,'--set variables', "Example: --set 'environment=staging&cluster=ecs-test'") do |set|
|
40
|
+
pairs = set.split('&')
|
41
|
+
pairs.each do |pair|
|
42
|
+
unless pair.include?('=')
|
43
|
+
Logger.instance.warn("Ignore variable set: #{pair}")
|
44
|
+
next
|
45
|
+
end
|
46
|
+
key, value = pair.split('=')
|
47
|
+
@variables << { key: key, value: value }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
opts.on('-f', '--file schedule_file', 'Default: config/schedule.rb') do |file|
|
51
|
+
@schedule_file = file
|
52
|
+
end
|
53
|
+
opts.on('--profile profile_name', 'AWS shared profile name') do |profile|
|
54
|
+
@profile = profile
|
55
|
+
end
|
56
|
+
opts.on('--access-key aws_access_key_id', 'AWS access key ID') do |key|
|
57
|
+
@access_key = key
|
58
|
+
end
|
59
|
+
opts.on('--secret-key aws_secret_access_key', 'AWS secret access key') do |key|
|
60
|
+
@secret_key = key
|
61
|
+
end
|
62
|
+
opts.on('--region region', 'AWS region') do |region|
|
63
|
+
@region = region
|
64
|
+
end
|
65
|
+
opts.on('-v', '--version', 'Print version') do
|
66
|
+
@mode = PRINT_VERSION_MODE
|
67
|
+
end
|
68
|
+
end.parse(args)
|
69
|
+
|
70
|
+
@credentials = if profile
|
71
|
+
Aws::SharedCredentials.new(profile_name: profile)
|
72
|
+
elsif access_key && secret_key
|
73
|
+
Aws::Credentials.new(access_key, secret_key)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def aws_config
|
78
|
+
{ credentials: credentials, region: region }.delete_if { |_k, v| v.nil? }
|
79
|
+
end
|
80
|
+
|
81
|
+
def validate!
|
82
|
+
raise InvalidOptionException.new("Can't find file: #{schedule_file}") unless File.exists?(schedule_file)
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
attr_reader :profile
|
88
|
+
attr_reader :access_key
|
89
|
+
attr_reader :secret_key
|
90
|
+
attr_reader :region
|
91
|
+
attr_reader :credentials
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ElasticWhenever
|
2
|
+
class Schedule
|
3
|
+
attr_reader :tasks
|
4
|
+
attr_reader :cluster
|
5
|
+
attr_reader :task_definition
|
6
|
+
attr_reader :container
|
7
|
+
|
8
|
+
class InvalidScheduleException < StandardError; end
|
9
|
+
|
10
|
+
def initialize(file, environment: "production")
|
11
|
+
@environment = environment
|
12
|
+
@tasks = []
|
13
|
+
@cluster = nil
|
14
|
+
@task_definition = nil
|
15
|
+
@container = nil
|
16
|
+
instance_eval(File.read(file), file)
|
17
|
+
end
|
18
|
+
|
19
|
+
def set(key, value)
|
20
|
+
instance_variable_set("@#{key}", value) unless key == 'tasks'
|
21
|
+
end
|
22
|
+
|
23
|
+
def every(frequency, options = {}, &block)
|
24
|
+
@tasks << Task.new(@environment, frequency, options).tap do |task|
|
25
|
+
task.instance_eval(&block)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def validate!
|
30
|
+
raise InvalidScheduleException.new("You must set cluster") unless cluster
|
31
|
+
raise InvalidScheduleException.new("You must set task definition") unless task_definition
|
32
|
+
raise InvalidScheduleException.new("You must set container") unless container
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module ElasticWhenever
|
2
|
+
class Task
|
3
|
+
attr_reader :commands
|
4
|
+
attr_reader :frequency
|
5
|
+
attr_reader :options
|
6
|
+
|
7
|
+
def initialize(environment, frequency, options = {})
|
8
|
+
@environment = environment
|
9
|
+
@frequency = frequency
|
10
|
+
@options = options
|
11
|
+
@commands = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def runner(src)
|
15
|
+
@commands << [
|
16
|
+
"bundle",
|
17
|
+
"exec",
|
18
|
+
"bin/rails",
|
19
|
+
"runner",
|
20
|
+
"-e",
|
21
|
+
@environment,
|
22
|
+
src
|
23
|
+
]
|
24
|
+
end
|
25
|
+
|
26
|
+
def rake(task)
|
27
|
+
@commands << [
|
28
|
+
"bundle",
|
29
|
+
"exec",
|
30
|
+
"rake",
|
31
|
+
task,
|
32
|
+
"--slient"
|
33
|
+
]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ElasticWhenever
|
2
|
+
class Task
|
3
|
+
class Cluster
|
4
|
+
class InvalidInputException < StandardError; end
|
5
|
+
|
6
|
+
def initialize(option, name)
|
7
|
+
@client = Aws::ECS::Client.new(option.aws_config)
|
8
|
+
@cluster = client.describe_clusters(
|
9
|
+
clusters: [name]
|
10
|
+
).clusters.first
|
11
|
+
end
|
12
|
+
|
13
|
+
def name
|
14
|
+
cluster.cluster_name
|
15
|
+
end
|
16
|
+
|
17
|
+
def arn
|
18
|
+
cluster.cluster_arn
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :client
|
24
|
+
attr_reader :cluster
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ElasticWhenever
|
2
|
+
class Task
|
3
|
+
class Definition
|
4
|
+
def initialize(option, family)
|
5
|
+
@client = Aws::ECS::Client.new(option.aws_config)
|
6
|
+
@definition = client.describe_task_definition(
|
7
|
+
task_definition: family
|
8
|
+
).task_definition
|
9
|
+
end
|
10
|
+
|
11
|
+
def name
|
12
|
+
"#{definition.family}:#{definition.revision}" if definition
|
13
|
+
end
|
14
|
+
|
15
|
+
def arn
|
16
|
+
definition&.task_definition_arn
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :client
|
22
|
+
attr_reader :definition
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module ElasticWhenever
|
2
|
+
class Task
|
3
|
+
class Role
|
4
|
+
NAME = "ecsEventsRole"
|
5
|
+
|
6
|
+
def initialize(option)
|
7
|
+
client = Aws::IAM::Client.new(option.aws_config)
|
8
|
+
@resource = Aws::IAM::Resource.new(client: client)
|
9
|
+
@role = resource.role(NAME)
|
10
|
+
end
|
11
|
+
|
12
|
+
def create
|
13
|
+
@role = resource.create_role(
|
14
|
+
role_name: NAME,
|
15
|
+
assume_role_policy_document: role_json,
|
16
|
+
)
|
17
|
+
role.attach_policy(
|
18
|
+
policy_arn: "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceEventsRole"
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def exists?
|
23
|
+
!!role
|
24
|
+
end
|
25
|
+
|
26
|
+
def arn
|
27
|
+
role&.arn
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :resource
|
33
|
+
attr_reader :role
|
34
|
+
|
35
|
+
def role_json
|
36
|
+
{
|
37
|
+
Version: "2012-10-17",
|
38
|
+
Statement: [
|
39
|
+
{
|
40
|
+
Sid: "",
|
41
|
+
Effect: "Allow",
|
42
|
+
Principal: {
|
43
|
+
Service: "events.amazonaws.com",
|
44
|
+
},
|
45
|
+
Action: "sts:AssumeRole",
|
46
|
+
}
|
47
|
+
],
|
48
|
+
}.to_json
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module ElasticWhenever
|
2
|
+
class Task
|
3
|
+
class Rule
|
4
|
+
attr_reader :name
|
5
|
+
attr_reader :expression
|
6
|
+
|
7
|
+
def self.fetch(option)
|
8
|
+
client = Aws::CloudWatchEvents::Client.new(option.aws_config)
|
9
|
+
client.list_rules(name_prefix: option.identifier).rules.map do |rule|
|
10
|
+
self.new(
|
11
|
+
option,
|
12
|
+
name: rule.name,
|
13
|
+
expression: rule.schedule_expression,
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.convert(option, task)
|
19
|
+
self.new(
|
20
|
+
option,
|
21
|
+
name: rule_name(option.identifier, task.commands),
|
22
|
+
expression: schedule_expression(task.frequency, task.options)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(option, name:, expression:)
|
27
|
+
@name = name
|
28
|
+
@expression = expression
|
29
|
+
@client = Aws::CloudWatchEvents::Client.new(option.aws_config)
|
30
|
+
end
|
31
|
+
|
32
|
+
def create
|
33
|
+
client.put_rule(
|
34
|
+
name: name,
|
35
|
+
schedule_expression: expression,
|
36
|
+
state: "ENABLED",
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def delete
|
41
|
+
targets = client.list_targets_by_rule(rule: name).targets
|
42
|
+
client.remove_targets(rule: name, ids: targets.map(&:id))
|
43
|
+
client.delete_rule(name: name)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def self.rule_name(identifier, commands)
|
49
|
+
"#{identifier}_#{Digest::SHA1.hexdigest(commands.map { |command| command.join("-") }.join("-"))}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.schedule_expression(frequency, options)
|
53
|
+
time = Chronic.parse(options[:at]) || Time.new(2017, 9, 9, 0, 0, 0)
|
54
|
+
|
55
|
+
case frequency
|
56
|
+
when :hour
|
57
|
+
"cron(#{time.hour} * * * ? *)"
|
58
|
+
when :day
|
59
|
+
"cron(#{time.min} #{time.hour} * * ? *)"
|
60
|
+
else
|
61
|
+
min, hour, day, mon, week, year = frequency.split(" ")
|
62
|
+
week.gsub!("*", "?")
|
63
|
+
year = year || "*"
|
64
|
+
"cron(#{min} #{hour} #{day} #{mon} #{week} #{year})"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
attr_reader :client
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module ElasticWhenever
|
2
|
+
class Task
|
3
|
+
class Target
|
4
|
+
attr_reader :cluster
|
5
|
+
attr_reader :definition
|
6
|
+
attr_reader :container
|
7
|
+
attr_reader :commands
|
8
|
+
|
9
|
+
def self.fetch(option, rule)
|
10
|
+
client = Aws::CloudWatchEvents::Client.new(option.aws_config)
|
11
|
+
targets = client.list_targets_by_rule(rule: rule.name).targets
|
12
|
+
targets.map do |target|
|
13
|
+
input = JSON.parse(target.input, symbolize_names: true)
|
14
|
+
|
15
|
+
self.new(
|
16
|
+
option,
|
17
|
+
cluster: Cluster.new(option, target.arn),
|
18
|
+
definition: Definition.new(option, target.ecs_parameters.task_definition_arn),
|
19
|
+
container: input[:containerOverrides].first[:name],
|
20
|
+
commands: input[:containerOverrides].first[:command],
|
21
|
+
rule: rule,
|
22
|
+
role: Role.new(option)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(option, cluster:, definition:, container:, commands:, rule:, role:)
|
28
|
+
@cluster = cluster
|
29
|
+
@definition = definition
|
30
|
+
@container = container
|
31
|
+
@commands = commands
|
32
|
+
@rule = rule
|
33
|
+
@role = role
|
34
|
+
@client = Aws::CloudWatchEvents::Client.new(option.aws_config)
|
35
|
+
end
|
36
|
+
|
37
|
+
def create
|
38
|
+
client.put_targets(
|
39
|
+
rule: rule.name,
|
40
|
+
targets: [
|
41
|
+
{
|
42
|
+
id: Digest::SHA1.hexdigest(commands.join("-")),
|
43
|
+
arn: cluster.arn,
|
44
|
+
input: input_json(container, commands),
|
45
|
+
role_arn: role.arn,
|
46
|
+
ecs_parameters: {
|
47
|
+
task_definition_arn: definition.arn,
|
48
|
+
task_count: 1,
|
49
|
+
}
|
50
|
+
}
|
51
|
+
]
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def input_json(container, commands)
|
58
|
+
{
|
59
|
+
containerOverrides: [
|
60
|
+
{
|
61
|
+
name: container,
|
62
|
+
command: commands
|
63
|
+
}
|
64
|
+
]
|
65
|
+
}.to_json
|
66
|
+
end
|
67
|
+
|
68
|
+
attr_reader :rule
|
69
|
+
attr_reader :role
|
70
|
+
attr_reader :client
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
metadata
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: elastic_whenever
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kazuma Watanabe
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-09-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.15'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.15'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: aws-sdk-ecs
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: aws-sdk-cloudwatchevents
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: aws-sdk-iam
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: chronic
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.10'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.10'
|
111
|
+
description: Manage ECS Scheduled Tasks like Whenever
|
112
|
+
email:
|
113
|
+
- watassbass@gmail.com
|
114
|
+
executables:
|
115
|
+
- elastic_whenever
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- ".gitignore"
|
120
|
+
- ".rspec"
|
121
|
+
- ".travis.yml"
|
122
|
+
- Gemfile
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- bin/console
|
126
|
+
- bin/setup
|
127
|
+
- elastic_whenever.gemspec
|
128
|
+
- exe/elastic_whenever
|
129
|
+
- lib/elastic_whenever.rb
|
130
|
+
- lib/elastic_whenever/cli.rb
|
131
|
+
- lib/elastic_whenever/logger.rb
|
132
|
+
- lib/elastic_whenever/option.rb
|
133
|
+
- lib/elastic_whenever/schedule.rb
|
134
|
+
- lib/elastic_whenever/task.rb
|
135
|
+
- lib/elastic_whenever/task/cluster.rb
|
136
|
+
- lib/elastic_whenever/task/definition.rb
|
137
|
+
- lib/elastic_whenever/task/role.rb
|
138
|
+
- lib/elastic_whenever/task/rule.rb
|
139
|
+
- lib/elastic_whenever/task/target.rb
|
140
|
+
- lib/elastic_whenever/version.rb
|
141
|
+
homepage: https://github.com/wata727/elastic_whenever
|
142
|
+
licenses: []
|
143
|
+
metadata: {}
|
144
|
+
post_install_message:
|
145
|
+
rdoc_options: []
|
146
|
+
require_paths:
|
147
|
+
- lib
|
148
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
requirements: []
|
159
|
+
rubyforge_project:
|
160
|
+
rubygems_version: 2.6.11
|
161
|
+
signing_key:
|
162
|
+
specification_version: 4
|
163
|
+
summary: Manage ECS Scheduled Tasks like Whenever
|
164
|
+
test_files: []
|