record_store 6.4.1 → 6.5.3
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/.github/workflows/ci.yml +28 -0
- data/CHANGELOG.md +20 -0
- data/lib/record_store/cli.rb +1 -1
- data/lib/record_store/version.rb +1 -1
- data/lib/record_store/zone/config/implicit_record_template.rb +19 -7
- data/lib/record_store/zone/yaml_definitions.rb +4 -1
- data/lib/record_store/zone.rb +59 -2
- data/lib/record_store.rb +1 -0
- data/template/templates/implicit_records/implicit_example.yml.erb +2 -0
- data/template/zones/dynect.example.com.yml +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9af43560e4a84302824fb5b1abef5476bc0995e752d3960de068b45949b1d154
|
|
4
|
+
data.tar.gz: 4bc999da542a5484d7cf51a7bb652593c323fecac6d79d8e73ccbb62bd638ea0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d72d0a1bffea56a12070eab25df96cd120f00107779063ae651a141310b369764eeaa5fa8993d77c67f81e2f4e98e16641e6d8dacde4f35fa09be4a6fe08236e
|
|
7
|
+
data.tar.gz: e84f52373f10caa1fa464845a13a33383c7461392f69c88c17c032f9d91e8b96ffcd87fc9f1fc798478b05b2de2660629e2d6d4b89e232b405d80009676eac5b
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
env:
|
|
6
|
+
SRB_SKIP_GEM_RBIS: true
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
ruby: [ 2.7.1 ]
|
|
15
|
+
name: Test Ruby ${{ matrix.ruby }}
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v2
|
|
18
|
+
- name: Set up Ruby
|
|
19
|
+
uses: ruby/setup-ruby@v1
|
|
20
|
+
with:
|
|
21
|
+
ruby-version: ${{ matrix.ruby }}
|
|
22
|
+
bundler-cache: true
|
|
23
|
+
- name: rubocop
|
|
24
|
+
run: bin/rubocop --version && bin/rubocop
|
|
25
|
+
- name: setup
|
|
26
|
+
run: bin/setup
|
|
27
|
+
- name: test
|
|
28
|
+
run: bundle exec rake test
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 6.5.3
|
|
4
|
+
- Adds check for detecting shadowed records, used when a record being added to a zone in record-store will have no effect because it is shadowed by another record.
|
|
5
|
+
|
|
6
|
+
## 6.5.2
|
|
7
|
+
- Ensure filters for implicit_records, `except_record` and `conflict_with`, are truly optional [BUGFIX]
|
|
8
|
+
|
|
9
|
+
## 6.5.1
|
|
10
|
+
|
|
11
|
+
Add support for a new parameter in the implicit records templates:
|
|
12
|
+
|
|
13
|
+
- `except_record`: the template will *NOT* generate the `injected_records` for records matching `except_record`, even if they matched `each_record`.
|
|
14
|
+
|
|
15
|
+
Also added support for regular expressions in the matching, using the already available `!ruby/regexp` text in a YAML value. The object loaded from the YAML
|
|
16
|
+
will be of `Regexp` type, and will thus be used to _match_ the value, instead of being identical only. This is supported in both `except_record` and
|
|
17
|
+
`each_record` fields.
|
|
18
|
+
|
|
19
|
+
## 6.5.0
|
|
20
|
+
|
|
21
|
+
...
|
|
22
|
+
|
|
3
23
|
## 6.4.0
|
|
4
24
|
|
|
5
25
|
Add support for injecting implicit records into a zone based on a pre-configured template. Brief overview of template keys:
|
data/lib/record_store/cli.rb
CHANGED
data/lib/record_store/version.rb
CHANGED
|
@@ -26,9 +26,14 @@ module RecordStore
|
|
|
26
26
|
def from_file(filename:)
|
|
27
27
|
filepath = template_filepath_for(filename: filename)
|
|
28
28
|
template_file = File.read(filepath)
|
|
29
|
-
filters_for_records_to_template = YAML.load(template_file).deep_symbolize_keys[:each_record]
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
template_file_yaml = YAML.load(template_file).deep_symbolize_keys
|
|
31
|
+
filters_for_records_to_template = template_file_yaml[:each_record]
|
|
32
|
+
filters_for_records_to_exclude = template_file_yaml[:except_record] || []
|
|
33
|
+
|
|
34
|
+
new(template: ERB.new(template_file),
|
|
35
|
+
filters_for_records_to_template: filters_for_records_to_template,
|
|
36
|
+
filters_for_records_to_exclude: filters_for_records_to_exclude)
|
|
32
37
|
end
|
|
33
38
|
|
|
34
39
|
private
|
|
@@ -38,9 +43,10 @@ module RecordStore
|
|
|
38
43
|
end
|
|
39
44
|
end
|
|
40
45
|
|
|
41
|
-
def initialize(template:, filters_for_records_to_template:)
|
|
46
|
+
def initialize(template:, filters_for_records_to_template:, filters_for_records_to_exclude:)
|
|
42
47
|
@template = template
|
|
43
48
|
@filters_for_records_to_template = filters_for_records_to_template
|
|
49
|
+
@filters_for_records_to_exclude = filters_for_records_to_exclude
|
|
44
50
|
end
|
|
45
51
|
|
|
46
52
|
def generate_records_to_inject(current_records:)
|
|
@@ -61,23 +67,29 @@ module RecordStore
|
|
|
61
67
|
|
|
62
68
|
private
|
|
63
69
|
|
|
64
|
-
attr_reader :template, :filters_for_records_to_template
|
|
70
|
+
attr_reader :template, :filters_for_records_to_template, :filters_for_records_to_exclude
|
|
65
71
|
|
|
66
72
|
def should_inject?(template_records:, current_records:)
|
|
73
|
+
conflict_with = template_records[:conflict_with] || []
|
|
67
74
|
current_records.none? do |record|
|
|
68
|
-
|
|
75
|
+
conflict_with.any? do |filter|
|
|
69
76
|
record_match?(record: record, filter: filter)
|
|
70
77
|
end
|
|
71
78
|
end
|
|
72
79
|
end
|
|
73
80
|
|
|
74
81
|
def should_template?(record:)
|
|
75
|
-
filters_for_records_to_template.any? { |filter| record_match?(record: record, filter: filter) }
|
|
82
|
+
filters_for_records_to_template.any? { |filter| record_match?(record: record, filter: filter) } && \
|
|
83
|
+
filters_for_records_to_exclude.none? { |filter| record_match?(record: record, filter: filter) }
|
|
76
84
|
end
|
|
77
85
|
|
|
78
86
|
def record_match?(record:, filter:)
|
|
79
87
|
filter.all? do |key, value|
|
|
80
|
-
|
|
88
|
+
if value.is_a?(Regexp)
|
|
89
|
+
value.match(record.public_send(key))
|
|
90
|
+
else
|
|
91
|
+
record.public_send(key) == value
|
|
92
|
+
end
|
|
81
93
|
end
|
|
82
94
|
end
|
|
83
95
|
|
|
@@ -66,7 +66,10 @@ module RecordStore
|
|
|
66
66
|
Dir["#{dir}/#{name}/*__*.yml"].each do |record_file|
|
|
67
67
|
definition['records'] += load_yml_record_definitions(name, record_file)
|
|
68
68
|
end
|
|
69
|
-
|
|
69
|
+
|
|
70
|
+
asts = { filename => Psych.parse_file(filename) }
|
|
71
|
+
|
|
72
|
+
Zone.new(name: name, records: definition['records'], config: definition['config'], abstract_syntax_trees: asts)
|
|
70
73
|
end
|
|
71
74
|
|
|
72
75
|
def load_yml_record_definitions(name, record_file)
|
data/lib/record_store/zone.rb
CHANGED
|
@@ -20,6 +20,8 @@ module RecordStore
|
|
|
20
20
|
validate :validate_provider_can_handle_zone_records
|
|
21
21
|
validate :validate_no_empty_non_terminal
|
|
22
22
|
validate :validate_can_handle_alias_records
|
|
23
|
+
validate :validate_no_duplicate_keys
|
|
24
|
+
validate :validate_zone_record_not_shadowed
|
|
23
25
|
|
|
24
26
|
class << self
|
|
25
27
|
def download(name, provider_name, **write_options)
|
|
@@ -61,7 +63,12 @@ module RecordStore
|
|
|
61
63
|
current_zone = nil
|
|
62
64
|
while zones.any?
|
|
63
65
|
mutex.synchronize { current_zone = zones.shift }
|
|
64
|
-
|
|
66
|
+
break if current_zone.nil? # account for the race between `zones.any?` and `zones.shift`
|
|
67
|
+
|
|
68
|
+
# `unchanged?` is deliberately outside locked context since it's a bit CPU/time heavy
|
|
69
|
+
unless current_zone.unchanged?
|
|
70
|
+
mutex.synchronize { modified_zones << current_zone }
|
|
71
|
+
end
|
|
65
72
|
end
|
|
66
73
|
end
|
|
67
74
|
end.each(&:join)
|
|
@@ -70,10 +77,11 @@ module RecordStore
|
|
|
70
77
|
end
|
|
71
78
|
end
|
|
72
79
|
|
|
73
|
-
def initialize(name:, records: [], config: {})
|
|
80
|
+
def initialize(name:, records: [], config: {}, abstract_syntax_trees: {})
|
|
74
81
|
@name = Record.ensure_ends_with_dot(name)
|
|
75
82
|
@config = RecordStore::Zone::Config.new(config.deep_symbolize_keys)
|
|
76
83
|
@records = build_records(records)
|
|
84
|
+
@abstract_syntax_trees = abstract_syntax_trees
|
|
77
85
|
end
|
|
78
86
|
|
|
79
87
|
def build_changesets(all: false)
|
|
@@ -270,6 +278,26 @@ module RecordStore
|
|
|
270
278
|
end
|
|
271
279
|
end
|
|
272
280
|
|
|
281
|
+
def validate_zone_record_not_shadowed
|
|
282
|
+
nameserver_fqdns = records
|
|
283
|
+
.select { |record| record.is_a?(Record::NS) && name != record.fqdn }
|
|
284
|
+
.map { |record| record.fqdn.delete_suffix(".") }
|
|
285
|
+
.uniq
|
|
286
|
+
|
|
287
|
+
nameserver_fqdns.each do |ns_record|
|
|
288
|
+
selected_records = records.reject do |record|
|
|
289
|
+
record.is_a?(Record::NS) && \
|
|
290
|
+
record.fqdn.delete_suffix(".") == ns_record
|
|
291
|
+
end
|
|
292
|
+
selected_records.each do |record|
|
|
293
|
+
normalized_record = record.fqdn.delete_suffix(".")
|
|
294
|
+
next unless normalized_record.end_with?(".#{ns_record}") || normalized_record == ns_record
|
|
295
|
+
errors.add(:records, "Record #{record.fqdn} #{record.type} in Zone #{name} " \
|
|
296
|
+
"is shadowed by #{ns_record} and will be ignored")
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
273
301
|
def validate_no_empty_non_terminal
|
|
274
302
|
return unless config.empty_non_terminal_over_wildcard?
|
|
275
303
|
|
|
@@ -307,5 +335,34 @@ module RecordStore
|
|
|
307
335
|
|
|
308
336
|
errors.add(:records, "ALIAS record should be defined on the root of the zone: #{alias_record}")
|
|
309
337
|
end
|
|
338
|
+
|
|
339
|
+
def validate_no_duplicate_keys
|
|
340
|
+
@abstract_syntax_trees.each do |filename, ast|
|
|
341
|
+
validate_no_duplicate_keys_in_node(filename, ast)
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def validate_no_duplicate_keys_in_node(filename, node)
|
|
346
|
+
if node.mapping?
|
|
347
|
+
keys = node
|
|
348
|
+
.children
|
|
349
|
+
.each_slice(2)
|
|
350
|
+
.map(&:first)
|
|
351
|
+
.map(&:value)
|
|
352
|
+
.sort
|
|
353
|
+
dup_keys = keys
|
|
354
|
+
.find_all { |k| keys.count(k) > 1 }
|
|
355
|
+
.uniq
|
|
356
|
+
unless dup_keys.empty?
|
|
357
|
+
location = "#{File.basename(filename)}:#{node.start_line}"
|
|
358
|
+
description = "multiple definitions for keys #{dup_keys}"
|
|
359
|
+
errors.add(:records, "#{location}: #{description}")
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
node.children&.each do |child|
|
|
364
|
+
validate_no_duplicate_keys_in_node(filename, child)
|
|
365
|
+
end
|
|
366
|
+
end
|
|
310
367
|
end
|
|
311
368
|
end
|
data/lib/record_store.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: record_store
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 6.
|
|
4
|
+
version: 6.5.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Willem van Bergen
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2021-09-23 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: thor
|
|
@@ -335,6 +335,7 @@ executables:
|
|
|
335
335
|
extensions: []
|
|
336
336
|
extra_rdoc_files: []
|
|
337
337
|
files:
|
|
338
|
+
- ".github/workflows/ci.yml"
|
|
338
339
|
- ".gitignore"
|
|
339
340
|
- ".rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml"
|
|
340
341
|
- ".rubocop.yml"
|
|
@@ -416,7 +417,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
416
417
|
- !ruby/object:Gem::Version
|
|
417
418
|
version: '0'
|
|
418
419
|
requirements: []
|
|
419
|
-
rubygems_version: 3.
|
|
420
|
+
rubygems_version: 3.2.20
|
|
420
421
|
signing_key:
|
|
421
422
|
specification_version: 4
|
|
422
423
|
summary: Manage DNS using git
|