dslimple 1.1.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 +5 -5
- data/.gitignore +3 -2
- data/.rspec +1 -0
- data/dslimple.gemspec +2 -1
- data/lib/dslimple/applier.rb +12 -14
- data/lib/dslimple/cli.rb +22 -25
- data/lib/dslimple/client.rb +67 -0
- data/lib/dslimple/dsl/record.rb +24 -10
- data/lib/dslimple/dsl/{domain.rb → zone.rb} +6 -4
- data/lib/dslimple/dsl.rb +10 -8
- data/lib/dslimple/exporter.rb +28 -28
- data/lib/dslimple/query.rb +16 -14
- data/lib/dslimple/query_builder.rb +27 -25
- data/lib/dslimple/record.rb +47 -79
- data/lib/dslimple/version.rb +1 -1
- data/lib/dslimple/zone.rb +46 -0
- data/lib/dslimple.rb +0 -17
- metadata +23 -7
- data/lib/dslimple/domain.rb +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c0e4eb296b2f5d52fc5b6e2193573eb53c1799f34e9eb6829e9c0976b20a3384
|
4
|
+
data.tar.gz: ff7fbd6fd7854910d2c297a1358a1563dbcef9b43364fc7a3135f3feaad523d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22bcecdfd5cbc84328903cf2ab8fbac86c188da4977ace84cff5eafac18c76ab2213674c89d68911ceea169277c9fe280cff4ed0d1599b71104391a68360e576
|
7
|
+
data.tar.gz: 98035651710a0273eaebb74fdba659226ea94e225a59f11da494f2fc4e2c0646be78d975d37ddc274a703b9548e5a95f9408352114e8f90cf786f5425a44e89c
|
data/.gitignore
CHANGED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--require spec_helper
|
data/dslimple.gemspec
CHANGED
@@ -21,8 +21,9 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
spec.add_development_dependency 'bundler', '~> 1.11'
|
23
23
|
spec.add_development_dependency 'rake', '~> 10.0'
|
24
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
24
25
|
spec.add_development_dependency 'rubocop'
|
25
26
|
|
26
|
-
spec.add_dependency 'dnsimple', '~>
|
27
|
+
spec.add_dependency 'dnsimple', '~> 4.0'
|
27
28
|
spec.add_dependency 'thor', '~> 0.19'
|
28
29
|
end
|
data/lib/dslimple/applier.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'dslimple'
|
2
|
-
require '
|
2
|
+
require 'dslimple/query_builder'
|
3
3
|
|
4
4
|
class Dslimple::Applier
|
5
5
|
OPERATION_COLORS = {
|
@@ -8,11 +8,10 @@ class Dslimple::Applier
|
|
8
8
|
deletion: :red
|
9
9
|
}.freeze
|
10
10
|
|
11
|
-
attr_reader :
|
11
|
+
attr_reader :client, :shell, :options
|
12
12
|
|
13
|
-
def initialize(
|
14
|
-
@
|
15
|
-
@account = account
|
13
|
+
def initialize(client, shell, options = {})
|
14
|
+
@client = client
|
16
15
|
@shell = shell
|
17
16
|
@options = options
|
18
17
|
end
|
@@ -22,10 +21,10 @@ class Dslimple::Applier
|
|
22
21
|
|
23
22
|
dsl.execute
|
24
23
|
|
25
|
-
|
26
|
-
|
24
|
+
expected_zones = dsl.transform
|
25
|
+
expected_zones.select! { |zone| options[:only].include?(zone.name) } unless options[:only].empty?
|
27
26
|
|
28
|
-
@buildler = Dslimple::QueryBuilder.new(
|
27
|
+
@buildler = Dslimple::QueryBuilder.new(fetch_zones, expected_zones, options)
|
29
28
|
@buildler.execute
|
30
29
|
queries = @buildler.filtered_queries(options)
|
31
30
|
|
@@ -41,11 +40,10 @@ class Dslimple::Applier
|
|
41
40
|
apply(queries)
|
42
41
|
end
|
43
42
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
domains
|
43
|
+
def fetch_zones
|
44
|
+
zones = client.all_zones(with_records: true)
|
45
|
+
zones.select! { |zone| options[:only].include?(zone.name) } if options[:only].any?
|
46
|
+
zones
|
49
47
|
end
|
50
48
|
|
51
49
|
def show_plan(queries)
|
@@ -59,7 +57,7 @@ class Dslimple::Applier
|
|
59
57
|
shell.say('Apply', :bold)
|
60
58
|
queries.each do |query|
|
61
59
|
show_query(query)
|
62
|
-
query.execute(
|
60
|
+
query.execute(client, client.account_id)
|
63
61
|
end
|
64
62
|
end
|
65
63
|
|
data/lib/dslimple/cli.rb
CHANGED
@@ -2,30 +2,32 @@ require 'thor'
|
|
2
2
|
require 'dnsimple'
|
3
3
|
require 'json'
|
4
4
|
require 'dslimple'
|
5
|
+
require 'dslimple/dsl'
|
6
|
+
require 'dslimple/client'
|
5
7
|
|
6
8
|
class Dslimple::CLI < Thor
|
7
9
|
include Thor::Actions
|
8
10
|
|
9
|
-
|
10
|
-
USER_AGENT = "DSLimple: Simple CLI DNSimple client(v#{Dslimple::VERSION})".freeze
|
11
|
-
|
12
|
-
class_option :email, type: :string, aliases: %w(-e), desc: 'Your E-Mail address'
|
13
|
-
class_option :api_token, type: :string, aliases: %w(-t), desc: 'Your API token'
|
14
|
-
class_option :domain_token, type: :string, aliases: %w(-dt), desc: 'Your Domain API token'
|
11
|
+
class_option :access_token, type: :string, aliases: %w(-a), desc: 'Your API token'
|
15
12
|
class_option :sandbox, type: :boolean, default: ENV['DSLIMPLE_ENV'] == 'test', desc: 'Use sandbox API(at sandbox.dnsimple.com)'
|
16
13
|
class_option :debug, type: :boolean, default: false
|
17
14
|
|
18
15
|
desc 'export', 'Export domain specifications'
|
19
16
|
method_option :only, type: :array, default: [], aliases: %w(-o), desc: 'Specify domains for export'
|
20
|
-
method_option :file, type: :string, default: '
|
21
|
-
method_option :dir, type: :string, default: './
|
17
|
+
method_option :file, type: :string, default: 'Zonefile', aliases: %w(-f), desc: 'Export zonefile path(- is stdout)'
|
18
|
+
method_option :dir, type: :string, default: './zonefiles', aliases: %w(-d), desc: 'Export directory path for split'
|
22
19
|
method_option :split, type: :boolean, default: false, aliases: %w(-s), desc: 'Export with split by domains'
|
23
20
|
method_option :modeline, type: :boolean, default: false, aliases: %w(-m), desc: 'Export with modeline for Vim'
|
24
|
-
method_option :
|
21
|
+
method_option :ignore, type: :array, default: ['system', 'child'], desc: 'Ignore record types'
|
25
22
|
def export
|
26
|
-
exporter
|
23
|
+
require 'dslimple/exporter'
|
24
|
+
fd = options[:file] == '-' ? STDOUT : File.open(options[:file].to_s, 'w')
|
25
|
+
|
26
|
+
exporter = Dslimple::Exporter.new(client, fd, options)
|
27
27
|
|
28
28
|
exporter.execute
|
29
|
+
|
30
|
+
fd.close
|
29
31
|
rescue => e
|
30
32
|
rescue_from(e)
|
31
33
|
end
|
@@ -33,13 +35,15 @@ class Dslimple::CLI < Thor
|
|
33
35
|
desc 'apply', 'Apply domain specifications'
|
34
36
|
method_option :only, type: :array, default: [], aliases: %w(-o), desc: 'Specify domains for apply'
|
35
37
|
method_option :dry_run, type: :boolean, default: false, aliases: %w(-d)
|
36
|
-
method_option :
|
37
|
-
method_option :
|
38
|
-
method_option :
|
39
|
-
method_option :
|
38
|
+
method_option :ignore, type: :array, default: ['system', 'child'], desc: 'Ignore record types'
|
39
|
+
method_option :file, type: :string, default: 'Zonefile', aliases: %w(-f), desc: 'Source Zonefile path'
|
40
|
+
method_option :addition, type: :boolean, default: true, desc: 'Allow add records'
|
41
|
+
method_option :modification, type: :boolean, default: true, desc: 'Allow modify records'
|
42
|
+
method_option :deletion, type: :boolean, default: true, desc: 'Allow delete records'
|
40
43
|
method_option :yes, type: :boolean, default: false, aliases: %w(-y), desc: 'Do not confirm on before apply'
|
41
44
|
def apply
|
42
|
-
applier
|
45
|
+
require 'dslimple/applier'
|
46
|
+
applier = Dslimple::Applier.new(client, self, options)
|
43
47
|
|
44
48
|
applier.execute
|
45
49
|
rescue => e
|
@@ -50,17 +54,10 @@ class Dslimple::CLI < Thor
|
|
50
54
|
|
51
55
|
private
|
52
56
|
|
53
|
-
def
|
54
|
-
@
|
55
|
-
end
|
56
|
-
|
57
|
-
def api_client
|
58
|
-
@api_client ||= Dnsimple::Client.new(
|
59
|
-
username: options[:email] || ENV['DSLIMPLE_EMAIL'],
|
57
|
+
def client
|
58
|
+
@aclient ||= Dslimple::Client.new(
|
60
59
|
access_token: options[:api_token] || ENV['DSLIMPLE_API_TOKEN'] || ENV['DSLIMPLE_ACCESS_TOKEN'],
|
61
|
-
|
62
|
-
base_url: options[:sandbox] ? SANDBOX_API_ENDPOINT : nil,
|
63
|
-
user_agent: USER_AGENT
|
60
|
+
sandbox: options[:sandbox]
|
64
61
|
)
|
65
62
|
end
|
66
63
|
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'dnsimple'
|
2
|
+
require 'dslimple/zone'
|
3
|
+
require 'dslimple/record'
|
4
|
+
|
5
|
+
class Dslimple::Client < Dnsimple::Client
|
6
|
+
SANDBOX_URL = 'https://api.sandbox.dnsimple.com'
|
7
|
+
USER_AGENT = "dslimple v#{Dslimple::VERSION}"
|
8
|
+
|
9
|
+
def initialize(sandbox: false, **options)
|
10
|
+
options[:base_url] = SANDBOX_URL if sandbox
|
11
|
+
options[:user_agent] = USER_AGENT
|
12
|
+
|
13
|
+
options[:access_token] ||= ENV['DNSIMPLE_ACCESS_TOKEN']
|
14
|
+
|
15
|
+
super(options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def account_id
|
19
|
+
return @account_id if instance_variable_defined?(:@account_id)
|
20
|
+
|
21
|
+
whoami = identity.whoami.data
|
22
|
+
@account_id = whoami.user&.id
|
23
|
+
@account_id = whoami.account.id if whoami.account
|
24
|
+
|
25
|
+
@account_id
|
26
|
+
end
|
27
|
+
|
28
|
+
def all_zones(with_records: false)
|
29
|
+
response = zones.list_zones(account_id)
|
30
|
+
|
31
|
+
zones = []
|
32
|
+
while response
|
33
|
+
zones = zones + response.data.map do |domain|
|
34
|
+
zone = Dslimple::Zone.new(domain.name)
|
35
|
+
zone.records = all_records(zone) if with_records
|
36
|
+
zone
|
37
|
+
end
|
38
|
+
|
39
|
+
response = if response.page < response.total_pages
|
40
|
+
zones.list_zones(account_id, domain, page: response.page + 1)
|
41
|
+
else
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
zones
|
46
|
+
end
|
47
|
+
|
48
|
+
def all_records(zone)
|
49
|
+
response = zones.list_records(account_id, zone.name)
|
50
|
+
|
51
|
+
records = []
|
52
|
+
while response
|
53
|
+
records = records + response.data.map do |record|
|
54
|
+
record = Dslimple::Record.new(record)
|
55
|
+
record.zone = zone.name
|
56
|
+
record
|
57
|
+
end
|
58
|
+
|
59
|
+
response = if response.page < response.total_pages
|
60
|
+
zones.list_records(account_id, domain, page: response.page + 1)
|
61
|
+
else
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
records
|
66
|
+
end
|
67
|
+
end
|
data/lib/dslimple/dsl/record.rb
CHANGED
@@ -1,25 +1,39 @@
|
|
1
1
|
require 'dslimple/dsl'
|
2
2
|
|
3
3
|
class Dslimple::DSL::Record
|
4
|
-
|
4
|
+
ATTRIBUTES = %i[zone name type content ttl priority regions]
|
5
|
+
attr_accessor *ATTRIBUTES
|
5
6
|
|
6
|
-
def initialize(
|
7
|
-
|
8
|
-
|
7
|
+
def initialize(options = {}, &block)
|
8
|
+
options.each_pair do |key, val|
|
9
|
+
break unless respond_to?("#{key}=")
|
10
|
+
|
11
|
+
send("#{key}=", val)
|
12
|
+
end
|
9
13
|
|
10
14
|
returned_content = instance_eval(&block)
|
11
15
|
@content ||= returned_content
|
12
16
|
end
|
13
17
|
|
14
|
-
def
|
15
|
-
|
18
|
+
def [](key)
|
19
|
+
respond_to?(key) ? send(key) : nil
|
20
|
+
end
|
21
|
+
|
22
|
+
ATTRIBUTES.each do |attr|
|
23
|
+
define_method(attr) do |v = nil|
|
24
|
+
v ? instance_variable_set("@#{attr}", v) : instance_variable_get("@#{attr}")
|
25
|
+
end
|
26
|
+
|
27
|
+
define_method("#{attr}=") do |v|
|
28
|
+
instance_variable_set("@#{attr}", v)
|
29
|
+
end
|
16
30
|
end
|
17
31
|
|
18
|
-
def
|
19
|
-
|
32
|
+
def region(v = nil)
|
33
|
+
self.regions = [v].flatten
|
20
34
|
end
|
21
35
|
|
22
|
-
def
|
23
|
-
|
36
|
+
def region=(v)
|
37
|
+
self.regions = [v].flatten
|
24
38
|
end
|
25
39
|
end
|
@@ -1,6 +1,7 @@
|
|
1
|
-
require 'dslimple/dsl'
|
1
|
+
require 'dslimple/dsl/record'
|
2
|
+
require 'dslimple/record'
|
2
3
|
|
3
|
-
class Dslimple::DSL::
|
4
|
+
class Dslimple::DSL::Zone
|
4
5
|
attr_reader :name, :records
|
5
6
|
|
6
7
|
def initialize(name, &block)
|
@@ -15,11 +16,12 @@ class Dslimple::DSL::Domain
|
|
15
16
|
options = options.merge(name)
|
16
17
|
name = ''
|
17
18
|
end
|
19
|
+
options = options.merge(zone: @name, name: name)
|
18
20
|
|
19
|
-
@records << Dslimple::DSL::Record.new(
|
21
|
+
@records << Dslimple::DSL::Record.new(options, &block)
|
20
22
|
end
|
21
23
|
|
22
|
-
Dslimple::Record::RECORD_TYPES.each do |type|
|
24
|
+
Dslimple::Record::RECORD_TYPES.map(&:downcase).each do |type|
|
23
25
|
class_eval(<<-EOC)
|
24
26
|
def #{type}_record(name = {}, options = {}, &block)
|
25
27
|
record(name, options.merge(type: :#{type}), &block)
|
data/lib/dslimple/dsl.rb
CHANGED
@@ -5,7 +5,7 @@ class Dslimple::DSL
|
|
5
5
|
def initialize(file, context = {})
|
6
6
|
@file = Pathname.new(file)
|
7
7
|
@dir = @file.dirname
|
8
|
-
@
|
8
|
+
@zones = []
|
9
9
|
@files = []
|
10
10
|
|
11
11
|
@context = context
|
@@ -20,6 +20,8 @@ class Dslimple::DSL
|
|
20
20
|
evaluate(@dir.join(path))
|
21
21
|
elsif @dir.join("#{path}.rb").exist?
|
22
22
|
evaluate(@dir.join("#{path}.rb"))
|
23
|
+
elsif @dir.join("#{path}.zone").exist?
|
24
|
+
evaluate(@dir.join("#{path}.zone"))
|
23
25
|
else
|
24
26
|
Kernel.require(path)
|
25
27
|
end
|
@@ -34,15 +36,15 @@ class Dslimple::DSL
|
|
34
36
|
raise Dslimple::DSL::Error, "#{e.class}: #{e.message}", cleanup_backtrace(e.backtrace)
|
35
37
|
end
|
36
38
|
|
37
|
-
def
|
38
|
-
@
|
39
|
+
def zone(name, &block)
|
40
|
+
@zones << Dslimple::DSL::Zone.new(name, &block)
|
39
41
|
end
|
40
42
|
|
41
43
|
def transform
|
42
|
-
@
|
43
|
-
Dslimple::
|
44
|
-
model.records =
|
45
|
-
Dslimple::Record.new(
|
44
|
+
@zones.map do |zone|
|
45
|
+
Dslimple::Zone.new(zone.name).tap do |model|
|
46
|
+
model.records = zone.records.map do |record|
|
47
|
+
Dslimple::Record.new(record)
|
46
48
|
end
|
47
49
|
end
|
48
50
|
end
|
@@ -60,6 +62,6 @@ class Dslimple::DSL
|
|
60
62
|
end
|
61
63
|
end
|
62
64
|
|
63
|
-
require 'dslimple/dsl/
|
65
|
+
require 'dslimple/dsl/zone'
|
64
66
|
require 'dslimple/dsl/record'
|
65
67
|
require 'dslimple/dsl/error'
|
data/lib/dslimple/exporter.rb
CHANGED
@@ -2,55 +2,55 @@ require 'pathname'
|
|
2
2
|
require 'dslimple'
|
3
3
|
|
4
4
|
class Dslimple::Exporter
|
5
|
-
attr_reader :
|
5
|
+
attr_reader :client, :file, :options, :zones, :onlies
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
@
|
9
|
-
@
|
7
|
+
def initialize(client, file, options)
|
8
|
+
@client = client
|
9
|
+
@file = file
|
10
10
|
@options = options
|
11
|
-
@
|
11
|
+
@onlies = [options[:only]].flatten.map(&:to_s).reject(&:empty?)
|
12
12
|
end
|
13
13
|
|
14
14
|
def execute
|
15
|
-
@
|
15
|
+
@zones = client.all_zones(with_records: true)
|
16
16
|
|
17
17
|
if options[:split] && options[:dir]
|
18
|
-
split_export(
|
18
|
+
split_export(file, options[:dir])
|
19
19
|
else
|
20
|
-
export(
|
20
|
+
export(file, clean_zones(zones))
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
-
domains.select! { |domain| options[:only].include?(domain.name) } unless options[:only].empty?
|
28
|
-
domains
|
24
|
+
def export(fd, export_zones)
|
25
|
+
write_modeline(fd)
|
26
|
+
fd.puts export_zones.map { |zone| zone.to_dsl(options) }.join("\n")
|
29
27
|
end
|
30
28
|
|
31
|
-
def
|
32
|
-
File.open(file.to_s, 'w') do |fd|
|
33
|
-
write_modeline(fd)
|
34
|
-
fd.puts export_domains.map { |domain| domain.to_dsl(options) }.join("\n")
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def split_export(dir, file)
|
29
|
+
def split_export(fd, dir)
|
39
30
|
dir = Pathname.new(dir)
|
40
|
-
file = Pathname.new(file)
|
41
31
|
Dir.mkdir(dir.to_s) unless dir.directory?
|
42
32
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
export(
|
48
|
-
fd.puts "require '#{domainfile.relative_path_from(file.dirname)}'"
|
33
|
+
write_modeline(file)
|
34
|
+
clean_zones(zones).each do |zone|
|
35
|
+
zonefile = dir.join("#{zone.name}.zone")
|
36
|
+
File.open(zonefile, 'w') do |fd|
|
37
|
+
export(fd, [zone])
|
49
38
|
end
|
39
|
+
fd.puts "require '#{zonefile.relative_path_from(file.dirname)}'"
|
50
40
|
end
|
51
41
|
end
|
52
42
|
|
53
43
|
def write_modeline(fd)
|
54
44
|
fd << "# -*- mode: ruby -*-\n# vi: set ft=ruby :\n\n" if options[:modeline]
|
55
45
|
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
def clean_zones(zones)
|
50
|
+
return zones if onlies.empty?
|
51
|
+
|
52
|
+
zones.select do |zone|
|
53
|
+
onlies.include?(zone.name)
|
54
|
+
end
|
55
|
+
end
|
56
56
|
end
|
data/lib/dslimple/query.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'dslimple'
|
2
2
|
|
3
3
|
class Dslimple::Query
|
4
|
-
attr_reader :operation, :target, :
|
4
|
+
attr_reader :operation, :target, :zone, :params
|
5
5
|
|
6
|
-
def initialize(operation, target,
|
6
|
+
def initialize(operation, target, zone, params = {})
|
7
7
|
@operation = operation
|
8
8
|
@target = target
|
9
|
-
@
|
9
|
+
@zone = zone
|
10
10
|
@params = params
|
11
11
|
end
|
12
12
|
|
@@ -19,17 +19,17 @@ class Dslimple::Query
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def to_s
|
22
|
-
if target == :
|
23
|
-
|
22
|
+
if target == :zone
|
23
|
+
zone.to_s
|
24
24
|
else
|
25
|
-
%(#{params[:
|
25
|
+
%(#{params[:type].to_s.rjust(5)} #{params[:name].to_s.rjust(10)}.#{zone.to_s} (#{record_options.join(', ')}) "#{params[:content]}")
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
29
|
def record_options
|
30
30
|
options = []
|
31
31
|
params.each_pair do |k, v|
|
32
|
-
options << "#{k}: #{v}" if %i(ttl
|
32
|
+
options << "#{k}: #{v}" if %i(ttl priority regions).include?(k) && v
|
33
33
|
end
|
34
34
|
options
|
35
35
|
end
|
@@ -38,23 +38,25 @@ class Dslimple::Query
|
|
38
38
|
__send__("execute_#{target}", api_client, account)
|
39
39
|
end
|
40
40
|
|
41
|
-
def
|
41
|
+
def execute_zone(api_client, account_id)
|
42
42
|
case operation
|
43
43
|
when :addition
|
44
|
-
|
44
|
+
# TODO: Support registration
|
45
|
+
# api_client.registrar.register_zone(account_id, zone, registrant_id: account.id, auto_renew: true)
|
45
46
|
when :deletion
|
46
|
-
|
47
|
+
# TODO: Support deletion
|
48
|
+
# api_client.zones.delete_zone(account_id, zone)
|
47
49
|
end
|
48
50
|
end
|
49
51
|
|
50
|
-
def execute_record(api_client,
|
52
|
+
def execute_record(api_client, account_id)
|
51
53
|
case operation
|
52
54
|
when :addition
|
53
|
-
api_client.zones.create_record(
|
55
|
+
api_client.zones.create_record(account_id, zone, params)
|
54
56
|
when :modification
|
55
|
-
api_client.zones.update_record(
|
57
|
+
api_client.zones.update_record(account_id, zone, params[:id], params)
|
56
58
|
when :deletion
|
57
|
-
api_client.zones.delete_record(
|
59
|
+
api_client.zones.delete_record(account_id, zone, params[:id])
|
58
60
|
end
|
59
61
|
end
|
60
62
|
end
|
@@ -1,41 +1,43 @@
|
|
1
1
|
require 'dslimple'
|
2
|
+
require 'dslimple/query'
|
2
3
|
|
3
4
|
class Dslimple::QueryBuilder
|
4
|
-
attr_reader :queries
|
5
|
-
attr_reader :
|
5
|
+
attr_reader :queries, :options
|
6
|
+
attr_reader :expected_zones, :current_zones
|
6
7
|
attr_reader :append_records, :change_records, :delete_records
|
7
8
|
|
8
|
-
def initialize(
|
9
|
-
@
|
10
|
-
@
|
9
|
+
def initialize(current_zones, expected_zones, options = {})
|
10
|
+
@current_zones = Hash[*current_zones.map { |zone| [zone.name, zone] }.flatten]
|
11
|
+
@expected_zones = Hash[*expected_zones.map { |zone| [zone.name, zone] }.flatten]
|
12
|
+
@options = options
|
11
13
|
end
|
12
14
|
|
13
|
-
def
|
14
|
-
@
|
15
|
+
def append_zones
|
16
|
+
@append_zones ||= expected_zones.values.reject { |zone| current_zones.key?(zone.name) }
|
15
17
|
end
|
16
18
|
|
17
|
-
def
|
18
|
-
@
|
19
|
+
def delete_zones
|
20
|
+
@delete_zones ||= current_zones.values.reject { |zone| expected_zones.key?(zone.name) }
|
19
21
|
end
|
20
22
|
|
21
23
|
def execute
|
22
|
-
@append_records =
|
24
|
+
@append_records = append_zones.map { |z| z.clean_records(options[:ignore]) }.flatten
|
23
25
|
@change_records = []
|
24
|
-
@delete_records =
|
26
|
+
@delete_records = delete_zones.map { |z| z.clean_records(options[:ignore]) }.flatten
|
25
27
|
|
26
|
-
|
27
|
-
execute_records(name,
|
28
|
+
expected_zones.each_pair do |name, zone|
|
29
|
+
execute_records(name, zone)
|
28
30
|
end
|
29
31
|
|
30
32
|
build_queries
|
31
33
|
end
|
32
34
|
|
33
|
-
def execute_records(
|
34
|
-
|
35
|
-
return unless
|
35
|
+
def execute_records(zone_name, zone)
|
36
|
+
current_zone = current_zones[zone_name]
|
37
|
+
return unless current_zone
|
36
38
|
|
37
|
-
current_records =
|
38
|
-
|
39
|
+
current_records = current_zone.clean_records(options[:ignore]).dup
|
40
|
+
zone.clean_records(options[:ignore]).each do |record|
|
39
41
|
at = current_records.index { |current| current == record }
|
40
42
|
current_record = at ? current_records.slice!(at) : nil
|
41
43
|
like_record = current_records.find { |current| current === record }
|
@@ -53,24 +55,24 @@ class Dslimple::QueryBuilder
|
|
53
55
|
def build_queries
|
54
56
|
@queries = []
|
55
57
|
|
56
|
-
|
57
|
-
@queries << Dslimple::Query.new(:addition, :
|
58
|
+
append_zones.each do |zone|
|
59
|
+
@queries << Dslimple::Query.new(:addition, :zone, zone)
|
58
60
|
end
|
59
61
|
|
60
62
|
append_records.each do |record|
|
61
|
-
@queries << Dslimple::Query.new(:addition, :record, record.
|
63
|
+
@queries << Dslimple::Query.new(:addition, :record, record.zone, record.to_params)
|
62
64
|
end
|
63
65
|
|
64
66
|
change_records.each do |old, new|
|
65
|
-
@queries << Dslimple::Query.new(:modification, :record, new.
|
67
|
+
@queries << Dslimple::Query.new(:modification, :record, new.zone, new.to_params.merge(id: old.id))
|
66
68
|
end
|
67
69
|
|
68
70
|
delete_records.each do |record|
|
69
|
-
@queries << Dslimple::Query.new(:deletion, :record, record.
|
71
|
+
@queries << Dslimple::Query.new(:deletion, :record, record.zone, record.to_params)
|
70
72
|
end
|
71
73
|
|
72
|
-
|
73
|
-
@queries << Dslimple::Query.new(:deletion, :
|
74
|
+
delete_zones.each do |zone|
|
75
|
+
@queries << Dslimple::Query.new(:deletion, :zone, zone)
|
74
76
|
end
|
75
77
|
|
76
78
|
@queries
|
data/lib/dslimple/record.rb
CHANGED
@@ -1,114 +1,82 @@
|
|
1
1
|
require 'dslimple'
|
2
|
+
require 'dnsimple'
|
3
|
+
require 'dslimple'
|
2
4
|
|
3
5
|
class Dslimple::Record
|
4
|
-
RECORD_TYPES = %
|
5
|
-
|
6
|
-
SPF_PREFIX = /\Av=spf1 /
|
7
|
-
|
8
|
-
attr_reader :domain, :type, :name, :id, :ttl, :priority, :content
|
9
|
-
|
10
|
-
def self.cleanup_records(records)
|
11
|
-
records = records.dup
|
12
|
-
alias_records = records.select(&:alias?)
|
13
|
-
spf_records = records.select(&:spf?)
|
14
|
-
txt_records = records.select { |record| record.like_spf? || record.like_alias? }
|
15
|
-
|
16
|
-
txt_records.each do |record|
|
17
|
-
reject = record.like_spf? ? spf_records.any? { |r| record.eql_spf?(r) } : alias_records.any? { |r| record.eql_alias?(r) }
|
18
|
-
|
19
|
-
records.delete(record) if reject
|
20
|
-
end
|
6
|
+
RECORD_TYPES = %w[A ALIAS CNAME MX SPF URL TXT NS SRV NAPTR PTR AAAA SSHFP HINFO POOL CAA]
|
7
|
+
DEFAULT_REGIONS = ['global']
|
21
8
|
|
22
|
-
|
23
|
-
end
|
24
|
-
|
25
|
-
RECORD_TYPES.each do |type|
|
26
|
-
class_eval(<<-EOC)
|
27
|
-
def #{type}?
|
28
|
-
type == :#{type}
|
29
|
-
end
|
30
|
-
EOC
|
31
|
-
end
|
9
|
+
attr_accessor :id, :zone, :name, :type, :ttl, :priority, :content, :regions, :system_record
|
32
10
|
|
33
|
-
def initialize(
|
34
|
-
|
35
|
-
@type = type.to_s.downcase.to_sym
|
36
|
-
@name = name
|
37
|
-
@content = content
|
38
|
-
@ttl = options[:ttl]
|
39
|
-
@priority = options[:priority]
|
40
|
-
@id = options[:id]
|
41
|
-
end
|
42
|
-
|
43
|
-
def escaped_name
|
44
|
-
Dslimple.escape_single_quote(name)
|
45
|
-
end
|
11
|
+
def initialize(attrs = {})
|
12
|
+
attrs = normalize_attrs(attrs) if attrs.is_a?(Dnsimple::Struct::ZoneRecord)
|
46
13
|
|
47
|
-
|
48
|
-
|
14
|
+
@id = attrs[:id]
|
15
|
+
@zone = attrs[:zone_id] || attrs[:zone]
|
16
|
+
@name = attrs[:name]
|
17
|
+
@type = attrs[:type].to_s.downcase.to_sym
|
18
|
+
@ttl = attrs[:ttl]
|
19
|
+
@parent_id = attrs[:parent_id]
|
20
|
+
@priority = attrs[:priority]
|
21
|
+
@content = attrs[:content]
|
22
|
+
@regions = attrs[:regions] || DEFAULT_REGIONS
|
23
|
+
@system_record = attrs[:system_record]
|
49
24
|
end
|
50
25
|
|
51
|
-
def
|
52
|
-
|
26
|
+
def [](key)
|
27
|
+
send(key)
|
53
28
|
end
|
54
29
|
|
55
|
-
def
|
56
|
-
|
30
|
+
def system_record?
|
31
|
+
@system_record == true
|
57
32
|
end
|
58
33
|
|
59
|
-
def
|
60
|
-
|
61
|
-
end
|
62
|
-
|
63
|
-
def eql_alias?(alias_record)
|
64
|
-
alias_record.ttl == ttl && content == "ALIAS for #{alias_record.content}"
|
34
|
+
def child_record?
|
35
|
+
!@parent_id.nil?
|
65
36
|
end
|
66
37
|
|
67
38
|
def ==(other)
|
68
|
-
other.is_a?(
|
39
|
+
other.is_a?(self.class) && hash == other.hash
|
69
40
|
end
|
70
|
-
alias_method :eql, :==
|
71
41
|
|
72
42
|
def ===(other)
|
73
|
-
other.is_a?(
|
43
|
+
other.is_a?(self.class) && %i[zone name type].all? { |attr| send(attr).to_s == other.send(attr).to_s }
|
74
44
|
end
|
75
45
|
|
76
46
|
def hash
|
77
|
-
|
78
|
-
end
|
79
|
-
|
80
|
-
def rough_hash
|
81
|
-
"#{type}:#{name}:#{content}"
|
47
|
+
[zone.to_s, name, type, ttl, priority, content, regions.join('|'), !!system_record].hash
|
82
48
|
end
|
83
49
|
|
84
|
-
def
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
<<"EOD"
|
94
|
-
#{type}_record #{to_dsl_options} do
|
95
|
-
'#{escaped_content}'
|
96
|
-
end
|
97
|
-
EOD
|
50
|
+
def to_dsl(options = {})
|
51
|
+
[
|
52
|
+
" #{type.to_s.downcase}_record(#{name.inspect}) do",
|
53
|
+
priority ? " priority #{priority.inspect}" : "",
|
54
|
+
ttl ? " ttl #{ttl}" : "",
|
55
|
+
regions != DEFAULT_REGIONS ? " regions #{regions.inspect}" : "",
|
56
|
+
" content #{content.inspect}",
|
57
|
+
" end",
|
58
|
+
].reject(&:empty?).join("\n")
|
98
59
|
end
|
99
60
|
|
100
61
|
def to_params
|
101
62
|
{
|
102
|
-
id: id,
|
103
|
-
type: type.to_s.upcase,
|
63
|
+
id: @id,
|
104
64
|
name: name,
|
65
|
+
type: type.to_s.upcase,
|
105
66
|
content: content,
|
106
67
|
ttl: ttl,
|
107
|
-
priority: priority
|
68
|
+
priority: priority,
|
69
|
+
regions: regions,
|
108
70
|
}
|
109
71
|
end
|
110
72
|
|
111
|
-
|
112
|
-
|
73
|
+
private
|
74
|
+
|
75
|
+
def normalize_attrs(record)
|
76
|
+
attrs = {}
|
77
|
+
record.instance_variables.each do |var|
|
78
|
+
attrs[:"#{var.to_s.sub(/^@/, '')}"] = record.instance_variable_get(var)
|
79
|
+
end
|
80
|
+
attrs
|
113
81
|
end
|
114
82
|
end
|
data/lib/dslimple/version.rb
CHANGED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'dslimple'
|
2
|
+
require 'dslimple/record'
|
3
|
+
|
4
|
+
class Dslimple::Zone
|
5
|
+
attr_reader :name
|
6
|
+
attr_accessor :records
|
7
|
+
|
8
|
+
def initialize(name)
|
9
|
+
@name = name
|
10
|
+
@records = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def ==(other)
|
14
|
+
other.is_a?(self.class) && hash == other.hash && records.hash == other.records.hash
|
15
|
+
end
|
16
|
+
|
17
|
+
def ===(other)
|
18
|
+
other.is_a?(self.class) && hash == other.hash
|
19
|
+
end
|
20
|
+
|
21
|
+
def hash
|
22
|
+
[name, records.map(&:hash)].hash
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
name
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_dsl(options = {})
|
30
|
+
<<DSL
|
31
|
+
zone #{name.inspect} do
|
32
|
+
#{clean_records(options[:ignore]).map { |record| record.to_dsl(options) }.join("\n\n")}
|
33
|
+
end
|
34
|
+
DSL
|
35
|
+
end
|
36
|
+
|
37
|
+
def clean_records(ignores)
|
38
|
+
ignores = [ignores].flatten.map(&:to_s)
|
39
|
+
|
40
|
+
records.select do |record|
|
41
|
+
next false if ignores.include?('system') && record.system_record?
|
42
|
+
next false if ignores.include?('child') && record.child_record?
|
43
|
+
true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/dslimple.rb
CHANGED
@@ -1,18 +1 @@
|
|
1
1
|
require 'dslimple/version'
|
2
|
-
require 'dslimple/domain'
|
3
|
-
require 'dslimple/record'
|
4
|
-
require 'dslimple/dsl'
|
5
|
-
require 'dslimple/query'
|
6
|
-
require 'dslimple/query_builder'
|
7
|
-
require 'dslimple/exporter'
|
8
|
-
require 'dslimple/applier'
|
9
|
-
|
10
|
-
module Dslimple
|
11
|
-
ESCAPE_SINGLE_QUOTE_REGEXP = /[^\\]'/
|
12
|
-
|
13
|
-
def self.escape_single_quote(string)
|
14
|
-
string.gsub(ESCAPE_SINGLE_QUOTE_REGEXP) do |match|
|
15
|
-
match[0] + "\\'"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dslimple
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sho Kusano
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rubocop
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +72,14 @@ dependencies:
|
|
58
72
|
requirements:
|
59
73
|
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
75
|
+
version: '4.0'
|
62
76
|
type: :runtime
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
80
|
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
82
|
+
version: '4.0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: thor
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -90,6 +104,7 @@ extensions: []
|
|
90
104
|
extra_rdoc_files: []
|
91
105
|
files:
|
92
106
|
- ".gitignore"
|
107
|
+
- ".rspec"
|
93
108
|
- ".rubocop.yml"
|
94
109
|
- Gemfile
|
95
110
|
- LICENSE.txt
|
@@ -102,16 +117,17 @@ files:
|
|
102
117
|
- lib/dslimple.rb
|
103
118
|
- lib/dslimple/applier.rb
|
104
119
|
- lib/dslimple/cli.rb
|
105
|
-
- lib/dslimple/
|
120
|
+
- lib/dslimple/client.rb
|
106
121
|
- lib/dslimple/dsl.rb
|
107
|
-
- lib/dslimple/dsl/domain.rb
|
108
122
|
- lib/dslimple/dsl/error.rb
|
109
123
|
- lib/dslimple/dsl/record.rb
|
124
|
+
- lib/dslimple/dsl/zone.rb
|
110
125
|
- lib/dslimple/exporter.rb
|
111
126
|
- lib/dslimple/query.rb
|
112
127
|
- lib/dslimple/query_builder.rb
|
113
128
|
- lib/dslimple/record.rb
|
114
129
|
- lib/dslimple/version.rb
|
130
|
+
- lib/dslimple/zone.rb
|
115
131
|
homepage: https://github.com/zeny-io/dslimple
|
116
132
|
licenses:
|
117
133
|
- MIT
|
@@ -132,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
132
148
|
version: '0'
|
133
149
|
requirements: []
|
134
150
|
rubyforge_project:
|
135
|
-
rubygems_version: 2.
|
151
|
+
rubygems_version: 2.7.6
|
136
152
|
signing_key:
|
137
153
|
specification_version: 4
|
138
154
|
summary: DSLimple is a tool to manage DNSimple.
|
data/lib/dslimple/domain.rb
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
require 'dslimple'
|
2
|
-
|
3
|
-
class Dslimple::Domain
|
4
|
-
attr_reader :name, :id
|
5
|
-
attr_accessor :api_client, :account, :records
|
6
|
-
|
7
|
-
def initialize(name, api_client, account, options = {})
|
8
|
-
@name = name
|
9
|
-
@id = options[:id]
|
10
|
-
@api_client = api_client
|
11
|
-
@account = account
|
12
|
-
@records = []
|
13
|
-
end
|
14
|
-
|
15
|
-
def escaped_name
|
16
|
-
Dslimple.escape_single_quote(name)
|
17
|
-
end
|
18
|
-
|
19
|
-
def records_without_soa_ns
|
20
|
-
records.select { |record| record.type != :soa && record.type != :ns }
|
21
|
-
end
|
22
|
-
|
23
|
-
def fetch_records
|
24
|
-
api_client.zones.all_records(account.id, name).data.map do |record|
|
25
|
-
Dslimple::Record.new(self, record.type, record.name, record.content, ttl: record.ttl, priority: record.priority, id: record.id)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def fetch_records!
|
30
|
-
@records = fetch_records
|
31
|
-
cleanup_records!
|
32
|
-
end
|
33
|
-
|
34
|
-
def cleanup_records!
|
35
|
-
@records = Dslimple::Record.cleanup_records(records)
|
36
|
-
end
|
37
|
-
|
38
|
-
def ==(other)
|
39
|
-
other.is_a?(Dslimple::Domain) && other.name == name
|
40
|
-
end
|
41
|
-
alias_method :eql, :==
|
42
|
-
|
43
|
-
def to_dsl(options = {})
|
44
|
-
<<"EOD"
|
45
|
-
domain '#{escaped_name}' do
|
46
|
-
#{(options[:soa_and_ns] ? records : records_without_soa_ns).map(&:to_dsl).join("\n")}end
|
47
|
-
EOD
|
48
|
-
end
|
49
|
-
end
|