dopi 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +322 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +102 -0
- data/LICENSE.txt +177 -0
- data/README.md +309 -0
- data/Rakefile +44 -0
- data/Vagrantfile +64 -0
- data/bin/dopi +4 -0
- data/doc/getting_started.md +247 -0
- data/doc/getting_started_examples/001_hello_world.yaml +17 -0
- data/doc/getting_started_examples/002_connecting_over_ssh.yaml +35 -0
- data/doc/plugins/custom.md +88 -0
- data/doc/plugins/mco/rpc.md +82 -0
- data/doc/plugins/ssh/custom.md +141 -0
- data/doc/plugins/ssh/file_contains.md +37 -0
- data/doc/plugins/ssh/file_deploy.md +52 -0
- data/doc/plugins/ssh/file_exists.md +31 -0
- data/doc/plugins/ssh/file_replace.md +37 -0
- data/doc/plugins/ssh/puppet_agent_run.md +50 -0
- data/doc/plugins/ssh/reboot.md +22 -0
- data/doc/plugins/ssh/wait_for_login.md +53 -0
- data/doc/plugins/winrm/cmd.md +161 -0
- data/doc/plugins/winrm/file_contains.md +39 -0
- data/doc/plugins/winrm/file_exists.md +31 -0
- data/doc/plugins/winrm/powershell.md +27 -0
- data/doc/plugins/winrm/puppet_agent_run.md +49 -0
- data/doc/plugins/winrm/reboot.md +17 -0
- data/doc/plugins/winrm/wait_for_login.md +55 -0
- data/dopi.gemspec +42 -0
- data/lib/dopi/cli/command_add.rb +35 -0
- data/lib/dopi/cli/command_list.rb +19 -0
- data/lib/dopi/cli/command_remove.rb +31 -0
- data/lib/dopi/cli/command_reset.rb +27 -0
- data/lib/dopi/cli/command_run.rb +68 -0
- data/lib/dopi/cli/command_show.rb +109 -0
- data/lib/dopi/cli/command_update.rb +37 -0
- data/lib/dopi/cli/command_validate.rb +27 -0
- data/lib/dopi/cli/global_options.rb +55 -0
- data/lib/dopi/cli/log.rb +33 -0
- data/lib/dopi/cli.rb +57 -0
- data/lib/dopi/command/custom.rb +52 -0
- data/lib/dopi/command/dummy.rb +27 -0
- data/lib/dopi/command/mco/rpc.rb +158 -0
- data/lib/dopi/command/ssh/custom.rb +48 -0
- data/lib/dopi/command/ssh/file_contains.rb +70 -0
- data/lib/dopi/command/ssh/file_deploy.rb +71 -0
- data/lib/dopi/command/ssh/file_exists.rb +54 -0
- data/lib/dopi/command/ssh/file_replace.rb +96 -0
- data/lib/dopi/command/ssh/puppet_agent_run.rb +63 -0
- data/lib/dopi/command/ssh/reboot.rb +50 -0
- data/lib/dopi/command/ssh/wait_for_login.rb +68 -0
- data/lib/dopi/command/winrm/cmd.rb +44 -0
- data/lib/dopi/command/winrm/file_contains.rb +66 -0
- data/lib/dopi/command/winrm/file_exists.rb +51 -0
- data/lib/dopi/command/winrm/powershell.rb +16 -0
- data/lib/dopi/command/winrm/puppet_agent_run.rb +61 -0
- data/lib/dopi/command/winrm/reboot.rb +33 -0
- data/lib/dopi/command/winrm/wait_for_login.rb +49 -0
- data/lib/dopi/command.rb +239 -0
- data/lib/dopi/command_parser/arguments.rb +38 -0
- data/lib/dopi/command_parser/credentials.rb +59 -0
- data/lib/dopi/command_parser/env.rb +37 -0
- data/lib/dopi/command_parser/exec.rb +27 -0
- data/lib/dopi/command_parser/exit_code.rb +73 -0
- data/lib/dopi/command_parser/output.rb +126 -0
- data/lib/dopi/command_set.rb +66 -0
- data/lib/dopi/connector/local.rb +77 -0
- data/lib/dopi/connector/ssh.rb +170 -0
- data/lib/dopi/connector/winrm.rb +167 -0
- data/lib/dopi/error.rb +43 -0
- data/lib/dopi/log.rb +18 -0
- data/lib/dopi/node.rb +70 -0
- data/lib/dopi/plan.rb +99 -0
- data/lib/dopi/pluginmanager.rb +62 -0
- data/lib/dopi/state.rb +226 -0
- data/lib/dopi/state_store.rb +155 -0
- data/lib/dopi/step.rb +227 -0
- data/lib/dopi/step_set.rb +70 -0
- data/lib/dopi/version.rb +3 -0
- data/lib/dopi.rb +165 -0
- data/spec/command_helper.rb +11 -0
- data/spec/fixtures/mco_client.cfg +26 -0
- data/spec/fixtures/plans/fail_on_timeout.yaml +20 -0
- data/spec/fixtures/plans/hello_world.yaml +34 -0
- data/spec/fixtures/plans/non_existing_node.yaml +26 -0
- data/spec/fixtures/plans/test_role_variable.yaml +29 -0
- data/spec/fixtures/puppet/Puppetfile +8 -0
- data/spec/fixtures/puppet/Puppetfile.lock +57 -0
- data/spec/fixtures/puppet/hiera.yaml +6 -0
- data/spec/fixtures/puppet/manifests/site.pp +52 -0
- data/spec/fixtures/test_configuration.yaml +54 -0
- data/spec/fixtures/test_credentials.yaml +11 -0
- data/spec/fixtures/test_deloyed_file.txt +5 -0
- data/spec/fixtures/test_infrastructure.yaml +12 -0
- data/spec/fixtures/test_nodes.yaml +45 -0
- data/spec/fixtures/testenv_plan.yaml +159 -0
- data/spec/integration/dopi/addrun_spec.rb +31 -0
- data/spec/integration/dopi/cli/command_run_spec.rb +38 -0
- data/spec/integration/dopi/cli/global_options_spec.rb +128 -0
- data/spec/integration/dopi/command_spec.rb +66 -0
- data/spec/integration/dopi/fail_check_plans/file_exists_fails.yaml +38 -0
- data/spec/integration/dopi/fail_check_plans/output_parser.yaml +39 -0
- data/spec/integration/dopi/fail_check_plans/powershell_fail.yaml +25 -0
- data/spec/integration/dopi/fail_check_plans/timeout.yaml +29 -0
- data/spec/integration/dopi/fail_check_plans/verify_commands.yaml +33 -0
- data/spec/integration/dopi/failplan.rb +27 -0
- data/spec/integration/dopi/plan.rb +27 -0
- data/spec/integration/dopi/plans/dummy.yaml +29 -0
- data/spec/integration/dopi/plans/max_per_role.yaml +55 -0
- data/spec/integration/dopi/plans/no_timeout.yaml +29 -0
- data/spec/integration/dopi/plans/node_and_role_patterns.yaml +58 -0
- data/spec/integration/dopi/plans/node_by_config.yaml +116 -0
- data/spec/integration/dopi/plans/plugin_defaults.yaml +86 -0
- data/spec/integration/dopi/plans/plugins/mco/rpc.yaml +33 -0
- data/spec/integration/dopi/plans/plugins/ssh/custom.yaml +97 -0
- data/spec/integration/dopi/plans/plugins/ssh/file_contains.yaml +51 -0
- data/spec/integration/dopi/plans/plugins/ssh/file_deploy.yaml +82 -0
- data/spec/integration/dopi/plans/plugins/ssh/file_exists.yaml +69 -0
- data/spec/integration/dopi/plans/plugins/ssh/file_replace.yaml +55 -0
- data/spec/integration/dopi/plans/plugins/ssh/puppet_agent_run.yaml +45 -0
- data/spec/integration/dopi/plans/plugins/ssh/reboot.yaml +43 -0
- data/spec/integration/dopi/plans/plugins/ssh/wait_for_login.yaml +45 -0
- data/spec/integration/dopi/plans/plugins/winrm/cmd.yaml +39 -0
- data/spec/integration/dopi/plans/plugins/winrm/file_contains.yaml +51 -0
- data/spec/integration/dopi/plans/plugins/winrm/file_exists.yaml +69 -0
- data/spec/integration/dopi/plans/plugins/winrm/reboot.yaml +31 -0
- data/spec/integration/dopi/plans/resolve_roles_on_validate.yaml +23 -0
- data/spec/integration/dopi/plans/ssh_parallel.yaml +37 -0
- data/spec/integration/dopi/plans/verify_commands.yaml +49 -0
- data/spec/spec_helper.rb +104 -0
- data/spec/unit/dopi/command/custom_spec.rb +58 -0
- data/spec/unit/dopi/command/mco/rpc_spec.rb +157 -0
- data/spec/unit/dopi/command/ssh/custom_spec.rb +30 -0
- data/spec/unit/dopi/command/ssh/file_deploy_spec.rb +42 -0
- data/spec/unit/dopi/command/ssh/file_replace_spec.rb +35 -0
- data/spec/unit/dopi/command_parser/credentials_spec.rb +53 -0
- data/spec/unit/dopi/command_parser/exit_code_spec.rb +63 -0
- data/spec/unit/dopi/command_parser/output_spec.rb +129 -0
- data/spec/unit/dopi/command_spec.rb +14 -0
- data/spec/unit/dopi/connector/winrm_spec.rb +111 -0
- data/spec/unit/dopi/node_spec.rb +24 -0
- data/spec/unit/dopi/plan_spec.rb +31 -0
- data/spec/unit/dopi/state_spec.rb +109 -0
- data/spec/unit/dopi/step_spec.rb +13 -0
- metadata +448 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
#
|
2
|
+
# DOPi Plugin: Wait For Login
|
3
|
+
#
|
4
|
+
|
5
|
+
module Dopi
|
6
|
+
class Command
|
7
|
+
class Winrm
|
8
|
+
class WaitForLogin < Dopi::Command
|
9
|
+
include Dopi::Connector::Winrm
|
10
|
+
include Dopi::CommandParser::ExitCode
|
11
|
+
|
12
|
+
DEFAULT_INTERVAL = 10
|
13
|
+
|
14
|
+
def validate
|
15
|
+
validate_winrm
|
16
|
+
validate_exit_code
|
17
|
+
log_validation_method(:interval_valid?, CommandParsingError)
|
18
|
+
end
|
19
|
+
|
20
|
+
def run
|
21
|
+
connected = false
|
22
|
+
until connected
|
23
|
+
begin connected = check_exit_code(winrm_command('exit')[2])
|
24
|
+
rescue Dopi::NodeConnectionError, Dopi::CommandConnectionError
|
25
|
+
end
|
26
|
+
unless connected
|
27
|
+
sleep interval
|
28
|
+
raise GracefulExit if signals[:stop]
|
29
|
+
log(:info, "Retrying connect to node")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
def interval
|
36
|
+
@interval ||= interval_valid? ?
|
37
|
+
hash[:interval] : DEFAULT_INTERVAL
|
38
|
+
end
|
39
|
+
|
40
|
+
def interval_valid?
|
41
|
+
return false if hash[:interval].nil? # is optional
|
42
|
+
hash[:interval].class == Fixnum or
|
43
|
+
raise CommandParsingError, "Plugin #{name}: the value of 'interval' has to be a number"
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/dopi/command.rb
ADDED
@@ -0,0 +1,239 @@
|
|
1
|
+
#
|
2
|
+
# This class loades the dopi command plugins
|
3
|
+
#
|
4
|
+
require 'dop_common'
|
5
|
+
require 'forwardable'
|
6
|
+
require 'timeout'
|
7
|
+
|
8
|
+
|
9
|
+
module Dopi
|
10
|
+
class Command
|
11
|
+
extend Forwardable
|
12
|
+
include Dopi::State
|
13
|
+
include DopCommon::Validator
|
14
|
+
|
15
|
+
def self.inherited(klass)
|
16
|
+
PluginManager << klass
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.create_plugin_instance(command_parser, step, node, is_verify_command = false)
|
20
|
+
plugin_type = PluginManager.get_plugin_name(self) + '/'
|
21
|
+
plugin_full_name = plugin_type + command_parser.plugin
|
22
|
+
Dopi.log.debug("Creating instance of plugin #{plugin_full_name}")
|
23
|
+
PluginManager.create_instance(plugin_full_name, command_parser, step, node, is_verify_command)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.set_plugin_defaults(node_name, hash)
|
27
|
+
@plugin_defaults ||= {}
|
28
|
+
@plugin_defaults[node_name] ||= {}
|
29
|
+
@plugin_defaults[node_name].merge!(DopCommon::HashParser.symbolize_keys(hash))
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.plugin_defaults(node_name)
|
33
|
+
@plugin_defaults ||= {}
|
34
|
+
@plugin_defaults[node_name] ||= {}
|
35
|
+
end
|
36
|
+
|
37
|
+
# wipe all the defaults for this plugin
|
38
|
+
def self.wipe_plugin_defaults
|
39
|
+
@plugin_defaults = {}
|
40
|
+
end
|
41
|
+
|
42
|
+
# delete all the defaults on this plugin for the node
|
43
|
+
def self.delete_plugin_defaults(node_name)
|
44
|
+
@plugin_defaults ||= {}
|
45
|
+
@plugin_defaults[node_name] = {}
|
46
|
+
end
|
47
|
+
|
48
|
+
# delete a specific default for the node
|
49
|
+
def self.delete_plugin_default(node_name, key)
|
50
|
+
@plugin_defaults ||= {}
|
51
|
+
@plugin_defaults[node_name] ||= {}
|
52
|
+
@plugin_defaults[node_name].delete(key)
|
53
|
+
end
|
54
|
+
|
55
|
+
attr_reader :node, :hash, :is_verify_command
|
56
|
+
|
57
|
+
def initialize(command_parser, step, node, is_verify_command)
|
58
|
+
@command_parser = command_parser
|
59
|
+
@step = step
|
60
|
+
@node = node
|
61
|
+
@is_verify_command = is_verify_command
|
62
|
+
@hash = merged_hash
|
63
|
+
log(:debug, "Plugin created with merged command hash: #{hash.inspect}")
|
64
|
+
# make sure verify commands are initialized as well
|
65
|
+
verify_commands
|
66
|
+
end
|
67
|
+
|
68
|
+
def merged_hash
|
69
|
+
if @command_parser.hash.kind_of?(Hash)
|
70
|
+
self.class.plugin_defaults(@node.name).merge(@command_parser.hash)
|
71
|
+
else
|
72
|
+
self.class.plugin_defaults(@node.name)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def_delegator :@command_parser, :plugin, :name
|
77
|
+
def_delegator :@command_parser, :title, :title
|
78
|
+
|
79
|
+
def meta_run(noop = false)
|
80
|
+
return if skip_run?(noop)
|
81
|
+
state_run unless noop
|
82
|
+
# Nest timeout in itself to fix working in combination with popen3() used
|
83
|
+
# by command connectors
|
84
|
+
Timeout::timeout(plugin_timeout) do
|
85
|
+
Timeout::timeout(plugin_timeout) do
|
86
|
+
log(:info, "Running command #{name}") unless @is_verify_command
|
87
|
+
if noop
|
88
|
+
run_noop
|
89
|
+
else
|
90
|
+
if run
|
91
|
+
if verify_after_run
|
92
|
+
verify_commands_ok? or
|
93
|
+
raise CommandExecutionError, "Verify commands failed to confirm a successful run"
|
94
|
+
end
|
95
|
+
state_finish
|
96
|
+
log(:info, "#{name} [OK]") if state_done?
|
97
|
+
else
|
98
|
+
state_fail
|
99
|
+
log(:info, "#{name} [FAILED]")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
rescue GracefulExit
|
105
|
+
log(:info, "Command excited gracefuly, resetting to ready")
|
106
|
+
state_reset(true) unless noop
|
107
|
+
rescue Timeout::Error
|
108
|
+
log(:error, "Command timed out (plugin_timeout is set to #{plugin_timeout})", false)
|
109
|
+
state_fail unless noop
|
110
|
+
send_signal(:abort)
|
111
|
+
rescue CommandExecutionError => e
|
112
|
+
log(:error, "Command failed: #{e.message}", false)
|
113
|
+
Dopi.log.error(e) if DopCommon.config.trace
|
114
|
+
state_fail unless noop
|
115
|
+
rescue => e
|
116
|
+
log(:error, "Unexpected error!!! This is a Bug", false)
|
117
|
+
Dopi.log.error(e.message)
|
118
|
+
Dopi.log.error(e.backtrace)
|
119
|
+
state_fail unless noop
|
120
|
+
raise e
|
121
|
+
end
|
122
|
+
|
123
|
+
def meta_valid?
|
124
|
+
validity = valid?
|
125
|
+
validity = false unless verify_commands.all? do |verify_command|
|
126
|
+
begin
|
127
|
+
verify_command.meta_valid?
|
128
|
+
rescue PluginLoaderError => e
|
129
|
+
Dopi.log.error("Step '#{@step.name}': Can't load plugin #{verify_command.plugin}: #{e.message}")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
validity
|
133
|
+
end
|
134
|
+
|
135
|
+
def_delegator :@command_parser, :verify_commands, :parsed_verify_commands
|
136
|
+
def_delegators :@command_parser, :plugin_timeout, :verify_after_run
|
137
|
+
|
138
|
+
def load_state(state_hash)
|
139
|
+
command_state = state_hash[:command_state] || :ready
|
140
|
+
unless command_state == :ready
|
141
|
+
@state = command_state
|
142
|
+
state_changed
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def state_hash
|
147
|
+
{:command_state => @state}
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def run
|
153
|
+
raise Dopi::CommandExecutionError, "No run method implemented in plugin #{name}"
|
154
|
+
end
|
155
|
+
|
156
|
+
def run_noop
|
157
|
+
Dopi.log.error("The plugin #{name} does not support noop runs and will not show the command")
|
158
|
+
end
|
159
|
+
|
160
|
+
def validate
|
161
|
+
Dopi.log.warn("No 'validate' method implemented in plugin #{name}. Validation not possible")
|
162
|
+
true
|
163
|
+
end
|
164
|
+
|
165
|
+
def skip_run?(noop = false)
|
166
|
+
if state_done?
|
167
|
+
log(:info, "Is already in state 'done'. Skipping")
|
168
|
+
true
|
169
|
+
elsif verify_commands.any? && verify_commands_ok?
|
170
|
+
if noop
|
171
|
+
log(:info, "All verify commands ok. Skipping")
|
172
|
+
else
|
173
|
+
log(:info, "All verify commands ok. Skipping and marked as 'done'")
|
174
|
+
state_run
|
175
|
+
state_finish
|
176
|
+
end
|
177
|
+
true
|
178
|
+
else
|
179
|
+
false
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def verify_commands
|
184
|
+
@verify_commands ||= parsed_verify_commands.map do |command|
|
185
|
+
Dopi::Command.create_plugin_instance(command, @step, @node, true)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def verify_commands_ok?
|
190
|
+
verify_commands.all? do |command|
|
191
|
+
command.state_reset(true)
|
192
|
+
command.meta_run
|
193
|
+
command.state_done?
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def log_prefix
|
198
|
+
if @is_verify_command
|
199
|
+
" [Verify] #{@node.name} : "
|
200
|
+
else
|
201
|
+
" [Command] #{@node.name} : "
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def log(severity, message, overwrite = true)
|
206
|
+
# Ignore verify command errors, because they are expected
|
207
|
+
if @is_verify_command && overwrite
|
208
|
+
severity = :debug if severity == :error || severity == :warn
|
209
|
+
end
|
210
|
+
# TODO: implement Node specific logging
|
211
|
+
# for now we simply forward to the global DOPi logger
|
212
|
+
Dopi.log.log(Logger.const_get(severity.upcase), log_prefix + message)
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
# load standard command plugins
|
220
|
+
require 'dopi/command/dummy'
|
221
|
+
require 'dopi/command/custom'
|
222
|
+
require 'dopi/command/ssh/custom'
|
223
|
+
require 'dopi/command/ssh/puppet_agent_run'
|
224
|
+
require 'dopi/command/ssh/wait_for_login'
|
225
|
+
require 'dopi/command/ssh/file_contains'
|
226
|
+
require 'dopi/command/ssh/file_exists'
|
227
|
+
require 'dopi/command/ssh/file_replace'
|
228
|
+
require 'dopi/command/ssh/file_deploy'
|
229
|
+
require 'dopi/command/ssh/reboot'
|
230
|
+
require 'dopi/command/mco/rpc'
|
231
|
+
require 'dopi/command/winrm/cmd'
|
232
|
+
require 'dopi/command/winrm/powershell'
|
233
|
+
require 'dopi/command/winrm/puppet_agent_run'
|
234
|
+
require 'dopi/command/winrm/wait_for_login'
|
235
|
+
require 'dopi/command/winrm/file_contains'
|
236
|
+
require 'dopi/command/winrm/file_exists'
|
237
|
+
require 'dopi/command/winrm/reboot'
|
238
|
+
|
239
|
+
# TODO: load plugins from the plugin paths
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#
|
2
|
+
# Simple command parser class to parse arguments
|
3
|
+
#
|
4
|
+
module Dopi
|
5
|
+
module CommandParser
|
6
|
+
module Arguments
|
7
|
+
|
8
|
+
def validate_arguments
|
9
|
+
log_validation_method('arguments_valid?', CommandParsingError)
|
10
|
+
end
|
11
|
+
|
12
|
+
def arguments
|
13
|
+
arguments_valid? ? parse_arguments : ""
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def arguments_valid?
|
19
|
+
return false unless hash.kind_of?(Hash) # plugin may not have parameters
|
20
|
+
return false if hash[:arguments].nil? # arguments are optional
|
21
|
+
hash[:arguments].kind_of?(Hash) or
|
22
|
+
hash[:arguments].kind_of?(Array) or
|
23
|
+
hash[:arguments].kind_of?(String) or
|
24
|
+
raise CommandParsingError, "The value for 'arguments' hast to be an Array, Hash or String"
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse_arguments
|
28
|
+
case hash[:arguments]
|
29
|
+
when Hash then hash[:arguments].to_a.flatten.join(' ')
|
30
|
+
when Array then hash[:arguments].flatten.join(' ')
|
31
|
+
when String then hash[:arguments]
|
32
|
+
else ""
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
#
|
2
|
+
# Dopi Credentials helper module
|
3
|
+
#
|
4
|
+
# This module will provide a credentials method which contains all the credential
|
5
|
+
# objects for the plugin.
|
6
|
+
#
|
7
|
+
# Make sure you call "validate_credentials" method from your validation method.
|
8
|
+
#
|
9
|
+
# Implement a method "supported_credential_types" in your plugin if you want to limit
|
10
|
+
# the types which are supported and trow an error during validation if some other type
|
11
|
+
# is assigned
|
12
|
+
#
|
13
|
+
module Dopi
|
14
|
+
module CommandParser
|
15
|
+
module Credentials
|
16
|
+
include DopCommon::Validator
|
17
|
+
include DopCommon::HashParser
|
18
|
+
|
19
|
+
public
|
20
|
+
|
21
|
+
def validate_credentials
|
22
|
+
log_validation_method('credentials_valid?', CommandParsingError)
|
23
|
+
end
|
24
|
+
|
25
|
+
def credentials
|
26
|
+
@credentials ||= credentials_valid? ? parse_credentials : []
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def credentials_valid?
|
32
|
+
return false unless hash.kind_of?(Hash) # plugin may not have parameters
|
33
|
+
key_aliases(hash, :credentials, ['credentials', :credential, 'credential'])
|
34
|
+
return false if hash[:credentials].nil? # credentials is optional
|
35
|
+
hash[:credentials].kind_of?(String) or hash[:credentials].kind_of?(Array) or
|
36
|
+
raise CommandParsingError, "the value for 'credentials' has to be a string or an array of strings"
|
37
|
+
[hash[:credentials]].flatten.each do |c|
|
38
|
+
c.kind_of?(String) or
|
39
|
+
raise CommandParsingError, "All values in the 'credentials' array have to be strings"
|
40
|
+
@step.plan.credentials.has_key?(c) or
|
41
|
+
raise CommandParsingError, "Credentials #{c} are not configured"
|
42
|
+
if self.methods.include?(:supported_credential_types)
|
43
|
+
cred_type = @step.plan.credentials[c].type
|
44
|
+
supported_credential_types.include?(cred_type) or
|
45
|
+
raise CommandParsingError, "Credential #{c} is of type #{cred_type}, which is not supported by the plugin"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
def parse_credentials
|
52
|
+
[hash[:credentials]].flatten.map{|c| @step.plan.credentials[c]}
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#
|
2
|
+
# Simple command parser module for environment variable hashes
|
3
|
+
#
|
4
|
+
# To set plugin specific defaults for the environment create
|
5
|
+
# a 'env_defaults' method which returns a hash with:
|
6
|
+
# { var => val, var2 => val2, ... }
|
7
|
+
# This hash will be merged with the user specified hash.
|
8
|
+
#
|
9
|
+
module Dopi
|
10
|
+
module CommandParser
|
11
|
+
module Env
|
12
|
+
|
13
|
+
def validate_env
|
14
|
+
log_validation_method(:env_valid?, CommandParsingError)
|
15
|
+
end
|
16
|
+
|
17
|
+
def env
|
18
|
+
env_valid? ? create_env.merge(hash[:env]) : create_env
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def env_valid?
|
24
|
+
return false unless hash.kind_of?(Hash) # plugin may not have parameters
|
25
|
+
return false if hash[:env].nil? # env is optional
|
26
|
+
hash[:env].kind_of?(Hash) or
|
27
|
+
raise CommandParsingError, "The value for 'env' has to be a hash"
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_env
|
31
|
+
defaults = respond_to?(:env_defaults) ? env_defaults : {}
|
32
|
+
{ 'DOP_NODE_FQDN' => @node.name }.merge(defaults)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#
|
2
|
+
# Simple command parser module for exec
|
3
|
+
#
|
4
|
+
module Dopi
|
5
|
+
module CommandParser
|
6
|
+
module Exec
|
7
|
+
|
8
|
+
def validate_exec
|
9
|
+
log_validation_method('exec_valid?', CommandParsingError)
|
10
|
+
end
|
11
|
+
|
12
|
+
def exec
|
13
|
+
exec_valid? ? hash[:exec] : nil
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def exec_valid?
|
19
|
+
hash[:exec] or
|
20
|
+
raise CommandParsingError, "#Step #{@step.name} | Plugin #{name} | No command to execute in 'exec' defined"
|
21
|
+
hash[:exec].kind_of?(String) or
|
22
|
+
raise CommandParsingError, "#Step #{@step.name} | Plugin #{name} | The value for 'exec' has to be a String"
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
#
|
2
|
+
# This is a mixin for command plugins that need to parse an exit Code of some sort
|
3
|
+
#
|
4
|
+
# To set plugin specific defaults you can implement the 'expect_exit_codes_defaults'
|
5
|
+
# method which returns an array of expected exit codes
|
6
|
+
#
|
7
|
+
module Dopi
|
8
|
+
module CommandParser
|
9
|
+
module ExitCode
|
10
|
+
|
11
|
+
public
|
12
|
+
|
13
|
+
def validate_exit_code
|
14
|
+
log_validation_method('expect_exit_codes_valid?', CommandParsingError)
|
15
|
+
end
|
16
|
+
|
17
|
+
def expect_exit_codes
|
18
|
+
@expect_exit_codes ||= expect_exit_codes_valid? ?
|
19
|
+
hash[:expect_exit_codes] : create_exit_codes
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns true if the exit code is one we expected, otherwise false
|
23
|
+
def check_exit_code(cmd_exit_code)
|
24
|
+
log(:debug, "Checking exit code '#{cmd_exit_code}'")
|
25
|
+
exit_code_ok = case expect_exit_codes
|
26
|
+
when 'all', 'ALL', 'All', :all then true
|
27
|
+
when Array then expect_exit_codes.include?(cmd_exit_code)
|
28
|
+
when Fixnum then expect_exit_codes == cmd_exit_code
|
29
|
+
else false
|
30
|
+
end
|
31
|
+
|
32
|
+
unless exit_code_ok
|
33
|
+
log(:error, "Wrong exit code in command #{name}")
|
34
|
+
if expect_exit_codes.kind_of?(Array)
|
35
|
+
log(:error, "Exit code was #{cmd_exit_code.to_s} should be one of #{expect_exit_codes.join(', ')}")
|
36
|
+
elsif expect_exit_codes.kind_of?(Fixnum)
|
37
|
+
log(:error, "Exit code was #{cmd_exit_code.to_s} should be #{expect_exit_codes.to_s}")
|
38
|
+
else
|
39
|
+
log(:error, "Exit code was #{cmd_exit_code.to_s} #{expect_exit_codes}")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
exit_code_ok
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def expect_exit_codes_valid?
|
49
|
+
return false unless hash.kind_of?(Hash) # plugin may not have parameters
|
50
|
+
return false if hash[:expect_exit_codes].nil? # expect_exit_codes is optional
|
51
|
+
hash[:expect_exit_codes].kind_of?(Fixnum) or
|
52
|
+
hash[:expect_exit_codes].kind_of?(String) or
|
53
|
+
hash[:expect_exit_codes].kind_of?(Symbol) or
|
54
|
+
hash[:expect_exit_codes].kind_of?(Array) or
|
55
|
+
raise CommandParsingError, "The value for 'expect_exit_codes' hast to be a number or an array of numbers or :all"
|
56
|
+
if hash[:expect_exit_codes].kind_of?(String) || hash[:expect_exit_codes].kind_of?(Symbol)
|
57
|
+
['all', 'All', 'ALL', :all].include? hash[:expect_exit_codes] or
|
58
|
+
raise CommandParsingError, "Unknown keyword for expect_exit_codes. This has to be a number, an array or :all"
|
59
|
+
end
|
60
|
+
if hash[:expect_exit_codes].kind_of?(Array)
|
61
|
+
hash[:expect_exit_codes].all?{|exit_code| exit_code.kind_of?(Fixnum)} or
|
62
|
+
raise CommandParsingError, "The array in 'expect_exit_codes' can only contain numbers"
|
63
|
+
end
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
67
|
+
def create_exit_codes
|
68
|
+
respond_to?(:expect_exit_codes_defaults) ? expect_exit_codes_defaults : 0
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
#
|
2
|
+
# This is a mixin for command plugins that need to parse an output of some sort
|
3
|
+
#
|
4
|
+
# Make sure to call the validation method from the class you use the module
|
5
|
+
#
|
6
|
+
# To set plugin specific output parser patterns, implement the method
|
7
|
+
# 'parse_output_defaults' which should return a hash with the patterns.
|
8
|
+
#
|
9
|
+
module Dopi
|
10
|
+
module CommandParser
|
11
|
+
module Output
|
12
|
+
include DopCommon::Validator
|
13
|
+
|
14
|
+
public
|
15
|
+
|
16
|
+
def validate_output
|
17
|
+
log_validation_method('parse_output_valid?', CommandParsingError)
|
18
|
+
unless parse_output.empty?
|
19
|
+
log_validation_method('error_patterns_valid?', CommandParsingError)
|
20
|
+
log_validation_method('warning_patterns_valid?', CommandParsingError)
|
21
|
+
end
|
22
|
+
log_validation_method('fail_on_warning_valid?', CommandParsingError)
|
23
|
+
end
|
24
|
+
|
25
|
+
def check_output(raw_output)
|
26
|
+
if error_patterns.empty? && warning_patterns.empty?
|
27
|
+
log(:debug, "No patterns defined to parse the output")
|
28
|
+
return true
|
29
|
+
end
|
30
|
+
|
31
|
+
output_ok = true
|
32
|
+
|
33
|
+
error_patterns.each do |pattern|
|
34
|
+
lines_with_matches(raw_output, pattern).each do |line_with_error|
|
35
|
+
log(:error, line_with_error)
|
36
|
+
output_ok = false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
warning_patterns.each do |pattern|
|
41
|
+
lines_with_matches(raw_output, pattern).each do |line_with_warning|
|
42
|
+
if fail_on_warning
|
43
|
+
log(:error, line_with_warning)
|
44
|
+
output_ok = false
|
45
|
+
else
|
46
|
+
log(:warn, line_with_warning)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
output_ok
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse_output
|
55
|
+
if parse_output_valid?
|
56
|
+
Hash[hash[:parse_output].map{|k,v| [k.to_sym, v]}]
|
57
|
+
else
|
58
|
+
respond_to?(:parse_output_defaults) ? parse_output_defaults : {}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def error_patterns
|
63
|
+
@error_patterns ||= parser_patterns_valid?(parse_output[:error]) ?
|
64
|
+
[ parse_output[:error] ].flatten : []
|
65
|
+
end
|
66
|
+
|
67
|
+
def warning_patterns
|
68
|
+
@warning_patterns ||= parser_patterns_valid?(parse_output[:warning]) ?
|
69
|
+
[ parse_output[:warning] ].flatten : []
|
70
|
+
end
|
71
|
+
|
72
|
+
def fail_on_warning
|
73
|
+
@fail_on_warning ||= fail_on_warning_valid? ?
|
74
|
+
hash[:fail_on_warning] : false
|
75
|
+
end
|
76
|
+
|
77
|
+
def lines_with_matches(raw_output, pattern)
|
78
|
+
regexp = Regexp.new(pattern)
|
79
|
+
raw_output.lines.find_all{ |line| line.scan(regexp).any? }
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def parse_output_valid?
|
85
|
+
return false unless hash.kind_of?(Hash)
|
86
|
+
return false if hash[:parse_output].nil? # optional
|
87
|
+
hash[:parse_output].kind_of?(Hash) or
|
88
|
+
raise CommandParsingError, "The value for 'parse_output' has to be a Hash"
|
89
|
+
true
|
90
|
+
end
|
91
|
+
|
92
|
+
def error_patterns_valid?
|
93
|
+
parser_patterns_valid?(parse_output[:error])
|
94
|
+
end
|
95
|
+
|
96
|
+
def warning_patterns_valid?
|
97
|
+
parser_patterns_valid?(parse_output[:warning])
|
98
|
+
end
|
99
|
+
|
100
|
+
def parser_patterns_valid?(pattern)
|
101
|
+
return false if pattern.nil? # optional
|
102
|
+
pattern.kind_of?(Array) or
|
103
|
+
raise CommandParsingError, "The value of 'error' and 'warning' in 'parse_output' has to be an Array"
|
104
|
+
pattern.each do |entry|
|
105
|
+
begin
|
106
|
+
Regexp.new(entry)
|
107
|
+
rescue
|
108
|
+
raise CommandParsingError, "The pattern #{entry} in 'parse_output' is not a valid regular expression"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
true
|
112
|
+
end
|
113
|
+
|
114
|
+
def fail_on_warning_valid?
|
115
|
+
return false unless hash.kind_of?(Hash)
|
116
|
+
return false if hash[:fail_on_warning].nil? # is optional
|
117
|
+
hash[:fail_on_warning].kind_of?(TrueClass) or hash[:fail_on_warning].kind_of?(FalseClass) or
|
118
|
+
raise CommandParsingError, "The value for 'fail_on_warning' must be boolean"
|
119
|
+
true
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
|