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