ezdyn 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +30 -0
- data/README.md +139 -0
- data/bin/ezdyn +226 -0
- data/ezdyn.gemspec +24 -0
- data/lib/ezdyn.rb +11 -0
- data/lib/ezdyn/changes.rb +111 -0
- data/lib/ezdyn/client.rb +203 -0
- data/lib/ezdyn/consts.rb +10 -0
- data/lib/ezdyn/crud.rb +199 -0
- data/lib/ezdyn/log.rb +47 -0
- data/lib/ezdyn/record.rb +142 -0
- data/lib/ezdyn/record_type.rb +78 -0
- data/lib/ezdyn/response.rb +82 -0
- data/lib/ezdyn/version.rb +4 -0
- data/lib/ezdyn/zone.rb +113 -0
- metadata +73 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d24c4fe0dc33f7c3811c278361eca07dc3238312
|
4
|
+
data.tar.gz: 77d401779004efe034f9e67a248798f289fe5042
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 605af431e632c95a448973f647fbc072cd8ef96765b47da25a089359f2a4bc96239f84f414945e503c561ef35ef2ca1f28e4e2eb4cc363482e7456c69b5cabd8
|
7
|
+
data.tar.gz: e08bc7239b87985c2a78dc77fd267c4c223886212fe5314f8009880b007d3853de83c93cf6242c0e1ee4abe9d1261e40e02a63c17f5767708b2920b564119a76
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# ezdyn changelog
|
2
|
+
|
3
|
+
## Version 0.2.0 - 2015-09-19
|
4
|
+
|
5
|
+
* ezdyn command line tool
|
6
|
+
* several bug fixes
|
7
|
+
|
8
|
+
|
9
|
+
## Version 0.1.1 - 2015-09-19
|
10
|
+
|
11
|
+
* Added YARD docs
|
12
|
+
* Added Rakefile with gem and doc commands
|
13
|
+
* Renamed Type to RecordType for clarity
|
14
|
+
|
15
|
+
|
16
|
+
## Version 0.1.0 - 2015-09-18
|
17
|
+
|
18
|
+
* Refactored client code to a new class
|
19
|
+
* Factored record type handling into its own class
|
20
|
+
* Added stderr verbose logger
|
21
|
+
* Keep track of pending changes globally, plus reporting
|
22
|
+
* Commit and Rollback do not require zone specification
|
23
|
+
|
24
|
+
|
25
|
+
## Version 0.0.0 - 2015-09-18
|
26
|
+
|
27
|
+
* Session management: login/logout
|
28
|
+
* Basic CRUD: create, update, upsert, delete, delete_all, records_for
|
29
|
+
* Transactions: Commit, Rollback
|
30
|
+
* Zone, Record, Response objects
|
data/README.md
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
# ezdyn
|
2
|
+
|
3
|
+
Gem library and command line tool for Dyn Managed DNS API access.
|
4
|
+
|
5
|
+
See https://help.dyn.com/dns-api-knowledge-base/
|
6
|
+
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
$ rake gem:install
|
11
|
+
|
12
|
+
|
13
|
+
## Command Line Usage
|
14
|
+
|
15
|
+
Use the `ezdyn` tool installed with the gem to make one-off or batched DNS changes from the command line.
|
16
|
+
|
17
|
+
See `ezdyn --help` for all options. Use command line flags or environment variables (see below) for authentication.
|
18
|
+
|
19
|
+
Important options include:
|
20
|
+
|
21
|
+
* `--file <filename>` provide a file of commands, one per line
|
22
|
+
* `--apply`: apply changes when done (default is dry-run mode)
|
23
|
+
* `--check`: check syntax only (does not connect to API or need credentials)
|
24
|
+
|
25
|
+
If `--file` is not specified, commands are read from the command line arguments. Each ezdyn command must be enclosed in quotes to distiguish it from other commands.
|
26
|
+
|
27
|
+
### Command syntax
|
28
|
+
|
29
|
+
There are three possible commands:
|
30
|
+
|
31
|
+
* `create <type> <fqdn> <value> [<ttl>]`
|
32
|
+
* `upsert <type> <fqdn> <value> [<ttl>]`
|
33
|
+
* `delete <type> <fqdn>`
|
34
|
+
|
35
|
+
The `ttl` field is always optional. You may use `update` as well as `upsert`, but both commands will create a record if it does not exist, and update an existing record if it does exist.
|
36
|
+
|
37
|
+
### Examples
|
38
|
+
|
39
|
+
You can issue commands directly from the command line.
|
40
|
+
|
41
|
+
To perform a dry-run of creating a new A record:
|
42
|
+
|
43
|
+
$ ezdyn "create A test.example.com 192.168.0.1"
|
44
|
+
|
45
|
+
To check the syntax (requiring no API interaction) on creating two CNAME records:
|
46
|
+
|
47
|
+
$ ezdyn --check "create cname test1.example.com other1.example.com" \
|
48
|
+
"create cname test2.example.com other2.example.com"
|
49
|
+
|
50
|
+
To actually update/upsert an A record:
|
51
|
+
|
52
|
+
$ ezdyn "update A test3.example.com 10.0.0.10" --apply
|
53
|
+
|
54
|
+
To process a list of commands from a file:
|
55
|
+
|
56
|
+
$ ezdyn --file dns-changes.txt
|
57
|
+
|
58
|
+
Where `dns-changes.txt` may look like:
|
59
|
+
|
60
|
+
create A web1.example.com 10.1.100.1 600
|
61
|
+
create A web2.example.com 10.1.200.1 1200
|
62
|
+
create A web3.example.com 192.168.0.2 30
|
63
|
+
delete ALL oldweb.example.com
|
64
|
+
|
65
|
+
|
66
|
+
### Notes
|
67
|
+
|
68
|
+
* Dry runs do interact with the Dyn API. This is useful to check for the validity of your changes before making them.
|
69
|
+
* ezdyn does not yet deal correctly with multiple records on a single node. Delete operations will delete multiple records on a node, but create and update operations should bail and complain about multiple records.
|
70
|
+
* Exception handling is poor at the moment, so when things go wrong it might look ugly, but most dangerous situations should be prevented.
|
71
|
+
* Only A and CNAME records are supported at present.
|
72
|
+
|
73
|
+
|
74
|
+
## Library Usage
|
75
|
+
|
76
|
+
require 'ezdyn'
|
77
|
+
|
78
|
+
# create new client object using explicit vars
|
79
|
+
dyn = EZDyn::Client.new(customer_name: customer_name,
|
80
|
+
username: username,
|
81
|
+
password: password)
|
82
|
+
|
83
|
+
# (or create one using environment variables (see below)
|
84
|
+
dyn2 = EZDyn::Client.new
|
85
|
+
|
86
|
+
# create a new A record
|
87
|
+
# (login step is implicit in first access)
|
88
|
+
dyn.create(type: "A",
|
89
|
+
fqdn: "website.example.com",
|
90
|
+
value: "10.0.0.1")
|
91
|
+
|
92
|
+
# no wait, take that back
|
93
|
+
dyn.rollback
|
94
|
+
|
95
|
+
# delete any CNAME records for alias.example.com
|
96
|
+
dyn.delete_all(type: "cname", fqdn: "alias.example.com")
|
97
|
+
|
98
|
+
# update the A record for mail.example.com, or, if it
|
99
|
+
# does not exist, create it with the given value and ttl
|
100
|
+
dyn.update(type: :a,
|
101
|
+
fqdn: "mail.example.com",
|
102
|
+
value: "10.0.0.2",
|
103
|
+
ttl: 600)
|
104
|
+
|
105
|
+
# print all pending changes
|
106
|
+
puts "Pending changes:"
|
107
|
+
dyn.pending_changes.each do |pc|
|
108
|
+
puts " #{pc}"
|
109
|
+
end
|
110
|
+
|
111
|
+
# commit all changes (with optional zone update message)
|
112
|
+
dyn.commit(message: "Just an example")
|
113
|
+
|
114
|
+
# close session (it will time out after 60 minutes)
|
115
|
+
dyn.logout
|
116
|
+
|
117
|
+
|
118
|
+
## Environment Variables
|
119
|
+
|
120
|
+
You may specify the `customer_name`, `username`, and `password` parameters via environment variables:
|
121
|
+
|
122
|
+
* `DYN_CUSTOMER_NAME`
|
123
|
+
* `DYN_USERNAME`
|
124
|
+
* `DYN_PASSWORD`
|
125
|
+
|
126
|
+
You may also enable verbose debug logging by setting `EZDYN_DEBUG` to any value.
|
127
|
+
|
128
|
+
|
129
|
+
## API Documentation
|
130
|
+
|
131
|
+
You can generate the YARD docs for the library by running `rake docs`. Then open `doc/index.html` in your web browser.
|
132
|
+
|
133
|
+
|
134
|
+
## TODO
|
135
|
+
|
136
|
+
* Implement actual Exception classes
|
137
|
+
* Fewer exceptions should be raised overall
|
138
|
+
* Support additional record types (esp MX, AAAA)
|
139
|
+
* Mock API endpoint for testing, also actual tests
|
data/bin/ezdyn
ADDED
@@ -0,0 +1,226 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'io/console'
|
5
|
+
|
6
|
+
def die(msg)
|
7
|
+
errsay "ERROR: #{msg}"
|
8
|
+
exit 1
|
9
|
+
end
|
10
|
+
|
11
|
+
def say(msg = nil)
|
12
|
+
puts msg unless $options[:quiet]
|
13
|
+
end
|
14
|
+
|
15
|
+
def errsay(msg = nil)
|
16
|
+
STDERR.puts msg unless $options[:quiet]
|
17
|
+
end
|
18
|
+
|
19
|
+
$options = {
|
20
|
+
dryrun: true,
|
21
|
+
ignore_syntax_errors: false,
|
22
|
+
check: false,
|
23
|
+
}
|
24
|
+
client_args = {}
|
25
|
+
OptionParser.new do |opts|
|
26
|
+
opts.banner = "Usage: #{File.basename($0)} [options]"
|
27
|
+
|
28
|
+
opts.on_head(
|
29
|
+
'-f', '--file FILENAME',
|
30
|
+
'File path to a batch of ezdyn commands'
|
31
|
+
) do |arg|
|
32
|
+
|
33
|
+
die("Input file '#{arg}' could not be found.") if not File.readable?(arg)
|
34
|
+
|
35
|
+
$options[:batchfile] = arg
|
36
|
+
$options[:batchmode] = true
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on_head('--apply', 'Actually apply changes, do not just perform a dry run.') do
|
40
|
+
$options[:dryrun] = false
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on_head('--check', 'Check all command syntax, but do not interact with the API.') do
|
44
|
+
$options[:check] = true
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on('-C', '--customer-name NAME', 'Dyn API Customer Name') do |arg|
|
48
|
+
client_args[:customer_name] = arg
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on('-u', '--user USERNAME', 'Dyn API Username') do |arg|
|
52
|
+
client_args[:username] = arg
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on('-p', '--password PASSWORD', 'Dyn API Password') do |arg|
|
56
|
+
client_args[:password] = arg
|
57
|
+
end
|
58
|
+
|
59
|
+
opts.on('--ignore-syntax-errors', 'Do not fail if a command has a syntax error. Just skip.') do
|
60
|
+
$options[:ignore_syntax_errors] = true
|
61
|
+
end
|
62
|
+
|
63
|
+
opts.on('--force', 'Do not ask for confirmation to commit changes.') do
|
64
|
+
$options[:force] = true
|
65
|
+
end
|
66
|
+
|
67
|
+
opts.on('-q', '--quiet', 'Quiet mode (implies --force: apply mode will not ask for confirmation!)') do
|
68
|
+
$options[:quiet] = true
|
69
|
+
$options[:force] = true
|
70
|
+
end
|
71
|
+
|
72
|
+
opts.on_tail('--debug', 'Enable debug logging') do
|
73
|
+
$options[:debug] = true
|
74
|
+
end
|
75
|
+
|
76
|
+
opts.on_tail('-v','--version', 'Display version') do
|
77
|
+
require 'ezdyn/version'
|
78
|
+
puts "ezdyn #{EZDyn::VERSION}"
|
79
|
+
exit
|
80
|
+
end
|
81
|
+
end.parse!
|
82
|
+
|
83
|
+
if $options[:debug]
|
84
|
+
ENV['EZDYN_DEBUG'] = "1"
|
85
|
+
end
|
86
|
+
|
87
|
+
if $options[:batchmode]
|
88
|
+
if ARGV.count > 0
|
89
|
+
die "Cannot handle commands both on the command line and in a file"
|
90
|
+
end
|
91
|
+
else
|
92
|
+
if ARGV.count < 1
|
93
|
+
die "Nothing to do"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
commands =
|
98
|
+
if $options[:batchmode]
|
99
|
+
File.read($options[:batchfile]).split("\n")
|
100
|
+
else
|
101
|
+
ARGV.dup
|
102
|
+
end
|
103
|
+
|
104
|
+
# in check mode, we won't interact with the API
|
105
|
+
if not $options[:check]
|
106
|
+
require 'ezdyn'
|
107
|
+
$dyn = EZDyn::Client.new(**client_args)
|
108
|
+
end
|
109
|
+
|
110
|
+
$check_warnings = 0
|
111
|
+
|
112
|
+
def handle_syntax_error(msg)
|
113
|
+
if $options[:check]
|
114
|
+
$check_warnings += 1
|
115
|
+
|
116
|
+
elsif not $options[:ignore_syntax_errors]
|
117
|
+
die msg
|
118
|
+
end
|
119
|
+
errsay "WARNING: #{msg}"
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
commands.each do |line|
|
124
|
+
tokens = line.chomp.strip.split(/\s+/)
|
125
|
+
next if tokens.empty?
|
126
|
+
|
127
|
+
cmd = tokens.shift.downcase
|
128
|
+
|
129
|
+
case cmd
|
130
|
+
when 'create'
|
131
|
+
type, fqdn, value, ttl = tokens.shift(4)
|
132
|
+
if type.nil? or type.empty? or
|
133
|
+
fqdn.nil? or fqdn.empty? or
|
134
|
+
value.nil? or value.empty? or
|
135
|
+
tokens.count > 0
|
136
|
+
handle_syntax_error("'create' command expects 3-4 arguments")
|
137
|
+
next
|
138
|
+
end
|
139
|
+
next if $options[:check]
|
140
|
+
|
141
|
+
say "DynAPI: Creating #{fqdn} #{ttl||"(#{EZDyn::Record::DefaultTTL})"} #{type.upcase} #{value}"
|
142
|
+
$dyn.create(type: type, fqdn: fqdn, value: value, ttl: ttl)
|
143
|
+
|
144
|
+
when 'update','upsert'
|
145
|
+
type, fqdn, value, ttl = tokens.shift(4)
|
146
|
+
if type.nil? or type.empty? or
|
147
|
+
fqdn.nil? or fqdn.empty? or
|
148
|
+
value.nil? or value.empty? or
|
149
|
+
tokens.count > 0
|
150
|
+
handle_syntax_error("'#{cmd}' command expects 3-4 arguments")
|
151
|
+
next
|
152
|
+
end
|
153
|
+
next if $options[:check]
|
154
|
+
|
155
|
+
say "DynAPI: Upserting #{fqdn} #{ttl||"(#{EZDyn::Record::DefaultTTL})"} #{type.upcase} #{value}"
|
156
|
+
$dyn.update(type: type, fqdn: fqdn, value: value, ttl: ttl)
|
157
|
+
|
158
|
+
when 'delete'
|
159
|
+
type, fqdn = tokens.shift(2)
|
160
|
+
if type.nil? or type.empty? or
|
161
|
+
fqdn.nil? or fqdn.empty? or
|
162
|
+
tokens.count > 0
|
163
|
+
handle_syntax_error("'delete' command expects exactly two arguments")
|
164
|
+
next
|
165
|
+
end
|
166
|
+
next if $options[:check]
|
167
|
+
|
168
|
+
if type.downcase == "all" or type.downcase == "any"
|
169
|
+
say "DynAPI: Deleting ALL records for '#{fqdn}'"
|
170
|
+
$dyn.records_for(fqdn: fqdn).each(&:delete!)
|
171
|
+
else
|
172
|
+
say "DynAPI: Deleting #{type.upcase} records for '#{fqdn}'"
|
173
|
+
$dyn.records_for(type: type, fqdn: fqdn).each(&:delete!)
|
174
|
+
end
|
175
|
+
|
176
|
+
else
|
177
|
+
handle_syntax_error("Invalid or unknown command '#{cmd}'")
|
178
|
+
next
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
if $options[:check]
|
183
|
+
if $check_warnings > 0
|
184
|
+
die "Found #{$check_warnings} syntax errors"
|
185
|
+
else
|
186
|
+
say "Syntax OK"
|
187
|
+
end
|
188
|
+
exit
|
189
|
+
end
|
190
|
+
|
191
|
+
say
|
192
|
+
if $dyn.pending_changes.nil? or $dyn.pending_changes.count < 1
|
193
|
+
say "NOTICE: No changes were made."
|
194
|
+
exit
|
195
|
+
end
|
196
|
+
|
197
|
+
say "Pending changes:"
|
198
|
+
$dyn.pending_changes.each do |pc|
|
199
|
+
say " #{pc}"
|
200
|
+
end
|
201
|
+
say
|
202
|
+
|
203
|
+
|
204
|
+
if $options[:dryrun]
|
205
|
+
say "DynAPI: This is a dry run. Rolling back changes."
|
206
|
+
$dyn.rollback
|
207
|
+
|
208
|
+
else
|
209
|
+
if not $options[:force]
|
210
|
+
print "Would you like to apply these changes? (y/N) "
|
211
|
+
if STDIN.getch != 'y'
|
212
|
+
say
|
213
|
+
say "DynAPI: Changes rejected. Rolling back."
|
214
|
+
$dyn.rollback
|
215
|
+
else
|
216
|
+
say
|
217
|
+
say "DynAPI: Apply mode in effect and changes confirmed. Committing!"
|
218
|
+
$dyn.commit
|
219
|
+
end
|
220
|
+
else
|
221
|
+
say "DynAPI: Apply and force modes in effect. Committing!"
|
222
|
+
$dyn.commit
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
$dyn.logout
|
data/ezdyn.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative 'lib/ezdyn/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "ezdyn"
|
5
|
+
s.version = EZDyn::VERSION
|
6
|
+
s.author = "David Adams"
|
7
|
+
s.email = "dadams@instructure.com"
|
8
|
+
s.date = Time.now.strftime("%Y-%m-%d")
|
9
|
+
s.license = "Nonstandard"
|
10
|
+
s.homepage = "https://github.com/instructure"
|
11
|
+
|
12
|
+
s.summary = "Simple library for Dyn Managed DNS"
|
13
|
+
s.description = "Library and CLI tool for easy Dynect DNS updates"
|
14
|
+
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
s.files =
|
17
|
+
Dir["lib/**/*.rb"] +
|
18
|
+
Dir["*.md"] +
|
19
|
+
["ezdyn.gemspec"]
|
20
|
+
s.bindir = "bin"
|
21
|
+
s.executables = ["ezdyn"]
|
22
|
+
|
23
|
+
s.add_development_dependency 'yard', '~>0.8'
|
24
|
+
end
|
data/lib/ezdyn.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'ezdyn/version'
|
3
|
+
require 'ezdyn/consts'
|
4
|
+
require 'ezdyn/log'
|
5
|
+
require 'ezdyn/client'
|
6
|
+
require 'ezdyn/zone'
|
7
|
+
require 'ezdyn/record_type'
|
8
|
+
require 'ezdyn/response'
|
9
|
+
require 'ezdyn/record'
|
10
|
+
require 'ezdyn/crud'
|
11
|
+
require 'ezdyn/changes'
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module EZDyn
|
2
|
+
class Client
|
3
|
+
# @private
|
4
|
+
def add_pending_change(chg)
|
5
|
+
@pending_changes ||= []
|
6
|
+
@pending_changes << chg
|
7
|
+
end
|
8
|
+
|
9
|
+
# @private
|
10
|
+
def pending_change_zones
|
11
|
+
@pending_changes.collect(&:zone).uniq
|
12
|
+
end
|
13
|
+
|
14
|
+
# List currently pending changes (optionally per zone).
|
15
|
+
#
|
16
|
+
# @param zone [String] (Optional) If specified, only return pending changes
|
17
|
+
# for the named zone.
|
18
|
+
# @return [Array] Array of [Change] objects awaiting commit.
|
19
|
+
def pending_changes(zone: nil)
|
20
|
+
if zone.nil?
|
21
|
+
@pending_changes
|
22
|
+
else
|
23
|
+
zone = Zone.new(client: self, name: zone)
|
24
|
+
@pending_changes.select do |pc|
|
25
|
+
pc.zone.name == zone.name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# @private
|
31
|
+
def clear_pending_changes(zone: nil)
|
32
|
+
if zone.nil?
|
33
|
+
@pending_changes = []
|
34
|
+
else
|
35
|
+
@pending_changes.delete_if do |pc|
|
36
|
+
pc.zone.name == zone.to_s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Generic base class for any pending change.
|
43
|
+
class Change
|
44
|
+
# Returns the zone this change is part of.
|
45
|
+
#
|
46
|
+
# @return [Zone] The zone this change is part of.
|
47
|
+
def zone
|
48
|
+
@record.zone
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# A pending record creation.
|
53
|
+
class CreateChange < Change
|
54
|
+
# @private
|
55
|
+
def initialize(record:)
|
56
|
+
@record = record
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns a string representation of the change.
|
60
|
+
#
|
61
|
+
# @return [String] A string representation of this change.
|
62
|
+
def to_s
|
63
|
+
"CREATE #{@record.fqdn}. #{@record.ttl} #{@record.type} #{@record.value}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# A pending record update.
|
68
|
+
class UpdateChange < Change
|
69
|
+
# @private
|
70
|
+
def initialize(record:, new_record:)
|
71
|
+
@record = record.sync!
|
72
|
+
@new_record = new_record
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns a string representation of the change.
|
76
|
+
#
|
77
|
+
# @return [String] A string representation of this change.
|
78
|
+
def to_s
|
79
|
+
ttl_string =
|
80
|
+
if @record.ttl == @new_record.ttl
|
81
|
+
@record.ttl
|
82
|
+
else
|
83
|
+
"(( #{@record.ttl} -> #{@new_record.ttl} ))"
|
84
|
+
end
|
85
|
+
|
86
|
+
value_string =
|
87
|
+
if @record.value == @new_record.value
|
88
|
+
@record.value
|
89
|
+
else
|
90
|
+
"(( #{@record.value} -> #{@new_record.value} ))"
|
91
|
+
end
|
92
|
+
|
93
|
+
"UPDATE #{@record.fqdn}. #{ttl_string} #{@record.type} #{value_string}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# A pending record deletion.
|
98
|
+
class DeleteChange < Change
|
99
|
+
# @private
|
100
|
+
def initialize(record:)
|
101
|
+
@record = record.sync!
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns a string representation of the change.
|
105
|
+
#
|
106
|
+
# @return [String] A string representation of this change.
|
107
|
+
def to_s
|
108
|
+
"DELETE #{@record.fqdn}. #{@record.ttl} #{@record.type} #{@record.value}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|