minicron 0.3 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +42 -33
  3. data/bin/minicron +3 -0
  4. data/lib/minicron.rb +12 -3
  5. data/lib/minicron/alert.rb +9 -6
  6. data/lib/minicron/cli.rb +15 -6
  7. data/lib/minicron/cli/commands.rb +35 -12
  8. data/lib/minicron/constants.rb +1 -1
  9. data/lib/minicron/cron.rb +71 -30
  10. data/lib/minicron/hub/app.rb +6 -0
  11. data/lib/minicron/hub/assets/app/components/schedules.js +1 -1
  12. data/lib/minicron/hub/assets/app/controllers/hosts.js +6 -2
  13. data/lib/minicron/hub/assets/app/controllers/jobs.js +5 -2
  14. data/lib/minicron/hub/assets/app/controllers/schedules.js +15 -13
  15. data/lib/minicron/hub/assets/app/models/host.js +1 -0
  16. data/lib/minicron/hub/assets/app/models/job.js +1 -0
  17. data/lib/minicron/hub/controllers/api/executions.rb +0 -1
  18. data/lib/minicron/hub/controllers/api/hosts.rb +12 -13
  19. data/lib/minicron/hub/controllers/api/job_execution_outputs.rb +0 -1
  20. data/lib/minicron/hub/controllers/api/jobs.rb +5 -3
  21. data/lib/minicron/hub/controllers/api/schedule.rb +3 -1
  22. data/lib/minicron/hub/db/schema.rb +8 -6
  23. data/lib/minicron/hub/db/schema.sql +4 -2
  24. data/lib/minicron/hub/views/handlebars/hosts.erb +18 -0
  25. data/lib/minicron/hub/views/handlebars/jobs.erb +18 -0
  26. data/lib/minicron/monitor.rb +14 -12
  27. data/lib/minicron/transport.rb +0 -1
  28. data/lib/minicron/transport/client.rb +19 -11
  29. data/lib/minicron/transport/faye/client.rb +16 -2
  30. data/lib/minicron/transport/faye/extensions/job_handler.rb +10 -4
  31. data/lib/minicron/transport/faye/server.rb +5 -4
  32. data/lib/minicron/transport/server.rb +9 -4
  33. data/lib/minicron/transport/ssh.rb +5 -4
  34. data/spec/minicron/alert/pagerduty_spec.rb +1 -0
  35. data/spec/minicron/alert/sms_spec.rb +1 -0
  36. data/spec/minicron/cli_spec.rb +1 -0
  37. data/spec/minicron/transport/client_spec.rb +1 -0
  38. data/spec/minicron/transport/faye/client_spec.rb +1 -0
  39. data/spec/minicron/transport/server_spec.rb +1 -0
  40. data/spec/minicron/transport_spec.rb +1 -0
  41. data/spec/minicron_spec.rb +6 -0
  42. metadata +88 -88
@@ -23,6 +23,10 @@ module Minicron::Hub
23
23
  # Disable internal middleware for presenting errors
24
24
  # as useful HTML pages
25
25
  set :show_exceptions, false
26
+
27
+ # Used to enable asset compression, currently nothing else
28
+ # relies on this
29
+ set :environment, :production
26
30
  end
27
31
 
28
32
  # Configure how we server assets
@@ -32,6 +36,8 @@ module Minicron::Hub
32
36
  serve '/fonts', :from => 'assets/fonts'
33
37
  serve '/app', :from => 'assets/app'
34
38
 
39
+ js_compression :simple
40
+
35
41
  # Set up the application css
36
42
  css :app, '/css/all.css', [
37
43
  '/css/bootswatch.min.css',
@@ -273,7 +273,7 @@
273
273
  });
274
274
  },
275
275
  cancel: function(schedule) {
276
- this.sendAction('cancel', schedule);
276
+ this.sendAction('cancel', this.get('job_id'));
277
277
  }
278
278
  }
279
279
  });
@@ -30,8 +30,10 @@
30
30
  // TODO: make this use a bootstrap model as a component
31
31
  var results = 'Test Results\n\n';
32
32
  results += 'connect: ' + data.connect + '\n';
33
- results += 'crontab readable: ' + data.read + '\n';
34
- results += 'crontab writeable: ' + data.write;
33
+ results += '/etc writeable: ' + data.etc.write + '\n';
34
+ results += '/etc executable: ' + data.etc.execute + '\n';
35
+ results += 'crontab readable: ' + data.crontab.read + '\n';
36
+ results += 'crontab writeable: ' + data.crontab.write;
35
37
 
36
38
  window.alert(results);
37
39
  } else {
@@ -73,6 +75,7 @@
73
75
  host = this.store.createRecord('host', {
74
76
  name: this.get('name'),
75
77
  fqdn: this.get('fqdn'),
78
+ user: this.get('user'),
76
79
  host: this.get('host'),
77
80
  port: this.get('port'),
78
81
  public_key: this.get('public_key')
@@ -101,6 +104,7 @@
101
104
  id: this.get('id'),
102
105
  name: this.get('name'),
103
106
  fqdn: this.get('fqdn'),
107
+ user: this.get('user'),
104
108
  host: this.get('host'),
105
109
  port: this.get('port'),
106
110
  public_key: this.get('public_key')
@@ -32,13 +32,15 @@
32
32
  Minicron.JobsNewController = Ember.ObjectController.extend({
33
33
  save_button: 'Save',
34
34
  job_name: null,
35
+ job_user: null,
35
36
  job_command: null,
36
37
  actions: {
37
38
  save: function() {
38
39
  var self = this,
39
40
  job = this.store.createRecord('job', {
40
41
  name: this.get('job_name'),
41
- command: this.get('job_command'),
42
+ user: this.get('job_user'),
43
+ command: this.get('job_command')
42
44
  });
43
45
  // Let the user know the job is being saved
44
46
  this.set('save_button', 'Saving..');
@@ -86,7 +88,8 @@
86
88
  var self = this,
87
89
  job = this.store.push('job', {
88
90
  id: this.get('id'),
89
- name: this.get('name')
91
+ name: this.get('name'),
92
+ user: this.get('user')
90
93
  });
91
94
 
92
95
  job.save().then(function(job) {
@@ -5,18 +5,20 @@
5
5
  var confirmation = "Are you sure want to delete this schedule? It will be removed from the host crontab also!\n";
6
6
  var job_id = schedule.get('job.id');
7
7
 
8
- schedule.deleteRecord();
8
+ if (window.confirm(confirmation)) {
9
+ schedule.deleteRecord();
9
10
 
10
- schedule.save().then(function() {
11
- // This is needed to reload all the relationships correctly after a delete
12
- // TODO: do this in a nicer way
13
- window.location.hash = '/jobs/' + job_id;
14
- window.location.reload();
15
- }, function(response) {
16
- schedule.rollback();
17
- console.log(response);
18
- window.prompt('Error deleting schedule, reason:', response.responseJSON.error);
19
- });
11
+ schedule.save().then(function() {
12
+ // This is needed to reload all the relationships correctly after a delete
13
+ // TODO: do this in a nicer way
14
+ window.location.hash = '/jobs/' + job_id;
15
+ window.location.reload();
16
+ }, function(response) {
17
+ schedule.rollback();
18
+ console.log(response);
19
+ window.prompt('Error deleting schedule, reason:', response.responseJSON.error);
20
+ });
21
+ }
20
22
  }
21
23
 
22
24
  Minicron.SchedulesNewController = Ember.ObjectController.extend({
@@ -38,8 +40,8 @@
38
40
  });
39
41
  });
40
42
  },
41
- cancel: function(schedule) {
42
- this.transitionToRoute('job', schedule.get('id'));
43
+ cancel: function(job_id) {
44
+ this.transitionToRoute('job', job_id);
43
45
  }
44
46
  }
45
47
  });
@@ -4,6 +4,7 @@
4
4
  Minicron.Host = DS.Model.extend({
5
5
  name: DS.attr('string'),
6
6
  fqdn: DS.attr('string'),
7
+ user: DS.attr('string'),
7
8
  host: DS.attr('string'),
8
9
  port: DS.attr('number'),
9
10
  public_key: DS.attr('string'),
@@ -4,6 +4,7 @@
4
4
  Minicron.Job = DS.Model.extend({
5
5
  job_hash: DS.attr('string'),
6
6
  name: DS.attr('string'),
7
+ user: DS.attr('string'),
7
8
  command: DS.attr('string'),
8
9
  created_at: DS.attr('date'),
9
10
  updated_at: DS.attr('date'),
@@ -1,6 +1,5 @@
1
1
  class Minicron::Hub::App
2
2
  # Get all job executions
3
- # TODO: Add offset/limit
4
3
  get '/api/executions' do
5
4
  content_type :json
6
5
  executions = Minicron::Hub::Execution.all.order(:created_at => :desc, :started_at => :desc)
@@ -2,7 +2,6 @@ require 'minicron/transport/ssh'
2
2
 
3
3
  class Minicron::Hub::App
4
4
  # Get all hosts that a job
5
- # TODO: Add offset/limit
6
5
  get '/api/hosts' do
7
6
  content_type :json
8
7
  hosts = Minicron::Hub::Host.all.includes(:jobs).order(:id => :asc)
@@ -26,10 +25,14 @@ class Minicron::Hub::App
26
25
  # Default the value of the port
27
26
  request_body['host']['port'] ||= 22
28
27
 
28
+ # Default the value of the user
29
+ request_body['host']['user'] ||= 'root'
30
+
29
31
  # Try and save the new host
30
32
  host = Minicron::Hub::Host.create(
31
33
  :name => request_body['host']['name'],
32
34
  :fqdn => request_body['host']['fqdn'],
35
+ :user => request_body['host']['user'],
33
36
  :host => request_body['host']['host'],
34
37
  :port => request_body['host']['port']
35
38
  )
@@ -66,6 +69,7 @@ class Minicron::Hub::App
66
69
  # Update its data
67
70
  host.name = request_body['host']['name']
68
71
  host.fqdn = request_body['host']['fqdn']
72
+ host.user = request_body['host']['user']
69
73
  host.host = request_body['host']['host']
70
74
  host.port = request_body['host']['port']
71
75
 
@@ -93,6 +97,7 @@ class Minicron::Hub::App
93
97
 
94
98
  # Get an ssh instance and open a connection
95
99
  ssh = Minicron::Transport::SSH.new(
100
+ :user => host.user,
96
101
  :host => host.host,
97
102
  :port => host.port,
98
103
  :private_key => "~/.ssh/minicron_host_#{host.id}_rsa"
@@ -131,29 +136,23 @@ class Minicron::Hub::App
131
136
 
132
137
  # Set up the ssh instance
133
138
  ssh = Minicron::Transport::SSH.new(
139
+ :user => host.user,
134
140
  :host => host.host,
135
141
  :port => host.port,
136
142
  :private_key => "~/.ssh/minicron_host_#{host.id}_rsa"
137
143
  )
138
144
 
139
- # Open the connection
140
- conn = ssh.open
141
-
142
- # Check if the crontab is readable
143
- read = conn.exec!('test -r /etc/crontab && echo "y" || echo "n"').strip
145
+ # Get an instance of the cron class
146
+ cron = Minicron::Cron.new(ssh)
144
147
 
145
- # Check if the crontab is writeable
146
- write = conn.exec!('test -w /etc/crontab && echo "y" || echo "n"').strip
148
+ # Test the SSH connection
149
+ test = cron.test_host_permissions
147
150
 
148
151
  # Tidy up
149
152
  ssh.close
150
153
 
151
154
  # Return the test results as JSON
152
- {
153
- :connect => true,
154
- :read => read == 'y',
155
- :write => write == 'y'
156
- }.to_json
155
+ test.to_json
157
156
  rescue Exception => e
158
157
  status 422
159
158
  { :connect => false, :error => e.message }.to_json
@@ -1,6 +1,5 @@
1
1
  class Minicron::Hub::App
2
2
  # Get all job execution outputs
3
- # TODO: Add offset/limit
4
3
  get '/api/jobExecutionOutputs' do
5
4
  content_type :json
6
5
 
@@ -2,7 +2,6 @@ require 'minicron/transport'
2
2
 
3
3
  class Minicron::Hub::App
4
4
  # Get all jobs
5
- # TODO: Add offset/limit
6
5
  get '/api/jobs' do
7
6
  content_type :json
8
7
 
@@ -11,7 +10,7 @@ class Minicron::Hub::App
11
10
  .where(:job_hash => params[:job_hash])
12
11
  else
13
12
  jobs = Minicron::Hub::Job.all.order(:created_at => :desc)
14
- .includes(:host, :schedules, :executions => :job_execution_outputs)
13
+ .includes(:host, :schedules, :executions => :job_execution_outputs)
15
14
  end
16
15
 
17
16
  Minicron::Hub::JobSerializer.new(jobs).serialize.to_json
@@ -39,6 +38,7 @@ class Minicron::Hub::App
39
38
  job = Minicron::Hub::Job.create(
40
39
  :job_hash => Minicron::Transport.get_job_hash(request_body['job']['command'], host.fqdn),
41
40
  :name => request_body['job']['name'],
41
+ :user => request_body['job']['user'],
42
42
  :command => request_body['job']['command'],
43
43
  :host_id => host.id
44
44
  )
@@ -65,8 +65,9 @@ class Minicron::Hub::App
65
65
  job = Minicron::Hub::Job.includes(:host, :schedules, :executions => :job_execution_outputs)
66
66
  .find(params[:id])
67
67
 
68
- # Update the name
68
+ # Update the name and user
69
69
  job.name = request_body['job']['name']
70
+ job.user = request_body['job']['user']
70
71
 
71
72
  job.save!
72
73
 
@@ -92,6 +93,7 @@ class Minicron::Hub::App
92
93
 
93
94
  # Get an ssh instance
94
95
  ssh = Minicron::Transport::SSH.new(
96
+ :user => job.host.user,
95
97
  :host => job.host.host,
96
98
  :port => job.host.port,
97
99
  :private_key => "~/.ssh/minicron_host_#{job.host.id}_rsa"
@@ -3,7 +3,6 @@ require 'minicron/cron'
3
3
 
4
4
  class Minicron::Hub::App
5
5
  # Get all schedules
6
- # TODO: Add offset/limit
7
6
  get '/api/schedules' do
8
7
  content_type :json
9
8
 
@@ -65,6 +64,7 @@ class Minicron::Hub::App
65
64
 
66
65
  # Get an ssh instance
67
66
  ssh = Minicron::Transport::SSH.new(
67
+ :user => job.host.user,
68
68
  :host => job.host.host,
69
69
  :port => job.host.port,
70
70
  :private_key => "~/.ssh/minicron_host_#{job.host.id}_rsa"
@@ -106,6 +106,7 @@ class Minicron::Hub::App
106
106
 
107
107
  # Get an ssh instance
108
108
  ssh = Minicron::Transport::SSH.new(
109
+ :user => schedule.job.host.user,
109
110
  :host => schedule.job.host.host,
110
111
  :port => schedule.job.host.port,
111
112
  :private_key => "~/.ssh/minicron_host_#{schedule.job.host.id}_rsa"
@@ -158,6 +159,7 @@ class Minicron::Hub::App
158
159
 
159
160
  # Get an ssh instance
160
161
  ssh = Minicron::Transport::SSH.new(
162
+ :user => schedule.job.host.user,
161
163
  :host => schedule.job.host.host,
162
164
  :port => schedule.job.host.port,
163
165
  :private_key => "~/.ssh/minicron_host_#{schedule.job.host.id}_rsa"
@@ -42,12 +42,13 @@ ActiveRecord::Schema.define(version: 0) do
42
42
 
43
43
  create_table "hosts", force: true do |t|
44
44
  t.string "name"
45
- t.string "fqdn", default: "", null: false
46
- t.string "host", default: "", null: false
47
- t.integer "port", null: false
45
+ t.string "fqdn", default: "", null: false
46
+ t.string "user", limit: 32, default: "", null: false
47
+ t.string "host", default: "", null: false
48
+ t.integer "port", null: false
48
49
  t.text "public_key"
49
- t.datetime "created_at", null: false
50
- t.datetime "updated_at", null: false
50
+ t.datetime "created_at", null: false
51
+ t.datetime "updated_at", null: false
51
52
  end
52
53
 
53
54
  add_index "hosts", ["fqdn"], name: "hostname", using: :btree
@@ -63,10 +64,11 @@ ActiveRecord::Schema.define(version: 0) do
63
64
  add_index "job_execution_outputs", ["seq"], name: "seq", using: :btree
64
65
 
65
66
  create_table "jobs", force: true do |t|
67
+ t.integer "host_id", null: false
66
68
  t.string "job_hash", limit: 32, default: "", null: false
67
69
  t.string "name"
70
+ t.string "user", limit: 32, null: false
68
71
  t.text "command", null: false
69
- t.integer "host_id", null: false
70
72
  t.datetime "created_at", null: false
71
73
  t.datetime "updated_at", null: false
72
74
  end
@@ -7,7 +7,7 @@
7
7
  #
8
8
  # Host: 127.0.0.1 (MySQL 5.6.16)
9
9
  # Database: minicron
10
- # Generation Time: 2014-04-01 03:35:39 +0000
10
+ # Generation Time: 2014-04-07 17:06:03 +0000
11
11
  # ************************************************************
12
12
 
13
13
 
@@ -73,6 +73,7 @@ CREATE TABLE `hosts` (
73
73
  `id` int(11) NOT NULL AUTO_INCREMENT,
74
74
  `name` varchar(255) DEFAULT NULL,
75
75
  `fqdn` varchar(255) NOT NULL DEFAULT '',
76
+ `user` varchar(32) NOT NULL DEFAULT '',
76
77
  `host` varchar(255) NOT NULL DEFAULT '',
77
78
  `port` int(11) NOT NULL,
78
79
  `public_key` text,
@@ -109,10 +110,11 @@ DROP TABLE IF EXISTS `jobs`;
109
110
 
110
111
  CREATE TABLE `jobs` (
111
112
  `id` int(11) NOT NULL AUTO_INCREMENT,
113
+ `host_id` int(11) NOT NULL,
112
114
  `job_hash` varchar(32) NOT NULL DEFAULT '',
113
115
  `name` varchar(255) DEFAULT NULL,
116
+ `user` varchar(32) NOT NULL,
114
117
  `command` text NOT NULL,
115
- `host_id` int(11) NOT NULL,
116
118
  `created_at` datetime NOT NULL,
117
119
  `updated_at` datetime NOT NULL,
118
120
  PRIMARY KEY (`id`),
@@ -55,6 +55,12 @@
55
55
  <div class="form-control form-control-view">{{fqdn}}</div>
56
56
  </div>
57
57
  </div>
58
+ <div class="form-group">
59
+ <label class="col-sm-2 control-label">User</label>
60
+ <div class="col-sm-10">
61
+ <div class="form-control form-control-view">{{user}}</div>
62
+ </div>
63
+ </div>
58
64
  <div class="form-group">
59
65
  <label class="col-sm-2 control-label">Host</label>
60
66
  <div class="col-sm-10">
@@ -141,6 +147,12 @@
141
147
  {{input value=fqdn type="text" class="form-control" placeholder="The machine fully qualified domain name e.g 'server1.host.com'"}}
142
148
  </div>
143
149
  </div>
150
+ <div class="form-group">
151
+ <label class="col-sm-2 control-label">User</label>
152
+ <div class="col-sm-10">
153
+ {{input value=user type="text" class="form-control" placeholder="The user to connect to the host as"}}
154
+ </div>
155
+ </div>
144
156
  <div class="form-group">
145
157
  <label class="col-sm-2 control-label">Host</label>
146
158
  <div class="col-sm-10">
@@ -183,6 +195,12 @@
183
195
  {{input value=fqdn type="text" class="form-control" placeholder="The machine fully qualified domain name e.g 'server1.host.com'"}}
184
196
  </div>
185
197
  </div>
198
+ <div class="form-group">
199
+ <label class="col-sm-2 control-label">User</label>
200
+ <div class="col-sm-10">
201
+ {{input value=user type="text" class="form-control" placeholder="The user to connect to the host as"}}
202
+ </div>
203
+ </div>
186
204
  <div class="form-group">
187
205
  <label class="col-sm-2 control-label">Host</label>
188
206
  <div class="col-sm-10">
@@ -48,6 +48,12 @@
48
48
  <div class="form-control form-control-view">{{name}}</div>
49
49
  </div>
50
50
  </div>
51
+ <div class="form-group">
52
+ <label class="col-sm-2 control-label">User</label>
53
+ <div class="col-sm-10">
54
+ <div class="form-control form-control-view">{{user}}</div>
55
+ </div>
56
+ </div>
51
57
  <div class="form-group">
52
58
  <label class="col-sm-2 control-label">Command</label>
53
59
  <div class="col-sm-10">
@@ -151,6 +157,12 @@
151
157
  {{input value=job_name type="text" class="form-control" placeholder="A nickname for the job"}}
152
158
  </div>
153
159
  </div>
160
+ <div class="form-group">
161
+ <label class="col-sm-2 control-label">User</label>
162
+ <div class="col-sm-10">
163
+ {{input value=job_user type="text" class="form-control" placeholder="What user should the job be run as"}}
164
+ </div>
165
+ </div>
154
166
  <div class="form-group">
155
167
  <label class="col-sm-2 control-label">Command</label>
156
168
  <div class="col-sm-10">
@@ -193,6 +205,12 @@
193
205
  {{input value=name type="text" class="form-control" placeholder="A nickname for the job"}}
194
206
  </div>
195
207
  </div>
208
+ <div class="form-group">
209
+ <label class="col-sm-2 control-label">User</label>
210
+ <div class="col-sm-10">
211
+ {{input value=user type="text" class="form-control" placeholder="What user should the job be run as"}}
212
+ </div>
213
+ </div>
196
214
  <div class="form-group">
197
215
  <div class="col-sm-offset-2 col-sm-10">
198
216
  <button type="submit" class="btn btn-success">Save</button>