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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: dce8b719b5649f05c0aeb1797495cbba95c8498c
4
- data.tar.gz: a3109feace113af1c23069889437e116d9684832
2
+ SHA256:
3
+ metadata.gz: c0e4eb296b2f5d52fc5b6e2193573eb53c1799f34e9eb6829e9c0976b20a3384
4
+ data.tar.gz: ff7fbd6fd7854910d2c297a1358a1563dbcef9b43364fc7a3135f3feaad523d5
5
5
  SHA512:
6
- metadata.gz: f2507e4cf8ddba6bc18f92e4e94e189952b7841a8fc3a6a466b38326b8f70f7f407ea0985c31b2459a0c43b32f9450150317ae4dc4ca4171af5d7384d840a133
7
- data.tar.gz: 90a69e82def3664778c36c3617d7700ff8c5dbfd590a2d01d59addbede675bba08f3ced38638c69b6a132b1bb7c55f0344ca2391f6d5ee27c5d542c8a87a3dbd
6
+ metadata.gz: 22bcecdfd5cbc84328903cf2ab8fbac86c188da4977ace84cff5eafac18c76ab2213674c89d68911ceea169277c9fe280cff4ed0d1599b71104391a68360e576
7
+ data.tar.gz: 98035651710a0273eaebb74fdba659226ea94e225a59f11da494f2fc4e2c0646be78d975d37ddc274a703b9548e5a95f9408352114e8f90cf786f5425a44e89c
data/.gitignore CHANGED
@@ -7,5 +7,6 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
- /Domainfile
11
- /domainfiles
10
+ /Zonefile
11
+ /zonefiles
12
+ /.envrc
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', '~> 3.0'
27
+ spec.add_dependency 'dnsimple', '~> 4.0'
27
28
  spec.add_dependency 'thor', '~> 0.19'
28
29
  end
@@ -1,5 +1,5 @@
1
1
  require 'dslimple'
2
- require 'pp'
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 :api_client, :account, :shell, :options
11
+ attr_reader :client, :shell, :options
12
12
 
13
- def initialize(api_client, account, shell, options = {})
14
- @api_client = api_client
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
- expected_domains = dsl.transform
26
- expected_domains.select! { |domain| options[:only].include?(domain.name) } if options[:only].any?
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(fetch_domains, expected_domains)
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 fetch_domains
45
- domains = api_client.domains.all_domains(account.id).data.map { |domain| Dslimple::Domain.new(domain.name, api_client, account, id: domain.id) }
46
- domains.each(&:fetch_records!)
47
- domains.select! { |domain| options[:only].include?(domain.name) } if options[:only].any?
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(api_client, account)
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
- SANDBOX_API_ENDPOINT = 'https://api.sandbox.dnsimple.com'.freeze
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: 'Domainfile', aliases: %w(-f), desc: 'Export Domainfile path'
21
- method_option :dir, type: :string, default: './domainfiles', aliases: %w(-d), desc: 'Export directory path for split'
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 :soa_and_ns, type: :boolean, default: false, desc: 'Export without SOA and NS records'
21
+ method_option :ignore, type: :array, default: ['system', 'child'], desc: 'Ignore record types'
25
22
  def export
26
- exporter = Dslimple::Exporter.new(api_client, account, options)
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 :file, type: :string, default: 'Domainfile', aliases: %w(-f), desc: 'Source Domainfile path'
37
- method_option :addition, type: :boolean, default: true, desc: 'Add specified records'
38
- method_option :modification, type: :boolean, default: true, desc: 'Modify specified records'
39
- method_option :deletion, type: :boolean, default: true, desc: 'Delete unspecified records'
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 = Dslimple::Applier.new(api_client, account, self, options)
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 account
54
- @account ||= api_client.identity.whoami.data[:account]
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
- domain_api_token: options[:domain_token] || ENV['DSLIMPLE_DOMAIN_TOKEN'],
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
@@ -1,25 +1,39 @@
1
1
  require 'dslimple/dsl'
2
2
 
3
3
  class Dslimple::DSL::Record
4
- attr_reader :name, :content, :options
4
+ ATTRIBUTES = %i[zone name type content ttl priority regions]
5
+ attr_accessor *ATTRIBUTES
5
6
 
6
- def initialize(name, options = {}, &block)
7
- @name = name
8
- @options = options
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 priority(n)
15
- @options[:priority] = n.to_s.to_i
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 ttl(n)
19
- @options[:ttl] = n.to_s.to_i
32
+ def region(v = nil)
33
+ self.regions = [v].flatten
20
34
  end
21
35
 
22
- def content(c = nil)
23
- c ? @content = c : @content
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::Domain
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(name, options, &block)
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
- @domains = []
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 domain(name, &block)
38
- @domains << Dslimple::DSL::Domain.new(name, &block)
39
+ def zone(name, &block)
40
+ @zones << Dslimple::DSL::Zone.new(name, &block)
39
41
  end
40
42
 
41
43
  def transform
42
- @domains.map do |domain|
43
- Dslimple::Domain.new(domain.name, nil, nil).tap do |model|
44
- model.records = domain.records.map do |record|
45
- Dslimple::Record.new(model, record.options[:type], record.name, record.content, record.options)
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/domain'
65
+ require 'dslimple/dsl/zone'
64
66
  require 'dslimple/dsl/record'
65
67
  require 'dslimple/dsl/error'
@@ -2,55 +2,55 @@ require 'pathname'
2
2
  require 'dslimple'
3
3
 
4
4
  class Dslimple::Exporter
5
- attr_reader :api_client, :account, :options, :domains
5
+ attr_reader :client, :file, :options, :zones, :onlies
6
6
 
7
- def initialize(api_client, account, options)
8
- @api_client = api_client
9
- @account = account
7
+ def initialize(client, file, options)
8
+ @client = client
9
+ @file = file
10
10
  @options = options
11
- @domains = []
11
+ @onlies = [options[:only]].flatten.map(&:to_s).reject(&:empty?)
12
12
  end
13
13
 
14
14
  def execute
15
- @domains = fetch_domains
15
+ @zones = client.all_zones(with_records: true)
16
16
 
17
17
  if options[:split] && options[:dir]
18
- split_export(options[:dir], options[:file])
18
+ split_export(file, options[:dir])
19
19
  else
20
- export(options[:file], domains)
20
+ export(file, clean_zones(zones))
21
21
  end
22
22
  end
23
23
 
24
- def fetch_domains
25
- domains = api_client.domains.all_domains(account.id).data.map { |domain| Dslimple::Domain.new(domain.name, api_client, account) }
26
- domains.each(&:fetch_records!)
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 export(file, export_domains)
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
- File.open(file.to_s, 'w') do |fd|
44
- write_modeline(fd)
45
- domains.each do |domain|
46
- domainfile = dir.join(domain.name)
47
- export(domainfile, [domain])
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
@@ -1,12 +1,12 @@
1
1
  require 'dslimple'
2
2
 
3
3
  class Dslimple::Query
4
- attr_reader :operation, :target, :domain, :params
4
+ attr_reader :operation, :target, :zone, :params
5
5
 
6
- def initialize(operation, target, domain, params = {})
6
+ def initialize(operation, target, zone, params = {})
7
7
  @operation = operation
8
8
  @target = target
9
- @domain = domain
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 == :domain
23
- domain.name.to_s
22
+ if target == :zone
23
+ zone.to_s
24
24
  else
25
- %(#{params[:record_type].to_s.rjust(5)} #{params[:name].to_s.rjust(10)}.#{domain.name} (#{record_options.join(', ')}) "#{params[:content]}")
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 prio).include?(k) && v
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 execute_domain(api_client, account)
41
+ def execute_zone(api_client, account_id)
42
42
  case operation
43
43
  when :addition
44
- api_client.registrar.register_domain(account.id, domain.name, registrant_id: account.id, auto_renew: true)
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
- api_client.domains.delete_domain(account.id, domain.name)
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, account)
52
+ def execute_record(api_client, account_id)
51
53
  case operation
52
54
  when :addition
53
- api_client.zones.create_record(account.id, domain.name, params)
55
+ api_client.zones.create_record(account_id, zone, params)
54
56
  when :modification
55
- api_client.zones.update_record(account.id, domain.name, params[:id], params)
57
+ api_client.zones.update_record(account_id, zone, params[:id], params)
56
58
  when :deletion
57
- api_client.zones.delete_record(account.id, domain.name, params[:id])
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 :expected_domains, :current_domains
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(current_domains, expected_domains)
9
- @current_domains = Hash[*current_domains.map { |domain| [domain.name, domain] }.flatten]
10
- @expected_domains = Hash[*expected_domains.map { |domain| [domain.name, domain] }.flatten]
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 append_domains
14
- @append_domains ||= expected_domains.values.reject { |domain| current_domains.key?(domain.name) }
15
+ def append_zones
16
+ @append_zones ||= expected_zones.values.reject { |zone| current_zones.key?(zone.name) }
15
17
  end
16
18
 
17
- def delete_domains
18
- @delete_domains ||= current_domains.values.reject { |domain| expected_domains.key?(domain.name) }
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 = append_domains.map(&:records_without_soa_ns).flatten
24
+ @append_records = append_zones.map { |z| z.clean_records(options[:ignore]) }.flatten
23
25
  @change_records = []
24
- @delete_records = delete_domains.map(&:records_without_soa_ns).flatten
26
+ @delete_records = delete_zones.map { |z| z.clean_records(options[:ignore]) }.flatten
25
27
 
26
- expected_domains.each_pair do |name, domain|
27
- execute_records(name, domain)
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(domain_name, domain)
34
- current_domain = current_domains[domain_name]
35
- return unless current_domain
35
+ def execute_records(zone_name, zone)
36
+ current_zone = current_zones[zone_name]
37
+ return unless current_zone
36
38
 
37
- current_records = current_domain.records_without_soa_ns.dup
38
- domain.records_without_soa_ns.each do |record|
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
- append_domains.each do |domain|
57
- @queries << Dslimple::Query.new(:addition, :domain, domain)
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.domain, record.to_params)
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.domain, new.to_params.merge(id: old.id))
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.domain, record.to_params)
71
+ @queries << Dslimple::Query.new(:deletion, :record, record.zone, record.to_params)
70
72
  end
71
73
 
72
- delete_domains.each do |domain|
73
- @queries << Dslimple::Query.new(:deletion, :domain, domain)
74
+ delete_zones.each do |zone|
75
+ @queries << Dslimple::Query.new(:deletion, :zone, zone)
74
76
  end
75
77
 
76
78
  @queries
@@ -1,114 +1,82 @@
1
1
  require 'dslimple'
2
+ require 'dnsimple'
3
+ require 'dslimple'
2
4
 
3
5
  class Dslimple::Record
4
- RECORD_TYPES = %i(a alias cname mx spf url txt ns srv naptr ptr aaaa sshfp hinfo pool).freeze
5
- ALIAS_PREFIX = /\AALIAS for /
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
- records
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(domain, type, name, content, options = {})
34
- @domain = domain
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
- def escaped_content
48
- Dslimple.escape_single_quote(content)
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 like_spf?
52
- txt? && content.match(SPF_PREFIX)
26
+ def [](key)
27
+ send(key)
53
28
  end
54
29
 
55
- def like_alias?
56
- txt? && content.match(ALIAS_PREFIX)
30
+ def system_record?
31
+ @system_record == true
57
32
  end
58
33
 
59
- def eql_spf?(spf_record)
60
- spf_record.ttl == ttl && spf_record.content == content
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?(Dslimple::Record) && other.domain == domain && other.hash == hash
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?(Dslimple::Record) && other.domain == domain && other.rough_hash == rough_hash
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
- "#{type}:#{name}:#{content}:#{ttl}:#{priority}"
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 to_dsl_options
85
- options = []
86
- options << "'#{escaped_name}'" unless escaped_name.empty?
87
- options << "ttl: #{ttl}" if ttl
88
- options << "priority: #{priority}" if priority
89
- options.join(', ')
90
- end
91
-
92
- def to_dsl
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
- def inspect
112
- "<Dslimple::Record #{type.to_s.upcase} #{name}: #{content}>"
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
@@ -1,3 +1,3 @@
1
1
  module Dslimple
2
- VERSION = '1.1.0'.freeze
2
+ VERSION = '2.0.0'.freeze
3
3
  end
@@ -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: 1.1.0
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: 2016-05-02 00:00:00.000000000 Z
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: '3.0'
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: '3.0'
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/domain.rb
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.5.1
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.
@@ -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