gandi_v5 0.3.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +56 -2
- data/.travis.yml +6 -1
- data/CHANGELOG.md +61 -0
- data/LICENSE.md +2 -6
- data/README.md +15 -9
- data/gandi_v5.gemspec +2 -2
- data/lib/gandi_v5.rb +40 -14
- data/lib/gandi_v5/data.rb +1 -2
- data/lib/gandi_v5/data/converter/integer.rb +26 -0
- data/lib/gandi_v5/domain.rb +13 -14
- data/lib/gandi_v5/email/forward.rb +2 -8
- data/lib/gandi_v5/email/mailbox.rb +57 -11
- data/lib/gandi_v5/email/slot.rb +11 -3
- data/lib/gandi_v5/live_dns.rb +0 -12
- data/lib/gandi_v5/live_dns/domain.rb +313 -29
- data/lib/gandi_v5/live_dns/domain/dnssec_key.rb +115 -0
- data/lib/gandi_v5/live_dns/domain/record.rb +81 -0
- data/lib/gandi_v5/live_dns/domain/snapshot.rb +107 -0
- data/lib/gandi_v5/live_dns/domain/tsig_key.rb +71 -0
- data/lib/gandi_v5/organization.rb +10 -0
- data/lib/gandi_v5/organization/customer.rb +90 -0
- data/lib/gandi_v5/sharing_space.rb +27 -0
- data/lib/gandi_v5/simple_hosting.rb +12 -0
- data/lib/gandi_v5/simple_hosting/instance.rb +242 -0
- data/lib/gandi_v5/simple_hosting/instance/application.rb +44 -0
- data/lib/gandi_v5/simple_hosting/instance/database.rb +19 -0
- data/lib/gandi_v5/simple_hosting/instance/language.rb +22 -0
- data/lib/gandi_v5/simple_hosting/instance/upgrade.rb +21 -0
- data/lib/gandi_v5/simple_hosting/instance/virtual_host.rb +187 -0
- data/lib/gandi_v5/simple_hosting/instance/virtual_host/linked_dns_zone.rb +74 -0
- data/lib/gandi_v5/version.rb +1 -1
- data/spec/.rubocop.yml +56 -2
- data/spec/fixtures/bodies/GandiV5_Domain/fetch.yml +8 -0
- data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain/fetch.yml +1 -2
- data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain/list_tsig.yml +3 -0
- data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain/nameservers.yml +3 -0
- data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain_DnssecKey/fetch.yml +12 -0
- data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain_DnssecKey/list.yml +9 -0
- data/spec/fixtures/bodies/{GandiV5_LiveDNS_Zone_Snapshot → GandiV5_LiveDNS_Domain_Snapshot}/fetch.yml +4 -3
- data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain_Snapshot/list.yml +5 -0
- data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain_TsigKey/fetch.yml +9 -0
- data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain_TsigKey/list.yml +4 -0
- data/spec/fixtures/bodies/GandiV5_Organization_Customer/list.yml +8 -0
- data/spec/fixtures/bodies/GandiV5_SimpleHosting_Instance/fetch.yml +80 -0
- data/spec/fixtures/bodies/GandiV5_SimpleHosting_Instance/list.yml +38 -0
- data/spec/fixtures/bodies/GandiV5_SimpleHosting_Instance_VirtualHost/fetch.yml +26 -0
- data/spec/fixtures/bodies/GandiV5_SimpleHosting_Instance_VirtualHost/list.yml +18 -0
- data/spec/units/gandi_v5/data/converter/integer_spec.rb +16 -0
- data/spec/units/gandi_v5/domain_spec.rb +53 -43
- data/spec/units/gandi_v5/email/forward_spec.rb +5 -34
- data/spec/units/gandi_v5/email/mailbox_spec.rb +119 -37
- data/spec/units/gandi_v5/email/slot_spec.rb +10 -2
- data/spec/units/gandi_v5/live_dns/domain/dnssec_key_spec.rb +128 -0
- data/spec/units/gandi_v5/live_dns/{record_set_spec.rb → domain/record_spec.rb} +1 -1
- data/spec/units/gandi_v5/live_dns/domain/snapshot_spec.rb +101 -0
- data/spec/units/gandi_v5/live_dns/domain/tsig_key_spec.rb +78 -0
- data/spec/units/gandi_v5/live_dns/domain_spec.rb +297 -118
- data/spec/units/gandi_v5/live_dns_spec.rb +0 -12
- data/spec/units/gandi_v5/organization/customer_spec.rb +81 -0
- data/spec/units/gandi_v5/organization_spec.rb +14 -0
- data/spec/units/gandi_v5/sharing_space_spec.rb +4 -0
- data/spec/units/gandi_v5/simple_hosting/instance/application_spec.rb +37 -0
- data/spec/units/gandi_v5/simple_hosting/instance/database_spec.rb +4 -0
- data/spec/units/gandi_v5/simple_hosting/instance/language_spec.rb +4 -0
- data/spec/units/gandi_v5/simple_hosting/instance/upgrade_spec.rb +4 -0
- data/spec/units/gandi_v5/simple_hosting/instance/virtual_host/linked_dns_zone_spec.rb +50 -0
- data/spec/units/gandi_v5/simple_hosting/instance/virtual_host_spec.rb +199 -0
- data/spec/units/gandi_v5/simple_hosting/instance_spec.rb +182 -0
- data/spec/units/gandi_v5/simple_hosting_spec.rb +9 -0
- data/spec/units/gandi_v5_spec.rb +139 -30
- metadata +50 -31
- data/lib/gandi_v5/domain/sharing_space.rb +0 -21
- data/lib/gandi_v5/live_dns/has_zone_records.rb +0 -153
- data/lib/gandi_v5/live_dns/record_set.rb +0 -79
- data/lib/gandi_v5/live_dns/zone.rb +0 -160
- data/lib/gandi_v5/live_dns/zone/snapshot.rb +0 -81
- data/spec/features/domain_spec.rb +0 -45
- data/spec/features/livedns_domain_spec.rb +0 -8
- data/spec/features/livedns_zone_spec.rb +0 -45
- data/spec/features/mailbox_spec.rb +0 -18
- data/spec/fixtures/bodies/GandiV5_LiveDNS_Zone/fetch.yml +0 -11
- data/spec/fixtures/bodies/GandiV5_LiveDNS_Zone/list.yml +0 -11
- data/spec/fixtures/bodies/GandiV5_LiveDNS_Zone_Snapshot/list.yml +0 -3
- data/spec/fixtures/vcr/Domain_features/List_domains.yml +0 -55
- data/spec/fixtures/vcr/Domain_features/Renew_domain.yml +0 -133
- data/spec/fixtures/vcr/LiveDNS_Domain_features/List_domains.yml +0 -32
- data/spec/fixtures/vcr/LiveDNS_Zone_features/List_zones.yml +0 -42
- data/spec/fixtures/vcr/LiveDNS_Zone_features/Make_and_save_snapshot.yml +0 -72
- data/spec/fixtures/vcr/LiveDNS_Zone_features/Save_zone_to_file.yml +0 -28
- data/spec/fixtures/vcr/Mailbox_features/List_mailboxes.yml +0 -39
- data/spec/units/gandi_v5/domain/sharing_space_spec.rb +0 -4
- data/spec/units/gandi_v5/live_dns/zone/snapshot_spec.rb +0 -66
- data/spec/units/gandi_v5/live_dns/zone_spec.rb +0 -347
@@ -76,18 +76,12 @@ class GandiV5
|
|
76
76
|
# e.g. ("alice" "*lice", "alic*").
|
77
77
|
# @return [Array<GandiV5::Email::Forward>]
|
78
78
|
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
79
|
-
def self.list(fqdn, page: (1..), **params)
|
80
|
-
page = [page.to_i] unless page.respond_to?(:each)
|
81
|
-
|
79
|
+
def self.list(fqdn, page: (1..), per_page: 100, **params)
|
82
80
|
params.reject! { |_k, v| v.nil? }
|
83
81
|
|
84
82
|
mailboxes = []
|
85
|
-
|
86
|
-
_response, data = GandiV5.get url(fqdn), params: params.merge(page: page_number)
|
87
|
-
break if data.empty?
|
88
|
-
|
83
|
+
GandiV5.paginated_get(url(fqdn), page, per_page, params: params) do |data|
|
89
84
|
mailboxes += data.map { |mailbox| from_gandi mailbox.merge(fqdn: fqdn) }
|
90
|
-
break if data.count < params.fetch(:per_page, 100)
|
91
85
|
end
|
92
86
|
mailboxes
|
93
87
|
end
|
@@ -105,6 +105,39 @@ class GandiV5
|
|
105
105
|
data['message']
|
106
106
|
end
|
107
107
|
|
108
|
+
# Upgrade a standard mailbox to premium.
|
109
|
+
# If the current slot is a free one, a new premium slot is created and
|
110
|
+
# used for the mailbox. Otherwise, the slot is upgraded to premium.
|
111
|
+
# @see https://api.gandi.net/docs/email#patch-v5-email-mailboxes-domain-mailbox_id-type
|
112
|
+
# @param sharing_id [String, #to_s, nil] (optional)
|
113
|
+
# the organisation ID to bill for the mailbox.
|
114
|
+
# @param dry_run [Boolean] whether the details should be checked instead
|
115
|
+
# of actually upgrading the mailbox.
|
116
|
+
# @return [true] if the mailbox was upgraded
|
117
|
+
# @return [false] if the mailbox was not upgraded (it's already premium)
|
118
|
+
# @return [Hash] if doing a dry run, you get what Gandi returns
|
119
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error
|
120
|
+
def upgrade(sharing_id: nil, dry_run: false)
|
121
|
+
patch_type :premium, sharing_id, dry_run
|
122
|
+
end
|
123
|
+
|
124
|
+
# Downgrade a premium mailbox to standard.
|
125
|
+
# If a free slot is available, the premium slot is destroyed
|
126
|
+
# (and refunded) and the free one is used for the mailbox.
|
127
|
+
# Otherwise, the slot is downgraded to standard.
|
128
|
+
# @see https://api.gandi.net/docs/email#patch-v5-email-mailboxes-domain-mailbox_id-type
|
129
|
+
# @param sharing_id [String, #to_s, nil] (optional)
|
130
|
+
# the organisation ID to bill for the mailbox.
|
131
|
+
# @param dry_run [Boolean] whether the details should be checked instead
|
132
|
+
# of actually downgrading the mailbox.
|
133
|
+
# @return [true] if the mailbox was downgraded
|
134
|
+
# @return [false] if the mailbox was not downgraded (it's already standard)
|
135
|
+
# @return [Hash] if doing a dry run, you get what Gandi returns
|
136
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error
|
137
|
+
def downgrade(sharing_id: nil, dry_run: false)
|
138
|
+
patch_type :standard, sharing_id, dry_run
|
139
|
+
end
|
140
|
+
|
108
141
|
# Create a new mailbox.
|
109
142
|
# Note that before you can create a mailbox, you must have a slot available.
|
110
143
|
# @see https://api.gandi.net/docs/email#post-v5-email-mailboxes-domain
|
@@ -113,10 +146,13 @@ class GandiV5
|
|
113
146
|
# @param password [String, #to_s] the password to use.
|
114
147
|
# @param aliases [Array<String, #to_s>] any alternative email address to be used.
|
115
148
|
# @param type [:standard, :premium] the type of mailbox slot to use.
|
149
|
+
# @param dry_run [Boolean] whether the details should be checked instead
|
150
|
+
# of actually creating the mailbox.
|
116
151
|
# @return [GandiV5::Email::Mailbox] The created mailbox.
|
117
152
|
# @raise [GandiV5::Error] if no slots are available.
|
118
153
|
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
119
|
-
|
154
|
+
# rubocop:disable Metrics/AbcSize
|
155
|
+
def self.create(fqdn, login, password, aliases: [], type: :standard, dry_run: false)
|
120
156
|
fail ArgumentError, "#{type.inspect} is not a valid type" unless TYPES.include?(type)
|
121
157
|
if GandiV5::Email::Slot.list.none? { |slot| slot.mailbox_type == type && slot.inactive? }
|
122
158
|
fail GandiV5::Error, "no available #{type} slots"
|
@@ -131,9 +167,11 @@ class GandiV5
|
|
131
167
|
aliases: aliases.push
|
132
168
|
}.to_json
|
133
169
|
|
134
|
-
response,
|
135
|
-
|
170
|
+
response, data = GandiV5.post(url(fqdn), body, 'Dry-Run': dry_run ? 1 : 0)
|
171
|
+
|
172
|
+
dry_run ? data : fetch(fqdn, response.headers[:location].split('/').last)
|
136
173
|
end
|
174
|
+
# rubocop:enable Metrics/AbcSize
|
137
175
|
|
138
176
|
# Get information for a mailbox.
|
139
177
|
# @see https://api.gandi.net/docs/email#get-v5-email-mailboxes-domain-mailbox_id
|
@@ -159,19 +197,13 @@ class GandiV5
|
|
159
197
|
# e.g. ("alice" "*lice", "alic*").
|
160
198
|
# @return [Array<GandiV5::Email::Mailbox>]
|
161
199
|
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
162
|
-
def self.list(fqdn, page: (1..), **params)
|
163
|
-
page = [page.to_i] unless page.respond_to?(:each)
|
164
|
-
|
200
|
+
def self.list(fqdn, page: (1..), per_page: 100, **params)
|
165
201
|
params['~login'] = params.delete(:login)
|
166
202
|
params.reject! { |_k, v| v.nil? }
|
167
203
|
|
168
204
|
mailboxes = []
|
169
|
-
|
170
|
-
_response, data = GandiV5.get url(fqdn), params: params.merge(page: page_number)
|
171
|
-
break if data.empty?
|
172
|
-
|
205
|
+
GandiV5.paginated_get(url(fqdn), page, per_page, params: params) do |data|
|
173
206
|
mailboxes += data.map { |mailbox| from_gandi mailbox }
|
174
|
-
break if data.count < params.fetch(:per_page, 100)
|
175
207
|
end
|
176
208
|
mailboxes
|
177
209
|
end
|
@@ -240,6 +272,20 @@ class GandiV5
|
|
240
272
|
def crypt_password(password)
|
241
273
|
self.class.send :crypt_password, password
|
242
274
|
end
|
275
|
+
|
276
|
+
def patch_type(new_type, sharing_id, dry_run)
|
277
|
+
fail ArgumentError unless TYPES.include?(new_type)
|
278
|
+
return false if type == new_type
|
279
|
+
|
280
|
+
url_ = "#{url}/type"
|
281
|
+
url_ = sharing_id ? "#{url_}?sharing_id=#{sharing_id}" : url_
|
282
|
+
body = { mailbox_type: new_type }
|
283
|
+
|
284
|
+
_response, data = GandiV5.patch(url_, body.to_json, 'Dry-Run': dry_run ? 1 : 0)
|
285
|
+
|
286
|
+
@type = new_type unless dry_run
|
287
|
+
dry_run ? data : true
|
288
|
+
end
|
243
289
|
end
|
244
290
|
end
|
245
291
|
end
|
data/lib/gandi_v5/email/slot.rb
CHANGED
@@ -78,15 +78,23 @@ class GandiV5
|
|
78
78
|
# but require more mailboxes on that domain, you must first purchase additional slots.
|
79
79
|
# @see https://api.gandi.net/docs/email#post-v5-email-slots-domain
|
80
80
|
# @param fqdn [String, #to_s] the fully qualified domain name to add the slot to.
|
81
|
-
# @param type [:standard, :premium]
|
81
|
+
# @param type [:standard, :premium] The type of slot to add.
|
82
|
+
# @param sharing_id [nil, String, #to_s] either:
|
83
|
+
# * nil (default) - nothing special happens
|
84
|
+
# * an organization ID - pay using another organization
|
85
|
+
# (you need to have billing permissions on the organization
|
86
|
+
# and use the same organization name for the domain name's owner).
|
87
|
+
# The invoice will be edited using this organization's information.
|
82
88
|
# @return [String] The confirmation message from Gandi.
|
83
89
|
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
84
|
-
def self.create(fqdn, type
|
90
|
+
def self.create(fqdn, type: :standard, sharing_id: nil)
|
85
91
|
body = {
|
86
92
|
mailbox_type: type
|
87
93
|
}.to_json
|
88
94
|
|
89
|
-
|
95
|
+
url_ = url(fqdn)
|
96
|
+
url_ += "?sharing_id=#{sharing_id}" if sharing_id
|
97
|
+
response, _data = GandiV5.post url_, body
|
90
98
|
fetch fqdn, response.headers[:location].split('/').last
|
91
99
|
end
|
92
100
|
|
data/lib/gandi_v5/live_dns.rb
CHANGED
@@ -4,8 +4,6 @@
|
|
4
4
|
class GandiV5
|
5
5
|
# Gandi LiveDNS Management API.
|
6
6
|
class LiveDNS
|
7
|
-
BASE = 'https://dns.api.gandi.net/api/v5/'
|
8
|
-
|
9
7
|
RECORD_TYPES = %w[
|
10
8
|
A AAAA CNAME MX NS TXT ALIAS
|
11
9
|
WKS SRV LOC SPF CAA DS SSHFP PTR KEY DNAME TLSA OPENPGPKEY CDS
|
@@ -21,16 +19,6 @@ class GandiV5
|
|
21
19
|
GandiV5::LiveDNS::Domain.list
|
22
20
|
end
|
23
21
|
|
24
|
-
# @see GandiV5::LiveDNS::Zone.fetch
|
25
|
-
def self.zone(uuid)
|
26
|
-
GandiV5::LiveDNS::Zone.fetch(uuid)
|
27
|
-
end
|
28
|
-
|
29
|
-
# @see GandiV5::LiveDNS::Zone.list
|
30
|
-
def self.zones
|
31
|
-
GandiV5::LiveDNS::Zone.list
|
32
|
-
end
|
33
|
-
|
34
22
|
# Raise an error if passed type is invalid.
|
35
23
|
# @param type [String] the record type to check.
|
36
24
|
# @return [nil]
|
@@ -5,19 +5,12 @@ class GandiV5
|
|
5
5
|
# A domain name within the LiveDNS system.
|
6
6
|
# @!attribute [r] fqdn
|
7
7
|
# @return [String]
|
8
|
-
# @!attribute [r]
|
9
|
-
# @return [
|
8
|
+
# @!attribute [r] automatic_snapshots
|
9
|
+
# @return [Boolean]
|
10
10
|
class Domain
|
11
11
|
include GandiV5::Data
|
12
|
-
include GandiV5::LiveDNS::HasZoneRecords
|
13
|
-
|
14
|
-
members :fqdn
|
15
12
|
|
16
|
-
|
17
|
-
:zone_uuid,
|
18
|
-
gandi_key: 'zone',
|
19
|
-
converter: GandiV5::Data::Converter.new(from_gandi: ->(zone) { zone&.split('/')&.last })
|
20
|
-
)
|
13
|
+
members :fqdn, :automatic_snapshots
|
21
14
|
|
22
15
|
# Refetch the information for this domain from Gandi.
|
23
16
|
# @return [GandiV5::LiveDNS::Domain]
|
@@ -27,35 +20,283 @@ class GandiV5
|
|
27
20
|
from_gandi data
|
28
21
|
end
|
29
22
|
|
30
|
-
#
|
31
|
-
# @param
|
23
|
+
# Update this domain's settings.
|
24
|
+
# @param automatic_snapshots [String, #to_s]
|
25
|
+
# Enable or disable the automatic creation of new snapshots when records are changed.
|
32
26
|
# @return [String] The confirmation message from Gandi.
|
33
27
|
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
self.zone_uuid = uuid
|
28
|
+
def update(automatic_snapshots:)
|
29
|
+
_response, data = GandiV5.patch url, { automatic_snapshots: automatic_snapshots }.to_json
|
30
|
+
self.automatic_snapshots = automatic_snapshots
|
38
31
|
data['message']
|
39
32
|
end
|
40
33
|
|
41
|
-
# @
|
42
|
-
|
43
|
-
|
34
|
+
# @overload fetch_records()
|
35
|
+
# Fetch all records for this domain.
|
36
|
+
# @param page [Integer, #each<Integer>] which page(s) of results to get.
|
37
|
+
# If page is not provided keep querying until an empty list is returned.
|
38
|
+
# If page responds to .each then iterate until an empty list is returned.
|
39
|
+
# @param per_page [Integer, #to_s] (optional default 100) how many results ot get per page.
|
40
|
+
# @overload fetch_records(name)
|
41
|
+
# Fetch records for a name.
|
42
|
+
# @param name [String] the name to fetch records for.
|
43
|
+
# @param page [Integer, #each<Integer>] which page(s) of results to get.
|
44
|
+
# If page is not provided keep querying until an empty list is returned.
|
45
|
+
# If page responds to .each then iterate until an empty list is returned.
|
46
|
+
# @param per_page [Integer, #to_s] (optional default 100) how many results ot get per page.
|
47
|
+
# @overload fetch_records(name, type)
|
48
|
+
# Fetch records of a type for a name.
|
49
|
+
# @param name [String] the name to fetch records for.
|
50
|
+
# @param type [String] the record type to fetch.
|
51
|
+
# @param page [Integer, #each<Integer>] which page(s) of results to get.
|
52
|
+
# If page is not provided keep querying until an empty list is returned.
|
53
|
+
# If page responds to .each then iterate until an empty list is returned.
|
54
|
+
# @param per_page [Integer, #to_s] (optional default 100) how many results ot get per page.
|
55
|
+
# @return [Array<GandiV5::LiveDNS::Domain::Record>]
|
56
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
57
|
+
def fetch_records(name = nil, type = nil, page: (1..), per_page: 100)
|
58
|
+
GandiV5::LiveDNS.require_valid_record_type type if type
|
59
|
+
|
60
|
+
url_ = "#{url}/records"
|
61
|
+
url_ += "/#{CGI.escape name}" if name
|
62
|
+
url_ += "/#{CGI.escape type}" if type
|
63
|
+
|
64
|
+
all = []
|
65
|
+
GandiV5.paginated_get(url_, page, per_page) do |data|
|
66
|
+
all += [*data].map { |item| GandiV5::LiveDNS::Domain::Record.from_gandi item }
|
67
|
+
end
|
68
|
+
all
|
44
69
|
end
|
45
70
|
|
46
|
-
#
|
47
|
-
#
|
71
|
+
# @overload fetch_zone_lines()
|
72
|
+
# Fetch all records for this domain.
|
73
|
+
# @overload fetch_zone_lines(name)
|
74
|
+
# Fetch records for a name.
|
75
|
+
# @param name [String] the name to fetch records for.
|
76
|
+
# @overload fetch_zone_lines(name, type)
|
77
|
+
# Fetch records of a type for a name.
|
78
|
+
# @param name [String] the name to fetch records for.
|
79
|
+
# @param type [String] the record type to fetch.
|
80
|
+
# @return [String]
|
48
81
|
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
49
|
-
def
|
50
|
-
|
82
|
+
def fetch_zone_lines(name = nil, type = nil)
|
83
|
+
GandiV5::LiveDNS.require_valid_record_type type if type
|
84
|
+
|
85
|
+
url_ = "#{url}/records"
|
86
|
+
url_ += "/#{CGI.escape name}" if name
|
87
|
+
url_ += "/#{CGI.escape type}" if type
|
88
|
+
|
89
|
+
GandiV5.get(url_, accept: 'text/plain').last
|
90
|
+
end
|
91
|
+
|
92
|
+
# Add record to this domain.
|
93
|
+
# @param name [String]
|
94
|
+
# @param type [String]
|
95
|
+
# @param ttl [Integer]
|
96
|
+
# @param values [Array<String>]
|
97
|
+
# @return [String] The confirmation message from Gandi.
|
98
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
99
|
+
def add_record(name, type, ttl, *values)
|
100
|
+
GandiV5::LiveDNS.require_valid_record_type type
|
101
|
+
fail ArgumentError, 'ttl must be positive and non-zero' unless ttl.positive?
|
102
|
+
fail ArgumentError, 'there must be at least one value' if values.none?
|
103
|
+
|
104
|
+
body = {
|
105
|
+
rrset_name: name,
|
106
|
+
rrset_type: type,
|
107
|
+
rrset_ttl: ttl,
|
108
|
+
rrset_values: values
|
109
|
+
}.to_json
|
110
|
+
_response, data = GandiV5.post "#{url}/records", body
|
111
|
+
data['message']
|
112
|
+
end
|
113
|
+
|
114
|
+
# @overload delete_records()
|
115
|
+
# Delete all records for this domain.
|
116
|
+
# @overload delete_records(name)
|
117
|
+
# Delete records for a name.
|
118
|
+
# @param name [String] the name to delete records for.
|
119
|
+
# @overload delete_records(name, type)
|
120
|
+
# Delete records of a type for a name.
|
121
|
+
# @param name [String] the name to delete records for.
|
122
|
+
# @param type [String] the record type to delete.
|
123
|
+
# @return [nil]
|
124
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
125
|
+
def delete_records(name = nil, type = nil)
|
126
|
+
GandiV5::LiveDNS.require_valid_record_type(type) if type
|
127
|
+
|
128
|
+
url_ = "#{url}/records"
|
129
|
+
url_ += "/#{CGI.escape name}" if name
|
130
|
+
url_ += "/#{CGI.escape type}" if type
|
131
|
+
GandiV5.delete(url_).last
|
132
|
+
end
|
133
|
+
|
134
|
+
# Replace records for the domain.
|
135
|
+
# @param name [String, nil] only replaces records for this name.
|
136
|
+
# @param type [String, nil] only replaces record of this type (requires name).
|
137
|
+
# @param values [Array<String>] the values to set for the record.
|
138
|
+
# @raise [ArgumentError] if ttl is present and type is absent.
|
139
|
+
# @return [String] The confirmation message from Gandi.
|
140
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
141
|
+
# rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
142
|
+
def replace_records(records, name: nil, type: nil)
|
143
|
+
if type
|
144
|
+
GandiV5::LiveDNS.require_valid_record_type(type) if type
|
145
|
+
fail ArgumentError, 'missing keyword: name' if name.nil?
|
146
|
+
end
|
147
|
+
|
148
|
+
url_ = "#{url}/records"
|
149
|
+
url_ += "/#{CGI.escape name}" if name
|
150
|
+
url_ += "/#{CGI.escape type}" if type
|
151
|
+
|
152
|
+
body = if type && name
|
153
|
+
{ rrset_values: records }
|
154
|
+
else
|
155
|
+
{ items: records.map { |r| r.transform_keys { |k| "rrset_#{k}" } } }
|
156
|
+
end
|
157
|
+
|
158
|
+
_response, data = GandiV5.put url_, body.to_json
|
159
|
+
data['message']
|
160
|
+
end
|
161
|
+
# rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
162
|
+
|
163
|
+
# Replace all records for this domain.
|
164
|
+
# @param text [String] zone file lines to replace the records with.
|
165
|
+
# @return [String] The confirmation message from Gandi.
|
166
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
167
|
+
def replace_zone_lines(text)
|
168
|
+
_response, data = GandiV5.put "#{url}/records", text, 'content-type': 'text/plain'
|
169
|
+
data['message']
|
170
|
+
end
|
171
|
+
|
172
|
+
# The list of nameservers that this domain is using according to LiveDNS' systems.
|
173
|
+
# * Either there are no NS records on @ and the 3 hashed nameservers are returned
|
174
|
+
# (ns-{123}-{abc}.gandi.net)
|
175
|
+
# * Or some NS records exist on @ and it will return those
|
176
|
+
# @see https://api.gandi.net/docs/domains#get-v5-domain-domains-domain-name$
|
177
|
+
# @return [Array<String>]
|
178
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
179
|
+
def name_servers
|
180
|
+
@name_servers ||= fetch_name_servers
|
181
|
+
end
|
182
|
+
|
183
|
+
# Requery Gandi for the domain's name servers.
|
184
|
+
# @see https://api.gandi.net/docs/domains#get-v5-domain-domains-domain-name$
|
185
|
+
# @return [Array<String>]
|
186
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
187
|
+
def fetch_name_servers
|
188
|
+
_response, data = GandiV5.get "#{url}/nameservers"
|
189
|
+
@name_servers = data
|
190
|
+
end
|
191
|
+
|
192
|
+
# The list of DNSSEC keys for the domain.
|
193
|
+
# If you need the fingerprint, public_key or tag attributes you'll need
|
194
|
+
# use GandiV5::LiveDNS::Domain::DnssecKey.fetch on each item.
|
195
|
+
# @return [Array<GandiV5::LiveDNS::Domain::DnssecKey>]
|
196
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
197
|
+
def dnssec_keys
|
198
|
+
@dnssec_keys ||= fetch_dnssec_keys
|
199
|
+
end
|
200
|
+
|
201
|
+
# Requery Gandi for the domain's DNSSEC keys.
|
202
|
+
# @return [Array<GandiV5::LiveDNS::Domain::DnssecKey>]
|
203
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
204
|
+
def fetch_dnssec_keys
|
205
|
+
@dnssec_keys = GandiV5::LiveDNS::Domain::DnssecKey.list(fqdn)
|
206
|
+
end
|
207
|
+
|
208
|
+
# The list of TSIG keys for the domain.
|
209
|
+
# If you need the secret, fingerprint, public_key or tag attributes you'll need
|
210
|
+
# to use GandiV5::LiveDNS::Domain::DnssecKey.fetch on each item.
|
211
|
+
# @return [Array<GandiV5::LiveDNS::Domain::TsigKey>]
|
212
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
213
|
+
def tsig_keys
|
214
|
+
@tsig_keys ||= fetch_tsig_keys
|
215
|
+
end
|
216
|
+
|
217
|
+
# Requery Gandi for the domain's TSIG keys.
|
218
|
+
# @return [Array<GandiV5::LiveDNS::Domain::TsigKey>]
|
219
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
220
|
+
def fetch_tsig_keys
|
221
|
+
_response, data = GandiV5.get "#{url}/axfr/tsig"
|
222
|
+
data.map { |item| GandiV5::LiveDNS::Domain::TsigKey.from_gandi item }
|
223
|
+
end
|
224
|
+
|
225
|
+
# Add a Tsig key to this domain.
|
226
|
+
# @param key [GandiV5::LiveDNS::Domain::TsigKey, #uuid, String, #to_s]
|
227
|
+
# the key to add.
|
228
|
+
# @param sharing_id [nil, String, #to_s]
|
229
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
230
|
+
def add_tsig_key(key, sharing_id: nil)
|
231
|
+
key = key.uuid if key.respond_to?(:uuid)
|
232
|
+
url_ = "#{url}/axfr/tsig/#{key}"
|
233
|
+
url_ += "?sharing_id=#{CGI.escape sharing_id}" if sharing_id
|
234
|
+
_response, _data = GandiV5.put url_
|
235
|
+
end
|
236
|
+
|
237
|
+
# Remove a Tsig key from this domain.
|
238
|
+
# @param key [GandiV5::LiveDNS::Domain::TsigKey, #uuid, String, #to_s]
|
239
|
+
# the key to remove.
|
240
|
+
# @param sharing_id [nil, String, #to_s]
|
241
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
242
|
+
def remove_tsig_key(key, sharing_id: nil)
|
243
|
+
key = key.uuid if key.respond_to?(:uuid)
|
244
|
+
url_ = "#{url}/axfr/tsig/#{key}"
|
245
|
+
url_ += "?sharing_id=#{CGI.escape sharing_id}" if sharing_id
|
246
|
+
_response, _data = GandiV5.delete url_
|
247
|
+
end
|
248
|
+
|
249
|
+
# The list of AXFR clients for the domain.
|
250
|
+
# @return [Array<String>] list of IP addresses.
|
251
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
252
|
+
def axfr_clients
|
253
|
+
@axfr_clients ||= fetch_axfr_clients
|
254
|
+
end
|
255
|
+
|
256
|
+
# Requery Gandi for the domain's AXFR clients.
|
257
|
+
# @return [Array<String>] list of IP addresses.
|
258
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
259
|
+
def fetch_axfr_clients
|
260
|
+
_response, data = GandiV5.get "#{url}/axfr/slaves"
|
261
|
+
data
|
262
|
+
end
|
263
|
+
|
264
|
+
# Add an AXFR client to this domain.
|
265
|
+
# @param ip [String, #to_s] the IP address to add.
|
266
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
267
|
+
def add_axfr_client(ip)
|
268
|
+
_response, _data = GandiV5.put "#{url}/axfr/slaves/#{ip}"
|
269
|
+
end
|
270
|
+
|
271
|
+
# Remove and AXFR client from this domain.
|
272
|
+
# @param ip [String, #to_s] the IP address to remove.
|
273
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
274
|
+
def remove_axfr_client(ip)
|
275
|
+
_response, _data = GandiV5.delete "#{url}/axfr/slaves/#{ip}"
|
51
276
|
end
|
52
277
|
|
53
278
|
# List the domains.
|
54
|
-
# @
|
279
|
+
# @param page [#each<Integer, #to_s>] the page(s) of results to retrieve.
|
280
|
+
# If page is not provided keep querying until an empty list is returned.
|
281
|
+
# If page responds to .each then iterate until an empty list is returned.
|
282
|
+
# @param per_page [Integer, #to_s] (optional default 100) how many results to get per page.
|
283
|
+
# @param sharing_id [String, #to_s] (optional) filter by domains assigned to a given user.
|
284
|
+
# @return [Array<String>]
|
55
285
|
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
56
|
-
def self.list
|
57
|
-
|
58
|
-
|
286
|
+
def self.list(page: (1..), per_page: 100, sharing_id: nil)
|
287
|
+
page = [page.to_i] unless page.respond_to?(:each)
|
288
|
+
params = { per_page: per_page }
|
289
|
+
params[:sharing_id] = sharing_id unless sharing_id.nil?
|
290
|
+
|
291
|
+
domains = []
|
292
|
+
page.each do |page_number|
|
293
|
+
_resp, data = GandiV5.get url, params: params.merge(page: page_number)
|
294
|
+
break if data.empty?
|
295
|
+
|
296
|
+
domains += data.map { |item| item['fqdn'] }
|
297
|
+
break if data.count < per_page
|
298
|
+
end
|
299
|
+
domains
|
59
300
|
end
|
60
301
|
|
61
302
|
# Get a domain.
|
@@ -67,14 +308,57 @@ class GandiV5
|
|
67
308
|
from_gandi data
|
68
309
|
end
|
69
310
|
|
311
|
+
# Create a new domain in the LiveDNS system.
|
312
|
+
# You must have sufficent permission to manage the domain to do this.
|
313
|
+
# @param fqdn [String, #to_s] the fully qualified domain to add to LiveDNS.
|
314
|
+
# @param records [Array<Hash, GandiV5::LiveDNS::Domain::Record, #to_h, nil>]
|
315
|
+
# @param ttl [Integer, #to_s, nil] the TTL of the SOA record.
|
316
|
+
# Note that this is not a default TTL that will be used for the records in the zone.
|
317
|
+
# the records (if any) to add to the created zone.
|
318
|
+
# @param sharing_id [nil, String, #to_s]
|
319
|
+
# @return [GandiV5::LiveDNS::Domain]
|
320
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
321
|
+
def self.create(fqdn, records = nil, soa_ttl: nil, sharing_id: nil)
|
322
|
+
body = { fqdn: fqdn, zone: {} }
|
323
|
+
body[:zone][:ttl] = soa_ttl if soa_ttl
|
324
|
+
if records
|
325
|
+
body[:zone][:items] = records.map do |r|
|
326
|
+
r.to_h.transform_keys { |k| "rrset_#{k}" }
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
url_ = url
|
331
|
+
url_ += "?sharing_id=#{CGI.escape sharing_id}" if sharing_id
|
332
|
+
|
333
|
+
GandiV5.post url_, body.to_json
|
334
|
+
fetch(fqdn)
|
335
|
+
end
|
336
|
+
|
337
|
+
# Fetch the list of known record types (A, CNAME, etc.)
|
338
|
+
# @return [Array<String>]
|
339
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
340
|
+
def self.record_types
|
341
|
+
GandiV5.get("#{BASE}livedns/dns/rrtypes").last
|
342
|
+
end
|
343
|
+
|
344
|
+
# Get the LiveDNS servers to use for a domain.
|
345
|
+
# Does not take into account any NS records that exist in the zone.
|
346
|
+
# @param fqdn [String, #to_s] the fully qualified domain to hash in
|
347
|
+
# in order to get the LiveDNS servers to use.
|
348
|
+
# @return [Array<String>]
|
349
|
+
# @raise [GandiV5::Error::GandiError] if Gandi returns an error.
|
350
|
+
def self.generic_name_servers(fqdn)
|
351
|
+
GandiV5.get("#{BASE}livedns/nameservers/#{CGI.escape fqdn}").last
|
352
|
+
end
|
353
|
+
|
70
354
|
private
|
71
355
|
|
72
356
|
def url
|
73
|
-
"#{BASE}domains/#{CGI.escape
|
357
|
+
"#{BASE}livedns/domains/#{CGI.escape fqdn}"
|
74
358
|
end
|
75
359
|
|
76
360
|
def self.url(fqdn = nil)
|
77
|
-
"#{BASE}domains" + (fqdn ? "/#{CGI.escape
|
361
|
+
"#{BASE}livedns/domains" + (fqdn ? "/#{CGI.escape fqdn}" : '')
|
78
362
|
end
|
79
363
|
private_class_method :url
|
80
364
|
end
|