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