dnsmadeeasy 0.3.2 → 0.3.5
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 +4 -4
- data/.rubocop.yml +2 -0
- data/.rubocop_todo.yml +174 -0
- data/.travis.yml +8 -4
- data/Gemfile +2 -0
- data/README.md +133 -31
- data/Rakefile +8 -7
- data/bin/console +1 -0
- data/dnsmadeeasy.gemspec +42 -28
- data/exe/dme +1 -3
- data/lib/dme.rb +9 -5
- data/lib/dnsmadeeasy.rb +18 -8
- data/lib/dnsmadeeasy/api/client.rb +86 -49
- data/lib/dnsmadeeasy/credentials.rb +4 -4
- data/lib/dnsmadeeasy/credentials/api_keys.rb +7 -5
- data/lib/dnsmadeeasy/credentials/yaml_file.rb +16 -9
- data/lib/dnsmadeeasy/dme.rb +2 -4
- data/lib/dnsmadeeasy/runner.rb +119 -104
- data/lib/dnsmadeeasy/version.rb +3 -1
- metadata +74 -34
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'yaml'
|
|
2
4
|
require 'dnsmadeeasy'
|
|
3
5
|
require 'hashie/extensions/mash/symbolize_keys'
|
|
@@ -71,9 +73,7 @@ module DnsMadeEasy
|
|
|
71
73
|
# )
|
|
72
74
|
|
|
73
75
|
module Credentials
|
|
74
|
-
|
|
75
76
|
class << self
|
|
76
|
-
|
|
77
77
|
# Create a new instance of Credentials::ApiKeys
|
|
78
78
|
def create(key, secret, encryption_key = nil)
|
|
79
79
|
ApiKeys.new(key, secret, encryption_key)
|
|
@@ -83,13 +83,13 @@ module DnsMadeEasy
|
|
|
83
83
|
account: nil,
|
|
84
84
|
encryption_key: nil)
|
|
85
85
|
|
|
86
|
-
YamlFile.new(file: file).keys(account:
|
|
86
|
+
YamlFile.new(file: file).keys(account: account,
|
|
87
87
|
encryption_key: encryption_key)
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
# @return String path to the default credentials file.
|
|
91
91
|
def default_credentials_path(user: nil)
|
|
92
|
-
user ?
|
|
92
|
+
user ? # rubocop:todo Style/MultilineTernaryOperator
|
|
93
93
|
File.expand_path(Dir.home(user) + '/.dnsmadeeasy/credentials.yml').freeze :
|
|
94
94
|
File.expand_path('~/.dnsmadeeasy/credentials.yml').freeze
|
|
95
95
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'yaml'
|
|
2
4
|
require 'dnsmadeeasy'
|
|
3
5
|
require 'hashie/extensions/mash/symbolize_keys'
|
|
@@ -11,7 +13,7 @@ module DnsMadeEasy
|
|
|
11
13
|
# Immutable instance with key and secret.
|
|
12
14
|
#
|
|
13
15
|
class ApiKeys
|
|
14
|
-
API_KEY_REGEX = /^([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})
|
|
16
|
+
API_KEY_REGEX = /^([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})$/.freeze
|
|
15
17
|
|
|
16
18
|
attr_reader :api_key,
|
|
17
19
|
:api_secret,
|
|
@@ -24,12 +26,12 @@ module DnsMadeEasy
|
|
|
24
26
|
def initialize(key, secret, encryption_key = nil, default: false, account: nil)
|
|
25
27
|
raise InvalidCredentialKeys, "Key and Secret can not be nil" if key.nil? || secret.nil?
|
|
26
28
|
|
|
27
|
-
@default
|
|
29
|
+
@default = default
|
|
28
30
|
@account = account
|
|
29
31
|
|
|
30
32
|
if !valid?(key, secret) && encryption_key
|
|
31
33
|
@encryption_key = sym_resolve(encryption_key)
|
|
32
|
-
@api_key
|
|
34
|
+
@api_key = decr(key, @encryption_key)
|
|
33
35
|
@api_secret = decr(secret, @encryption_key)
|
|
34
36
|
else
|
|
35
37
|
@api_key = key
|
|
@@ -54,10 +56,10 @@ module DnsMadeEasy
|
|
|
54
56
|
end
|
|
55
57
|
|
|
56
58
|
def rofl(key)
|
|
57
|
-
Digest::SHA256
|
|
59
|
+
Digest::SHA256.hexdigest(key) if key
|
|
58
60
|
end
|
|
59
61
|
|
|
60
|
-
def valid?(key =
|
|
62
|
+
def valid?(key = api_key, secret = api_secret)
|
|
61
63
|
key &&
|
|
62
64
|
secret &&
|
|
63
65
|
API_KEY_REGEX.match(key) &&
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'yaml'
|
|
2
4
|
require 'dnsmadeeasy'
|
|
3
5
|
require 'hashie/extensions/symbolize_keys'
|
|
@@ -6,7 +8,6 @@ require_relative 'api_keys'
|
|
|
6
8
|
|
|
7
9
|
module DnsMadeEasy
|
|
8
10
|
module Credentials
|
|
9
|
-
|
|
10
11
|
class YamlFile
|
|
11
12
|
attr_accessor :filename, :account, :mash
|
|
12
13
|
|
|
@@ -23,17 +24,23 @@ module DnsMadeEasy
|
|
|
23
24
|
account = if account
|
|
24
25
|
mash.accounts.find { |a| a.name == account.to_s }
|
|
25
26
|
elsif
|
|
27
|
+
# rubocop:todo Layout/ConditionPosition
|
|
26
28
|
mash.accounts.size == 1
|
|
29
|
+
# rubocop:enable Layout/ConditionPosition
|
|
27
30
|
mash.accounts.first
|
|
28
31
|
else
|
|
29
|
-
mash.accounts.find
|
|
32
|
+
mash.accounts.find(&:default_account)
|
|
30
33
|
end
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
unless account
|
|
36
|
+
raise DnsMadeEasy::APIKeyAndSecretMissingError,
|
|
37
|
+
(account ? "account #{account} was not found" : 'default account does not exist')
|
|
38
|
+
end
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
unless account.credentials
|
|
41
|
+
raise DnsMadeEasy::InvalidCredentialsFormatError,
|
|
42
|
+
'Expected account entry to have "credentials" key'
|
|
43
|
+
end
|
|
37
44
|
|
|
38
45
|
account.credentials
|
|
39
46
|
|
|
@@ -45,9 +52,11 @@ module DnsMadeEasy
|
|
|
45
52
|
'expected either "accounts" or "credentials" as the top-level key'
|
|
46
53
|
end
|
|
47
54
|
|
|
55
|
+
# rubocop:todo Style/MultilineTernaryOperator
|
|
48
56
|
creds ? ApiKeys.new(creds.api_key,
|
|
49
57
|
creds.api_secret,
|
|
50
58
|
encryption_key || creds.encryption_key) : nil
|
|
59
|
+
# rubocop:enable Style/MultilineTernaryOperator
|
|
51
60
|
end
|
|
52
61
|
|
|
53
62
|
def to_s
|
|
@@ -69,10 +78,8 @@ module DnsMadeEasy
|
|
|
69
78
|
end
|
|
70
79
|
|
|
71
80
|
def load_hash
|
|
72
|
-
Hashie::Mash.new(YAML.
|
|
81
|
+
Hashie::Mash.new(YAML.safe_load(contents))
|
|
73
82
|
end
|
|
74
|
-
|
|
75
83
|
end
|
|
76
|
-
|
|
77
84
|
end
|
|
78
85
|
end
|
data/lib/dnsmadeeasy/dme.rb
CHANGED
data/lib/dnsmadeeasy/runner.rb
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
require 'colored2'
|
|
4
5
|
require 'awesome_print'
|
|
5
6
|
require 'dnsmadeeasy'
|
|
6
7
|
require 'dnsmadeeasy/api/client'
|
|
7
|
-
|
|
8
|
+
require 'etc'
|
|
8
9
|
|
|
9
10
|
module DnsMadeEasy
|
|
10
11
|
class Runner
|
|
11
|
-
SUPPORTED_FORMATS = %w(json json_pretty yaml pp)
|
|
12
|
+
SUPPORTED_FORMATS = %w(json json_pretty yaml pp).freeze
|
|
12
13
|
|
|
13
14
|
attr_accessor :format, :argv, :operation
|
|
14
15
|
|
|
@@ -18,7 +19,7 @@ module DnsMadeEasy
|
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
def execute!
|
|
21
|
-
if argv.empty? || argv.
|
|
22
|
+
if argv.empty? || argv.empty?
|
|
22
23
|
print_help_message
|
|
23
24
|
else
|
|
24
25
|
configure_authentication
|
|
@@ -30,59 +31,62 @@ module DnsMadeEasy
|
|
|
30
31
|
private
|
|
31
32
|
|
|
32
33
|
def call_through_client(method)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
print_formatted(result, format)
|
|
44
|
-
end
|
|
45
|
-
0
|
|
46
|
-
rescue ArgumentError => e
|
|
47
|
-
sig = method_signature(e, method)
|
|
48
|
-
(sig.shift == method.to_s) ?
|
|
49
|
-
print_signature(method, sig) :
|
|
50
|
-
print_error('Action', "#{method.to_s.bold.yellow}", 'has generated an error'.red, exception: e)
|
|
51
|
-
1
|
|
52
|
-
rescue NoMethodError => e
|
|
53
|
-
|
|
54
|
-
puts e.backtrace.reverse.join("\n").red
|
|
55
|
-
|
|
56
|
-
print_error('Action', "#{method.to_s.bold.yellow}", 'is not valid.'.red)
|
|
57
|
-
puts 'HINT: try running ' + 'dme operations'.bold.green + ' to see the list of valid operations.'
|
|
58
|
-
2
|
|
59
|
-
rescue Net::HTTPServerException => e
|
|
60
|
-
print_error(exception: e)
|
|
61
|
-
3
|
|
34
|
+
result = DnsMadeEasy.client.send(method, *argv)
|
|
35
|
+
case result
|
|
36
|
+
when NilClass
|
|
37
|
+
puts 'No records returned.'
|
|
38
|
+
when Hashie::Mash
|
|
39
|
+
print_formatted(result.to_hash, format)
|
|
40
|
+
when Array
|
|
41
|
+
print_formatted(result, format)
|
|
42
|
+
else
|
|
43
|
+
print_formatted(result, format)
|
|
62
44
|
end
|
|
45
|
+
0
|
|
46
|
+
rescue ArgumentError => e
|
|
47
|
+
sig = method_signature(e, method)
|
|
48
|
+
# rubocop:todo Style/MultilineTernaryOperator
|
|
49
|
+
sig.shift == method.to_s ?
|
|
50
|
+
print_signature(method, sig) :
|
|
51
|
+
print_error('Action', method.to_s.bold.yellow.to_s, 'has generated an error'.red, exception: e)
|
|
52
|
+
# rubocop:enable Style/MultilineTernaryOperator
|
|
53
|
+
1
|
|
54
|
+
rescue NoMethodError => e
|
|
55
|
+
print_error('Action', method.to_s.bold.yellow.to_s, 'is not valid.'.red)
|
|
56
|
+
puts 'HINT: try running ' + 'dme operations'.bold.green + ' to see the list of valid operations.'
|
|
57
|
+
2
|
|
58
|
+
rescue Net::HTTPServerException => e
|
|
59
|
+
print_error(exception: e)
|
|
60
|
+
3
|
|
63
61
|
end
|
|
64
62
|
|
|
65
63
|
def print_error(*args, exception: nil)
|
|
66
64
|
unless args.empty?
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
# rubocop:todo Naming/HeredocDelimiterNaming
|
|
66
|
+
puts <<~EOF
|
|
67
|
+
# rubocop:enable Naming/HeredocDelimiterNaming
|
|
68
|
+
#{'Error:'.bold.red} #{args.join(' ').red}
|
|
69
69
|
EOF
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
if exception
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
# rubocop:todo Naming/HeredocDelimiterNaming
|
|
74
|
+
puts <<~EOF
|
|
75
|
+
# rubocop:enable Naming/HeredocDelimiterNaming
|
|
76
|
+
#{'Exception: '.bold.red}#{exception.inspect.red}
|
|
75
77
|
EOF
|
|
76
78
|
end
|
|
77
79
|
end
|
|
78
80
|
|
|
79
81
|
def print_signature(method, sig)
|
|
80
|
-
|
|
81
|
-
#
|
|
82
|
-
|
|
82
|
+
# rubocop:todo Naming/HeredocDelimiterNaming
|
|
83
|
+
puts <<~EOF # rubocop:todo Naming/HeredocDelimiterNaming
|
|
84
|
+
# rubocop:enable Naming/HeredocDelimiterNaming
|
|
85
|
+
#{'Error: '.bold.yellow}
|
|
86
|
+
#{'You are missing some arguments for this operation:'.red}
|
|
83
87
|
|
|
84
|
-
|
|
85
|
-
|
|
88
|
+
#{'Correct Usage: '.bold.yellow}
|
|
89
|
+
#{method.to_s.bold.green} #{sig.join(' ').blue}
|
|
86
90
|
|
|
87
91
|
EOF
|
|
88
92
|
end
|
|
@@ -98,7 +102,7 @@ module DnsMadeEasy
|
|
|
98
102
|
puts "Supported values are: #{SUPPORTED_FORMATS.join(', ')}"
|
|
99
103
|
exit 1
|
|
100
104
|
end
|
|
101
|
-
elsif argv.first =~ /^
|
|
105
|
+
elsif argv.first =~ /^op(erations)?$/i
|
|
102
106
|
print_supported_operations
|
|
103
107
|
exit 0
|
|
104
108
|
end
|
|
@@ -106,32 +110,36 @@ module DnsMadeEasy
|
|
|
106
110
|
end
|
|
107
111
|
|
|
108
112
|
def configure_authentication
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
credentials_file = ENV['DNSMADEEASY_CREDENTIALS_FILE'] || DnsMadeEasy::Credentials.default_credentials_path(user: Etc.getlogin)
|
|
114
|
+
if ENV['DNSMADEEASY_API_KEY'] && ENV['DNSMADEEASY_API_SECRET']
|
|
115
|
+
DnsMadeEasy.api_key = ENV['DNSMADEEASY_API_KEY']
|
|
116
|
+
DnsMadeEasy.api_secret = ENV['DNSMADEEASY_API_SECRET']
|
|
117
|
+
elsif credentials_file && ::File.exist?(credentials_file)
|
|
118
|
+
keys = DnsMadeEasy::Credentials.keys_from_file(file: credentials_file)
|
|
119
|
+
if keys
|
|
120
|
+
DnsMadeEasy.api_key = keys.api_key
|
|
121
|
+
DnsMadeEasy.api_secret = keys.api_secret
|
|
122
|
+
end
|
|
117
123
|
end
|
|
118
|
-
rescue DnsMadeEasy::APIKeyAndSecretMissingError => e
|
|
119
|
-
print_error(e.message)
|
|
120
|
-
|
|
121
|
-
puts('You can also set two environment variables: ')
|
|
122
|
-
puts(' DNSMADEEASY_API_KEY and DNSMADEEASY_API_SECRET')
|
|
123
124
|
|
|
124
|
-
|
|
125
|
+
if DnsMadeEasy.api_key.nil?
|
|
126
|
+
print_error('API Key/Secret was not detected or read from file')
|
|
127
|
+
puts('You can also set two environment variables: ')
|
|
128
|
+
puts(' DNSMADEEASY_API_KEY and DNSMADEEASY_API_SECRET')
|
|
129
|
+
exit 123
|
|
130
|
+
end
|
|
125
131
|
end
|
|
126
132
|
|
|
127
133
|
def print_usage_message
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
134
|
+
# rubocop:todo Naming/HeredocDelimiterNaming
|
|
135
|
+
puts <<~EOF
|
|
136
|
+
# rubocop:enable Naming/HeredocDelimiterNaming
|
|
137
|
+
#{'Usage:'.bold.yellow}
|
|
138
|
+
#{'# Execute an API call:'.dark}
|
|
139
|
+
#{"dme [ #{SUPPORTED_FORMATS.map { |f| "--#{f}" }.join(' | ')} ] operation [ arg1 arg2 ... ] ".bold.green}
|
|
132
140
|
|
|
133
|
-
|
|
134
|
-
|
|
141
|
+
#{'# Print suported operations:'.dark}
|
|
142
|
+
#{'dme op[erations]'.bold.green}
|
|
135
143
|
|
|
136
144
|
EOF
|
|
137
145
|
end
|
|
@@ -139,53 +147,57 @@ module DnsMadeEasy
|
|
|
139
147
|
def print_help_message
|
|
140
148
|
print_usage_message
|
|
141
149
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
150
|
+
# rubocop:todo Naming/HeredocDelimiterNaming
|
|
151
|
+
puts <<~EOF
|
|
152
|
+
# rubocop:enable Naming/HeredocDelimiterNaming
|
|
153
|
+
#{header 'Credentials'}
|
|
154
|
+
Store your credentials in a YAML file
|
|
155
|
+
#{DnsMadeEasy::Credentials.default_credentials_path(user: ENV['USER'])} as follows:
|
|
156
|
+
|
|
157
|
+
#{'credentials:
|
|
158
|
+
api_key: XXXX
|
|
159
|
+
api_secret: YYYY'.bold.magenta}
|
|
160
|
+
|
|
161
|
+
Or a multi-account version:
|
|
162
|
+
|
|
163
|
+
#{'accounts:
|
|
164
|
+
- name: production
|
|
165
|
+
credentials:
|
|
166
|
+
api_key: XXXX
|
|
167
|
+
api_secret: YYYY
|
|
168
|
+
encryption_key: my_key
|
|
169
|
+
- name: development
|
|
170
|
+
default_account: true
|
|
171
|
+
credentials:
|
|
172
|
+
api_key: ZZZZ
|
|
173
|
+
api_secret: WWWW'.bold.magenta}
|
|
174
|
+
|
|
175
|
+
#{header 'Examples:'}
|
|
176
|
+
#{'dme domain moo.com
|
|
177
|
+
dme --json domain moo.com
|
|
178
|
+
dme find_all moo.com A www
|
|
179
|
+
dme find_first moo.com CNAME vpn-west
|
|
180
|
+
dme --yaml find_first moo.com CNAME vpn-west'.green}
|
|
171
181
|
|
|
172
182
|
EOF
|
|
173
183
|
exit 1
|
|
174
184
|
end
|
|
175
185
|
|
|
176
186
|
def header(message)
|
|
177
|
-
|
|
187
|
+
message.bold.yellow.to_s
|
|
178
188
|
end
|
|
179
189
|
|
|
180
190
|
def print_supported_operations
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
191
|
+
# rubocop:todo Naming/HeredocDelimiterNaming
|
|
192
|
+
puts <<~EOF
|
|
193
|
+
# rubocop:enable Naming/HeredocDelimiterNaming
|
|
194
|
+
#{header 'Actions:'}
|
|
195
|
+
Checkout the README and RubyDoc for the arguments to each operation,
|
|
196
|
+
which is basically a method on a DnsMadeEasy::Api::Client instance.
|
|
197
|
+
#{'http://www.rubydoc.info/gems/dnsmadeeasy/DnsMadeEasy/Api/Client'.blue.bold.underlined}
|
|
186
198
|
|
|
187
|
-
|
|
188
|
-
|
|
199
|
+
#{header 'Valid Operations Are:'}
|
|
200
|
+
#{DnsMadeEasy::Api::Client.public_operations.join("\n ").green.bold}
|
|
189
201
|
|
|
190
202
|
EOF
|
|
191
203
|
end
|
|
@@ -208,23 +220,26 @@ module DnsMadeEasy
|
|
|
208
220
|
|
|
209
221
|
# e.backtrack.first looks like this:
|
|
210
222
|
# ..../dnsmadeeasy/lib/dnsmadeeasy/api/client.rb:143:in `create_a_record'
|
|
223
|
+
# rubocop:todo Naming/MethodParameterName
|
|
211
224
|
def method_signature(e, method)
|
|
212
225
|
file, line, call_method = e.backtrace.first.split(':')
|
|
213
226
|
call_method = call_method.gsub(/[']/, '').split('`').last
|
|
214
227
|
if call_method && call_method.to_sym == method.to_sym
|
|
215
228
|
source_line = File.open(file).to_a[line.to_i - 1].chomp!
|
|
216
229
|
if source_line =~ /def #{method}/
|
|
217
|
-
signature = source_line.strip.gsub(/,/, '').split(
|
|
230
|
+
signature = source_line.strip.gsub(/,/, '').split(/[ ()]/)
|
|
218
231
|
signature.shift # remove def
|
|
219
232
|
return signature.reject { |a| a =~ /^([={}\)\(])*$/ }
|
|
220
233
|
end
|
|
221
234
|
end
|
|
222
235
|
[]
|
|
223
|
-
rescue
|
|
236
|
+
rescue StandardError
|
|
224
237
|
[]
|
|
225
238
|
end
|
|
239
|
+
# rubocop:enable Naming/MethodParameterName
|
|
240
|
+
|
|
241
|
+
def puts(*args)
|
|
242
|
+
super(*args)
|
|
243
|
+
end
|
|
226
244
|
end
|
|
227
245
|
end
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|