ridoku 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 +23 -0
- data/AUTHORS +3 -0
- data/Gemfile +24 -0
- data/Gemfile.lock +45 -0
- data/LICENSE.txt +23 -0
- data/README.md +271 -0
- data/Rakefile +1 -0
- data/bin/rid +3 -0
- data/bin/ridoku +350 -0
- data/lib/helpers.rb +13 -0
- data/lib/io-colorize.rb +35 -0
- data/lib/options.rb +142 -0
- data/lib/ridoku.rb +11 -0
- data/lib/ridoku/backup.rb +393 -0
- data/lib/ridoku/base.rb +717 -0
- data/lib/ridoku/config_wizard.rb +195 -0
- data/lib/ridoku/cook.rb +103 -0
- data/lib/ridoku/create.rb +101 -0
- data/lib/ridoku/cron.rb +227 -0
- data/lib/ridoku/db.rb +235 -0
- data/lib/ridoku/defaults.rb +68 -0
- data/lib/ridoku/deploy.rb +157 -0
- data/lib/ridoku/domain.rb +124 -0
- data/lib/ridoku/dump.rb +132 -0
- data/lib/ridoku/env.rb +118 -0
- data/lib/ridoku/list.rb +168 -0
- data/lib/ridoku/log.rb +77 -0
- data/lib/ridoku/maintenance.rb +76 -0
- data/lib/ridoku/packages.rb +93 -0
- data/lib/ridoku/rails_defaults.rb +160 -0
- data/lib/ridoku/run.rb +137 -0
- data/lib/ridoku/service.rb +158 -0
- data/lib/ridoku/services/postgres.rb +77 -0
- data/lib/ridoku/services/rabbitmq.rb +48 -0
- data/lib/ridoku/version.rb +5 -0
- data/lib/ridoku/workers.rb +138 -0
- data/ridoku.gemspec +32 -0
- metadata +211 -0
@@ -0,0 +1,195 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Ridoku
|
4
|
+
class ConfigWizard
|
5
|
+
attr_accessor :fields
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
self.fields = {
|
9
|
+
service_role_arn: :arn_string,
|
10
|
+
instance_profile_arn: :arn_string,
|
11
|
+
ssh_key: :path
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
$stdout.puts $stdout.colorize(ConfigWizard.help_text, :bold)
|
17
|
+
|
18
|
+
$stdout.puts "Do you wish to run the wizard now? [#{$stdout.colorize('Y', :bold)}|n]"
|
19
|
+
res = $stdin.gets
|
20
|
+
if res.match(%r(^[Nn]))
|
21
|
+
Base.config[:local_init] = true
|
22
|
+
Base.save_config(::RUNCOM)
|
23
|
+
exit 1
|
24
|
+
end
|
25
|
+
|
26
|
+
sra_info = <<-EOF
|
27
|
+
Service Role ARN is used to access OpsWorks and issue commands on your
|
28
|
+
behalf. No suitable role was found.
|
29
|
+
|
30
|
+
Please enter the appropriate Role used to issue commands on OpsWorks.
|
31
|
+
Leave the field blank to attempt to generate one or to refresh from account
|
32
|
+
credentials. 's' or 'skip' if you wish to use existing values.
|
33
|
+
|
34
|
+
#{$stdout.colorize('Current Service ARN:', [:white, :bold])} #{$stdout.colorize(Base.config[:service_arn], :green)}
|
35
|
+
EOF
|
36
|
+
inst_info = <<-EOF
|
37
|
+
Instance Profile ARN is used to for each instance created for OpsWorks.
|
38
|
+
|
39
|
+
Please enter the appropriate default Role used for instance profiles.
|
40
|
+
Leave the field blank to attempt to generate one or to refresh from account
|
41
|
+
credentials. 's' or 'skip' if you wish to use existing values.
|
42
|
+
|
43
|
+
#{$stdout.colorize('Current Instance ARN:', [:white, :bold])} #{$stdout.colorize(Base.config[:instance_arn], :green)}
|
44
|
+
EOF
|
45
|
+
info = {
|
46
|
+
service_role_arn: sra_info,
|
47
|
+
instance_profile_arn: inst_info
|
48
|
+
}
|
49
|
+
|
50
|
+
ConfigWizard.fetch_input(Base.config, fields, info)
|
51
|
+
|
52
|
+
case Base.config[:service_role_arn]
|
53
|
+
when ''
|
54
|
+
Base.config.delete(:service_arn)
|
55
|
+
Base.configure_service_roles
|
56
|
+
when 's', 'skip'
|
57
|
+
else
|
58
|
+
Base.config[:service_arn] = Base.config[:service_role_arn]
|
59
|
+
end
|
60
|
+
|
61
|
+
case Base.config[:instance_profile_arn]
|
62
|
+
when ''
|
63
|
+
Base.config.delete(:instance_arn)
|
64
|
+
Base.configure_instance_roles
|
65
|
+
when 's', 'skip'
|
66
|
+
else
|
67
|
+
Base.config[:instance_arn] = Base.config[:instance_profile_arn]
|
68
|
+
end
|
69
|
+
|
70
|
+
Base.config[:local_init] = true
|
71
|
+
Base.save_config(::RUNCOM)
|
72
|
+
|
73
|
+
$stdout.puts 'Updating required Security Groups'
|
74
|
+
Base.update_pg_security_groups_in_all_regions
|
75
|
+
|
76
|
+
$stdout.puts 'Configuration complete.'
|
77
|
+
exit 0
|
78
|
+
end
|
79
|
+
protected
|
80
|
+
|
81
|
+
class << self
|
82
|
+
|
83
|
+
def help_text
|
84
|
+
help = <<-EOF
|
85
|
+
Configuration Wizard:
|
86
|
+
|
87
|
+
In order to get ridoku configured with your OpsWorks account, Ridoku must
|
88
|
+
collect pertinent required info. The wizard can be run at any time after the
|
89
|
+
first with the command line option of `--wizard`.
|
90
|
+
|
91
|
+
Values to be configured:
|
92
|
+
ssh_key:
|
93
|
+
Path to the SSH key to be used for git repositories
|
94
|
+
(cook books, apps, etc). It is recommended that this be generated
|
95
|
+
separately from your personal SSH keys so that they can be revoked
|
96
|
+
effecting other logins.
|
97
|
+
|
98
|
+
service_role_arn:
|
99
|
+
If a valid service_role_arn cannot be found, Ridoku will attempt to
|
100
|
+
generate one for you. If you've already used OpsWorks, Ridoku should be
|
101
|
+
able to find the necessary Roles for you.
|
102
|
+
|
103
|
+
instance_role_arn:
|
104
|
+
If a valid instance_role_arn cannot be found, Ridoku will attempt to
|
105
|
+
generate one for you. If you've already used OpsWorks, Ridoku should be
|
106
|
+
able to find the necessary Roles for you.
|
107
|
+
EOF
|
108
|
+
end
|
109
|
+
|
110
|
+
def fetch_input(output, required = {}, info = {})
|
111
|
+
info ||= {}
|
112
|
+
info.merge!({
|
113
|
+
ssh_key: <<-EOF
|
114
|
+
Key files (such as SSH keys) should be provided by file path. In the case of
|
115
|
+
GIT repository SSH keys (custom cookbooks, application respository), these
|
116
|
+
should be to the private keys. I recommend generating keys specifically for
|
117
|
+
each use, so that the keys can be easily tracked and removed if necessary,
|
118
|
+
without requiring you replace your keys on every machine you access.
|
119
|
+
Enter 's' or 'skip' if you wish to keep the currently configured value.
|
120
|
+
EOF
|
121
|
+
})
|
122
|
+
|
123
|
+
recurse_required(required, output, info)
|
124
|
+
end
|
125
|
+
|
126
|
+
protected
|
127
|
+
|
128
|
+
# Recurse through required hash to collected all necessary information.
|
129
|
+
def recurse_required(req, user, info, buffer = '')
|
130
|
+
req.each do |k,v|
|
131
|
+
if v.is_a?(Hash)
|
132
|
+
buffer += info_for(k, info)
|
133
|
+
buffer += "In #{$stdout.colorize(k, :bold)}:"
|
134
|
+
recurse_required(v, user[k] ||= {}, info, buffer)
|
135
|
+
buffer = ''
|
136
|
+
else
|
137
|
+
next if user[k]
|
138
|
+
$stdout.puts '-'*80
|
139
|
+
$stdout.puts buffer if buffer.length > 0
|
140
|
+
info_block = info_for(k, info)
|
141
|
+
$stdout.puts info_block if info_block.length > 0
|
142
|
+
$stdout.puts "For #{$stdout.colorize(k, :bold)} (#{$stdout.colorize(v, :red)}):"
|
143
|
+
get_response(user, k, v)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Handle getting the response from the user and validating input.
|
149
|
+
def get_response(user, key, value)
|
150
|
+
done = false
|
151
|
+
while !done
|
152
|
+
begin
|
153
|
+
val = validate_response(key, value, $stdin.gets.chomp)
|
154
|
+
user[key] = val unless val == :skip
|
155
|
+
done = true
|
156
|
+
rescue ArgumentError => e
|
157
|
+
$stdout.puts e.to_s
|
158
|
+
$stdout.puts 'Retry?'
|
159
|
+
res = $stdin.gets
|
160
|
+
done = (res.match(/^(Y|y)/) == nil)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Validate input is as expected.
|
166
|
+
def validate_response(key, expect, value)
|
167
|
+
return :skip if value.match(%r(s|skip))
|
168
|
+
|
169
|
+
case key
|
170
|
+
when :ssh_key
|
171
|
+
value.gsub!(%r(^~), ENV['HOME']) if value.match(%r(^~))
|
172
|
+
|
173
|
+
fail ArgumentError.new('Invalid input provided.') unless
|
174
|
+
File.exists?(value) || value == ''
|
175
|
+
end
|
176
|
+
|
177
|
+
case expect
|
178
|
+
when :arn_string
|
179
|
+
fail ArgumentError.new('Invalid ARN provided.') unless
|
180
|
+
value.match(/^.*:.*:.*:.*:([0-9]+):.*$||/)
|
181
|
+
when :array
|
182
|
+
value = value.split(%r([^\\](:|\|)))
|
183
|
+
end
|
184
|
+
|
185
|
+
value
|
186
|
+
end
|
187
|
+
|
188
|
+
# Print warning info associated with a particular attributes.
|
189
|
+
def info_for(key, info)
|
190
|
+
return $stdout.colorize(info[key], :bold) if info.key?(key)
|
191
|
+
''
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
data/lib/ridoku/cook.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
#
|
2
|
+
# Command: cook
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'rugged'
|
6
|
+
require 'ridoku/base'
|
7
|
+
|
8
|
+
module Ridoku
|
9
|
+
register :cook
|
10
|
+
|
11
|
+
class Cook < Base
|
12
|
+
|
13
|
+
def run
|
14
|
+
command = Base.config[:command]
|
15
|
+
sub_command = (command.length > 0 && command[1]) || nil
|
16
|
+
|
17
|
+
Base.fetch_stack
|
18
|
+
|
19
|
+
case sub_command
|
20
|
+
# when 'list', nil
|
21
|
+
# list
|
22
|
+
when 'update'
|
23
|
+
update
|
24
|
+
when 'run'
|
25
|
+
cook
|
26
|
+
else
|
27
|
+
print_cook_help
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def list
|
34
|
+
update unless File.exists?(cookbook_path)
|
35
|
+
end
|
36
|
+
|
37
|
+
def update
|
38
|
+
$stdout.puts "Updating custom cookbooks..."
|
39
|
+
|
40
|
+
Base.run_command(Base.update_cookbooks(Base.extract_instance_ids))
|
41
|
+
end
|
42
|
+
|
43
|
+
class << self
|
44
|
+
def valid_recipe_format?(recipes)
|
45
|
+
return false if recipes.length == 0
|
46
|
+
|
47
|
+
recipe = %r(^[a-zA-Z0-9_\-]+(::[a-zA-Z0-9_\-]+){0,1}$)
|
48
|
+
recipes.each do |cr|
|
49
|
+
return false unless cr.match(recipe)
|
50
|
+
end
|
51
|
+
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
def cook_recipe(recipes, custom_json = nil)
|
56
|
+
cook_recipe_on_layers(recipes, nil, custom_json)
|
57
|
+
end
|
58
|
+
|
59
|
+
def cook_recipe_on_layers(recipes, layers, custom_json = nil)
|
60
|
+
Base.fetch_app()
|
61
|
+
|
62
|
+
recipes = [recipes] unless recipes.is_a?(Array)
|
63
|
+
|
64
|
+
unless valid_recipe_format?(recipes)
|
65
|
+
$stderr.puts 'Invalid recipes provided.'
|
66
|
+
print_cook_help
|
67
|
+
exit 1
|
68
|
+
end
|
69
|
+
|
70
|
+
instance_ids = Base.extract_instance_ids(layers)
|
71
|
+
|
72
|
+
if instance_ids.length == 0
|
73
|
+
$stderr.puts 'No valid instances available.'
|
74
|
+
exit 1
|
75
|
+
end
|
76
|
+
|
77
|
+
$stdout.puts "Running recipes:"
|
78
|
+
recipes.each do |arg|
|
79
|
+
$stdout.puts " #{$stdout.colorize(arg, :green)}"
|
80
|
+
end
|
81
|
+
|
82
|
+
command = Base.execute_recipes(Base.app[:app_id], instance_ids,
|
83
|
+
Base.config[:comment], recipes, custom_json)
|
84
|
+
|
85
|
+
Base.run_command(command)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def cook
|
90
|
+
Ridoku::Cook.cook_recipe(ARGV)
|
91
|
+
end
|
92
|
+
|
93
|
+
def print_cook_help
|
94
|
+
$stderr.puts <<-EOF
|
95
|
+
Command: cook
|
96
|
+
|
97
|
+
List/Modify the current app's associated domains.
|
98
|
+
cook:run run a specific or set of 'cookbook::recipe'
|
99
|
+
cook:update update the specified instance 'cookboooks'
|
100
|
+
EOF
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
#
|
2
|
+
#
|
3
|
+
|
4
|
+
require 'ridoku/base'
|
5
|
+
|
6
|
+
module Ridoku
|
7
|
+
register :create
|
8
|
+
|
9
|
+
class Create < Base
|
10
|
+
|
11
|
+
def run
|
12
|
+
command = Base.config[:command]
|
13
|
+
sub_command = (command.length > 0 && command[1]) || nil
|
14
|
+
type = (command.length > 1 && command[2]) || nil
|
15
|
+
|
16
|
+
case sub_command
|
17
|
+
when 'app'
|
18
|
+
app(type || 'rails')
|
19
|
+
else
|
20
|
+
print_create_help
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def configure_from_cwd(opt)
|
27
|
+
return unless File.exists?('.git')
|
28
|
+
|
29
|
+
`git remote -v | grep fetch`.match(%r(origin\s*(git@[^\s]*).*)) do |m|
|
30
|
+
opt[:type] = 'git'
|
31
|
+
opt[:url] = m[1]
|
32
|
+
$stdout.puts "Setting Git Application Source from environment:"
|
33
|
+
$stdout.puts "Url: #{$stdout.colorize(m[1], :green)}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def print_create_help
|
38
|
+
$stderr.puts <<-EOF
|
39
|
+
Command: create
|
40
|
+
|
41
|
+
List/Modify the current layer's package dependencies.
|
42
|
+
create show this help
|
43
|
+
create:app[:rails] <name> create a new app on the --stack
|
44
|
+
|
45
|
+
Currently, if the stack does not exist for a particular app type, you
|
46
|
+
will have to create it manually.
|
47
|
+
EOF
|
48
|
+
end
|
49
|
+
|
50
|
+
def app(type)
|
51
|
+
Base.fetch_stack
|
52
|
+
Base.fetch_app
|
53
|
+
|
54
|
+
unless ARGV.length > 0
|
55
|
+
$stderr.puts $stderr.colorize('App name not specified', :red)
|
56
|
+
print_create_help
|
57
|
+
exit 1
|
58
|
+
end
|
59
|
+
|
60
|
+
existing = Base.app_list.select do |app|
|
61
|
+
app[:name] == ARGV[0]
|
62
|
+
end
|
63
|
+
|
64
|
+
if existing.length > 0
|
65
|
+
$stderr.puts $stderr.colorize("App #{ARGV[0]} already exists", :red)
|
66
|
+
print_create_help
|
67
|
+
exit 1
|
68
|
+
end
|
69
|
+
|
70
|
+
config = {
|
71
|
+
type: type,
|
72
|
+
name: ARGV[0],
|
73
|
+
shortname: ARGV[0].downcase.gsub(%r([^a-z0-9]), '-')
|
74
|
+
}
|
75
|
+
|
76
|
+
config.tap do |opt|
|
77
|
+
opt[:domains] = Base.config[:domains] if Base.config.key?(:domains)
|
78
|
+
opt[:app_source] = {}.tap do |as|
|
79
|
+
configure_from_cwd(as)
|
80
|
+
as[:ssh_key] = Base.config[:ssh_key] if Base.config.key?(:ssh_key)
|
81
|
+
end
|
82
|
+
opt[:attributes] = {}.tap do |atr|
|
83
|
+
atr[:rails_env] = Base.config[:rails_env] if Base.config.key?(:rails_env)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
appconfig = RailsDefaults.new.defaults_with_wizard(:app, config)
|
88
|
+
|
89
|
+
begin
|
90
|
+
Base.create_app(appconfig)
|
91
|
+
rescue ::ArgumentError => e
|
92
|
+
$stderr.puts e.to_s
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def instance
|
97
|
+
$stderr.puts 'Create instance not yet implemented.'
|
98
|
+
$stderr.puts 'Create an instance for a given layer using OpsWorks Dashboard.'
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/lib/ridoku/cron.rb
ADDED
@@ -0,0 +1,227 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#
|
4
|
+
# Command: cron
|
5
|
+
#
|
6
|
+
|
7
|
+
# [:day, :hour, :minute, :month, :weekday, :path, :type, :action]
|
8
|
+
|
9
|
+
require 'ridoku/base'
|
10
|
+
|
11
|
+
module Ridoku
|
12
|
+
register :cron
|
13
|
+
|
14
|
+
class Cron < Base
|
15
|
+
attr_accessor :cron
|
16
|
+
|
17
|
+
def run
|
18
|
+
command = Base.config[:command]
|
19
|
+
sub_command = (command.length > 0 && command[1]) || nil
|
20
|
+
|
21
|
+
load_environment
|
22
|
+
|
23
|
+
case sub_command
|
24
|
+
when 'list', nil
|
25
|
+
list
|
26
|
+
when 'set', 'add', 'update'
|
27
|
+
add
|
28
|
+
when 'delete'
|
29
|
+
delete
|
30
|
+
when 'remove'
|
31
|
+
remove
|
32
|
+
else
|
33
|
+
print_cron_help
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
def load_environment
|
40
|
+
Base.fetch_stack
|
41
|
+
Base.fetch_instance('workers')
|
42
|
+
|
43
|
+
@default_instance = Base.instances.first[:hostname]
|
44
|
+
self.cron = (Base.custom_json['deploy'][Base.config[:app].downcase]['cron'] ||= {})
|
45
|
+
end
|
46
|
+
|
47
|
+
def print_cron_help
|
48
|
+
$stderr.puts <<-EOF
|
49
|
+
Command: cron
|
50
|
+
|
51
|
+
List/Modify the current app's associated cron.
|
52
|
+
cron[:list] lists the cron jobs associated with an application.
|
53
|
+
cron:add cron job, e.g., runner:scripts/runme.rb hour:0 minute:0
|
54
|
+
cron:delete delete this specific cron job
|
55
|
+
cron:remove removes a cron from the list (run after a delete and push)
|
56
|
+
cron:push update running cron jobs
|
57
|
+
|
58
|
+
Columns Value (default: *) Label
|
59
|
+
M Minute 0-59 minute
|
60
|
+
H Hour 0-23 hour
|
61
|
+
DM Day of Month 0-30 day_of_month
|
62
|
+
MO Month 0-11 month
|
63
|
+
DW Day of Week 0-6 day_of_week
|
64
|
+
|
65
|
+
All cron jobs are run on the Workers layer or the AssetMaster on the
|
66
|
+
Rails Application layer if one is not set, unless otherwise specified.
|
67
|
+
|
68
|
+
Example 'cron:list' output:
|
69
|
+
Type Scripts M H DM MO DW
|
70
|
+
runner scripts/runme.rb 0 0 * * *
|
71
|
+
|
72
|
+
The above list output indicates a DelayedJob cron and two separate
|
73
|
+
delayed_job processes. One processing 'mail'; the other processing 'sms'.
|
74
|
+
|
75
|
+
examples:
|
76
|
+
$ cron
|
77
|
+
No cron specified!
|
78
|
+
$ cron:add type:runner path:scripts/runme.rb hour:0 minute:0
|
79
|
+
$ cron:add type:runner path:scripts/runme_also.rb minute:*/5 instance:mukujara
|
80
|
+
$ cron:list
|
81
|
+
Type Scripts M H DM MO DW Instance
|
82
|
+
runner scripts/runme.rb 0 0 * * * oiwa
|
83
|
+
runner scripts/runme_also.rb */5 * * * * mukujara
|
84
|
+
$ cron:delete scrips/runme_also.rb
|
85
|
+
Type Scripts M H DM MO DW
|
86
|
+
runner scripts/runme.rb 0 0 * * * oiwa
|
87
|
+
delete scripts/runme_also.rb - - - - - mukujara
|
88
|
+
EOF
|
89
|
+
end
|
90
|
+
|
91
|
+
def list
|
92
|
+
if cron.length == 0
|
93
|
+
$stdout.puts 'No cron jobs specified!'
|
94
|
+
else
|
95
|
+
|
96
|
+
columns = {
|
97
|
+
type: 'Type',
|
98
|
+
path: 'Scripts',
|
99
|
+
minute: 'M',
|
100
|
+
hour: 'H',
|
101
|
+
day_of_month: 'DM',
|
102
|
+
month: 'MO',
|
103
|
+
day_of_week: 'DW',
|
104
|
+
instance: 'Instance'
|
105
|
+
}
|
106
|
+
|
107
|
+
offset = {}
|
108
|
+
|
109
|
+
self.cron.each do |cr|
|
110
|
+
columns.keys.each do |key|
|
111
|
+
skey = key.to_s
|
112
|
+
cr[skey] = '*' unless cr.key?(skey)
|
113
|
+
val = cr[skey].length
|
114
|
+
offset[key] = val if cr.key?(skey) && val > (offset[key] || 0)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
columns.keys.each do |key|
|
119
|
+
offset[key] = columns[key].length if
|
120
|
+
columns[key].length > (offset[key] || 0)
|
121
|
+
end
|
122
|
+
|
123
|
+
$stdout.puts $stdout.colorize(line(offset, columns), :bold)
|
124
|
+
self.cron.each { |cr| $stdout.puts line(offset, cr, columns.keys) }
|
125
|
+
end
|
126
|
+
rescue =>e
|
127
|
+
puts e.backtrace
|
128
|
+
puts e
|
129
|
+
end
|
130
|
+
|
131
|
+
def add
|
132
|
+
|
133
|
+
croninfo = {}
|
134
|
+
cronindex = nil
|
135
|
+
|
136
|
+
ARGV.each do |cron|
|
137
|
+
info = cron.split(':',2)
|
138
|
+
croninfo[info[0].to_s] = info[1]
|
139
|
+
cronindex = get_path_index(info[1]) if info[0] == 'path'
|
140
|
+
end
|
141
|
+
|
142
|
+
croninfo['instance'] = @default_instance unless croninfo.key?('instance')
|
143
|
+
|
144
|
+
if cronindex.nil?
|
145
|
+
self.cron << croninfo
|
146
|
+
else
|
147
|
+
self.cron[cronindex] = croninfo
|
148
|
+
end
|
149
|
+
|
150
|
+
list
|
151
|
+
Base.save_stack
|
152
|
+
end
|
153
|
+
|
154
|
+
def delete
|
155
|
+
return print_cron_help unless ARGV.length > 0
|
156
|
+
cronindex = get_path_index(ARGV.first)
|
157
|
+
|
158
|
+
if cronindex.nil?
|
159
|
+
$stdout.puts $stdout.colorize(
|
160
|
+
'Unable to find the specified script path in the cron list.', :red)
|
161
|
+
return list
|
162
|
+
end
|
163
|
+
|
164
|
+
cr = self.cron[cronindex]
|
165
|
+
cr['type'] = 'delete'
|
166
|
+
cr['minute'] = '-'
|
167
|
+
cr['hour'] = '-'
|
168
|
+
cr['day_of_month'] = '-'
|
169
|
+
cr['month'] = '-'
|
170
|
+
cr['day_of_week'] = '-'
|
171
|
+
|
172
|
+
list
|
173
|
+
Base.save_stack
|
174
|
+
end
|
175
|
+
|
176
|
+
def remove
|
177
|
+
return print_cron_help unless ARGV.length > 0
|
178
|
+
cronindex = get_path_index(ARGV.first)
|
179
|
+
|
180
|
+
if cronindex.nil?
|
181
|
+
$stdout.puts $stdout.colorize(
|
182
|
+
'Unable to find the specified script path in the cron list.', :red)
|
183
|
+
return list
|
184
|
+
end
|
185
|
+
|
186
|
+
self.cron.delete_at(cronindex)
|
187
|
+
|
188
|
+
list
|
189
|
+
Base.save_stack
|
190
|
+
end
|
191
|
+
|
192
|
+
protected
|
193
|
+
|
194
|
+
def get_path_index(path)
|
195
|
+
cron.each_with_index do |cr, idx|
|
196
|
+
return idx if cr['path'] == path
|
197
|
+
end
|
198
|
+
|
199
|
+
return nil
|
200
|
+
end
|
201
|
+
|
202
|
+
def line(offset, columns, keys = nil)
|
203
|
+
keys ||= columns.keys
|
204
|
+
output = ''
|
205
|
+
|
206
|
+
keys.each do |key|
|
207
|
+
skey = key.to_s
|
208
|
+
content = columns[key] || columns[skey]
|
209
|
+
if skey == 'type'
|
210
|
+
case content
|
211
|
+
when 'delete'
|
212
|
+
output += $stdout.colorize(content, :red)
|
213
|
+
when 'runner'
|
214
|
+
output += $stdout.colorize(content, :green)
|
215
|
+
else
|
216
|
+
output += $stdout.colorize(content, :yellow)
|
217
|
+
end
|
218
|
+
else
|
219
|
+
output += content
|
220
|
+
end
|
221
|
+
output += ' '*(offset[key] - content.length + 2)
|
222
|
+
end
|
223
|
+
|
224
|
+
output
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|