ridoku 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 +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
|