rubysync 0.1.1 → 0.2.1
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.
- data.tar.gz.sig +0 -0
- data/HISTORY.txt +11 -0
- data/Manifest.txt +4 -1
- data/Rakefile +3 -4
- data/bin/rubysync +242 -152
- data/bin/rubysync.rb +242 -152
- data/lib/ruby_sync.rb +1 -1
- data/lib/ruby_sync/connectors/base_connector.rb +286 -378
- data/lib/ruby_sync/connectors/connector_event_processing.rb +24 -20
- data/lib/ruby_sync/connectors/csv_file_connector.rb +3 -5
- data/lib/ruby_sync/connectors/dbm_association_tracking.rb +88 -0
- data/lib/ruby_sync/connectors/dbm_change_tracking.rb +110 -0
- data/lib/ruby_sync/connectors/ldap_changelog_connector.rb +79 -78
- data/lib/ruby_sync/connectors/ldap_connector.rb +84 -57
- data/lib/ruby_sync/connectors/memory_association_tracking.rb +60 -0
- data/lib/ruby_sync/connectors/memory_change_tracking.rb +84 -0
- data/lib/ruby_sync/connectors/memory_connector.rb +3 -0
- data/lib/ruby_sync/connectors/xml_connector.rb +6 -2
- data/lib/ruby_sync/event.rb +73 -50
- data/lib/ruby_sync/operation.rb +3 -3
- data/lib/ruby_sync/pipelines/base_pipeline.rb +76 -48
- data/lib/ruby_sync/util/utilities.rb +72 -29
- data/test/tc_csv_file_connector.rb +2 -2
- data/test/tc_ldap_connector.rb +2 -2
- data/test/tc_memory_connectors.rb +0 -2
- data/test/tc_transformation.rb +14 -13
- data/test/tc_xml_connectors.rb +4 -2
- data/test/ts_rubysync.rb +1 -1
- metadata +102 -84
- metadata.gz.sig +0 -0
- data/lib/ruby_sync/connectors/ldap_associations.rb +0 -126
data/lib/ruby_sync/operation.rb
CHANGED
@@ -27,7 +27,7 @@ module RubySync
|
|
27
27
|
self.new(:add, subject, values)
|
28
28
|
end
|
29
29
|
|
30
|
-
def self.delete subject, values
|
30
|
+
def self.delete subject, values=nil
|
31
31
|
self.new(:delete, subject, values)
|
32
32
|
end
|
33
33
|
|
@@ -58,11 +58,11 @@ module RubySync
|
|
58
58
|
|
59
59
|
remove_method :values=
|
60
60
|
def values=(values)
|
61
|
-
@values = as_array(values)
|
61
|
+
@values = (values == nil)? nil : as_array(values)
|
62
62
|
end
|
63
63
|
|
64
64
|
def value
|
65
|
-
@values[0]
|
65
|
+
@values and @values[0]
|
66
66
|
end
|
67
67
|
|
68
68
|
def value= new_value
|
@@ -40,12 +40,11 @@ module RubySync
|
|
40
40
|
class BasePipeline
|
41
41
|
|
42
42
|
include RubySync::Utilities
|
43
|
+
meta_eval {include(RubySync::Utilities)}
|
43
44
|
|
45
|
+
#add_dump_options
|
44
46
|
attr_accessor :delay # delay in seconds between checking connectors
|
45
47
|
|
46
|
-
array_option :dump_before, :dump_after
|
47
|
-
dump_before []
|
48
|
-
dump_after []
|
49
48
|
|
50
49
|
def initialize
|
51
50
|
@delay = 5
|
@@ -56,30 +55,47 @@ module RubySync
|
|
56
55
|
end
|
57
56
|
|
58
57
|
def self.client(connector_name, options={})
|
59
|
-
|
58
|
+
options = HashWithIndifferentAccess.new(options)
|
59
|
+
connector_class = class_called(connector_name, "connector")
|
60
|
+
unless connector_class
|
61
|
+
log.error "No connector called #connector_name}"
|
62
|
+
return
|
63
|
+
end
|
60
64
|
options[:name] ||= "#{self.name}(client)"
|
61
65
|
options[:is_vault] = false
|
62
66
|
class_def 'client' do
|
63
|
-
@client ||=
|
67
|
+
@client ||= connector_class.new(options)
|
64
68
|
end
|
65
69
|
end
|
66
70
|
|
67
71
|
def self.vault(connector_name, options={})
|
68
|
-
|
72
|
+
options = HashWithIndifferentAccess.new(options)
|
73
|
+
connector_class = class_called(connector_name, "connector")
|
74
|
+
unless connector_class
|
75
|
+
log.error "No connector called #{connector_name}"
|
76
|
+
return
|
77
|
+
end
|
69
78
|
options[:name] ||= "#{self.name}(vault)"
|
70
79
|
options[:is_vault] = true
|
71
80
|
class_def 'vault' do
|
72
81
|
unless @vault
|
73
|
-
@vault =
|
82
|
+
@vault = connector_class.new(options)
|
74
83
|
@vault.pipeline = self
|
75
84
|
end
|
76
85
|
@vault
|
77
86
|
end
|
78
87
|
end
|
79
88
|
|
89
|
+
array_option :dump_before, :dump_after
|
90
|
+
dump_before HashWithIndifferentAccess.new
|
91
|
+
dump_after HashWithIndifferentAccess.new
|
80
92
|
|
81
|
-
def self.in_transform(&blk)
|
82
|
-
def self.
|
93
|
+
def self.in_transform(&blk) deprecated_event_method :in_transform, :in_event_transform, &blk; end
|
94
|
+
def self.in_event_transform(&blk) event_method :in_event_transform,&blk; end
|
95
|
+
def self.in_command_transform(&blk) event_method :in_command_transform,&blk; end
|
96
|
+
def self.out_transform(&blk) deprecated_event_method :out_transform, :out_event_transform, &blk; end
|
97
|
+
def self.out_event_transform(&blk) event_method :out_event_transform,&blk; end
|
98
|
+
def self.out_command_transform(&blk) event_method :out_command_transform,&blk; end
|
83
99
|
def self.in_match(&blk) event_method :in_match,&blk; end
|
84
100
|
def self.out_match(&blk) event_method :out_match,&blk; end
|
85
101
|
def self.in_create(&blk) event_method :in_create,&blk; end
|
@@ -89,14 +105,28 @@ module RubySync
|
|
89
105
|
|
90
106
|
def self.event_method name,&blk
|
91
107
|
define_method name do |event|
|
92
|
-
event.instance_eval
|
108
|
+
event.instance_eval(&blk)
|
93
109
|
end
|
94
110
|
end
|
95
111
|
|
112
|
+
def self.deprecated_event_method name, replacement, &blk
|
113
|
+
log.warn "'#{name}' has been deprecated. Use '#{replacement}' instead."
|
114
|
+
event_method(replacement, &blk)
|
115
|
+
end
|
116
|
+
|
117
|
+
|
96
118
|
def in_match(event)
|
97
119
|
log.debug "Default matching rule - vault[in_place] exists?"
|
98
|
-
|
99
|
-
|
120
|
+
if vault.respond_to?('[]')
|
121
|
+
path = in_place(event)
|
122
|
+
if path
|
123
|
+
log.debug "Checking for object at '#{path}' on vault."
|
124
|
+
vault[path] and path
|
125
|
+
end
|
126
|
+
else
|
127
|
+
log.debug "Vault doesn't support random access - no match"
|
128
|
+
nil
|
129
|
+
end
|
100
130
|
end
|
101
131
|
|
102
132
|
def out_match(event)
|
@@ -128,19 +158,10 @@ module RubySync
|
|
128
158
|
def out_place_transform(event)
|
129
159
|
event.target_path = out_place(event)
|
130
160
|
end
|
131
|
-
|
132
|
-
def perform_transform name, event, hint=""
|
133
|
-
log.info "Performing #{name}"
|
134
|
-
log.info event.to_yaml if dump_before.include?(name.to_sym)
|
135
|
-
call_if_exists name, event, hint
|
136
|
-
event.commit_changes
|
137
|
-
log.info event.to_yaml if dump_after.include?(name.to_sym)
|
138
|
-
#log_progress name, event, hint
|
139
|
-
end
|
140
161
|
|
141
|
-
#
|
162
|
+
# execute the pipeline once then return.
|
142
163
|
def run_once
|
143
|
-
log.info "
|
164
|
+
log.info "running #{name} pipeline once"
|
144
165
|
started
|
145
166
|
run_in_once
|
146
167
|
run_out_once
|
@@ -157,26 +178,26 @@ module RubySync
|
|
157
178
|
vault.stopped
|
158
179
|
end
|
159
180
|
|
160
|
-
#
|
181
|
+
# execute the in pipe once and then return
|
161
182
|
def run_in_once
|
162
183
|
return unless allowed_in
|
163
|
-
log.debug "
|
184
|
+
log.debug "running #{name} 'in' pipeline once"
|
164
185
|
client.once_only = true
|
165
186
|
client.start {|event| in_handler(event)}
|
166
187
|
end
|
167
188
|
|
168
|
-
#
|
189
|
+
# execute the out pipe once and then return
|
169
190
|
def run_out_once
|
170
191
|
return unless allowed_out
|
171
|
-
log.debug "
|
192
|
+
log.debug "running #{name} 'out' pipeline once"
|
172
193
|
vault.once_only = true
|
173
194
|
vault.start {|event| out_handler(event)}
|
174
195
|
end
|
175
196
|
|
176
197
|
def start
|
177
|
-
log.info "
|
198
|
+
log.info "starting #{name} pipeline"
|
178
199
|
@running = true
|
179
|
-
trap("
|
200
|
+
trap("sigint") {self.stop}
|
180
201
|
started
|
181
202
|
while @running
|
182
203
|
run_in_once
|
@@ -208,6 +229,8 @@ module RubySync
|
|
208
229
|
log.info "Processing incoming #{event.type} event "+event.hint
|
209
230
|
perform_transform :in_filter, event, event.hint
|
210
231
|
|
232
|
+
perform_transform :in_event_transform, event, event.hint
|
233
|
+
|
211
234
|
associated_entry = nil
|
212
235
|
unless event.type == :disassociate
|
213
236
|
associated_entry = vault.find_associated(event.association) if event.associated?
|
@@ -218,22 +241,22 @@ module RubySync
|
|
218
241
|
event.association = Association.new(association_context, event.source_path)
|
219
242
|
vault.associate event.association, match
|
220
243
|
associated_entry = vault[match]
|
244
|
+
else
|
245
|
+
log.info "No match found for unassociated entry."
|
221
246
|
end
|
222
247
|
end
|
223
248
|
end
|
224
|
-
|
249
|
+
|
225
250
|
if associated_entry
|
226
251
|
if event.type == :add
|
227
|
-
|
228
|
-
event.convert_to_modify
|
252
|
+
log.info "Associated entry in vault for add event. Converting to modify"
|
253
|
+
event.convert_to_modify associated_entry, allowed_in
|
229
254
|
end
|
230
255
|
elsif event.type == :modify
|
231
|
-
|
232
|
-
|
256
|
+
log.info "No associated entry in vault for modify event. Converting to add"
|
257
|
+
event.convert_to_add
|
233
258
|
end
|
234
259
|
|
235
|
-
perform_transform :in_transform, event, event.hint
|
236
|
-
|
237
260
|
case event.type
|
238
261
|
when :add
|
239
262
|
if in_create(event)
|
@@ -252,20 +275,25 @@ module RubySync
|
|
252
275
|
end
|
253
276
|
end
|
254
277
|
|
255
|
-
|
278
|
+
perform_transform :in_command_transform, event, event.hint
|
279
|
+
|
280
|
+
if event.effective_operation?
|
281
|
+
with_rescue("#{vault.name}: Processing command") {vault.process(event)}
|
282
|
+
else
|
283
|
+
log.info "No change."
|
284
|
+
end
|
256
285
|
log.info "---\n"
|
257
286
|
|
258
287
|
end
|
259
288
|
|
260
|
-
# Called by the '
|
261
|
-
# Note: The client can't really know whether the event is an add or a modify because it doesn't store
|
262
|
-
# the association.
|
289
|
+
# Called by the 'vault' connector in the 'out' thread to process events generated by the vault.
|
263
290
|
def out_handler(event)
|
264
291
|
event.target = @client
|
265
292
|
event.retrieve_association(association_context)
|
266
293
|
|
267
294
|
log.info "Processing outgoing #{event.type} event "+ event.hint
|
268
295
|
perform_transform :out_filter, event, event.hint
|
296
|
+
perform_transform :out_event_transform, event, event.hint
|
269
297
|
|
270
298
|
associated_entry = nil
|
271
299
|
unless event.type == :disassociate
|
@@ -283,16 +311,14 @@ module RubySync
|
|
283
311
|
|
284
312
|
if associated_entry
|
285
313
|
if event.type == :add
|
286
|
-
|
287
|
-
event.convert_to_modify
|
314
|
+
log.info "Associated entry in client for add event. Converting to modify"
|
315
|
+
event.convert_to_modify(associated_entry,allowed_out)
|
288
316
|
end
|
289
317
|
elsif event.type == :modify
|
290
|
-
|
291
|
-
|
318
|
+
log.info "No associated entry in client for modify event. Converting to add"
|
319
|
+
event.convert_to_add
|
292
320
|
end
|
293
321
|
|
294
|
-
perform_transform :out_transform, event, event.hint
|
295
|
-
|
296
322
|
case event.type
|
297
323
|
when :add
|
298
324
|
if out_create(event)
|
@@ -311,6 +337,8 @@ module RubySync
|
|
311
337
|
end
|
312
338
|
end
|
313
339
|
|
340
|
+
perform_transform :out_command_transform, event, event.hint
|
341
|
+
|
314
342
|
with_rescue("#{client.name}: Processing command") {client.process(event)}
|
315
343
|
log.info "---\n"
|
316
344
|
|
@@ -434,7 +462,7 @@ module RubySync
|
|
434
462
|
|
435
463
|
|
436
464
|
def in_filter(event)
|
437
|
-
|
465
|
+
allowed_in == [] or event.drop_all_but_changes_to(allowed_in || [])
|
438
466
|
end
|
439
467
|
|
440
468
|
|
@@ -450,7 +478,7 @@ module RubySync
|
|
450
478
|
def allowed_out; nil; end
|
451
479
|
|
452
480
|
def out_filter(event)
|
453
|
-
|
481
|
+
allowed_out == [] or event.drop_all_but_changes_to(allowed_out || [])
|
454
482
|
end
|
455
483
|
|
456
484
|
|
@@ -24,15 +24,15 @@ require 'irb'
|
|
24
24
|
|
25
25
|
|
26
26
|
module Kernel
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
27
|
+
# Make the log method globally available
|
28
|
+
def log
|
29
|
+
unless defined? @@log
|
30
|
+
@@log = Logger.new(STDOUT)
|
31
|
+
#@@log.level = Logger::DEBUG
|
32
|
+
@@log.datetime_format = "%H:%M:%S"
|
33
|
+
end
|
34
|
+
@@log
|
35
|
+
end
|
36
36
|
end
|
37
37
|
|
38
38
|
class Array
|
@@ -41,6 +41,7 @@ class Array
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
+
|
44
45
|
# Generally useful methods
|
45
46
|
module RubySync
|
46
47
|
module Utilities
|
@@ -59,7 +60,20 @@ module RubySync
|
|
59
60
|
log.warn "#{text}: #{exception.message}"
|
60
61
|
log.debug exception.backtrace.join("\n")
|
61
62
|
end
|
62
|
-
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def dump_before
|
66
|
+
[]
|
67
|
+
end
|
68
|
+
|
69
|
+
def dump_after() []; end
|
70
|
+
def perform_transform name, event, hint=""
|
71
|
+
log.info event.to_yaml if dump_before.include?(name.to_sym)
|
72
|
+
log.info "performing #{name}"
|
73
|
+
call_if_exists name, event, hint
|
74
|
+
event.commit_changes
|
75
|
+
log.info event.to_yaml if dump_after.include?(name.to_sym)
|
76
|
+
end
|
63
77
|
|
64
78
|
def call_if_exists(method, event, hint="")
|
65
79
|
result = nil
|
@@ -92,19 +106,45 @@ module RubySync
|
|
92
106
|
end
|
93
107
|
|
94
108
|
def pipeline_called name
|
95
|
-
|
109
|
+
begin
|
110
|
+
something_called name, "pipeline"
|
111
|
+
rescue
|
112
|
+
log.error "Pipeline named '#{name}' not found."
|
113
|
+
nil
|
114
|
+
end
|
96
115
|
end
|
97
116
|
|
98
117
|
|
99
|
-
def connector_called name
|
100
|
-
|
118
|
+
def connector_called name, message=nil
|
119
|
+
begin
|
120
|
+
something_called name, "connector"
|
121
|
+
rescue
|
122
|
+
message ||= "Connector named '#{name}' not found."
|
123
|
+
log.error message
|
124
|
+
nil
|
125
|
+
end
|
101
126
|
end
|
102
127
|
|
103
128
|
# Locates and returns an instance of a class for
|
104
129
|
# the given name.
|
105
|
-
def something_called name, extension
|
106
|
-
|
107
|
-
|
130
|
+
def something_called name, extension, message=nil
|
131
|
+
klass = class_called(name, extension, message) and klass.new()
|
132
|
+
end
|
133
|
+
|
134
|
+
def class_called name, extension, message=nil
|
135
|
+
class_for_name(class_name_for(name, extension), message)
|
136
|
+
end
|
137
|
+
|
138
|
+
def class_for_name(name, message=nil)
|
139
|
+
eval(name)
|
140
|
+
rescue
|
141
|
+
message ||= "Unable to find class called '#{name}'"
|
142
|
+
log.error message
|
143
|
+
nil
|
144
|
+
end
|
145
|
+
|
146
|
+
def class_name_for name, extension
|
147
|
+
"#{name.to_s}_#{extension}".camelize
|
108
148
|
end
|
109
149
|
|
110
150
|
# Ensure that path is in the search path
|
@@ -130,7 +170,7 @@ module RubySync
|
|
130
170
|
# Keep going up until we start repeating ourselves
|
131
171
|
while File.directory?(bp) && bp != last && bp != "/"
|
132
172
|
return bp if File.directory?("#{bp}/pipelines") &&
|
133
|
-
|
173
|
+
File.directory?("#{bp}/connectors")
|
134
174
|
last = bp
|
135
175
|
bp = File.expand_path("#{bp}/..")
|
136
176
|
end
|
@@ -163,25 +203,28 @@ module RubySync
|
|
163
203
|
log.warn "!!!!!!!!!! PROBLEM, DUMP FOLLOWS: !!!!!!!!!!!!!!"
|
164
204
|
p op
|
165
205
|
end
|
166
|
-
|
206
|
+
key = op.subject
|
207
|
+
next if subjects and !subjects.include?(key)
|
167
208
|
case op.type
|
168
209
|
when :add
|
169
|
-
if record[
|
170
|
-
existing = as_array(record[
|
210
|
+
if record[key]
|
211
|
+
existing = as_array(record[key])
|
171
212
|
next if existing == op.values # already same so ignore
|
172
213
|
(existing & op.values).empty? or
|
173
|
-
|
174
|
-
record[
|
214
|
+
raise "Attempt to add duplicate elements to #{name}"
|
215
|
+
record[key] = existing + op.values
|
175
216
|
else
|
176
|
-
record[
|
217
|
+
record[key] = op.values
|
177
218
|
end
|
178
219
|
when :replace
|
179
|
-
record[
|
220
|
+
record[key] = op.values
|
180
221
|
when :delete
|
181
|
-
if
|
182
|
-
|
183
|
-
|
184
|
-
|
222
|
+
if record[key]
|
223
|
+
unless op.value
|
224
|
+
record.delete(op.subject)
|
225
|
+
else
|
226
|
+
record[key] -= op.values
|
227
|
+
end
|
185
228
|
end
|
186
229
|
else
|
187
230
|
raise Exception.new("Unknown operation '#{op.type}'")
|
@@ -214,7 +257,7 @@ module RubySync
|
|
214
257
|
effective << op
|
215
258
|
end
|
216
259
|
when :delete
|
217
|
-
|
260
|
+
unless op.value
|
218
261
|
effective << op if record[op.subject]
|
219
262
|
else
|
220
263
|
targets = op.values & existing
|