r53z 0.2.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 +7 -0
- data/.gitignore +53 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +84 -0
- data/LICENSE +674 -0
- data/LICENSE.txt +16 -0
- data/README.md +141 -0
- data/README.rdoc +1 -0
- data/Rakefile +62 -0
- data/bin/console +16 -0
- data/bin/r53z +39 -0
- data/bin/setup +8 -0
- data/lib/r53z.rb +10 -0
- data/lib/r53z/cli.rb +117 -0
- data/lib/r53z/client.rb +220 -0
- data/lib/r53z/config.rb +22 -0
- data/lib/r53z/file.rb +33 -0
- data/lib/r53z/version.rb +3 -0
- data/r53z.gemspec +38 -0
- metadata +189 -0
data/LICENSE.txt
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Name: r53z
|
2
|
+
Copyright (c) 2016 Joe Cooper
|
3
|
+
|
4
|
+
This program is free software: you can redistribute it and/or modify
|
5
|
+
it under the terms of the GNU General Public License as published by
|
6
|
+
the Free Software Foundation, either version 3 of the License, or
|
7
|
+
(at your option) any later version.
|
8
|
+
|
9
|
+
This program is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License
|
15
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
data/README.md
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
# R53z
|
2
|
+
|
3
|
+
A simple CLI, REPL, and library for managing Route 53. It's primary purpose is to back up and restore Route 53 zones. It can write zones to files in JSON format. It also provides a simple API (not a whole lot easier than the Amazon official API, but for backups and restores, it's much easier to script with and removes tons of boilerplate).
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'r53z'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install r53z
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Configure a credentials file in `~/.aws/credentials` (this should be an INI file; same as several other AWS utilities expect). It'll look something like this:
|
24
|
+
|
25
|
+
```ini
|
26
|
+
[default]
|
27
|
+
aws_access_key_id = ACCESS_KEY_ID
|
28
|
+
aws_secret_access_key = SECRET_ACCESS_KEY
|
29
|
+
region=us-east-1
|
30
|
+
```
|
31
|
+
|
32
|
+
You can use the `--section` option to choose which section to use from the credentials file, and the credentials file can be specified with the `--credentials` option. Region is irrelevant with Route 53, but the aws-sdk weirdly still requires it be present.
|
33
|
+
|
34
|
+
```
|
35
|
+
Usage: r53z [options] [args...]
|
36
|
+
|
37
|
+
Simple CLI to manage, backup, and restore, Route 53 zones
|
38
|
+
|
39
|
+
v0.2.0
|
40
|
+
|
41
|
+
Options:
|
42
|
+
-h, --help Show command line help
|
43
|
+
-x, --export Export zones to files in specified directory, optionally specify one or more zones.
|
44
|
+
-r, --restore Restore zone from directory, optionally specify one or more zones.
|
45
|
+
-l, --list List name and ID of one or all zones.
|
46
|
+
-s, --record-sets List record sets for the given zone.
|
47
|
+
-c, --create NAME Create a zone with the given name and optional --comment and --delegation-set.
|
48
|
+
-n, --comment COMMENT Optional comment when creating a zone.
|
49
|
+
-d, --delete Delete one or more zone(s) by name (WARNING: No confirmation!)
|
50
|
+
-C, --credentials File containing credentials information.
|
51
|
+
-u, --section Section (user) in the credentials file to use.
|
52
|
+
-g, --delegation-set ID Delegation set ID to use for various operations.
|
53
|
+
-t, --list-delegation-sets List delegation set for named zone, or all sets if no zone specified.
|
54
|
+
-D, --delete-delegation-sets Delete one or more delegation sets by ID (WARNING: No confirmation!
|
55
|
+
-N, --name-servers ID List name servers for delegation set.
|
56
|
+
--version Show help/version info
|
57
|
+
--log-level LEVEL Set the logging level
|
58
|
+
(debug|info|warn|error|fatal)
|
59
|
+
(Default: info)
|
60
|
+
|
61
|
+
```
|
62
|
+
|
63
|
+
### Command Line Options
|
64
|
+
|
65
|
+
#### --export|-x {path} [zones]
|
66
|
+
|
67
|
+
Export one or more zones to the named directory.
|
68
|
+
|
69
|
+
Requires a directory path (e.g. /tmp/zonedumps), and optionally one or more zone names. All zones will be exported if none are specified. If a delegation set ID is given, only zones that share the given delegation set will be exported.
|
70
|
+
|
71
|
+
Two files will be generated in the directory specified, one for the zone metadata (with a zoneinfo.json extension) and the zone records information (with a .json extension). The zone metadata file will contain all of the information needed to recreate the zone, incluing the delegation set ID. And, the record set file will contain all of the record sets needed to repopulate the zone; SOA and NS records are not restored as they are defined by the delegation set, rather than by records within the zone.
|
72
|
+
|
73
|
+
##### Example
|
74
|
+
|
75
|
+
```
|
76
|
+
$ r53z --export /home/joe/zones swelljoe.com
|
77
|
+
```
|
78
|
+
|
79
|
+
#### --restore|-r {path} [zones]
|
80
|
+
|
81
|
+
Restore one or more zones from files in the named directory.
|
82
|
+
|
83
|
+
Requires a directory path, and optionally one or more zone names. If no names are given, all files in the directory will be restored. If a delegation set is specified, all zones will be added to the delegation set specified. (The zone info and the record sets don't contain delegation set information, making delegation set selection on restore a little difficult to automate.)
|
84
|
+
|
85
|
+
If `--delegation-set` is specified on the command line, it will override the delegation-set information provided in the zoneinfo file. This can be used if the delegation set specified in the file is no longer available, for some reason (deletion, migrating to a new account, etc.).
|
86
|
+
|
87
|
+
##### Example
|
88
|
+
|
89
|
+
```
|
90
|
+
$ r53z --restore /home/joe/zones swelljoe.com
|
91
|
+
```
|
92
|
+
|
93
|
+
#### --list|-l [--delegation-set ID] [zones]
|
94
|
+
|
95
|
+
List hosted zones. List can be restricted to just the listed zones, or to a given delegation set. Output is JSON encoded, and will contain the name and ID of each zone. If no zones are specified, all zones will be listed.
|
96
|
+
|
97
|
+
#### --create|-c NAME [--comment COMMENT] [--delegation-set ID]
|
98
|
+
|
99
|
+
Create zone of the NAME provided. An optional command an delegation set ID may be provided.
|
100
|
+
|
101
|
+
##### Example
|
102
|
+
|
103
|
+
```
|
104
|
+
$ r53z --create swelljoe.com --comment "My domain"
|
105
|
+
```
|
106
|
+
|
107
|
+
#### --delete|-d {zone}
|
108
|
+
|
109
|
+
Delete one or more zones. Argument is the name of the zone, or zones, to delete. This command deletes the record sets for the zone first, and then deletes the zone itself (a zone with records cannot be deleted). There is no confirmation step for this option.
|
110
|
+
|
111
|
+
##### Example
|
112
|
+
|
113
|
+
```
|
114
|
+
$ r53z --delete swelljoe.com virtualmin.com
|
115
|
+
```
|
116
|
+
|
117
|
+
#### --credentials|-c {path/filename}
|
118
|
+
|
119
|
+
Specify the credentials configuration file on the command line. The file must be an INI file. By default, it will look for a file in ~/.aws/credentials (which is common across several AWS management tools). You can use the `--section` option to choose what section of the file to use.
|
120
|
+
|
121
|
+
## Development
|
122
|
+
|
123
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
124
|
+
|
125
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
126
|
+
|
127
|
+
#### Running Tests
|
128
|
+
|
129
|
+
To run the full test suite, you need a read/write capable account. There must be a configuration file in `test/data/secret-credentials` containing a `[default]` section with your keys. The format of this file is, as above, a .ini file.
|
130
|
+
|
131
|
+
To run all tests:
|
132
|
+
|
133
|
+
```
|
134
|
+
$ rake test
|
135
|
+
```
|
136
|
+
|
137
|
+
This will create a few test zones in your account, but unless something goes wrong during the test, they will be removed immediately after, never triggering billing from Amazon. The zones will have somewhat randomly generated names, so they should never clash with existing names (but you may wish to create a non-production account just for testing).
|
138
|
+
|
139
|
+
## Contributing
|
140
|
+
|
141
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/swelljoe/r53z.
|
data/README.rdoc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
See README.md
|
data/Rakefile
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
def dump_load_path
|
2
|
+
puts $LOAD_PATH.join("\n")
|
3
|
+
found = nil
|
4
|
+
$LOAD_PATH.each do |path|
|
5
|
+
if File.exists?(File.join(path,"rspec"))
|
6
|
+
puts "Found rspec in #{path}"
|
7
|
+
if File.exists?(File.join(path,"rspec","core"))
|
8
|
+
puts "Found core"
|
9
|
+
if File.exists?(File.join(path,"rspec","core","rake_task"))
|
10
|
+
puts "Found rake_task"
|
11
|
+
found = path
|
12
|
+
else
|
13
|
+
puts "!! no rake_task"
|
14
|
+
end
|
15
|
+
else
|
16
|
+
puts "!!! no core"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
if found.nil?
|
21
|
+
puts "Didn't find rspec/core/rake_task anywhere"
|
22
|
+
else
|
23
|
+
puts "Found in #{path}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
require 'bundler'
|
27
|
+
require 'rake/clean'
|
28
|
+
|
29
|
+
require 'rake/testtask'
|
30
|
+
|
31
|
+
require 'cucumber'
|
32
|
+
require 'cucumber/rake/task'
|
33
|
+
gem 'rdoc' # we need the installed RDoc gem, not the system one
|
34
|
+
require 'rdoc/task'
|
35
|
+
|
36
|
+
include Rake::DSL
|
37
|
+
|
38
|
+
Bundler::GemHelper.install_tasks
|
39
|
+
|
40
|
+
|
41
|
+
Rake::TestTask.new do |t|
|
42
|
+
t.pattern = 'test/tc_*.rb'
|
43
|
+
t.warning = false # Get rid of purious warnings from the AWS and Methadone libs?
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
CUKE_RESULTS = 'results.html'
|
48
|
+
CLEAN << CUKE_RESULTS
|
49
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
50
|
+
t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty --no-source -x"
|
51
|
+
t.fork = false
|
52
|
+
end
|
53
|
+
|
54
|
+
Rake::RDocTask.new do |rd|
|
55
|
+
|
56
|
+
rd.main = "README.rdoc"
|
57
|
+
|
58
|
+
rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
|
59
|
+
end
|
60
|
+
|
61
|
+
task :default => [:test,:features]
|
62
|
+
|
data/bin/console
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "r53z"
|
5
|
+
require "pry"
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
creds = R53z::Config.new()
|
10
|
+
client = R53z::Client.new('default', creds)
|
11
|
+
|
12
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
13
|
+
binding.pry
|
14
|
+
|
15
|
+
#require "irb"
|
16
|
+
#IRB.start
|
data/bin/r53z
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'methadone'
|
5
|
+
require 'r53z.rb'
|
6
|
+
|
7
|
+
class App
|
8
|
+
include Methadone::Main
|
9
|
+
include Methadone::CLILogging
|
10
|
+
|
11
|
+
main do |*args|
|
12
|
+
help_now! "No options provided." if options.empty?
|
13
|
+
R53z::Cli.new(:options => options, :args => args)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Command line interface specification
|
17
|
+
description "Simple CLI to manage, backup, and restore, Route 53 zones"
|
18
|
+
#
|
19
|
+
# Accept flags via:
|
20
|
+
on("-x", "--export", "Export zones to files in specified directory, optionally specify one or more zones")
|
21
|
+
on("-r", "--restore", "Restore zone from directory, optionally specify one or more zones")
|
22
|
+
on("-l", "--list", "List name and ID of one or all zones")
|
23
|
+
on("-s", "--record-sets", "List record sets for the given zone")
|
24
|
+
on("-d", "--delete", "Delete one or more zone(s) by name (WARNING: No confirmation!)")
|
25
|
+
on("-c", "--credentials", "File containing credentials information")
|
26
|
+
on("-u", "--section", "Section (user) in the credentials file to use")
|
27
|
+
on("-g ID", "--delegation-set", "Delegation set ID to use for various operations")
|
28
|
+
on("-t", "--list-delegation-sets", "List all delegation sets")
|
29
|
+
on("-n ID", "--name-servers", "List name servers for delegation set")
|
30
|
+
# args greedily grabs any files or zones listed after the options
|
31
|
+
arg(:args, :any)
|
32
|
+
|
33
|
+
version R53z::VERSION
|
34
|
+
|
35
|
+
use_log_level_option :toggle_debug_on_signal => 'USR1'
|
36
|
+
|
37
|
+
go!
|
38
|
+
end
|
39
|
+
|
data/bin/setup
ADDED
data/lib/r53z.rb
ADDED
data/lib/r53z/cli.rb
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module R53z
|
4
|
+
class Cli
|
5
|
+
include Methadone::Main
|
6
|
+
include Methadone::CLILogging
|
7
|
+
|
8
|
+
def initialize(options:, args:)
|
9
|
+
section = options[:section] || 'default'
|
10
|
+
config_file = options[:credentials]
|
11
|
+
creds = R53z::Config.new(config_file)
|
12
|
+
@client = R53z::Client.new(section, creds)
|
13
|
+
|
14
|
+
# XXX Dispath table seems smarter...can't figure out how to calls methods based
|
15
|
+
# directly on hash keys at the moment.
|
16
|
+
if options[:export]
|
17
|
+
help_now! "Export requires a directory path for zone files" if args.length < 1
|
18
|
+
export(:options => options, :args => args)
|
19
|
+
end
|
20
|
+
|
21
|
+
if options[:restore]
|
22
|
+
if args.empty?
|
23
|
+
help_now! "Restore requires a directory containing zone files and optionally one or more zones to restore"
|
24
|
+
end
|
25
|
+
restore(:options => options, :args => args)
|
26
|
+
end
|
27
|
+
|
28
|
+
if options[:list]
|
29
|
+
list(options: options, args: args)
|
30
|
+
end
|
31
|
+
|
32
|
+
if options[:delete]
|
33
|
+
if args.empty?
|
34
|
+
help_now! "Delete requires one or more zone names"
|
35
|
+
end
|
36
|
+
args.each do |name|
|
37
|
+
@client.delete(name)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
if options['list-delegation-sets']
|
42
|
+
sets = @client.list_delegation_sets
|
43
|
+
sets.each do |set|
|
44
|
+
puts JSON.pretty_generate(set.to_h)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
if options['record-sets']
|
49
|
+
if args.empty?
|
50
|
+
help_now! "List record sets requires one or more zone names"
|
51
|
+
end
|
52
|
+
args.each do |name|
|
53
|
+
sets = @client.record_list(@client.get_zone_id(name))
|
54
|
+
puts JSON.pretty_generate(sets)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if options['name-servers']
|
59
|
+
dset = @client.get_delegation_set(id: options['name-servers'])
|
60
|
+
puts JSON.pretty_generate(dset.delegation_set[:name_servers])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def export(options:, args:)
|
65
|
+
path = args.shift
|
66
|
+
# If no zones, dump all zones
|
67
|
+
zones = []
|
68
|
+
# One zone, multiple, or all?
|
69
|
+
if args.empty?
|
70
|
+
@client.list.each do |zone|
|
71
|
+
zones.push(zone[:name])
|
72
|
+
end
|
73
|
+
else
|
74
|
+
zones = args
|
75
|
+
end
|
76
|
+
|
77
|
+
zones.each do |name|
|
78
|
+
@client.dump(path, name)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def restore(options:, args:)
|
83
|
+
path = args.shift
|
84
|
+
# If no zones, restore all zones in directory
|
85
|
+
zones = []
|
86
|
+
if args.empty?
|
87
|
+
# derive list of zones from files in path
|
88
|
+
zones = Dir[File.join(path, "*.json")].reject {|n| n.match("zoneinfo")}
|
89
|
+
else
|
90
|
+
# restore the ones specified
|
91
|
+
args.each do |zone|
|
92
|
+
zones.push(zone)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
zones.each do |zone|
|
97
|
+
@client.restore(path, zone)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def list(options:, args:)
|
102
|
+
if args.any?
|
103
|
+
args.each do |name|
|
104
|
+
puts JSON.pretty_generate(
|
105
|
+
@client.list(
|
106
|
+
:name => name,
|
107
|
+
:delegation_set_id => options['delegation-set']))
|
108
|
+
end
|
109
|
+
else
|
110
|
+
puts JSON.pretty_generate(
|
111
|
+
@client.list(:delegation_set_id => options['delegation-set'])
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
data/lib/r53z/client.rb
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
module R53z
|
2
|
+
class Client
|
3
|
+
include Methadone::Main
|
4
|
+
include Methadone::CLILogging
|
5
|
+
attr_accessor :client
|
6
|
+
|
7
|
+
def initialize(section, creds)
|
8
|
+
@client = Aws::Route53::Client.new(
|
9
|
+
access_key_id: creds[section]['aws_access_key_id'],
|
10
|
+
secret_access_key: creds[section]['aws_secret_access_key'],
|
11
|
+
region: creds[section]['region']
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
# list one or all zones by name and ID
|
16
|
+
def list(name: nil, delegation_set_id: nil)
|
17
|
+
begin
|
18
|
+
zones = self.client.list_hosted_zones(
|
19
|
+
delegation_set_id: delegation_set_id
|
20
|
+
)['hosted_zones']
|
21
|
+
rescue Aws::Route53::Errors::ServiceError
|
22
|
+
error "Failed to list zones" # XXX How do we get AWS error message out of it?
|
23
|
+
end
|
24
|
+
|
25
|
+
rv = []
|
26
|
+
zones.each do |zone|
|
27
|
+
if name
|
28
|
+
unless name[-1] == '.'
|
29
|
+
name = name + '.'
|
30
|
+
end
|
31
|
+
unless name == zone[:name]
|
32
|
+
next
|
33
|
+
end
|
34
|
+
end
|
35
|
+
rv.push({:name => zone[:name], :id => zone[:id]})
|
36
|
+
end
|
37
|
+
rv
|
38
|
+
end
|
39
|
+
|
40
|
+
# Create zone with record(s) from an info and records hash
|
41
|
+
def create(info:, records:)
|
42
|
+
rv = {} # pile up the responses in a single hash
|
43
|
+
#if self.list(info[:name]).any?
|
44
|
+
# error(info[:name] + "exists")
|
45
|
+
#end
|
46
|
+
# XXX: AWS sends out a data structure with config:, but expects
|
47
|
+
# hosted_zone_config on create/restore. argh.
|
48
|
+
# XXX: also, private_zone is not accepted here for some reason
|
49
|
+
zone = info[:hosted_zone]
|
50
|
+
zone_data = {
|
51
|
+
:name => zone[:name],
|
52
|
+
:caller_reference => 'r53z-create-' + self.random_string,
|
53
|
+
:hosted_zone_config => {
|
54
|
+
:comment => zone[:config][:comment]
|
55
|
+
}
|
56
|
+
}
|
57
|
+
# command line overrides everything else
|
58
|
+
if options['delegation-set']
|
59
|
+
zone_data[:delegation_set_id] = options['delegation-set']
|
60
|
+
elsif info[:delegation_set] and info[:delegation_set][:id]
|
61
|
+
zone_data[:delegation_set_id] = info[:delegation_set][:id]
|
62
|
+
end
|
63
|
+
zone_resp = self.client.create_hosted_zone(zone_data)
|
64
|
+
rv[:hosted_zone_resp] = zone_resp
|
65
|
+
rv[:record_set_resp] = []
|
66
|
+
records.each do |record|
|
67
|
+
# skip these, as they are handled separately (delegation set?)
|
68
|
+
unless (record[:type] == "NS" || record[:type] == "SOA")
|
69
|
+
record_resp = self.client.change_resource_record_sets({
|
70
|
+
:hosted_zone_id => zone_resp[:hosted_zone][:id],
|
71
|
+
:change_batch => {
|
72
|
+
:changes => [
|
73
|
+
{
|
74
|
+
:action => "CREATE",
|
75
|
+
:resource_record_set => record
|
76
|
+
}
|
77
|
+
]
|
78
|
+
}
|
79
|
+
})
|
80
|
+
rv[:record_set_resp].push(record_resp)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
return rv
|
84
|
+
end
|
85
|
+
|
86
|
+
# delete a zone by name
|
87
|
+
def delete(name)
|
88
|
+
# get the ID
|
89
|
+
zone_id = self.list(:name => name).first[:id]
|
90
|
+
self.delete_all_rr_sets(zone_id)
|
91
|
+
client.delete_hosted_zone(:id => zone_id)
|
92
|
+
end
|
93
|
+
|
94
|
+
# delete all of the resource record sets in a zone (this is required to delete
|
95
|
+
# a zone
|
96
|
+
def delete_all_rr_sets(zone_id)
|
97
|
+
self.record_list(zone_id).reject do |rs|
|
98
|
+
(rs[:type] == "NS" || rs[:type] == "SOA")
|
99
|
+
end.each do |record_set|
|
100
|
+
self.client.change_resource_record_sets({
|
101
|
+
:hosted_zone_id => zone_id,
|
102
|
+
:change_batch => {
|
103
|
+
:changes => [{
|
104
|
+
:action=> "DELETE",
|
105
|
+
:resource_record_set => record_set
|
106
|
+
}]
|
107
|
+
}
|
108
|
+
})
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# dump a zone to a direcory. Will generate two files; a zoneinfo file and a
|
113
|
+
# records file.
|
114
|
+
def dump(dirpath, name)
|
115
|
+
# Get the ID
|
116
|
+
zone_id = self.list(:name => name).first[:id]
|
117
|
+
|
118
|
+
# normalize name
|
119
|
+
unless name[-1] == '.'
|
120
|
+
name = name + '.'
|
121
|
+
end
|
122
|
+
|
123
|
+
# dump the record sets
|
124
|
+
R53z::JsonFile.write_json(
|
125
|
+
path: File.join(dirpath, name),
|
126
|
+
data: self.record_list(zone_id))
|
127
|
+
|
128
|
+
# Dump the zone metadata, plus the delegated set info
|
129
|
+
out = { :hosted_zone =>
|
130
|
+
self.client.get_hosted_zone({ :id => zone_id}).hosted_zone.to_h,
|
131
|
+
:delegation_set =>
|
132
|
+
self.client.get_hosted_zone({:id => zone_id}).delegation_set.to_h
|
133
|
+
}
|
134
|
+
|
135
|
+
R53z::JsonFile.write_json(
|
136
|
+
path: File.join(dirpath, name + "zoneinfo"),
|
137
|
+
data: out)
|
138
|
+
#data: self.client.get_hosted_zone({
|
139
|
+
# :id => zone_id}).hosted_zone.to_h)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Restore a zone from the given path. It expects files named
|
143
|
+
# zone.zoneinfo.json and zone.json
|
144
|
+
def restore(path, domain)
|
145
|
+
# normalize domain
|
146
|
+
unless domain[-1] == '.'
|
147
|
+
domain = domain + '.'
|
148
|
+
end
|
149
|
+
# Load up the zone info file
|
150
|
+
file = File.join(path, domain)
|
151
|
+
info = R53z::JsonFile.read_json(path: file + "zoneinfo")
|
152
|
+
records = R53z::JsonFile.read_json(path: file)
|
153
|
+
# create the zone and the record sets
|
154
|
+
self.create(:info => info, :records => records)
|
155
|
+
end
|
156
|
+
|
157
|
+
def record_list(zone_id)
|
158
|
+
records = self.client.list_resource_record_sets(hosted_zone_id: zone_id)
|
159
|
+
rv = []
|
160
|
+
records[:resource_record_sets].each do |record|
|
161
|
+
rv.push(record.to_h)
|
162
|
+
end
|
163
|
+
rv
|
164
|
+
end
|
165
|
+
|
166
|
+
# create a new delegation set, optionally associated with an existing zone
|
167
|
+
def create_delegation_set(zone_id = nil)
|
168
|
+
self.client.create_reusable_delegation_set({
|
169
|
+
caller_reference: 'r53z-create-del-set-' + self.random_string,
|
170
|
+
hosted_zone_id: zone_id
|
171
|
+
})
|
172
|
+
end
|
173
|
+
|
174
|
+
# list all delegation sets
|
175
|
+
def list_delegation_sets
|
176
|
+
resp = self.client.list_reusable_delegation_sets({})
|
177
|
+
return resp.delegation_sets
|
178
|
+
end
|
179
|
+
|
180
|
+
# get details of a delegation set specified by ID, incuding name servers
|
181
|
+
def get_delegation_set(id)
|
182
|
+
self.client.get_reusable_delegation_set({
|
183
|
+
id: id
|
184
|
+
})
|
185
|
+
end
|
186
|
+
|
187
|
+
# delete a delegation set by ID or name
|
188
|
+
def delete_delegation_set(id: nil, name: nil)
|
189
|
+
if name and not id
|
190
|
+
id = get_delegation_set_id(name)
|
191
|
+
end
|
192
|
+
self.client.delete_reusable_delegation_set({
|
193
|
+
id: id
|
194
|
+
})
|
195
|
+
end
|
196
|
+
|
197
|
+
# Get delegation set ID for the given zone
|
198
|
+
def get_delegation_set_id(name)
|
199
|
+
begin
|
200
|
+
zone_id = self.list(:name => name).first[:id]
|
201
|
+
rescue
|
202
|
+
return nil
|
203
|
+
end
|
204
|
+
return self.client.get_hosted_zone({
|
205
|
+
id: zone_id
|
206
|
+
}).delegation_set[:id]
|
207
|
+
end
|
208
|
+
|
209
|
+
# Get the zone id from name
|
210
|
+
def get_zone_id(name)
|
211
|
+
return self.list(:name => name).first[:id]
|
212
|
+
end
|
213
|
+
|
214
|
+
# random string generator helper function
|
215
|
+
def random_string(len=16)
|
216
|
+
rand(36**len).to_s(36)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|