zonesync 0.12.1 → 0.13.0
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.
- checksums.yaml +4 -4
- data/CLAUDE.md +0 -12
- data/lib/zonesync/cli.rb +1 -7
- data/lib/zonesync/cloudflare/proxied_support.rb +61 -0
- data/lib/zonesync/cloudflare.rb +48 -35
- data/lib/zonesync/diff.rb +1 -9
- data/lib/zonesync/errors.rb +5 -39
- data/lib/zonesync/generate.rb +1 -5
- data/lib/zonesync/http.rb +10 -21
- data/lib/zonesync/logger.rb +2 -7
- data/lib/zonesync/manifest.rb +7 -24
- data/lib/zonesync/parser.rb +3 -5
- data/lib/zonesync/provider.rb +19 -51
- data/lib/zonesync/record.rb +6 -18
- data/lib/zonesync/record_hash.rb +3 -6
- data/lib/zonesync/route53.rb +9 -24
- data/lib/zonesync/sync.rb +2 -6
- data/lib/zonesync/validator.rb +4 -17
- data/lib/zonesync/version.rb +1 -1
- data/lib/zonesync/zonefile.rb +2 -10
- data/lib/zonesync.rb +7 -17
- data/zonesync.gemspec +0 -3
- metadata +6 -100
- data/sorbet/config +0 -4
- data/sorbet/rbi/annotations/.gitattributes +0 -1
- data/sorbet/rbi/annotations/activesupport.rbi +0 -457
- data/sorbet/rbi/annotations/minitest.rbi +0 -119
- data/sorbet/rbi/annotations/webmock.rbi +0 -9
- data/sorbet/rbi/gems/.gitattributes +0 -1
- data/sorbet/rbi/gems/activesupport@8.0.1.rbi +0 -18474
- data/sorbet/rbi/gems/addressable@2.8.7.rbi +0 -1994
- data/sorbet/rbi/gems/base64@0.2.0.rbi +0 -507
- data/sorbet/rbi/gems/benchmark@0.4.0.rbi +0 -618
- data/sorbet/rbi/gems/bigdecimal@3.1.9.rbi +0 -9
- data/sorbet/rbi/gems/concurrent-ruby@1.3.4.rbi +0 -11645
- data/sorbet/rbi/gems/connection_pool@2.4.1.rbi +0 -9
- data/sorbet/rbi/gems/crack@1.0.0.rbi +0 -145
- data/sorbet/rbi/gems/date@3.4.1.rbi +0 -75
- data/sorbet/rbi/gems/diff-lcs@1.5.1.rbi +0 -1131
- data/sorbet/rbi/gems/drb@2.2.1.rbi +0 -1347
- data/sorbet/rbi/gems/erubi@1.13.1.rbi +0 -155
- data/sorbet/rbi/gems/hashdiff@1.1.2.rbi +0 -353
- data/sorbet/rbi/gems/i18n@1.14.6.rbi +0 -2275
- data/sorbet/rbi/gems/io-console@0.8.0.rbi +0 -9
- data/sorbet/rbi/gems/logger@1.6.4.rbi +0 -940
- data/sorbet/rbi/gems/minitest@5.25.4.rbi +0 -1547
- data/sorbet/rbi/gems/netrc@0.11.0.rbi +0 -159
- data/sorbet/rbi/gems/parallel@1.26.3.rbi +0 -291
- data/sorbet/rbi/gems/polyglot@0.3.5.rbi +0 -42
- data/sorbet/rbi/gems/prism@1.3.0.rbi +0 -40040
- data/sorbet/rbi/gems/psych@5.2.2.rbi +0 -1785
- data/sorbet/rbi/gems/public_suffix@6.0.1.rbi +0 -936
- data/sorbet/rbi/gems/rake@13.2.1.rbi +0 -3028
- data/sorbet/rbi/gems/rbi@0.2.2.rbi +0 -4527
- data/sorbet/rbi/gems/rdoc@6.10.0.rbi +0 -12766
- data/sorbet/rbi/gems/reline@0.6.0.rbi +0 -9
- data/sorbet/rbi/gems/rexml@3.4.0.rbi +0 -4974
- data/sorbet/rbi/gems/rspec-core@3.13.2.rbi +0 -10896
- data/sorbet/rbi/gems/rspec-expectations@3.13.3.rbi +0 -8183
- data/sorbet/rbi/gems/rspec-mocks@3.13.2.rbi +0 -5341
- data/sorbet/rbi/gems/rspec-support@3.13.2.rbi +0 -1630
- data/sorbet/rbi/gems/rspec@3.13.0.rbi +0 -83
- data/sorbet/rbi/gems/securerandom@0.4.1.rbi +0 -75
- data/sorbet/rbi/gems/spoom@1.5.0.rbi +0 -4932
- data/sorbet/rbi/gems/stringio@3.1.2.rbi +0 -9
- data/sorbet/rbi/gems/tapioca@0.16.6.rbi +0 -3611
- data/sorbet/rbi/gems/thor@1.3.2.rbi +0 -4378
- data/sorbet/rbi/gems/treetop@1.6.12.rbi +0 -1895
- data/sorbet/rbi/gems/tzinfo@2.0.6.rbi +0 -5918
- data/sorbet/rbi/gems/uri@1.0.2.rbi +0 -2340
- data/sorbet/rbi/gems/webmock@3.24.0.rbi +0 -1780
- data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +0 -435
- data/sorbet/rbi/gems/yard@0.9.37.rbi +0 -18379
- data/sorbet/rbi/todo.rbi +0 -7
- data/sorbet/tapioca/config.yml +0 -13
- data/sorbet/tapioca/require.rb +0 -4
data/lib/zonesync/parser.rb
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
#
|
|
2
|
-
require "sorbet-runtime"
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
require "treetop"
|
|
5
4
|
Treetop.load File.join(__dir__, "zonefile")
|
|
@@ -7,7 +6,7 @@ Treetop.load File.join(__dir__, "zonefile")
|
|
|
7
6
|
module Zonesync
|
|
8
7
|
class Parser
|
|
9
8
|
def self.parse(zone_string)
|
|
10
|
-
parser =
|
|
9
|
+
parser = ZonefileParser.new
|
|
11
10
|
result = parser.parse(zone_string)
|
|
12
11
|
if !result
|
|
13
12
|
puts zone_string
|
|
@@ -90,7 +89,7 @@ module Zonesync
|
|
|
90
89
|
attr_accessor :comment, :host
|
|
91
90
|
|
|
92
91
|
def type
|
|
93
|
-
|
|
92
|
+
self.class.name.split("::").last
|
|
94
93
|
end
|
|
95
94
|
|
|
96
95
|
attr_reader :rdata
|
|
@@ -334,4 +333,3 @@ module Zonesync
|
|
|
334
333
|
end
|
|
335
334
|
end
|
|
336
335
|
end
|
|
337
|
-
|
data/lib/zonesync/provider.rb
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
#
|
|
2
|
-
require "sorbet-runtime"
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
require "zonesync/record"
|
|
5
4
|
require "zonesync/zonefile"
|
|
@@ -9,91 +8,70 @@ require "zonesync/validator"
|
|
|
9
8
|
|
|
10
9
|
module Zonesync
|
|
11
10
|
class Provider
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
sig { params(config: T::Hash[Symbol, String]).void }
|
|
15
|
-
def initialize config
|
|
16
|
-
@config = T.let(config, T::Hash[Symbol, String])
|
|
11
|
+
def initialize(config)
|
|
12
|
+
@config = config
|
|
17
13
|
end
|
|
18
|
-
sig { returns(T::Hash[Symbol, String]) }
|
|
19
14
|
attr_reader :config
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
def self.from config
|
|
16
|
+
def self.from(config)
|
|
23
17
|
Zonesync.const_get(config.fetch(:provider)).new(config)
|
|
24
18
|
end
|
|
25
19
|
|
|
26
|
-
|
|
27
|
-
def diff! other, force: false
|
|
20
|
+
def diff!(other, force: false)
|
|
28
21
|
operations = diff(other).call
|
|
29
22
|
Validator.call(operations, self, other, force: force)
|
|
30
23
|
operations
|
|
31
24
|
end
|
|
32
25
|
|
|
33
|
-
|
|
34
|
-
def diff other
|
|
26
|
+
def diff(other)
|
|
35
27
|
Diff.new(
|
|
36
28
|
from: diffable_records,
|
|
37
29
|
to: other.diffable_records,
|
|
38
30
|
)
|
|
39
31
|
end
|
|
40
32
|
|
|
41
|
-
sig { returns(T::Array[Record]) }
|
|
42
33
|
def records
|
|
43
34
|
zonefile.records
|
|
44
35
|
end
|
|
45
36
|
|
|
46
|
-
sig { returns(T::Array[Record]) }
|
|
47
37
|
def diffable_records
|
|
48
38
|
records.select do |record|
|
|
49
39
|
manifest.diffable?(record)
|
|
50
40
|
end.sort
|
|
51
41
|
end
|
|
52
42
|
|
|
53
|
-
sig { returns(Manifest) }
|
|
54
43
|
def manifest
|
|
55
44
|
Manifest.new(records, zonefile)
|
|
56
45
|
end
|
|
57
46
|
|
|
58
|
-
sig { returns(Zonefile) }
|
|
59
47
|
private def zonefile
|
|
60
|
-
@zonefile ||=
|
|
48
|
+
@zonefile ||= Zonefile.load(read)
|
|
61
49
|
end
|
|
62
50
|
|
|
63
|
-
sig { returns(String) }
|
|
64
51
|
def read
|
|
65
|
-
|
|
52
|
+
raise NotImplementedError
|
|
66
53
|
end
|
|
67
54
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
Kernel.raise NotImplementedError
|
|
55
|
+
def write(string)
|
|
56
|
+
raise NotImplementedError
|
|
71
57
|
end
|
|
72
58
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
Kernel.raise NotImplementedError
|
|
59
|
+
def remove(record)
|
|
60
|
+
raise NotImplementedError
|
|
76
61
|
end
|
|
77
62
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
Kernel.raise NotImplementedError
|
|
63
|
+
def change(old_record, new_record)
|
|
64
|
+
raise NotImplementedError
|
|
81
65
|
end
|
|
82
66
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
Kernel.raise NotImplementedError
|
|
67
|
+
def add(record)
|
|
68
|
+
raise NotImplementedError
|
|
86
69
|
end
|
|
87
70
|
|
|
88
|
-
|
|
89
|
-
# Child classes can use this in their add method implementations
|
|
90
|
-
sig { params(record: Record, block: T.proc.void).void }
|
|
91
|
-
def add_with_duplicate_handling record, &block
|
|
71
|
+
def add_with_duplicate_handling(record, &block)
|
|
92
72
|
begin
|
|
93
73
|
block.call
|
|
94
74
|
rescue DuplicateRecordError => e
|
|
95
|
-
# Gracefully handle duplicate records - this means the record
|
|
96
|
-
# already exists and we just want to start tracking it
|
|
97
75
|
puts "Record already exists in #{self.class.name}: #{e.record.name} #{e.record.type} - will start tracking it"
|
|
98
76
|
return
|
|
99
77
|
end
|
|
@@ -101,7 +79,6 @@ module Zonesync
|
|
|
101
79
|
|
|
102
80
|
private
|
|
103
81
|
|
|
104
|
-
sig { params(remote_records: T::Array[Record], expected_hashes: T::Array[String]).returns(T::Array[Record]) }
|
|
105
82
|
def hash_based_diffable_records(remote_records, expected_hashes)
|
|
106
83
|
require 'set'
|
|
107
84
|
expected_set = Set.new(expected_hashes)
|
|
@@ -129,33 +106,24 @@ module Zonesync
|
|
|
129
106
|
require "zonesync/route53"
|
|
130
107
|
|
|
131
108
|
class Memory < Provider
|
|
132
|
-
extend T::Sig
|
|
133
|
-
|
|
134
|
-
sig { returns(String) }
|
|
135
109
|
def read
|
|
136
110
|
config.fetch(:string)
|
|
137
111
|
end
|
|
138
112
|
|
|
139
|
-
|
|
140
|
-
def write string
|
|
113
|
+
def write(string)
|
|
141
114
|
config[:string] = string
|
|
142
115
|
nil
|
|
143
116
|
end
|
|
144
117
|
end
|
|
145
118
|
|
|
146
119
|
class Filesystem < Provider
|
|
147
|
-
extend T::Sig
|
|
148
|
-
|
|
149
|
-
sig { returns(String) }
|
|
150
120
|
def read
|
|
151
121
|
File.read(config.fetch(:path))
|
|
152
122
|
end
|
|
153
123
|
|
|
154
|
-
|
|
155
|
-
def write string
|
|
124
|
+
def write(string)
|
|
156
125
|
File.write(config.fetch(:path), string)
|
|
157
126
|
nil
|
|
158
127
|
end
|
|
159
128
|
end
|
|
160
129
|
end
|
|
161
|
-
|
data/lib/zonesync/record.rb
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
#
|
|
2
|
-
require "sorbet-runtime"
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
module Zonesync
|
|
5
4
|
Record = Struct.new(:name, :type, :ttl, :rdata, :comment, keyword_init: true) do
|
|
6
|
-
|
|
5
|
+
# Make Record immutable by removing setters
|
|
6
|
+
undef_method :name=, :type=, :ttl=, :rdata=, :comment=, :[]=
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
def self.from_dns_zonefile_record record
|
|
8
|
+
def self.from_dns_zonefile_record(record)
|
|
10
9
|
new(
|
|
11
10
|
name: record.host,
|
|
12
11
|
type: record.type,
|
|
@@ -16,50 +15,42 @@ module Zonesync
|
|
|
16
15
|
)
|
|
17
16
|
end
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
def short_name origin
|
|
18
|
+
def short_name(origin)
|
|
21
19
|
ret = name.sub(origin, "")
|
|
22
20
|
ret = ret.sub(/\.$/, "")
|
|
23
21
|
ret = "@" if ret == ""
|
|
24
22
|
ret
|
|
25
23
|
end
|
|
26
24
|
|
|
27
|
-
sig { returns(T::Boolean) }
|
|
28
25
|
def manifest?
|
|
29
26
|
type == "TXT" &&
|
|
30
27
|
name.match?(/^zonesync_manifest\./)
|
|
31
28
|
end
|
|
32
29
|
|
|
33
|
-
sig { returns(T::Boolean) }
|
|
34
30
|
def checksum?
|
|
35
31
|
type == "TXT" &&
|
|
36
32
|
name.match?(/^zonesync_checksum\./)
|
|
37
33
|
end
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
def <=> other
|
|
35
|
+
def <=>(other)
|
|
41
36
|
to_sortable <=> other.to_sortable
|
|
42
37
|
end
|
|
43
38
|
|
|
44
|
-
sig { returns([Integer, String, String, String, Integer]) }
|
|
45
39
|
def to_sortable
|
|
46
40
|
is_soa = type == "SOA" ? 0 : 1
|
|
47
41
|
[is_soa, type, name, rdata, ttl.to_i]
|
|
48
42
|
end
|
|
49
43
|
|
|
50
|
-
sig { returns(String) }
|
|
51
44
|
def to_s
|
|
52
45
|
string = [name, ttl, type, rdata].join(" ")
|
|
53
46
|
string << " ; #{comment}" if comment
|
|
54
47
|
string
|
|
55
48
|
end
|
|
56
49
|
|
|
57
|
-
sig { params(other: Record).returns(T::Boolean) }
|
|
58
50
|
def identical_to?(other)
|
|
59
51
|
name == other.name && type == other.type && ttl == other.ttl && rdata == other.rdata
|
|
60
52
|
end
|
|
61
53
|
|
|
62
|
-
sig { params(other: Record).returns(T::Boolean) }
|
|
63
54
|
def conflicts_with?(other)
|
|
64
55
|
return false unless name == other.name && type == other.type
|
|
65
56
|
|
|
@@ -75,15 +66,12 @@ module Zonesync
|
|
|
75
66
|
end
|
|
76
67
|
end
|
|
77
68
|
|
|
78
|
-
sig { params(type: String).returns(T::Boolean) }
|
|
79
69
|
def self.single_record_per_name?(type)
|
|
80
70
|
type == "CNAME" || type == "SOA"
|
|
81
71
|
end
|
|
82
72
|
|
|
83
|
-
sig { params(records: T::Array[Record]).returns(T::Array[Record]) }
|
|
84
73
|
def self.non_meta(records)
|
|
85
74
|
records.reject { |r| r.manifest? || r.checksum? }
|
|
86
75
|
end
|
|
87
76
|
end
|
|
88
77
|
end
|
|
89
|
-
|
data/lib/zonesync/record_hash.rb
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
3
|
require "zlib"
|
|
4
4
|
|
|
5
5
|
module Zonesync
|
|
6
6
|
module RecordHash
|
|
7
|
-
extend T::Sig
|
|
8
|
-
|
|
9
|
-
sig { params(record: Record).returns(String) }
|
|
10
7
|
def self.generate(record)
|
|
11
8
|
identity = "#{record.name}:#{record.type}:#{record.ttl}:#{record.rdata}"
|
|
12
9
|
Zlib.crc32(identity).to_s(36)
|
|
13
10
|
end
|
|
14
11
|
end
|
|
15
|
-
end
|
|
12
|
+
end
|
data/lib/zonesync/route53.rb
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
#
|
|
2
|
-
require "sorbet-runtime"
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
require "zonesync/record"
|
|
5
4
|
require "zonesync/http"
|
|
@@ -8,9 +7,7 @@ require "erb"
|
|
|
8
7
|
|
|
9
8
|
module Zonesync
|
|
10
9
|
class Route53 < Provider
|
|
11
|
-
sig { returns(String) }
|
|
12
10
|
def read
|
|
13
|
-
@read = T.let(@read, T.nilable(String))
|
|
14
11
|
@read ||= begin
|
|
15
12
|
doc = REXML::Document.new(http.get(""))
|
|
16
13
|
records = doc.elements.collect("*/ResourceRecordSets/ResourceRecordSet") do |record_set|
|
|
@@ -20,7 +17,6 @@ module Zonesync
|
|
|
20
17
|
end
|
|
21
18
|
end
|
|
22
19
|
|
|
23
|
-
sig { params(record: Record).void }
|
|
24
20
|
def remove(record)
|
|
25
21
|
if record.type == "TXT"
|
|
26
22
|
# Route53 requires all TXT records with the same name to be managed together
|
|
@@ -76,13 +72,11 @@ module Zonesync
|
|
|
76
72
|
end
|
|
77
73
|
end
|
|
78
74
|
|
|
79
|
-
sig { params(old_record: Record, new_record: Record).void }
|
|
80
75
|
def change(old_record, new_record)
|
|
81
76
|
remove(old_record)
|
|
82
77
|
add(new_record)
|
|
83
78
|
end
|
|
84
79
|
|
|
85
|
-
sig { params(record: Record).void }
|
|
86
80
|
def add(record)
|
|
87
81
|
add_with_duplicate_handling(record) do
|
|
88
82
|
begin
|
|
@@ -113,7 +107,6 @@ module Zonesync
|
|
|
113
107
|
|
|
114
108
|
private
|
|
115
109
|
|
|
116
|
-
sig { params(action: String, record: Record).void }
|
|
117
110
|
def change_record(action, record)
|
|
118
111
|
http.post("", <<~XML)
|
|
119
112
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -139,7 +132,6 @@ module Zonesync
|
|
|
139
132
|
XML
|
|
140
133
|
end
|
|
141
134
|
|
|
142
|
-
sig { params(action: String, records_list: T::Array[Record]).void }
|
|
143
135
|
def change_records(action, records_list)
|
|
144
136
|
# Group records by name and type to handle multiple values
|
|
145
137
|
grouped = records_list.group_by { |r| [r.name, r.type, r.ttl] }
|
|
@@ -172,7 +164,6 @@ module Zonesync
|
|
|
172
164
|
XML
|
|
173
165
|
end
|
|
174
166
|
|
|
175
|
-
sig { params(el: REXML::Element).returns(T::Array[Record]) }
|
|
176
167
|
def to_records(el)
|
|
177
168
|
el.elements.collect("ResourceRecords/ResourceRecord") do |rr|
|
|
178
169
|
name = normalize_trailing_period(get_value(el, "Name"))
|
|
@@ -181,38 +172,34 @@ module Zonesync
|
|
|
181
172
|
rdata = get_value(rr, "Value")
|
|
182
173
|
|
|
183
174
|
record = Record.new(
|
|
184
|
-
name
|
|
185
|
-
type
|
|
186
|
-
ttl
|
|
187
|
-
rdata
|
|
175
|
+
name: name,
|
|
176
|
+
type: type,
|
|
177
|
+
ttl: ttl,
|
|
178
|
+
rdata: rdata,
|
|
188
179
|
comment: nil, # Route 53 does not have a direct comment field
|
|
189
180
|
)
|
|
190
181
|
end
|
|
191
182
|
end
|
|
192
183
|
|
|
193
|
-
|
|
194
|
-
def get_value el, field
|
|
184
|
+
def get_value(el, field)
|
|
195
185
|
el.elements[field].text.gsub(/\\(\d{3})/) { $1.to_i(8).chr } # unescape octal
|
|
196
186
|
end
|
|
197
187
|
|
|
198
|
-
sig { params(value: String).returns(String) }
|
|
199
188
|
def normalize_trailing_period(value)
|
|
200
189
|
value =~ /\.$/ ? value : value + "."
|
|
201
190
|
end
|
|
202
191
|
|
|
203
|
-
sig { returns(HTTP) }
|
|
204
192
|
def http
|
|
205
193
|
return @http if @http
|
|
206
|
-
@http =
|
|
207
|
-
|
|
194
|
+
@http = HTTP.new("https://route53.amazonaws.com/2013-04-01/hostedzone/#{config.fetch(:hosted_zone_id)}/rrset")
|
|
195
|
+
@http.before_request do |request, uri, body|
|
|
208
196
|
request["Content-Type"] = "application/xml"
|
|
209
197
|
request["X-Amz-Date"] = Time.now.utc.strftime("%Y%m%dT%H%M%SZ")
|
|
210
198
|
request["Authorization"] = sign_request(request.method, uri, body)
|
|
211
199
|
end
|
|
212
|
-
|
|
200
|
+
@http
|
|
213
201
|
end
|
|
214
202
|
|
|
215
|
-
sig { params(method: String, uri: URI::HTTPS, body: T.nilable(String)).returns(String) }
|
|
216
203
|
def sign_request(method, uri, body)
|
|
217
204
|
service = "route53"
|
|
218
205
|
date = Time.now.utc.strftime("%Y%m%d")
|
|
@@ -246,7 +233,6 @@ module Zonesync
|
|
|
246
233
|
"#{algorithm} Credential=#{config.fetch(:aws_access_key_id)}/#{credential_scope}, SignedHeaders=#{signed_headers}, Signature=#{signature}"
|
|
247
234
|
end
|
|
248
235
|
|
|
249
|
-
sig { params(key: String, date_stamp: String, region_name: String, service_name: String).returns(String) }
|
|
250
236
|
def get_signature_key(key, date_stamp, region_name, service_name)
|
|
251
237
|
k_date = OpenSSL::HMAC.digest("SHA256", "AWS4" + key, date_stamp)
|
|
252
238
|
k_region = OpenSSL::HMAC.digest("SHA256", k_date, region_name)
|
|
@@ -255,4 +241,3 @@ module Zonesync
|
|
|
255
241
|
end
|
|
256
242
|
end
|
|
257
243
|
end
|
|
258
|
-
|
data/lib/zonesync/sync.rb
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
#
|
|
2
|
-
require "sorbet-runtime"
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
require "zonesync/logger"
|
|
5
4
|
|
|
6
5
|
module Zonesync
|
|
7
6
|
Sync = Struct.new(:source, :destination) do
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
sig { params(dry_run: T::Boolean, force: T::Boolean).void }
|
|
11
|
-
def call dry_run: false, force: false
|
|
7
|
+
def call(dry_run: false, force: false)
|
|
12
8
|
operations = destination.diff!(source, force: force)
|
|
13
9
|
|
|
14
10
|
smanifest = source.manifest.generate
|
data/lib/zonesync/validator.rb
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
3
|
require "zonesync/record_hash"
|
|
4
4
|
|
|
5
5
|
module Zonesync
|
|
6
6
|
Validator = Struct.new(:operations, :destination, :source) do
|
|
7
|
-
extend T::Sig
|
|
8
|
-
|
|
9
|
-
sig { params(operations: T::Array[Operation], destination: Provider, source: T.nilable(Provider), force: T::Boolean).void }
|
|
10
7
|
def self.call(operations, destination, source = nil, force: false)
|
|
11
8
|
new(operations, destination, source).call(force: force)
|
|
12
9
|
end
|
|
13
10
|
|
|
14
|
-
sig { params(force: T::Boolean).void }
|
|
15
11
|
def call(force: false)
|
|
16
12
|
validation_error = ValidationError.new
|
|
17
13
|
|
|
@@ -44,14 +40,12 @@ module Zonesync
|
|
|
44
40
|
|
|
45
41
|
private
|
|
46
42
|
|
|
47
|
-
sig { returns(Manifest) }
|
|
48
43
|
def manifest
|
|
49
44
|
destination.manifest
|
|
50
45
|
end
|
|
51
46
|
|
|
52
|
-
sig { returns(T.nilable(ChecksumMismatchError)) }
|
|
53
47
|
def validate_v2_manifest_integrity
|
|
54
|
-
manifest_data =
|
|
48
|
+
manifest_data = manifest.existing.rdata[1..-2]
|
|
55
49
|
expected_hashes = manifest_data.split(",")
|
|
56
50
|
actual_records = Record.non_meta(destination.records)
|
|
57
51
|
actual_hash_to_record = actual_records.map { |r| [RecordHash.generate(r), r] }.to_h
|
|
@@ -69,7 +63,6 @@ module Zonesync
|
|
|
69
63
|
)
|
|
70
64
|
end
|
|
71
65
|
|
|
72
|
-
sig { params(missing_hash: String).returns(T.nilable(Record)) }
|
|
73
66
|
def find_expected_record(missing_hash)
|
|
74
67
|
return nil unless source
|
|
75
68
|
|
|
@@ -77,7 +70,6 @@ module Zonesync
|
|
|
77
70
|
source_records.find { |r| RecordHash.generate(r) == missing_hash }
|
|
78
71
|
end
|
|
79
72
|
|
|
80
|
-
sig { params(expected_record: T.nilable(Record), actual_records: T::Array[Record]).returns(T.nilable(Record)) }
|
|
81
73
|
def find_modified_record(expected_record, actual_records)
|
|
82
74
|
return nil unless expected_record
|
|
83
75
|
|
|
@@ -96,8 +88,7 @@ module Zonesync
|
|
|
96
88
|
end
|
|
97
89
|
end
|
|
98
90
|
|
|
99
|
-
|
|
100
|
-
def validate_addition record, force: false
|
|
91
|
+
def validate_addition(record, force: false)
|
|
101
92
|
return nil if manifest.matches?(record)
|
|
102
93
|
return nil if force
|
|
103
94
|
|
|
@@ -115,7 +106,6 @@ module Zonesync
|
|
|
115
106
|
[conflicting_record, record]
|
|
116
107
|
end
|
|
117
108
|
|
|
118
|
-
sig { params(record: Record, expected_hashes: T::Array[String]).returns(T.nilable(Record)) }
|
|
119
109
|
def find_v2_conflict(record, expected_hashes)
|
|
120
110
|
destination.records.find do |r|
|
|
121
111
|
next if r.manifest? || r.checksum?
|
|
@@ -126,7 +116,6 @@ module Zonesync
|
|
|
126
116
|
end
|
|
127
117
|
end
|
|
128
118
|
|
|
129
|
-
sig { params(record: Record).returns(T.nilable(Record)) }
|
|
130
119
|
def find_v1_conflict(record)
|
|
131
120
|
shorthand = manifest.shorthand_for(record, with_type: true)
|
|
132
121
|
destination.records.find do |r|
|
|
@@ -134,7 +123,6 @@ module Zonesync
|
|
|
134
123
|
end
|
|
135
124
|
end
|
|
136
125
|
|
|
137
|
-
sig { params(record: Record).returns(T.nilable(Record)) }
|
|
138
126
|
def find_unmanaged_conflict(record)
|
|
139
127
|
destination.records.find do |r|
|
|
140
128
|
r.identical_to?(record)
|
|
@@ -142,4 +130,3 @@ module Zonesync
|
|
|
142
130
|
end
|
|
143
131
|
end
|
|
144
132
|
end
|
|
145
|
-
|
data/lib/zonesync/version.rb
CHANGED
data/lib/zonesync/zonefile.rb
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
#
|
|
2
|
-
require "sorbet-runtime"
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
require "zonesync/parser"
|
|
5
4
|
|
|
6
5
|
module Zonesync
|
|
7
6
|
class Zonefile
|
|
8
|
-
extend T::Sig
|
|
9
|
-
|
|
10
|
-
sig { params(zone_string: String).returns(Zonefile) }
|
|
11
7
|
def self.load(zone_string)
|
|
12
8
|
if zone_string !~ /\sSOA\s/ # insert dummy SOA to trick parser if needed
|
|
13
9
|
zone_string.sub!(/\n([^$])/, "\n@ 1 SOA example.com example.com ( 2000010101 1 1 1 1 )\n\\1")
|
|
@@ -19,16 +15,12 @@ module Zonesync
|
|
|
19
15
|
new(records, origin: zone.origin)
|
|
20
16
|
end
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
def initialize records, origin:
|
|
18
|
+
def initialize(records, origin:)
|
|
24
19
|
@records = records
|
|
25
20
|
@origin = origin
|
|
26
21
|
end
|
|
27
22
|
|
|
28
|
-
sig { returns(T::Array[Zonesync::Record]) }
|
|
29
23
|
attr_reader :records
|
|
30
|
-
|
|
31
|
-
sig { returns(String) }
|
|
32
24
|
attr_reader :origin
|
|
33
25
|
end
|
|
34
26
|
end
|
data/lib/zonesync.rb
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
#
|
|
2
|
-
require "sorbet-runtime"
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
require "zonesync/sync"
|
|
5
4
|
require "zonesync/generate"
|
|
@@ -16,29 +15,21 @@ begin # optional active_support dependency
|
|
|
16
15
|
rescue LoadError; end
|
|
17
16
|
|
|
18
17
|
module Zonesync
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
sig { params(source: T.nilable(String), destination: T.nilable(String), dry_run: T::Boolean, force: T::Boolean).void }
|
|
22
|
-
def self.call source: "Zonefile", destination: "zonesync", dry_run: false, force: false
|
|
23
|
-
source = T.must(source)
|
|
24
|
-
destination = T.must(destination).to_sym
|
|
18
|
+
def self.call(source: "Zonefile", destination: "zonesync", dry_run: false, force: false)
|
|
25
19
|
Sync.new(
|
|
26
20
|
Provider.from({ provider: "Filesystem", path: source }),
|
|
27
|
-
Provider.from(credentials(destination)),
|
|
21
|
+
Provider.from(credentials(destination.to_sym)),
|
|
28
22
|
).call(dry_run: dry_run, force: force)
|
|
29
23
|
end
|
|
30
24
|
|
|
31
|
-
|
|
32
|
-
def self.generate source: "zonesync", destination: "Zonefile"
|
|
33
|
-
source = T.must(source).to_sym
|
|
25
|
+
def self.generate(source: "zonesync", destination: "Zonefile")
|
|
34
26
|
Generate.new(
|
|
35
|
-
Provider.from(credentials(source)),
|
|
36
|
-
Provider.from({ provider: "Filesystem", path:
|
|
27
|
+
Provider.from(credentials(source.to_sym)),
|
|
28
|
+
Provider.from({ provider: "Filesystem", path: destination }),
|
|
37
29
|
).call
|
|
38
30
|
end
|
|
39
31
|
|
|
40
|
-
|
|
41
|
-
def self.credentials key
|
|
32
|
+
def self.credentials(key)
|
|
42
33
|
ActiveSupport::EncryptedConfiguration.new(
|
|
43
34
|
config_path: "config/credentials.yml.enc",
|
|
44
35
|
key_path: "config/master.key",
|
|
@@ -47,4 +38,3 @@ module Zonesync
|
|
|
47
38
|
).config[key]
|
|
48
39
|
end
|
|
49
40
|
end
|
|
50
|
-
|
data/zonesync.gemspec
CHANGED
|
@@ -29,11 +29,8 @@ Gem::Specification.new do |spec|
|
|
|
29
29
|
|
|
30
30
|
spec.add_dependency "thor", "~>1.0"
|
|
31
31
|
spec.add_dependency "treetop", "~>1.6"
|
|
32
|
-
spec.add_dependency "sorbet-runtime"
|
|
33
32
|
|
|
34
33
|
spec.add_development_dependency "rake"
|
|
35
34
|
spec.add_development_dependency "rspec"
|
|
36
35
|
spec.add_development_dependency "webmock"
|
|
37
|
-
spec.add_development_dependency "sorbet"
|
|
38
|
-
spec.add_development_dependency "tapioca"
|
|
39
36
|
end
|