rubysync 0.1.1 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|