ezdyn 0.2.2
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 +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
|