vop 0.3.1 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/Gemfile +2 -1
  4. data/Gemfile.lock +34 -33
  5. data/README.md +184 -2
  6. data/bin/console +17 -0
  7. data/bin/setup +8 -0
  8. data/bin/vop.sh +6 -2
  9. data/exe/vop +3 -25
  10. data/lib/boot.rb +3 -0
  11. data/lib/core/meta/commands/new_command.rb +1 -0
  12. data/lib/core/meta/commands/new_plugin.rb +47 -0
  13. data/lib/core/meta/commands/search_gems_for_plugins.rb +38 -0
  14. data/lib/core/meta/commands/search_path.rb +6 -0
  15. data/lib/core/meta/commands/set.rb +12 -0
  16. data/lib/core/meta/commands/show.rb +6 -0
  17. data/lib/core/meta/commands/show_config.rb +9 -0
  18. data/lib/core/meta/commands/who_provides.rb +6 -0
  19. data/lib/core/meta/meta.plugin +1 -0
  20. data/lib/core/shell/commands/change_loglevel.rb +6 -0
  21. data/lib/{vop/plugins/core → core/shell}/commands/clear_context.rb +0 -0
  22. data/lib/core/shell/commands/edit.rb +12 -0
  23. data/lib/core/shell/commands/help.rb +49 -0
  24. data/lib/core/shell/commands/reset.rb +4 -0
  25. data/lib/{vop/plugins/core → core/shell}/commands/show_context.rb +0 -0
  26. data/lib/core/shell/commands/source.rb +21 -0
  27. data/lib/{vop/plugins/core/helpers/plugin_loader/plugin_syntax.rb → core/shell/shell.plugin} +0 -0
  28. data/lib/core/structure/commands/collect_contributions.rb +46 -0
  29. data/lib/core/structure/commands/disable_contributor.rb +16 -0
  30. data/lib/core/structure/commands/generate_entity_commands.rb +52 -0
  31. data/lib/core/structure/commands/generate_invalidation_commands.rb +26 -0
  32. data/lib/core/structure/commands/list_commands.rb +11 -0
  33. data/lib/core/structure/commands/list_contributors.rb +10 -0
  34. data/lib/core/structure/commands/list_entities.rb +3 -0
  35. data/lib/core/structure/commands/list_plugins.rb +3 -0
  36. data/lib/core/structure/commands/register_contributor.rb +14 -0
  37. data/lib/core/structure/structure.plugin +4 -0
  38. data/lib/vop/objects/chain.rb +25 -0
  39. data/lib/vop/objects/command.rb +86 -0
  40. data/lib/vop/objects/command_param.rb +39 -0
  41. data/lib/vop/objects/entities.rb +21 -0
  42. data/lib/vop/objects/entity.rb +75 -0
  43. data/lib/vop/objects/entity_definition.rb +33 -0
  44. data/lib/vop/objects/filter.rb +48 -0
  45. data/lib/vop/objects/plugin.rb +208 -0
  46. data/lib/vop/objects/request.rb +73 -0
  47. data/lib/vop/objects/response.rb +17 -0
  48. data/lib/vop/objects/thing_with_params.rb +17 -0
  49. data/lib/vop/{command_loader.rb → parts/command_loader.rb} +8 -12
  50. data/lib/vop/parts/dependency_resolver.rb +56 -0
  51. data/lib/vop/parts/entity_loader.rb +46 -0
  52. data/lib/vop/parts/executor.rb +155 -0
  53. data/lib/vop/parts/filter_loader.rb +41 -0
  54. data/lib/vop/parts/plugin_finder.rb +46 -0
  55. data/lib/vop/parts/plugin_loader.rb +72 -0
  56. data/lib/vop/shell/shell.rb +221 -0
  57. data/lib/vop/shell/shell_formatter.rb +110 -0
  58. data/lib/vop/shell/shell_input.rb +14 -0
  59. data/lib/vop/shell/shell_input_readline.rb +20 -0
  60. data/lib/vop/shell/shell_input_testable.rb +27 -0
  61. data/lib/vop/syntax/command_syntax.rb +90 -0
  62. data/lib/vop/syntax/entity_syntax.rb +35 -0
  63. data/lib/vop/syntax/filter_syntax.rb +11 -0
  64. data/lib/vop/syntax/plugin_syntax.rb +55 -0
  65. data/lib/vop/util/errors.rb +45 -0
  66. data/lib/vop/util/pluralizer.rb +26 -0
  67. data/lib/vop/util/worker.rb +24 -0
  68. data/lib/vop/version.rb +1 -1
  69. data/lib/vop/vop.rb +216 -0
  70. data/lib/vop.rb +16 -229
  71. data/vop.gemspec +18 -15
  72. metadata +95 -63
  73. data/bin/vop.rb +0 -28
  74. data/lib/vop/command.rb +0 -168
  75. data/lib/vop/entity.rb +0 -61
  76. data/lib/vop/loader.rb +0 -35
  77. data/lib/vop/plugin.rb +0 -141
  78. data/lib/vop/plugin_loader.rb +0 -88
  79. data/lib/vop/plugins/core/commands/collect_contributions.rb +0 -31
  80. data/lib/vop/plugins/core/commands/edit.rb +0 -12
  81. data/lib/vop/plugins/core/commands/help.rb +0 -38
  82. data/lib/vop/plugins/core/commands/identity.rb +0 -4
  83. data/lib/vop/plugins/core/commands/list_contributors.rb +0 -8
  84. data/lib/vop/plugins/core/commands/list_entities.rb +0 -3
  85. data/lib/vop/plugins/core/commands/pry.rb +0 -9
  86. data/lib/vop/plugins/core/commands/reset.rb +0 -5
  87. data/lib/vop/plugins/core/commands/source.rb +0 -5
  88. data/lib/vop/plugins/core/commands/system_call.rb +0 -5
  89. data/lib/vop/plugins/core/core.plugin +0 -4
  90. data/lib/vop/plugins/core/helpers/command_loader/command_syntax.rb +0 -45
  91. data/lib/vop/plugins/core/helpers/command_loader/contributions.rb +0 -28
  92. data/lib/vop/plugins/core/helpers/command_loader/entities.rb +0 -57
  93. data/lib/vop/plugins/core/helpers/helper.rb +0 -3
  94. data/lib/vop/plugins/meta/commands/add_search_path.rb +0 -6
  95. data/lib/vop/plugins/meta/commands/delete_plugin.rb +0 -13
  96. data/lib/vop/plugins/meta/commands/list_commands.rb +0 -17
  97. data/lib/vop/plugins/meta/commands/list_plugins.rb +0 -8
  98. data/lib/vop/plugins/meta/commands/new_command.rb +0 -14
  99. data/lib/vop/plugins/meta/commands/new_plugin.rb +0 -25
  100. data/lib/vop/plugins/meta/commands/show_search_path.rb +0 -3
  101. data/lib/vop/plugins/meta/commands/who_provides.rb +0 -5
  102. data/lib/vop/plugins/meta/meta.plugin +0 -1
  103. data/lib/vop/plugins/ssh/commands/scp.rb +0 -11
  104. data/lib/vop/plugins/ssh/commands/ssh.rb +0 -19
  105. data/lib/vop/plugins/ssh/ssh.plugin +0 -1
  106. data/lib/vop/shell/backend.rb +0 -28
  107. data/lib/vop/shell/base_shell.rb +0 -112
  108. data/lib/vop/shell/formatter.rb +0 -46
  109. data/lib/vop/shell/vop_shell_backend.rb +0 -257
  110. data/lib/vop/shell.rb +0 -52
data/lib/vop/vop.rb ADDED
@@ -0,0 +1,216 @@
1
+ require "pathname"
2
+ require "pp"
3
+ require "logger"
4
+
5
+ require_relative "parts/plugin_finder"
6
+ require_relative "parts/plugin_loader"
7
+ require_relative "parts/dependency_resolver"
8
+ require_relative "objects/request"
9
+ require_relative "objects/entity"
10
+ require_relative "objects/entities"
11
+ require_relative "util/errors"
12
+ require_relative "util/pluralizer"
13
+ require_relative "util/worker"
14
+
15
+ module Vop
16
+
17
+ $logger = Logger.new(STDOUT)
18
+
19
+ class Vop
20
+
21
+ attr_reader :plugins
22
+ attr_reader :commands
23
+ attr_reader :entities
24
+
25
+ attr_reader :filters
26
+ attr_reader :filter_chain
27
+
28
+ attr_reader :finder, :loader, :sorter
29
+
30
+ def initialize(options = {})
31
+ @options = {
32
+ config_path: "/etc/vop",
33
+ log_level: Logger::INFO
34
+ }.merge(options)
35
+ $logger.level = @options[:log_level]
36
+
37
+ @finder = PluginFinder.new
38
+ @loader = PluginLoader.new(self)
39
+ @sorter = DependencyResolver.new(self)
40
+
41
+ _reset
42
+ end
43
+
44
+ def clear
45
+ @plugins = []
46
+ @commands = {}
47
+ @entities = {}
48
+ @filters = {}
49
+ @filter_chain = []
50
+ @hooks = Hash.new { |h,k| h[k] = [] }
51
+ end
52
+
53
+ def _reset
54
+ clear
55
+ load
56
+ end
57
+
58
+ def to_s
59
+ "Vop (#{@plugins.size} plugins)"
60
+ end
61
+
62
+ def inspect
63
+ {
64
+ plugins: @plugins.map(&:name)
65
+ }.to_json()
66
+ end
67
+
68
+ def plugin(name)
69
+ result = @plugins.select { |x| x.name == name }.first
70
+ raise "no such plugin: #{name}" if result.nil?
71
+ result
72
+ end
73
+
74
+ def lib_path
75
+ Pathname.new(File.join(File.dirname(__FILE__), "..")).realpath
76
+ end
77
+
78
+ def core_location
79
+ File.join(lib_path, "core")
80
+ end
81
+
82
+ def plugin_locations
83
+ result = []
84
+
85
+ # during development, we might find checkouts for "plugins" and "services"
86
+ # next to the core
87
+ vop_dir = Pathname.new(File.join(lib_path, "..", "..")).realpath
88
+ unless vop_dir.to_s.start_with? "/usr"
89
+ %w|plugins services|.each do |thing|
90
+ sibling_dir = File.join(vop_dir, thing)
91
+ result << sibling_dir
92
+ end
93
+ end
94
+
95
+ # for distribution packages (?)
96
+ result << "/usr/lib/vop-plugins"
97
+
98
+ # an extra path might have been passed in the options
99
+ if @options.has_key? :plugin_path
100
+ result << @options[:plugin_path]
101
+ end
102
+
103
+ result
104
+ end
105
+
106
+ def config_path
107
+ @options[:config_path]
108
+ end
109
+
110
+ def plugin_config_path
111
+ @plugin_config_path ||= File.join(config_path, "plugins.d")
112
+ end
113
+
114
+ def load_from(locations, load_options = {})
115
+ found = finder.find(locations)
116
+ plugins = loader.load(found, load_options)
117
+ new_plugins = sorter.sort(plugins.loaded)
118
+
119
+ new_plugins.each do |plugin|
120
+ plugin.init
121
+ self << plugin
122
+ end
123
+
124
+ $logger.debug "loaded #{new_plugins.size} plugins from #{locations}"
125
+ $logger.debug plugins.loaded.map(&:name)
126
+ end
127
+
128
+ def load
129
+ load_from(core_location, { core: true })
130
+ load_from(plugin_locations)
131
+ load_from(search_path)
132
+
133
+ call_global_hook :loading_finished
134
+
135
+ $logger.info "init complete : #{@plugins.size} plugins, #{@commands.size} commands"
136
+ end
137
+
138
+ def <<(stuff)
139
+ if stuff.is_a? Array
140
+ stuff.each do |thing|
141
+ self << thing
142
+ end
143
+ else
144
+ if stuff.is_a? Plugin
145
+ @plugins << stuff
146
+ elsif stuff.is_a? EntityDefinition
147
+ entity = stuff
148
+ @entities[entity.short_name] = stuff
149
+ elsif stuff.is_a? Command
150
+ command = stuff
151
+ unless command.dont_register
152
+ $logger.debug "registering #{command.name}"
153
+ if @commands.keys.include? command.short_name
154
+ $logger.warn "overriding previous declaration of #{command.short_name}"
155
+ end
156
+ @commands[command.short_name] = stuff
157
+
158
+ self.class.send(:define_method, command.short_name) do |*args, &block|
159
+ ruby_args = args.length > 0 ? args[0] : {}
160
+ # TODO we might want to do this only if there's a block param defined
161
+ if block
162
+ ruby_args["block"] = block
163
+ end
164
+ self.execute(command.short_name, ruby_args)
165
+ end
166
+ end
167
+ elsif stuff.is_a? Filter
168
+ short_name = stuff.short_name
169
+ @filters[short_name] = stuff
170
+ @filter_chain.unshift short_name
171
+ else
172
+ raise Errors::LoadError.new "unexpected type '#{stuff.class}'"
173
+ end
174
+ end
175
+ end
176
+
177
+ def hook(hook_sym, &block)
178
+ @hooks[hook_sym] << block
179
+ end
180
+
181
+ def call_global_hook(hook_sym, payload = {})
182
+ @hooks[hook_sym].each do |h|
183
+ h.call(payload)
184
+ end
185
+ end
186
+
187
+ def execute_request(request)
188
+ call_global_hook(:before_execute, { request: request })
189
+ begin
190
+ response = request.execute()
191
+ rescue => e
192
+ response = ::Vop::Response.new(nil, {})
193
+ response.status = "error"
194
+ raise e
195
+ ensure
196
+ call_global_hook(:after_execute, { request: request, response: response })
197
+ end
198
+
199
+ #$logger.debug "executed : #{request.command.name}, response : #{response.pretty_inspect}"
200
+ response
201
+ end
202
+
203
+ def execute(command_name, param_values = {}, extra = {})
204
+ request = Request.new(self, command_name, param_values, extra)
205
+ response = execute_request(request)
206
+
207
+ response.result
208
+ end
209
+
210
+ def execute_async(request)
211
+ AsyncExecutorWorker.perform_async(request.to_json)
212
+ end
213
+
214
+ end
215
+
216
+ end
data/lib/vop.rb CHANGED
@@ -1,242 +1,29 @@
1
- require 'pp'
2
- require 'logger'
3
- require 'pathname'
1
+ require "pathname"
4
2
 
5
- require 'vop/version'
6
- require 'vop/plugin_loader'
7
- require 'active_support/inflector'
3
+ require_relative "vop/version"
4
+ require_relative "vop/shell/shell"
5
+ require_relative "vop/vop"
8
6
 
9
7
  module Vop
10
8
 
11
- VOP_ROOT = Pathname.new(File.join(File.dirname(__FILE__), '..')).realpath
12
- CORE_PLUGIN_PATH = Pathname.new(File.join(File.dirname(__FILE__), 'vop', 'plugins')).realpath
13
- #CONFIG_PATH = '/etc/vop'
14
- #PLUGIN_CONFIG_PATH = File.join(CONFIG_PATH, 'plugins.d')
15
-
16
- class Vop
17
-
18
- DEFAULTS = {
19
- :search_path => [
20
- File.join(VOP_ROOT, '..', 'plugins/standard'),
21
- File.join(VOP_ROOT, '..', 'plugins/extended')
22
- ],
23
- :command_dir_name => 'commands',
24
- :plugin_config => {
25
-
26
- }
27
- }
28
-
29
- attr_reader :config
30
-
31
- attr_reader :plugins
32
- attr_reader :commands
33
-
34
- def initialize(options = {})
35
- at_exit {
36
- self.shutdown
37
- }
38
-
39
- @version = ::Vop::VERSION
40
-
41
- @config = DEFAULTS
42
- @config.merge! options
43
-
44
- $logger = Logger.new(STDOUT)
45
- $logger.level = options['--verbose'] ? Logger::DEBUG : Logger::INFO
46
-
47
- _reset
48
-
49
- $logger.info "virtualop (#{@version}) init complete."
50
- $logger.info "hello."
51
- end
52
-
53
- def _pry
54
- binding.pry
55
- end
56
-
57
- def _reset
58
- $logger.debug "loading..."
59
-
60
- load_plugins
61
-
62
- $logger.info "loaded #{@commands.size} commands from #{@plugins.size} plugins"
63
- end
64
-
65
- def _search_path
66
- [ CORE_PLUGIN_PATH ] + config[:search_path]
67
- end
68
-
69
- def inspect
70
- chunk_size = 25
71
- plugin_string = @plugins.keys.sort[0..chunk_size-1].join(' ')
72
- if @plugins.length > chunk_size
73
- plugin_string += " + #{@plugins.length - chunk_size} more"
74
- end
75
- "vop #{@version} (#{plugin_string})"
76
- end
77
-
78
- def eat(command)
79
- @commands[command.short_name] = command
80
-
81
- self.class.send(:define_method, command.short_name) do |*args|
82
- ruby_args = args.length > 0 ? args[0] : {}
83
- self.execute(command.short_name, ruby_args)
84
- end
85
- end
86
-
87
- def load_plugins
88
- @plugins = {}
89
- @commands = {}
90
- @hooks = Hash.new { |h,k| h[k] = [] }
91
-
92
- # step 1 : read plugins from all existing source dirs
93
- candidates = _search_path
94
- search_path = candidates.select { |path| File.exists? path }
95
- search_path.each do |path|
96
- PluginLoader.read(self, path)
97
- end
98
-
99
- # step 2 : activate plugins (in the right order)
100
- ordered_plugins.each do |plugin|
101
- plugin.init
102
- end
103
-
104
- # step 3 : expand entities
105
- @plugins['core'].state[:entities].each do |entity|
106
- entity_name = entity[:name]
107
- entity_command = @commands[entity_name]
108
- list_command_name = "list_#{entity_name.pluralize(42)}"
109
- $logger.debug "generating #{list_command_name}"
110
- list_command = Command.new(entity_command.plugin, list_command_name)
111
-
112
- if entity[:options][:on]
113
- list_command.params << {
114
- :name => entity[:options][:on],
115
- :multi => false,
116
- :mandatory => true,
117
- :default_param => true
118
- }
119
- end
120
- list_command.block = entity_command.param(entity[:key])[:lookup]
121
- eat(list_command)
122
- # TODO add pseudo source code so that `source <list_command_name>` works
123
- end
124
-
125
- # TODO add pre-flight hook so that plugins can attach logic to execute here
126
- end
127
-
128
- def resolve(plugin, resolved, unresolved, level = 0)
129
- unresolved << plugin.name
130
-
131
- plugin.dependencies.each do |dep|
132
- unless resolved.include? dep
133
- if unresolved.include? dep
134
- raise "running in circles #{plugin.name} -> #{dep}"
135
- else
136
- unless @plugins.has_key? dep
137
- raise "missing dependency: #{plugin.name} depends on #{dep}"
138
- end
139
- dependency = @plugins[dep]
140
- resolve(dependency, resolved, unresolved, level + 1)
141
- end
142
- end
143
- end
144
- resolved << plugin.name
145
- end
146
-
147
- def ordered_plugins
148
- root_plugin = Plugin.new(self, '__root__', nil)
149
- @plugins.values.each do |plugin|
150
- root_plugin.dependencies << plugin.name
151
- end
152
- resolved = []
153
- unresolved = []
154
-
155
- resolve(root_plugin, resolved, unresolved)
156
- resolved.delete_if { |x| x == root_plugin.name }
157
-
158
- resolved.map { |x| @plugins[x] }
159
- end
160
-
161
- def command(name)
162
- unless commands.has_key?(name)
163
- raise "no such command : #{name}"
164
- end
165
-
166
- commands[name]
167
- end
168
-
169
- def core
170
- @plugins['core']
171
- end
172
-
173
- def hook(name, plugin_name)
174
- @hooks[name] << plugin_name
175
- end
176
-
177
- def call_hook(name, *payload)
178
- @hooks[name].each do |plugin_name, block|
179
- @plugins[plugin_name].call_hook(name, payload)
180
- end
181
- end
182
-
183
- def before_execute(request)
184
- #puts ">> #{request.command_name} #{request.param_values.keys}"
185
- call_hook :before_execute, request
186
- end
187
-
188
- def after_execute(request, response)
189
- #puts "<< #{request.command_name} #{response.result}"
190
- call_hook :after_execute, request, response
191
- end
192
-
193
- def execute(command_name, param_values, extra = {})
194
- request = Request.new(command_name, param_values, extra)
195
- before_execute(request)
196
-
197
- (result, context) = execute_command(command_name, param_values, extra)
198
-
199
- response = Response.new(result, context)
200
- after_execute(request, response)
201
-
202
- result
203
- end
204
-
205
- def execute_command(command_name, param_values, extra = {})
206
- $logger.debug "+++ #{command_name} +++"
207
- command = @commands[command_name]
208
-
209
- command.execute(param_values, extra)
210
- end
211
-
212
- def shutdown()
213
- $logger.debug "shutting down..."
214
- end
215
-
9
+ def self.setup(options = {})
10
+ ::Vop::Vop.new(options)
216
11
  end
217
12
 
218
- class Request
219
-
220
- attr_reader :command_name, :param_values
221
-
222
- def initialize(command_name, param_values, extra = {})
223
- @command_name = command_name
224
- @param_values = param_values
225
- # fuck extra
13
+ def self.boot(options = {})
14
+ if ENV["VOP_DEV_MODE"]
15
+ sibling_lib_dir = Pathname.new(File.join(File.dirname(__FILE__), "..", "lib")).realpath
16
+ if File.exists? sibling_lib_dir
17
+ #puts "adding local lib path #{sibling_lib_dir}"
18
+ $: << sibling_lib_dir
19
+ end
226
20
  end
227
21
 
22
+ ::Vop.setup(options)
228
23
  end
229
24
 
230
- class Response
231
-
232
- attr_reader :result
233
-
234
- def initialize(result, context)
235
- @result = result
236
- @context = context
237
- end
238
-
25
+ def vop_setup(options = {})
26
+ ::Vop.boot(options)
239
27
  end
240
28
 
241
-
242
29
  end
data/vop.gemspec CHANGED
@@ -1,31 +1,34 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # encoding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'vop/version'
4
+ require "vop/version"
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "vop"
8
8
  spec.version = Vop::VERSION
9
9
  spec.authors = ["Philipp T."]
10
- spec.email = ["philipp@virtualop.org"]
10
+ spec.email = ["philipp@hitchhackers.net"]
11
11
 
12
- spec.summary = %q{The virtualop is a tool for automating things.}
13
- spec.description = %q{Automated processes fail more consistently, that's why I wrote a tool to make scripts that will do things that we won't have to do ourselves then. (see xkcd #1629)}
14
- spec.homepage = "http://www.virtualop.org"
12
+ spec.summary = %q{The vop is a scripting framework.}
13
+ spec.description = %q{Automation framework with a plugin/command architecture, entities, contributions, filters and asynchronous workers. Shell included, web interface in a separate project.}
14
+ spec.homepage = "http://virtualop.org"
15
15
 
16
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
17
19
  spec.bindir = "exe"
18
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
21
  spec.require_paths = ["lib"]
20
22
 
21
- spec.add_dependency "terminal-table"
22
23
  spec.add_dependency "net-ssh"
23
24
  spec.add_dependency "net-scp"
24
- spec.add_dependency "docopt"
25
- spec.add_dependency "activesupport"
26
- spec.add_dependency "pry"
25
+ spec.add_dependency "redis"
26
+ spec.add_dependency "sidekiq"
27
+ spec.add_dependency "terminal-table"
28
+ spec.add_dependency "xml-simple"
27
29
 
28
- spec.add_development_dependency "bundler", "~> 1.10"
29
- spec.add_development_dependency "rake"
30
- spec.add_development_dependency "rspec"
30
+ spec.add_development_dependency "bundler", "~> 1.15"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency "rspec", "~> 3.0"
33
+ spec.add_development_dependency "simplecov"
31
34
  end