zonesync 0.6.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,586 @@
1
+ grammar Zonefile
2
+ rule zone
3
+ (variable / space_or_break / comment)* soa (resource_record / variable / comment / space / linebreak)* {
4
+ def variables
5
+ @variables ||= begin
6
+ raw = elements[0].elements.select { |e| e.to_s =~ /^\$/ }
7
+ variables = {}
8
+ raw.each do |e|
9
+ variables[e.name.text_value.to_s] = e.value.text_value.to_s
10
+ end
11
+ variables
12
+ end
13
+ end
14
+
15
+ def origin
16
+ soa.origin.host.to_s
17
+ end
18
+
19
+ def to_s
20
+ text_value
21
+ end
22
+
23
+ def rr
24
+ elements[-1].elements.select { |e| e.to_s !~ /\A\s*([;$].*)?\z|\A\z/; }
25
+ end
26
+
27
+ def entries
28
+ elements[0].elements.select { |e| e.to_s !~ /\A\s*(;.*)?\z|\A\z/; } +
29
+ [soa] +
30
+ elements[-1].elements.select { |e| e.to_s !~ /\A\s*(;.*)?\z|\A\z/; }
31
+ end
32
+ }
33
+ end
34
+
35
+ rule variable
36
+ "$" name:([a-zA-Z0-9]+) space value:([a-zA-Z0-9\.\-_]+) space? comment? {
37
+ def to_s
38
+ "$#{name.text_value.to_s} #{value.text_value.to_s}"
39
+ end
40
+
41
+ def parse_type ; :variable ; end
42
+ }
43
+ end
44
+
45
+ rule soa
46
+ (
47
+ origin space ttl klass "SOA" space ns space "("? multiline_comment* space_or_break* rp multiline_comment* space_or_break* "("? multiline_comment* space_or_break* serial multiline_comment* space_or_break refresh multiline_comment* space_or_break reretry multiline_comment* space_or_break expiry multiline_comment* space_or_break nxttl multiline_comment* space_or_break* ")"? /
48
+ origin space klass ttl "SOA" space ns space "("? multiline_comment* space_or_break* rp multiline_comment* space_or_break* "("? multiline_comment* space_or_break* serial multiline_comment* space_or_break refresh multiline_comment* space_or_break reretry multiline_comment* space_or_break expiry multiline_comment* space_or_break nxttl multiline_comment* space_or_break* ")"?
49
+ ) {
50
+ def to_s
51
+ "#{origin} #{ttl} #{klass} SOA #{ns} #{rp} (#{serial} #{refresh} #{reretry} #{expiry} #{nxttl})"
52
+ end
53
+
54
+ def parse_type ; :soa ; end
55
+ }
56
+ end
57
+
58
+ rule resource_record
59
+ record:(a_record / aaaa_record / caa_record / cname_record / mx_record / naptr_record / ns_record / ptr_record / srv_record / spf_record / sshfp_record / txt_record / soa_record) space* comment:comment? linebreak {
60
+ def zone
61
+ p = parent
62
+ while p.respond_to?(:parent) && p.parent
63
+ p = p.parent
64
+ end
65
+ p
66
+ end
67
+
68
+ def to_s
69
+ text_value
70
+ end
71
+
72
+ def record_type
73
+ record.record_type
74
+ end
75
+
76
+ def ttl
77
+ record.ttl || zone.variables['TTL'].to_i
78
+ end
79
+
80
+ def comment
81
+ return if super.empty?
82
+ super
83
+ end
84
+
85
+ def method_missing(method_name, *args)
86
+ if record.respond_to?(method_name)
87
+ record.send(method_name, *args)
88
+ end
89
+ end
90
+
91
+ def respond_to?(method_name)
92
+ super || record.respond_to?(method_name)
93
+ end
94
+
95
+ def parse_type ; :record ; end
96
+ }
97
+ end
98
+
99
+ rule a_record
100
+ (
101
+ host space ms_age ttl klass "A" space ip_address /
102
+ host space ms_age klass ttl "A" space ip_address
103
+ ) {
104
+ def to_s
105
+ "#{host} #{ms_age} #{ttl} #{klass} A #{ip_address}"
106
+ end
107
+
108
+ def record_type
109
+ "A"
110
+ end
111
+ }
112
+ end
113
+
114
+ rule ip_address
115
+ [\d]+ "." [\d]+ "." [\d]+ "." [\d]+ {
116
+ def to_s
117
+ text_value
118
+ end
119
+ }
120
+ end
121
+
122
+ rule aaaa_record
123
+ (
124
+ host space ms_age ttl klass "AAAA" space ip_address:ip6_address /
125
+ host space ms_age klass ttl "AAAA" space ip_address:ip6_address
126
+ ) {
127
+ def to_s
128
+ "#{host} #{ttl} #{klass} AAAA #{ip_address}"
129
+ end
130
+
131
+ def record_type
132
+ "AAAA"
133
+ end
134
+ }
135
+ end
136
+
137
+ rule ip6_address
138
+ [\da-fA-F:.] 2..39 {
139
+ def to_s
140
+ text_value.downcase
141
+ end
142
+ }
143
+ end
144
+
145
+ rule caa_record
146
+ (
147
+ host space ms_age ttl klass "CAA" space flags:integer space tag:unquoted_string space value:caa_value /
148
+ host space ms_age klass ttl "CAA" space flags:integer space tag:unquoted_string space value:caa_value
149
+ ) {
150
+ def to_s
151
+ "#{host} #{ttl} #{klass} CAA #{flags} #{tag} #{value}"
152
+ end
153
+
154
+ def record_type
155
+ "CAA"
156
+ end
157
+ }
158
+ end
159
+
160
+ rule caa_value
161
+ (quoted_string / unquoted_string) {
162
+ def to_s
163
+ text_value
164
+ end
165
+ }
166
+ end
167
+
168
+ rule cname_record
169
+ (
170
+ host space ms_age ttl klass "CNAME" space target:host /
171
+ host space klass ms_age ttl "CNAME" space target:host /
172
+ host space ms_age ttl "CNAME" space target:host /
173
+ host space klass "CNAME" space target:host
174
+ ) {
175
+ def to_s
176
+ "#{host} #{ttl} #{klass} CNAME #{target}"
177
+ end
178
+
179
+ def record_type
180
+ "CNAME"
181
+ end
182
+ }
183
+ end
184
+
185
+ rule mx_record
186
+ (
187
+ host space ttl klass "MX" space priority:integer space exchanger:host /
188
+ host space klass ttl "MX" space priority:integer space exchanger:host
189
+ ) {
190
+ def to_s
191
+ "#{host} #{ttl} #{klass} MX #{priority} #{exchanger}"
192
+ end
193
+
194
+ def record_type
195
+ "MX"
196
+ end
197
+ }
198
+ end
199
+
200
+ rule naptr_record
201
+ (
202
+ host space ms_age ttl klass "NAPTR" space data /
203
+ host space ms_age klass ttl "NAPTR" space data
204
+ ) {
205
+ def to_s
206
+ "#{host} #{ttl} #{klass} NAPTR #{data}"
207
+ end
208
+
209
+ def record_type
210
+ "NAPTR"
211
+ end
212
+ }
213
+ end
214
+
215
+ rule ns_record
216
+ (
217
+ host space ms_age ttl klass "NS" space nameserver:host /
218
+ host space ms_age klass ttl "NS" space nameserver:host
219
+ ) {
220
+ def to_s
221
+ "#{host} #{ttl} #{klass} NS #{nameserver}"
222
+ end
223
+
224
+ def record_type
225
+ "NS"
226
+ end
227
+ }
228
+ end
229
+
230
+ rule ptr_record
231
+ (
232
+ host space ms_age ttl klass "PTR" space target:host /
233
+ host space ms_age klass ttl "PTR" space target:host
234
+ ) {
235
+ def to_s
236
+ "#{host} #{ttl} #{klass} PTR #{target}"
237
+ end
238
+
239
+ def record_type
240
+ "PTR"
241
+ end
242
+ }
243
+ end
244
+
245
+ rule soa_record
246
+ (
247
+ origin space ms_age ttl klass "SOA" space ns space rp space data /
248
+ origin space ms_age klass ttl "SOA" space ns space rp space data
249
+ ) {
250
+ def to_s
251
+ "#{origin} #{ttl} #{klass} SOA #{ns} #{rp} (#{space})"
252
+ end
253
+
254
+ def record_type
255
+ "SOA"
256
+ end
257
+ }
258
+ end
259
+
260
+ rule srv_record
261
+ (
262
+ host space ms_age ttl klass "SRV" space priority:integer space weight:integer space port:integer space target:host /
263
+ host space klass ms_age ttl "SRV" space priority:integer space weight:integer space port:integer space target:host /
264
+ host space ms_age ttl "SRV" space priority:integer space weight:integer space port:integer space target:host /
265
+ host space klass "SRV" space priority:integer space weight:integer space port:integer space target:host
266
+ ) {
267
+ def to_s
268
+ "#{host} #{ttl} #{klass} SRV #{priority} #{weight} #{port} #{target}"
269
+ end
270
+
271
+ def record_type
272
+ "SRV"
273
+ end
274
+ }
275
+ end
276
+
277
+ rule spf_record
278
+ (
279
+ host space ms_age ttl klass "SPF" space data:txt_data /
280
+ host space ms_age klass ttl "SPF" space data:txt_data
281
+ ) {
282
+ def to_s
283
+ "#{host} #{ttl} #{klass} SPF #{data}"
284
+ end
285
+
286
+ def record_type
287
+ "SPF"
288
+ end
289
+ }
290
+ end
291
+
292
+ rule sshfp_record
293
+ (
294
+ host space ms_age ttl klass "SSHFP" space alg:integer space fptype:integer space fp:fingerprint /
295
+ host space ms_age klass ttl "SSHFP" space alg:integer space fptype:integer space fp:fingerprint
296
+ ) {
297
+ def to_s
298
+ "#{host} #{ttl} #{klass} SSHFP #{alg} #{fptype} #{fp}"
299
+ end
300
+
301
+ def record_type
302
+ "SSHFP"
303
+ end
304
+ }
305
+ end
306
+
307
+ rule txt_record
308
+ (
309
+ host space ms_age ttl klass "TXT" space data:ms_txt_data /
310
+ host space ms_age klass ttl "TXT" space data:ms_txt_data
311
+ ) {
312
+ def to_s
313
+ "#{host} #{ttl} #{klass} TXT #{data}"
314
+ end
315
+
316
+ def record_type
317
+ "TXT"
318
+ end
319
+ }
320
+ end
321
+
322
+ rule origin
323
+ host comment* {
324
+ def to_s
325
+ "#{host}"
326
+ end
327
+ }
328
+ end
329
+
330
+ rule multiline_comment
331
+ linebreak+ comment* {
332
+ def to_s
333
+ text_value.strip
334
+ end
335
+ }
336
+ end
337
+
338
+ rule space
339
+ [ \t]+ {
340
+ def to_s
341
+ text_value
342
+ end
343
+ }
344
+ end
345
+
346
+ rule linebreak
347
+ [\n\r]+ {
348
+ def to_s
349
+ ''
350
+ end
351
+ }
352
+ end
353
+
354
+ rule space_or_break
355
+ [\s]+ {
356
+ def to_s
357
+ text_value
358
+ end
359
+ }
360
+ end
361
+
362
+ rule klass
363
+ (("IN" space) / '') {
364
+ def to_s
365
+ text_value.strip
366
+ end
367
+ }
368
+ end
369
+
370
+ rule comment
371
+ space* ";" [^\n\r]* {
372
+ def to_s
373
+ text_value[2..].to_s.strip
374
+ end
375
+
376
+ def parse_type ; :comment ; end
377
+ }
378
+ end
379
+
380
+ rule ns
381
+ host comment* {
382
+ def to_s
383
+ "#{host}"
384
+ end
385
+ }
386
+ end
387
+
388
+ rule rp
389
+ rp_value comment* {
390
+ def to_s
391
+ "#{rp_value}"
392
+ end
393
+ }
394
+ end
395
+
396
+ rule rp_value
397
+ ("." / (("\\." / [a-zA-Z0-9\-)])+ "."?)+) {
398
+ def to_s
399
+ text_value
400
+ end
401
+ }
402
+ end
403
+
404
+ rule serial
405
+ integer comment* {
406
+ def to_i
407
+ integer.to_i
408
+ end
409
+ def to_s
410
+ "#{to_i}"
411
+ end
412
+ }
413
+ end
414
+
415
+ rule time_interval
416
+ integer time_multiplier {
417
+ def to_s
418
+ text_value
419
+ end
420
+
421
+ def to_i
422
+ time_multiplier.to_i * integer.to_i
423
+ end
424
+ }
425
+ end
426
+
427
+ rule refresh
428
+ time_interval comment* {
429
+ def to_i
430
+ time_interval.to_i
431
+ end
432
+ def to_s
433
+ time_interval.to_s
434
+ end
435
+ }
436
+ end
437
+
438
+ rule integer
439
+ [0-9]+ {
440
+ def to_i
441
+ text_value.to_i
442
+ end
443
+ def to_s
444
+ "#{to_i}"
445
+ end
446
+ }
447
+ end
448
+
449
+ rule time_multiplier
450
+ ( 's' / 'S' / 'm' / 'M' / 'h' / 'H' / 'd' / 'D' / 'w' / 'W' / '' ) {
451
+ def to_s
452
+ text_value
453
+ end
454
+ def to_i
455
+ case text_value.downcase
456
+ when 'm' then 60
457
+ when 'h' then 60 * 60
458
+ when 'd' then 60 * 60 * 24
459
+ when 'w' then 60 * 60 * 24 * 7
460
+ else
461
+ 1
462
+ end
463
+ end
464
+ }
465
+ end
466
+
467
+ rule reretry
468
+ time_interval comment* {
469
+ def to_i
470
+ time_interval.to_i
471
+ end
472
+ def to_s
473
+ time_interval.to_s
474
+ end
475
+ }
476
+ end
477
+
478
+ rule expiry
479
+ time_interval comment* {
480
+ def to_i
481
+ time_interval.to_i
482
+ end
483
+ def to_s
484
+ time_interval.to_s
485
+ end
486
+ }
487
+ end
488
+
489
+ rule nxttl
490
+ time_interval comment* {
491
+ def to_i
492
+ time_interval.to_i
493
+ end
494
+ def to_s
495
+ time_interval.to_s
496
+ end
497
+ }
498
+ end
499
+
500
+ rule ms_age
501
+ ( "[AGE:" [\d]+ "]" space / '' ) {
502
+ def to_s
503
+ text_value
504
+ end
505
+ }
506
+ end
507
+
508
+ rule ttl
509
+ ((time_interval space) / '') {
510
+ def to_i
511
+ respond_to?(:time_interval) ? time_interval.to_i : nil
512
+ end
513
+ def to_s
514
+ respond_to?(:time_interval) ? time_interval.to_s : ''
515
+ end
516
+ }
517
+ end
518
+
519
+ rule host
520
+ ( ([*a-zA-Z0-9\-\._]+) / "@" / ' ' / "\t" ) {
521
+ def to_s
522
+ text_value
523
+ end
524
+ }
525
+ end
526
+
527
+ rule fingerprint
528
+ [a-fA-Z0-9:]+ {
529
+ def to_s
530
+ text_value.strip
531
+ end
532
+ }
533
+ end
534
+
535
+ rule data
536
+ [^;\n\r]+ {
537
+ def to_s
538
+ text_value.strip
539
+ end
540
+ }
541
+ end
542
+
543
+ rule ms_txt_data
544
+ (
545
+ "(" space* data:txt_data space* ")" /
546
+ data:txt_data
547
+ ) {
548
+ def to_s
549
+ data.to_s
550
+ end
551
+ }
552
+ end
553
+
554
+ rule txt_data
555
+ txt_string (space txt_data)* {
556
+ def to_s
557
+ text_value
558
+ end
559
+ }
560
+ end
561
+
562
+ rule txt_string
563
+ (quoted_string / unquoted_string) {
564
+ def to_s
565
+ text_value
566
+ end
567
+ }
568
+ end
569
+
570
+ rule quoted_string
571
+ ( '"' ( '\"' / [^"] )* '"') {
572
+ def to_s
573
+ text_value
574
+ end
575
+ }
576
+ end
577
+
578
+ rule unquoted_string
579
+ '[a-zA-Z0-9=_\.\-\@\:\~]+'r {
580
+ def to_s
581
+ text_value
582
+ end
583
+ }
584
+ end
585
+
586
+ end
data/lib/zonesync.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  require "zonesync/provider"
2
2
  require "zonesync/diff"
3
+ require "zonesync/validator"
3
4
  require "zonesync/logger"
4
5
  require "zonesync/cli"
5
6
  require "zonesync/rake"
7
+ require "zonesync/errors"
6
8
 
7
9
  module Zonesync
8
10
  def self.call zonefile: "Zonefile", credentials: default_credentials, dry_run: false
@@ -33,7 +35,33 @@ module Zonesync
33
35
  def call dry_run: false
34
36
  source = Provider.from(self.source)
35
37
  destination = Provider.from(self.destination)
36
- operations = Diff.call(from: destination, to: source)
38
+ operations = Diff.call(
39
+ from: destination.diffable_records,
40
+ to: source.diffable_records,
41
+ )
42
+
43
+ Validator.call(operations, destination)
44
+
45
+ smanifest = source.manifest.generate
46
+ dmanifest = destination.manifest.existing
47
+ if smanifest != dmanifest
48
+ if dmanifest
49
+ operations << [:change, [dmanifest, smanifest]]
50
+ else
51
+ operations << [:add, [smanifest]]
52
+ end
53
+ end
54
+
55
+ schecksum = source.manifest.generate_checksum
56
+ dchecksum = destination.manifest.existing_checksum
57
+ if schecksum != dchecksum
58
+ if dchecksum
59
+ operations << [:change, [dchecksum, schecksum]]
60
+ else
61
+ operations << [:add, [schecksum]]
62
+ end
63
+ end
64
+
37
65
  operations.each do |method, args|
38
66
  Logger.log(method, args, dry_run: dry_run)
39
67
  destination.send(method, *args) unless dry_run
data/zonesync.gemspec CHANGED
@@ -27,9 +27,9 @@ Gem::Specification.new do |spec|
27
27
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
28
  spec.require_paths = ["lib"]
29
29
 
30
- spec.add_dependency "dns-zonefile", "~>1.0"
31
30
  spec.add_dependency "diff-lcs", "~>1.4"
32
31
  spec.add_dependency "thor", "~>1.0"
32
+ spec.add_dependency "treetop", "~>1.6"
33
33
 
34
34
  spec.add_development_dependency "rake"
35
35
  spec.add_development_dependency "rspec"