cronicle 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/.travis.yml +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +127 -0
- data/Rakefile +5 -0
- data/Vagrantfile +56 -0
- data/bin/cronicle +6 -0
- data/cronicle.gemspec +31 -0
- data/cronicle.us-east-1.pem.enc +0 -0
- data/lib/cronicle.rb +28 -0
- data/lib/cronicle/cli.rb +105 -0
- data/lib/cronicle/client.rb +161 -0
- data/lib/cronicle/driver.rb +157 -0
- data/lib/cronicle/dsl.rb +7 -0
- data/lib/cronicle/dsl/context.rb +48 -0
- data/lib/cronicle/dsl/context/job.rb +58 -0
- data/lib/cronicle/exporter.rb +56 -0
- data/lib/cronicle/ext/hash_ext.rb +8 -0
- data/lib/cronicle/ext/sshkit_ext.rb +147 -0
- data/lib/cronicle/ext/string_ext.rb +11 -0
- data/lib/cronicle/host_list.rb +89 -0
- data/lib/cronicle/logger.rb +92 -0
- data/lib/cronicle/utils.rb +43 -0
- data/lib/cronicle/version.rb +3 -0
- data/spec/cronicle_create_spec.rb +295 -0
- data/spec/cronicle_delete_spec.rb +334 -0
- data/spec/cronicle_exec_spec.rb +159 -0
- data/spec/cronicle_update_spec.rb +365 -0
- data/spec/host_list_spec.rb +162 -0
- data/spec/spec_helper.rb +150 -0
- metadata +223 -0
@@ -0,0 +1,147 @@
|
|
1
|
+
SSHKit::Backend::Netssh.config.pty = true
|
2
|
+
|
3
|
+
class SSHKit::Backend::Netssh
|
4
|
+
def sudo(command, *args)
|
5
|
+
opts = args.last.kind_of?(Hash) ? args.pop : {}
|
6
|
+
|
7
|
+
password = host.options[:sudo_password] || ''
|
8
|
+
password = Shellwords.shellescape(password)
|
9
|
+
|
10
|
+
with_sudo = [:echo, password, '|', :sudo, '-S']
|
11
|
+
with_sudo << '-u' << opts[:user] if opts[:user]
|
12
|
+
with_sudo.concat(args)
|
13
|
+
|
14
|
+
raise_on_non_zero_exit = opts.fetch(:raise_on_non_zero_exit, true)
|
15
|
+
retval = send(command, *with_sudo, :raise_on_non_zero_exit => raise_on_non_zero_exit)
|
16
|
+
Cronicle::Utils.remove_prompt!(retval) if retval.kind_of?(String)
|
17
|
+
retval
|
18
|
+
end
|
19
|
+
|
20
|
+
CRON_DIRS = %w(/var/spool/cron/crontabs /var/spool/cron)
|
21
|
+
|
22
|
+
def find_cron_dir
|
23
|
+
@cron_dir ||= CRON_DIRS.find do |path|
|
24
|
+
execute(:test, '-d', path, :raise_on_non_zero_exit => false)
|
25
|
+
end
|
26
|
+
|
27
|
+
unless @cron_dir
|
28
|
+
raise "Cannot find cron directory: #{CRON_DIRS.join(', ')}"
|
29
|
+
end
|
30
|
+
|
31
|
+
@cron_dir
|
32
|
+
end
|
33
|
+
|
34
|
+
def list_crontabs
|
35
|
+
cron_dir = find_cron_dir
|
36
|
+
@crontab_list ||= sudo(:capture, :find, cron_dir, '-type', :f, '-maxdepth', 1, '2> /dev/null',
|
37
|
+
:raise_on_non_zero_exit => false).each_line.map(&:strip)
|
38
|
+
end
|
39
|
+
|
40
|
+
def fetch_crontabs
|
41
|
+
return @crontabs if @crontabs
|
42
|
+
|
43
|
+
@crontabs = {}
|
44
|
+
|
45
|
+
list_crontabs.each do |path|
|
46
|
+
user = File.basename(path)
|
47
|
+
crontab = sudo(:capture, :cat, path)
|
48
|
+
@crontabs[user] = crontab
|
49
|
+
end
|
50
|
+
|
51
|
+
@crontabs
|
52
|
+
end
|
53
|
+
|
54
|
+
def list_libexec_scripts
|
55
|
+
@libexec_scripts ||= capture(:find, libexec_dir, '-type', :f, '2> /dev/null',
|
56
|
+
:raise_on_non_zero_exit => false).each_line.map(&:strip)
|
57
|
+
end
|
58
|
+
|
59
|
+
def fetch_libexec_scripts
|
60
|
+
script_contents = {}
|
61
|
+
|
62
|
+
list_libexec_scripts.each do |script|
|
63
|
+
script_contents[script] = crlf_to_lf(capture(:cat, script))
|
64
|
+
end
|
65
|
+
|
66
|
+
script_contents
|
67
|
+
end
|
68
|
+
|
69
|
+
def delete_cron_entry(user, name = nil)
|
70
|
+
sed_cmd = '/' + Cronicle::Utils.sed_escape(script_path(user, name)) + ' /d'
|
71
|
+
sed_cmd = Shellwords.shellescape(sed_cmd)
|
72
|
+
|
73
|
+
sudo(:execute, :sed, '-i', sed_cmd, user_crontab(user), :raise_on_non_zero_exit => false)
|
74
|
+
end
|
75
|
+
|
76
|
+
def add_cron_entry(user, name, schedule, temp_dir)
|
77
|
+
script = script_path(user, name)
|
78
|
+
temp_entry = [temp_dir, name + '.entry'].join('/')
|
79
|
+
|
80
|
+
cron_entry = "#{schedule}\\t#{script} 2>&1 | logger -t cronicle/#{user}/#{name}"
|
81
|
+
cron_entry = Shellwords.shellescape(cron_entry)
|
82
|
+
sudo(:execute, :echo, '-e', cron_entry, '>', temp_entry)
|
83
|
+
|
84
|
+
entry_cat = "cat #{temp_entry} >> #{user_crontab(user)}"
|
85
|
+
entry_cat = Shellwords.shellescape(entry_cat)
|
86
|
+
sudo(:execute, :bash, '-c', entry_cat)
|
87
|
+
end
|
88
|
+
|
89
|
+
def upload_script(temp_dir, name, content)
|
90
|
+
temp_script = [temp_dir, name].join
|
91
|
+
upload!(StringIO.new(content), temp_script)
|
92
|
+
execute(:chmod, 755, temp_script)
|
93
|
+
yield(temp_script)
|
94
|
+
end
|
95
|
+
|
96
|
+
def mktemp(user = nil)
|
97
|
+
temp_dir = capture(:mktemp, '-d', '/var/tmp/cronicle.XXXXXXXXXX')
|
98
|
+
block_args = [temp_dir]
|
99
|
+
|
100
|
+
begin
|
101
|
+
execute(:chmod, 755, temp_dir)
|
102
|
+
|
103
|
+
if user
|
104
|
+
user_temp_dir = [temp_dir, user].join('/')
|
105
|
+
execute(:mkdir, '-p', user_temp_dir)
|
106
|
+
execute(:chmod, 755, user_temp_dir)
|
107
|
+
block_args << user_temp_dir
|
108
|
+
end
|
109
|
+
|
110
|
+
yield(*block_args)
|
111
|
+
ensure
|
112
|
+
execute(:rm, '-rf', temp_dir, :raise_on_non_zero_exit => false) rescue nil
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def libexec_dir
|
117
|
+
host.options.fetch(:libexec)
|
118
|
+
end
|
119
|
+
|
120
|
+
def user_libexec_dir(user)
|
121
|
+
[libexec_dir, user].join('/')
|
122
|
+
end
|
123
|
+
|
124
|
+
def user_crontab(user)
|
125
|
+
cron_dir = find_cron_dir
|
126
|
+
[cron_dir, user].join('/')
|
127
|
+
end
|
128
|
+
|
129
|
+
def script_path(user, name)
|
130
|
+
[libexec_dir, user, name].join('/')
|
131
|
+
end
|
132
|
+
|
133
|
+
def log_for_cronicle(level, message, opts = {})
|
134
|
+
opts = host.options.merge(opts)
|
135
|
+
Cronicle::Logger.log(level, message, opts)
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def crlf_to_lf(str)
|
141
|
+
str.gsub("\r\n", "\n")
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
class SSHKit::Host
|
146
|
+
attr_reader :options
|
147
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
class Cronicle::HostList
|
2
|
+
def initialize(src, options = {})
|
3
|
+
@hosts = Set.new
|
4
|
+
@host_by_role = Hash.new {|h, k| h[k] = Set.new }
|
5
|
+
|
6
|
+
options.assert_valid_keys(:roles)
|
7
|
+
target_roles = Cronicle::Utils.regexp_union(options[:roles])
|
8
|
+
|
9
|
+
begin
|
10
|
+
host_list = JSON.parse(src)
|
11
|
+
rescue JSON::ParserError
|
12
|
+
host_list = {'servers' => {}}
|
13
|
+
|
14
|
+
src.split(/[,\s]+/).each do |host|
|
15
|
+
host.strip!
|
16
|
+
host_list['servers'][host] = [] unless host.empty?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
host_list.assert_valid_keys('servers', 'roles')
|
21
|
+
servers = host_list['servers'] || {}
|
22
|
+
roles = host_list['roles'] || {}
|
23
|
+
|
24
|
+
unless roles.kind_of?(Hash)
|
25
|
+
raise TypeError, "wrong roles type #{roles.class} (expected Hash)"
|
26
|
+
end
|
27
|
+
|
28
|
+
initialize_servers(servers, target_roles)
|
29
|
+
initialize_roles(roles, target_roles)
|
30
|
+
end
|
31
|
+
|
32
|
+
def all
|
33
|
+
@hosts.to_a
|
34
|
+
end
|
35
|
+
|
36
|
+
def select(options = {})
|
37
|
+
options.assert_valid_keys(:servers, :roles)
|
38
|
+
target_servers, target_roles = options.values_at(:servers, :roles)
|
39
|
+
|
40
|
+
target_servers = Cronicle::Utils.regexp_union(target_servers)
|
41
|
+
target_roles = Cronicle::Utils.regexp_union(target_roles)
|
42
|
+
|
43
|
+
host_set = Set.new
|
44
|
+
|
45
|
+
@hosts.each do |host|
|
46
|
+
host_set << host if host =~ target_servers
|
47
|
+
end
|
48
|
+
|
49
|
+
@host_by_role.each do |role, hosts|
|
50
|
+
host_set.merge(hosts) if role =~ target_roles
|
51
|
+
end
|
52
|
+
|
53
|
+
host_set.to_a
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def initialize_servers(servers, target_roles)
|
59
|
+
unless servers.kind_of?(Hash)
|
60
|
+
servers_hash = {}
|
61
|
+
|
62
|
+
Array(servers).each do |host|
|
63
|
+
servers_hash[host.to_s] = []
|
64
|
+
end
|
65
|
+
|
66
|
+
servers = servers_hash
|
67
|
+
end
|
68
|
+
|
69
|
+
servers.each do |host, roles|
|
70
|
+
roles = Array(roles).map(&:to_s)
|
71
|
+
|
72
|
+
if target_roles.nil? or roles.any? {|r| r =~ target_servers }
|
73
|
+
@hosts << host
|
74
|
+
roles.each {|r| @host_by_role[r] << host }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def initialize_roles(roles, target_roles)
|
80
|
+
roles.each do |role, hosts|
|
81
|
+
hosts = Array(hosts).map(&:to_s)
|
82
|
+
|
83
|
+
if target_roles.nil? or roles.any? {|r| r =~ target_servers }
|
84
|
+
@hosts.merge(hosts)
|
85
|
+
@host_by_role[role].merge(hosts)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
class Cronicle::Logger < ::Logger
|
2
|
+
include Singleton
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
super($stdout)
|
6
|
+
|
7
|
+
self.formatter = proc do |severity, datetime, progname, msg|
|
8
|
+
"#{msg}\n"
|
9
|
+
end
|
10
|
+
|
11
|
+
self.level = INFO
|
12
|
+
end
|
13
|
+
|
14
|
+
def set_debug(value)
|
15
|
+
if value
|
16
|
+
self.level = DEBUG
|
17
|
+
SSHKit.config.output_verbosity = :debug
|
18
|
+
else
|
19
|
+
self.level = INFO
|
20
|
+
SSHKit.config.output_verbosity = :warn
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def log(level, message, opts = {})
|
26
|
+
message = "#{level.to_s.downcase}: #{message}" unless level == :info
|
27
|
+
message << ' (dry-run)' if opts[:dry_run]
|
28
|
+
message.gsub!(/\s+\z/, '')
|
29
|
+
message = message.send(opts[:color]) if opts[:color]
|
30
|
+
|
31
|
+
job_info = ''
|
32
|
+
|
33
|
+
if opts[:job]
|
34
|
+
job_info << opts[:job]
|
35
|
+
end
|
36
|
+
|
37
|
+
host_user = [:host, :user].map {|key|
|
38
|
+
value = opts[key]
|
39
|
+
value = Cronicle::Utils.short_hostname(value) if key == :host
|
40
|
+
value
|
41
|
+
}.compact
|
42
|
+
|
43
|
+
unless host_user.empty?
|
44
|
+
job_info << ' on ' unless job_info.empty?
|
45
|
+
job_info << host_user.join('/')
|
46
|
+
end
|
47
|
+
|
48
|
+
unless job_info.empty?
|
49
|
+
job_info = "#{job_info}>".light_black
|
50
|
+
message = "#{job_info} #{message}"
|
51
|
+
end
|
52
|
+
|
53
|
+
logger = opts[:logger] || Cronicle::Logger.instance
|
54
|
+
logger.send(level, message)
|
55
|
+
end
|
56
|
+
end # of class methods
|
57
|
+
|
58
|
+
# XXX:
|
59
|
+
module Helper
|
60
|
+
def log(level, message, opts = {})
|
61
|
+
opts = (@options || {}).merge(opts)
|
62
|
+
Cronicle::Logger.log(level, message, opts)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class SSHKitIO
|
67
|
+
def initialize(io = $stdout)
|
68
|
+
@io = io
|
69
|
+
end
|
70
|
+
|
71
|
+
def <<(obj)
|
72
|
+
@io << mask_password(obj)
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
MASK_REGEXP = /\becho\s+([^|]+)\s+\|\s+sudo\s+-S\s+/
|
78
|
+
MASK = 'XXXXXXXX'
|
79
|
+
|
80
|
+
def mask_password(obj)
|
81
|
+
if obj.kind_of?(String) and obj =~ MASK_REGEXP
|
82
|
+
password = $1
|
83
|
+
obj.sub(password, MASK)
|
84
|
+
else
|
85
|
+
obj
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
SSHKit.config.output = SSHKit::Formatter::Pretty.new(Cronicle::Logger::SSHKitIO.new)
|
92
|
+
SSHKit.config.output_verbosity = :warn
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Cronicle::Utils
|
2
|
+
class << self
|
3
|
+
def regexp_union(list)
|
4
|
+
return nil if list.nil?
|
5
|
+
return list if list.kind_of?(Regexp)
|
6
|
+
|
7
|
+
list = Array(list)
|
8
|
+
return nil if list.empty?
|
9
|
+
|
10
|
+
Regexp.union(list.map {|str_or_reg|
|
11
|
+
if str_or_reg.kind_of?(Regexp)
|
12
|
+
str_or_reg
|
13
|
+
else
|
14
|
+
/\A#{str_or_reg}\z/
|
15
|
+
end
|
16
|
+
})
|
17
|
+
end
|
18
|
+
|
19
|
+
IPADDR_REGEXP = /\A\d+(?:\.\d+){3}\z/
|
20
|
+
|
21
|
+
def short_hostname(hostname)
|
22
|
+
if hostname =~ IPADDR_REGEXP
|
23
|
+
hostname
|
24
|
+
else
|
25
|
+
hostname.split('.').first
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def sed_escape(cmd)
|
30
|
+
cmd.gsub('/', '\\/')
|
31
|
+
end
|
32
|
+
|
33
|
+
def remove_prompt!(str)
|
34
|
+
str.sub!(/\A[^:]*:\s*/, '')
|
35
|
+
end
|
36
|
+
|
37
|
+
def diff(file1, file2)
|
38
|
+
file1 = file1.chomp + "\n"
|
39
|
+
file2 = file2.chomp + "\n"
|
40
|
+
Diffy::Diff.new(file1, file2).to_s(:text)
|
41
|
+
end
|
42
|
+
end # of class methods
|
43
|
+
end
|
@@ -0,0 +1,295 @@
|
|
1
|
+
describe 'Cronicle::Client#apply (create)' do
|
2
|
+
context 'when empty cron' do
|
3
|
+
let(:jobfile) do
|
4
|
+
<<-RUBY.undent
|
5
|
+
on servers: /.*/ do
|
6
|
+
job :foo, user: :root, schedule: '1 2 * * *' do
|
7
|
+
puts `uname`
|
8
|
+
puts `whoami`
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
on servers: /.*/ do
|
13
|
+
job :bar, user: :root, schedule: :@hourly, content: <<-SH.undent
|
14
|
+
#!/bin/sh
|
15
|
+
echo hello
|
16
|
+
SH
|
17
|
+
end
|
18
|
+
|
19
|
+
on servers: /amazon_linux/ do
|
20
|
+
job :foo, user: 'ec2-user', schedule: '1 * * * *' do
|
21
|
+
puts 100
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
on servers: /ubuntu/ do
|
26
|
+
job :foo, user: :ubuntu, schedule: :@daily do
|
27
|
+
puts 200
|
28
|
+
end
|
29
|
+
end
|
30
|
+
RUBY
|
31
|
+
end
|
32
|
+
|
33
|
+
let(:amzn_crontab) do
|
34
|
+
{
|
35
|
+
"/var/spool/cron/ec2-user" =>
|
36
|
+
"1 * * * *\t/var/lib/cronicle/libexec/ec2-user/foo 2>&1 | logger -t cronicle/ec2-user/foo
|
37
|
+
",
|
38
|
+
"/var/spool/cron/root" =>
|
39
|
+
"1 2 * * *\t/var/lib/cronicle/libexec/root/foo 2>&1 | logger -t cronicle/root/foo
|
40
|
+
@hourly\t/var/lib/cronicle/libexec/root/bar 2>&1 | logger -t cronicle/root/bar
|
41
|
+
"
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
let(:ubuntu_crontab) do
|
46
|
+
{
|
47
|
+
"/var/spool/cron/crontabs/root" =>
|
48
|
+
"1 2 * * *\t/var/lib/cronicle/libexec/root/foo 2>&1 | logger -t cronicle/root/foo
|
49
|
+
@hourly\t/var/lib/cronicle/libexec/root/bar 2>&1 | logger -t cronicle/root/bar
|
50
|
+
",
|
51
|
+
"/var/spool/cron/crontabs/ubuntu" =>
|
52
|
+
"@daily\t/var/lib/cronicle/libexec/ubuntu/foo 2>&1 | logger -t cronicle/ubuntu/foo
|
53
|
+
"
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
before do
|
58
|
+
cronicle(:apply) { jobfile }
|
59
|
+
end
|
60
|
+
|
61
|
+
it do
|
62
|
+
on :amazon_linux do
|
63
|
+
expect(get_uname).to match /amzn/
|
64
|
+
expect(get_crontabs).to eq amzn_crontab
|
65
|
+
|
66
|
+
expect(get_file('/var/lib/cronicle/libexec/root/foo')).to eq <<-EOS.undent
|
67
|
+
#!/usr/bin/env ruby
|
68
|
+
puts `uname`
|
69
|
+
puts `whoami`
|
70
|
+
EOS
|
71
|
+
|
72
|
+
expect(get_file('/var/lib/cronicle/libexec/root/bar')).to eq <<-EOS.undent
|
73
|
+
#!/bin/sh
|
74
|
+
echo hello
|
75
|
+
EOS
|
76
|
+
|
77
|
+
expect(get_file('/var/lib/cronicle/libexec/ec2-user/foo')).to eq <<-EOS.undent
|
78
|
+
#!/usr/bin/env ruby
|
79
|
+
puts 100
|
80
|
+
EOS
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
it do
|
85
|
+
on :ubuntu do
|
86
|
+
expect(get_uname).to match /Ubuntu/
|
87
|
+
expect(get_crontabs).to eq ubuntu_crontab
|
88
|
+
|
89
|
+
expect(get_file('/var/lib/cronicle/libexec/root/foo')).to eq <<-EOS.undent
|
90
|
+
#!/usr/bin/env ruby
|
91
|
+
puts `uname`
|
92
|
+
puts `whoami`
|
93
|
+
EOS
|
94
|
+
|
95
|
+
expect(get_file('/var/lib/cronicle/libexec/root/bar')).to eq <<-EOS.undent
|
96
|
+
#!/bin/sh
|
97
|
+
echo hello
|
98
|
+
EOS
|
99
|
+
|
100
|
+
expect(get_file('/var/lib/cronicle/libexec/ubuntu/foo')).to eq <<-EOS.undent
|
101
|
+
#!/usr/bin/env ruby
|
102
|
+
puts 200
|
103
|
+
EOS
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'when default cron' do
|
109
|
+
before do
|
110
|
+
on TARGET_HOSTS do |ssh_options|
|
111
|
+
user = ssh_options[:user]
|
112
|
+
|
113
|
+
set_crontab user, <<-CRON.undent
|
114
|
+
FOO=bar
|
115
|
+
ZOO=baz
|
116
|
+
1 1 1 1 1 echo #{user} > /dev/null
|
117
|
+
CRON
|
118
|
+
|
119
|
+
set_crontab :root, <<-CRON.undent
|
120
|
+
FOO=bar
|
121
|
+
ZOO=baz
|
122
|
+
1 1 1 1 1 echo root > /dev/null
|
123
|
+
CRON
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
let(:jobfile) do
|
128
|
+
<<-RUBY.undent
|
129
|
+
on servers: /.*/ do
|
130
|
+
job :foo, user: :root, schedule: '1 2 * * *' do
|
131
|
+
puts `uname`
|
132
|
+
puts `whoami`
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
on servers: /.*/ do
|
137
|
+
job :bar, user: :root, schedule: :@hourly, content: <<-SH.undent
|
138
|
+
#!/bin/sh
|
139
|
+
echo hello
|
140
|
+
SH
|
141
|
+
end
|
142
|
+
|
143
|
+
on servers: /amazon_linux/ do
|
144
|
+
job :foo, user: 'ec2-user', schedule: '1 * * * *' do
|
145
|
+
puts 100
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
on servers: /ubuntu/ do
|
150
|
+
job :foo, user: :ubuntu, schedule: :@daily do
|
151
|
+
puts 200
|
152
|
+
end
|
153
|
+
end
|
154
|
+
RUBY
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'when apply' do
|
158
|
+
let(:amzn_crontab) do
|
159
|
+
{
|
160
|
+
"/var/spool/cron/ec2-user" =>
|
161
|
+
"FOO=bar
|
162
|
+
ZOO=baz
|
163
|
+
1 1 1 1 1 echo ec2-user > /dev/null
|
164
|
+
1 * * * *\t/var/lib/cronicle/libexec/ec2-user/foo 2>&1 | logger -t cronicle/ec2-user/foo
|
165
|
+
",
|
166
|
+
"/var/spool/cron/root" =>
|
167
|
+
"FOO=bar
|
168
|
+
ZOO=baz
|
169
|
+
1 1 1 1 1 echo root > /dev/null
|
170
|
+
1 2 * * *\t/var/lib/cronicle/libexec/root/foo 2>&1 | logger -t cronicle/root/foo
|
171
|
+
@hourly\t/var/lib/cronicle/libexec/root/bar 2>&1 | logger -t cronicle/root/bar
|
172
|
+
"
|
173
|
+
}
|
174
|
+
end
|
175
|
+
|
176
|
+
let(:ubuntu_crontab) do
|
177
|
+
{
|
178
|
+
"/var/spool/cron/crontabs/root" =>
|
179
|
+
"FOO=bar
|
180
|
+
ZOO=baz
|
181
|
+
1 1 1 1 1 echo root > /dev/null
|
182
|
+
1 2 * * *\t/var/lib/cronicle/libexec/root/foo 2>&1 | logger -t cronicle/root/foo
|
183
|
+
@hourly\t/var/lib/cronicle/libexec/root/bar 2>&1 | logger -t cronicle/root/bar
|
184
|
+
",
|
185
|
+
"/var/spool/cron/crontabs/ubuntu" =>
|
186
|
+
"FOO=bar
|
187
|
+
ZOO=baz
|
188
|
+
1 1 1 1 1 echo ubuntu > /dev/null
|
189
|
+
@daily\t/var/lib/cronicle/libexec/ubuntu/foo 2>&1 | logger -t cronicle/ubuntu/foo
|
190
|
+
"
|
191
|
+
}
|
192
|
+
end
|
193
|
+
|
194
|
+
before do
|
195
|
+
cronicle(:apply) { jobfile }
|
196
|
+
end
|
197
|
+
|
198
|
+
it do
|
199
|
+
on :amazon_linux do
|
200
|
+
expect(get_uname).to match /amzn/
|
201
|
+
expect(get_crontabs).to eq amzn_crontab
|
202
|
+
|
203
|
+
expect(get_file('/var/lib/cronicle/libexec/root/foo')).to eq <<-EOS.undent
|
204
|
+
#!/usr/bin/env ruby
|
205
|
+
puts `uname`
|
206
|
+
puts `whoami`
|
207
|
+
EOS
|
208
|
+
|
209
|
+
expect(get_file('/var/lib/cronicle/libexec/root/bar')).to eq <<-EOS.undent
|
210
|
+
#!/bin/sh
|
211
|
+
echo hello
|
212
|
+
EOS
|
213
|
+
|
214
|
+
expect(get_file('/var/lib/cronicle/libexec/ec2-user/foo')).to eq <<-EOS.undent
|
215
|
+
#!/usr/bin/env ruby
|
216
|
+
puts 100
|
217
|
+
EOS
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
it do
|
222
|
+
on :ubuntu do
|
223
|
+
expect(get_uname).to match /Ubuntu/
|
224
|
+
expect(get_crontabs).to eq ubuntu_crontab
|
225
|
+
|
226
|
+
expect(get_file('/var/lib/cronicle/libexec/root/foo')).to eq <<-EOS.undent
|
227
|
+
#!/usr/bin/env ruby
|
228
|
+
puts `uname`
|
229
|
+
puts `whoami`
|
230
|
+
EOS
|
231
|
+
|
232
|
+
expect(get_file('/var/lib/cronicle/libexec/root/bar')).to eq <<-EOS.undent
|
233
|
+
#!/bin/sh
|
234
|
+
echo hello
|
235
|
+
EOS
|
236
|
+
|
237
|
+
expect(get_file('/var/lib/cronicle/libexec/ubuntu/foo')).to eq <<-EOS.undent
|
238
|
+
#!/usr/bin/env ruby
|
239
|
+
puts 200
|
240
|
+
EOS
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
context 'when apply (dry-run)' do
|
246
|
+
let(:amzn_crontab) do
|
247
|
+
{
|
248
|
+
"/var/spool/cron/ec2-user" =>
|
249
|
+
"FOO=bar
|
250
|
+
ZOO=baz
|
251
|
+
1 1 1 1 1 echo ec2-user > /dev/null
|
252
|
+
",
|
253
|
+
"/var/spool/cron/root" =>
|
254
|
+
"FOO=bar
|
255
|
+
ZOO=baz
|
256
|
+
1 1 1 1 1 echo root > /dev/null
|
257
|
+
"
|
258
|
+
}
|
259
|
+
end
|
260
|
+
|
261
|
+
let(:ubuntu_crontab) do
|
262
|
+
{
|
263
|
+
"/var/spool/cron/crontabs/root" =>
|
264
|
+
"FOO=bar
|
265
|
+
ZOO=baz
|
266
|
+
1 1 1 1 1 echo root > /dev/null
|
267
|
+
",
|
268
|
+
"/var/spool/cron/crontabs/ubuntu" =>
|
269
|
+
"FOO=bar
|
270
|
+
ZOO=baz
|
271
|
+
1 1 1 1 1 echo ubuntu > /dev/null
|
272
|
+
"
|
273
|
+
}
|
274
|
+
end
|
275
|
+
|
276
|
+
before do
|
277
|
+
cronicle(:apply, dry_run: true) { jobfile }
|
278
|
+
end
|
279
|
+
|
280
|
+
it do
|
281
|
+
on :amazon_linux do
|
282
|
+
expect(get_uname).to match /amzn/
|
283
|
+
expect(get_crontabs).to eq amzn_crontab
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
it do
|
288
|
+
on :ubuntu do
|
289
|
+
expect(get_uname).to match /Ubuntu/
|
290
|
+
expect(get_crontabs).to eq ubuntu_crontab
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|