Class: Minicron::Cron

Inherits:
Object
  • Object
show all
Defined in:
lib/minicron/cron.rb

Overview

Used to interact with the crontab on hosts over an ssh connection

Instance Method Summary collapse

Constructor Details

#initialize(ssh) ⇒ Cron

Initialise the cron class

Parameters:


10
11
12
# File 'lib/minicron/cron.rb', line 10

def initialize(ssh)
  @ssh = ssh
end

Instance Method Details

#add_schedule(job, schedule, conn = nil) ⇒ Object

Add the schedule for this job to the crontab

Parameters:

  • job (Minicron::Hub::Job)

    an instance of a job model

  • schedule (String)

    the job schedule as a string

  • conn (defaults to: nil)

    an instance of an open ssh connection


80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/minicron/cron.rb', line 80

def add_schedule(job, schedule, conn = nil)
  # Open an SSH connection
  conn ||= @ssh.open

  # Prepare the line we are going to write to the crontab
  line = build_minicron_command(schedule, job.user, job.command)
  escaped_line = line.shellescape
  echo_line = "echo #{escaped_line} >> /etc/crontab"

  # Append it to the end of the crontab
  conn.exec!(echo_line).to_s.strip

  # Check the line is there
  tail = conn.exec!('tail -n 1 /etc/crontab').to_s.strip

  # Throw an exception if we can't see our new line at the end of the file
  if tail != line
    fail Exception, "Expected to find '#{line}' at eof but found '#{tail}'"
  end
end

#build_minicron_command(schedule, user, command) ⇒ String

Build the minicron command to be used in the crontab

Parameters:

  • schedule (String)
  • user (String)
  • command (String)

Returns:

  • (String)

20
21
22
23
24
25
26
# File 'lib/minicron/cron.rb', line 20

def build_minicron_command(schedule, user, command)
  # Escape the command so it will work in bourne shells
  command = Escape.shell_command(['minicron', 'run', command])
  cron_command = Escape.shell_command(['/bin/bash', '-l', '-c', command])

  "#{schedule} #{user} #{cron_command}"
end

#delete_host(host, conn = nil) ⇒ Object

Delete a host and all it's jobs from the crontab

Parameters:

  • job (Minicron::Hub::Job)

    a job instance with it's schedules

  • conn (defaults to: nil)

    an instance of an open ssh connection


159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/minicron/cron.rb', line 159

def delete_host(host, conn = nil)
  conn ||= @ssh.open

  # Loop through each job and delete them one by one
  # TODO: share the ssh connection for this so it's faster when
  # many schedules exist
  # TODO: what if one schedule removal fails but others don't? Should
  # we try and rollback somehow or just return the job with half its
  # schedules deleted?
  host.jobs.each do |job|
    delete_job(job, conn)
  end
end

#delete_job(job, conn = nil) ⇒ Object

Delete a job and all it's schedules from the crontab

Parameters:

  • job (Minicron::Hub::Job)

    a job instance with it's schedules

  • conn (defaults to: nil)

    an instance of an open ssh connection


141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/minicron/cron.rb', line 141

def delete_job(job, conn = nil)
  conn ||= @ssh.open

  # Loop through each schedule and delete them one by one
  # TODO: share the ssh connection for this so it's faster when
  # many schedules exist
  # TODO: what if one schedule removal fails but others don't? Should
  # we try and rollback somehow or just return the job with half its
  # schedules deleted?
  job.schedules.each do |schedule|
    delete_schedule(job, schedule.formatted, conn)
  end
end

#delete_schedule(job, schedule, conn = nil) ⇒ Object

Remove the schedule for this job from the crontab

Parameters:

  • job (Minicron::Hub::Job)

    an instance of a job model

  • schedule (String)

    the job schedule as a string

  • conn (defaults to: nil)

    an instance of an open ssh connection


126
127
128
129
130
131
132
133
134
135
# File 'lib/minicron/cron.rb', line 126

def delete_schedule(job, schedule, conn = nil)
  # Open an SSH connection
  conn ||= @ssh.open

  # We are looking for the current value of the schedule
  find = build_minicron_command(schedule, job.user, job.command)

  # Replace the old schedule with nothing i.e deleting it
  find_and_replace(conn, find, '')
end

#find_and_replace(conn, find, replace) ⇒ Object

Used to find a string and replace it with another in the crontab by using the sed command

Parameters:

  • conn

    an instance of an open ssh connection

  • find (String)
  • replace (String)

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/minicron/cron.rb', line 34

def find_and_replace(conn, find, replace)
  # Get the full crontab
  crontab = conn.exec!('cat /etc/crontab').to_s.strip

  # Replace the full string with the replacement string
  begin
    crontab[find] = replace
  rescue Exception => e
    raise Exception, "Unable to replace '#{find}' with '#{replace}' in the crontab, reason: #{e}"
  end

  # Echo the crontab back to the tmp crontab
  conn.exec!("echo #{crontab.shellescape} > /etc/crontab.tmp").to_s.strip

  # If it's a delete
  if replace == ''
    # Check the original line is no longer there
    grep = conn.exec!("grep -F #{find.shellescape} /etc/crontab.tmp").to_s.strip

    # Throw an exception if we can't see our new line at the end of the file
    if grep != replace
      fail Exception, "Expected to find nothing when grepping crontab but found #{grep}"
    end
  else
    # Check the updated line is there
    grep = conn.exec!("grep -F #{replace.shellescape} /etc/crontab.tmp").to_s.strip

    # Throw an exception if we can't see our new line at the end of the file
    if grep != replace
      fail Exception, "Expected to find '#{replace}' when grepping crontab but found #{grep}"
    end
  end

  # And finally replace the crontab with the new one now we now the change worked
  move = conn.exec!("/bin/sh -c 'mv /etc/crontab.tmp /etc/crontab && echo \"y\" || echo \"n\"'").to_s.strip

  if move != 'y'
    fail Exception, 'Unable to move tmp crontab with updated crontab'
  end
end

#update_schedule(job, old_schedule, new_schedule, conn = nil) ⇒ Object

Update the schedule for this job in the crontab

Parameters:

  • job (Minicron::Hub::Job)

    an instance of a job model

  • old_schedule (String)

    the old job schedule as a string

  • new_schedule (String)

    the new job schedule as a string

  • conn (defaults to: nil)

    an instance of an open ssh connection


107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/minicron/cron.rb', line 107

def update_schedule(job, old_schedule, new_schedule, conn = nil)
  # Open an SSH connection
  conn ||= @ssh.open

  # We are looking for the current value of the schedule
  find = build_minicron_command(old_schedule, job.user, job.command)

  # And replacing it with the updated value
  replace = build_minicron_command(new_schedule, job.user, job.command)

  # Replace the old schedule with the new schedule
  find_and_replace(conn, find, replace)
end