record_store 1.0.0 → 2.0.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/.gitignore +1 -0
- data/README.md +2 -1
- data/bin/console +10 -0
- data/dev.yml +10 -0
- data/lib/record_store/cli.rb +19 -1
- data/lib/record_store/version.rb +1 -1
- data/lib/record_store/zone/config.rb +7 -0
- data/lib/record_store/zone.rb +70 -14
- data/lib/record_store.rb +1 -0
- data/template/zones/dnsimple.example.com/A__a-record.yml +2 -0
- data/template/zones/dnsimple.example.com/TXT__marco.yml +2 -0
- data/template/zones/dnsimple.example.com/TXT__ping.yml +2 -0
- data/template/zones/dnsimple.example.com.yml +0 -31
- data/template/zones/dynect.example.com.yml +13 -31
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e96a230fae8c5a5a73e2c51a2097c5b14094f82d
|
4
|
+
data.tar.gz: 55c913c54e64b78454624b0844ebf1e3ca74fa36
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 512431b9f45815ee10a9b8b10228660b944b862a99be5e1ecb42705cab33fd069f8787971881e0a905722ddf20b127c121c091da61af93b93d1ee568b48f66f9
|
7
|
+
data.tar.gz: d0f22e77f0a914a0c3ee11cef077cc8bd1e31eb4ce04acc8e351e01c43cff7218831e9621350ab631bcd9e62f9c943e2b690c1f275242be05479567ca36c5b75
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -80,10 +80,11 @@ When running `bin/record-store apply`, a `Changeset` is generated by comparing t
|
|
80
80
|
# Development
|
81
81
|
|
82
82
|
To get started developing on Record Store, run `bin/setup`. This will create a development directory, `dev/`, that mimics what a production directory managing DNS records using Record Store would look like. Use it as a sandbox when developing Record Store.
|
83
|
+
You can use `bin/console` to get play with the dev data, or you can `cd` into `dev/` and use `bin/record-store` to test out the CLI.
|
83
84
|
|
84
85
|
### Adding new Providers
|
85
86
|
|
86
|
-
To add a new Provider, create a class
|
87
|
+
To add a new Provider, create a class inheriting `Provider` in [`lib/record_store/provider/`](lib/record_store/provider/). The [DynECT provider](lib/record_store/provider/dynect.rb) is good to use as a reference implementation.
|
87
88
|
|
88
89
|
**Note**: _there's no need to wrap `Provider#apply_changeset` unless it's necessary to do something before/after making changes to a zone._
|
89
90
|
|
data/bin/console
ADDED
data/dev.yml
ADDED
data/lib/record_store/cli.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module RecordStore
|
2
2
|
class CLI < Thor
|
3
3
|
class_option :config, desc: 'Path to config.yml', aliases: '-c'
|
4
|
+
FORMATS = %w[file directory]
|
4
5
|
|
5
6
|
def initialize(*args)
|
6
7
|
super
|
@@ -92,6 +93,7 @@ module RecordStore
|
|
92
93
|
|
93
94
|
option :name, desc: 'Zone to download', aliases: '-n', type: :string, required: true
|
94
95
|
option :provider, desc: 'Provider in which this zone exists', aliases: '-p', type: :string
|
96
|
+
option :format, desc: 'Format', aliases: '-f', type: :string, default: 'file', enum: FORMATS
|
95
97
|
desc 'download', 'Downloads all records from zone and creates YAML zone definition in zones/ e.g. record-store download --name=shopify.io'
|
96
98
|
def download
|
97
99
|
name = options.fetch('name')
|
@@ -107,10 +109,26 @@ module RecordStore
|
|
107
109
|
end
|
108
110
|
|
109
111
|
puts "Downloading records for #{name}"
|
110
|
-
Zone.download(name, provider)
|
112
|
+
Zone.download(name, provider, format: options.fetch('format').to_sym)
|
111
113
|
puts "Records have been downloaded & can be found in zones/#{name}.yml"
|
112
114
|
end
|
113
115
|
|
116
|
+
option :name, desc: 'Zone to reformat', aliases: '-n', type: :string, required: false
|
117
|
+
option :format, desc: 'Format', aliases: '-f', type: :string, default: 'file', enum: FORMATS
|
118
|
+
desc 'reformat', 'Sorts and re-outputs the zone (or all zones) as specified format (file)'
|
119
|
+
def reformat
|
120
|
+
name = options['name']
|
121
|
+
zones = if name
|
122
|
+
[Zone.find(name)]
|
123
|
+
else
|
124
|
+
Zone.all
|
125
|
+
end
|
126
|
+
zones.each do |zone|
|
127
|
+
puts "Writing #{zone.name}"
|
128
|
+
zone.write(format: options.fetch('format').to_sym)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
114
132
|
option :name, desc: 'Zone to sort', aliases: '-n', type: :string, required: true
|
115
133
|
desc 'sort', 'Sorts the zonefile alphabetically e.g. record-store sort --name=shopify.io'
|
116
134
|
def sort
|
data/lib/record_store/version.rb
CHANGED
data/lib/record_store/zone.rb
CHANGED
@@ -8,7 +8,7 @@ module RecordStore
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def defined
|
11
|
-
@defined ||= yaml_files.inject({}) { |zones, file| zones.merge(
|
11
|
+
@defined ||= yaml_files.inject({}) { |zones, file| zones.merge(load_yml_zone_definition(file)) }
|
12
12
|
end
|
13
13
|
|
14
14
|
def [](name)
|
@@ -21,19 +21,77 @@ module RecordStore
|
|
21
21
|
|
22
22
|
def find(name)
|
23
23
|
return unless File.exists?(zone_path = "#{RecordStore.zones_path}/#{name}.yml")
|
24
|
-
|
24
|
+
load_yml_zone_definition(zone_path).first.last
|
25
|
+
end
|
26
|
+
|
27
|
+
def write(name, config:, records:, format: :file)
|
28
|
+
raise ArgumentError, "format must be :directory or :file" unless %i(file directory).include?(format)
|
29
|
+
name = name.chomp('.')
|
30
|
+
zone_file = "#{RecordStore.zones_path}/#{name}.yml"
|
31
|
+
zone = { name => { config: config.to_hash } }
|
32
|
+
records = records.map(&:to_hash).sort_by! {|r| [r.fetch(:fqdn), r.fetch(:type), r[:nsdname] || r[:address]]}
|
33
|
+
|
34
|
+
if format == :file
|
35
|
+
zone[name][:records] = records
|
36
|
+
write_yml_file(zone_file, zone.deep_stringify_keys)
|
37
|
+
remove_record_files(name)
|
38
|
+
else
|
39
|
+
write_yml_file(zone_file, zone.deep_stringify_keys)
|
40
|
+
remove_record_files(name)
|
41
|
+
write_record_files(name, records)
|
42
|
+
end
|
25
43
|
end
|
26
44
|
|
27
45
|
private
|
28
46
|
|
29
|
-
def
|
47
|
+
def write_yml_file(filename, data)
|
48
|
+
lines = data.to_yaml.lines
|
49
|
+
lines.shift if lines.first == "---\n"
|
50
|
+
File.write(filename, lines.join)
|
51
|
+
end
|
52
|
+
|
53
|
+
def load_yml_zone_definition(filename)
|
30
54
|
result = {}
|
55
|
+
dir = File.dirname(filename)
|
31
56
|
YAML.load_file(filename).each do |name, definition|
|
57
|
+
definition['records'] ||= []
|
58
|
+
Dir["#{dir}/#{name}/*__*.yml"].each do |record_file|
|
59
|
+
definition['records'] += load_yml_record_definitions(name, record_file)
|
60
|
+
end
|
32
61
|
result[name] = Zone.from_yaml_definition(name, definition)
|
33
62
|
end
|
34
63
|
result
|
35
64
|
end
|
36
65
|
|
66
|
+
def load_yml_record_definitions(name, record_file)
|
67
|
+
type, domain = File.basename(record_file, '.yml').split('__')
|
68
|
+
Array.wrap(YAML.load_file(record_file)).map do |record_definition|
|
69
|
+
record_definition.merge(fqdn: "#{domain}.#{name}", type: type)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def remove_record_files(name)
|
74
|
+
dir = "#{RecordStore.zones_path}/#{name}"
|
75
|
+
File.unlink(*Dir["#{dir}/*"])
|
76
|
+
Dir.unlink(dir)
|
77
|
+
rescue Errno::ENOENT
|
78
|
+
end
|
79
|
+
|
80
|
+
def write_record_files(name, records)
|
81
|
+
dir = "#{RecordStore.zones_path}/#{name}"
|
82
|
+
Dir.mkdir(dir)
|
83
|
+
records.group_by { |record| [record.fetch(:fqdn), record.fetch(:type)] }.each do |(fqdn, type), grouped_records|
|
84
|
+
grouped_records.each do |record|
|
85
|
+
record.delete(:fqdn)
|
86
|
+
record.delete(:type)
|
87
|
+
record.deep_stringify_keys!
|
88
|
+
end
|
89
|
+
grouped_records = grouped_records.first if grouped_records.size == 1
|
90
|
+
domain = fqdn.chomp('.').chomp(name).chomp('.')
|
91
|
+
write_yml_file("#{dir}/#{type}__#{domain}.yml", grouped_records)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
37
95
|
def yaml_files
|
38
96
|
Dir["#{RecordStore.zones_path}/*.yml"]
|
39
97
|
end
|
@@ -59,19 +117,13 @@ module RecordStore
|
|
59
117
|
new(name, definition.deep_symbolize_keys)
|
60
118
|
end
|
61
119
|
|
62
|
-
def self.download(name, provider_name)
|
120
|
+
def self.download(name, provider_name, **write_options)
|
63
121
|
dns = new(name, config: {provider: provider_name}).provider
|
64
122
|
current_records = dns.retrieve_current_records
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
provider: provider_name,
|
70
|
-
ignore_patterns: [{type: "NS", fqdn: "#{name}."}],
|
71
|
-
},
|
72
|
-
records: current_records.map(&:to_hash).sort_by! {|r| [r.fetch(:fqdn), r.fetch(:type), r[:nsdname] || r[:address]]}
|
73
|
-
}
|
74
|
-
}.deep_stringify_keys.to_yaml.gsub("---\n", ''))
|
123
|
+
write(name, records: current_records, config: {
|
124
|
+
provider: provider_name,
|
125
|
+
ignore_patterns: [{type: "NS", fqdn: "#{name}."}],
|
126
|
+
}, **write_options)
|
75
127
|
end
|
76
128
|
|
77
129
|
def initialize(name, records: [], config: {})
|
@@ -106,6 +158,10 @@ module RecordStore
|
|
106
158
|
Provider.const_get(config.provider).new(zone: name.gsub(/\.\z/, ''))
|
107
159
|
end
|
108
160
|
|
161
|
+
def write(**write_options)
|
162
|
+
self.class.write(name, config: config, records: records, **write_options)
|
163
|
+
end
|
164
|
+
|
109
165
|
private
|
110
166
|
|
111
167
|
def build_records(records)
|
data/lib/record_store.rb
CHANGED
@@ -2,36 +2,5 @@ dnsimple.example.com:
|
|
2
2
|
config:
|
3
3
|
provider: DNSimple
|
4
4
|
ignore_patterns:
|
5
|
-
- type: NS
|
6
|
-
fqdn: dnsimple.example.com.
|
7
|
-
|
8
|
-
records:
|
9
5
|
- type: NS
|
10
6
|
fqdn: dnsimple.example.com.
|
11
|
-
ttl: 3600
|
12
|
-
nsdname: ns1.dnsimple.com.
|
13
|
-
- type: NS
|
14
|
-
fqdn: dnsimple.example.com.
|
15
|
-
ttl: 3600
|
16
|
-
nsdname: ns2.dnsimple.com.
|
17
|
-
- type: NS
|
18
|
-
fqdn: dnsimple.example.com.
|
19
|
-
ttl: 3600
|
20
|
-
nsdname: ns3.dnsimple.com.
|
21
|
-
- type: NS
|
22
|
-
fqdn: dnsimple.example.com.
|
23
|
-
ttl: 3600
|
24
|
-
nsdname: ns4.dnsimple.com.
|
25
|
-
|
26
|
-
- type: A
|
27
|
-
fqdn: a-record.dnsimple.example.com.
|
28
|
-
address: 10.10.10.42
|
29
|
-
ttl: 86400
|
30
|
-
- type: TXT
|
31
|
-
fqdn: ping.dnsimple.example.com.
|
32
|
-
txtdata: pong
|
33
|
-
ttl: 300
|
34
|
-
- type: TXT
|
35
|
-
fqdn: marco.dnsimple.example.com.
|
36
|
-
txtdata: polo
|
37
|
-
ttl: 300
|
@@ -2,36 +2,18 @@ dynect.example.com:
|
|
2
2
|
config:
|
3
3
|
provider: DynECT
|
4
4
|
ignore_patterns:
|
5
|
-
- type: NS
|
6
|
-
fqdn: dynect.example.com.
|
7
|
-
|
8
|
-
records:
|
9
|
-
- type: NS
|
10
|
-
fqdn: dynect.example.com.
|
11
|
-
ttl: 86400
|
12
|
-
nsdname: ns1.p19.dynect.net.
|
13
|
-
- type: NS
|
14
|
-
fqdn: dynect.example.com.
|
15
|
-
ttl: 86400
|
16
|
-
nsdname: ns2.p19.dynect.net.
|
17
|
-
- type: NS
|
18
|
-
fqdn: dynect.example.com.
|
19
|
-
ttl: 86400
|
20
|
-
nsdname: ns3.p19.dynect.net.
|
21
5
|
- type: NS
|
22
6
|
fqdn: dynect.example.com.
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
txtdata: polo
|
37
|
-
ttl: 300
|
7
|
+
records:
|
8
|
+
- type: A
|
9
|
+
fqdn: a-record.dynect.example.com.
|
10
|
+
ttl: 86400
|
11
|
+
address: 10.10.10.42
|
12
|
+
- type: TXT
|
13
|
+
fqdn: marco.dynect.example.com.
|
14
|
+
ttl: 300
|
15
|
+
txtdata: polo
|
16
|
+
- type: TXT
|
17
|
+
fqdn: ping.dynect.example.com.
|
18
|
+
ttl: 300
|
19
|
+
txtdata: pong
|
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:
|
4
|
+
version: 2.0.0
|
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: 2016-
|
12
|
+
date: 2016-08-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: thor
|
@@ -209,10 +209,12 @@ files:
|
|
209
209
|
- LICENSE
|
210
210
|
- README.md
|
211
211
|
- Rakefile
|
212
|
+
- bin/console
|
212
213
|
- bin/record-store
|
213
214
|
- bin/setup
|
214
215
|
- bin/test
|
215
216
|
- circle.yml
|
217
|
+
- dev.yml
|
216
218
|
- lib/record_store.rb
|
217
219
|
- lib/record_store/changeset.rb
|
218
220
|
- lib/record_store/cli.rb
|
@@ -240,6 +242,9 @@ files:
|
|
240
242
|
- template/config.yml
|
241
243
|
- template/secrets.json
|
242
244
|
- template/zones/dnsimple.example.com.yml
|
245
|
+
- template/zones/dnsimple.example.com/A__a-record.yml
|
246
|
+
- template/zones/dnsimple.example.com/TXT__marco.yml
|
247
|
+
- template/zones/dnsimple.example.com/TXT__ping.yml
|
243
248
|
- template/zones/dynect.example.com.yml
|
244
249
|
homepage: https://github.com/Shopify/record_store
|
245
250
|
licenses:
|
@@ -261,7 +266,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
261
266
|
version: '0'
|
262
267
|
requirements: []
|
263
268
|
rubyforge_project:
|
264
|
-
rubygems_version: 2.
|
269
|
+
rubygems_version: 2.5.1
|
265
270
|
signing_key:
|
266
271
|
specification_version: 4
|
267
272
|
summary: Manage DNS using git
|