firespring_dev_commands 2.2.2 → 2.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b60fb9f23da32fe3006bd63627788753dc822b14d16782e253ecdfe3e516ae12
4
- data.tar.gz: 94e3b4fc79749292e56e9db5de9f170def61c64e154daa11ef66e8bdbc4049cf
3
+ metadata.gz: 599f3f484514462356186c8d52adfac32e8aa925f7aeaa488bb5fa109db678f3
4
+ data.tar.gz: 3dffab80edf84b75d073adfc21c1a2c3e0d9dcc666a526f2bda2d5471ff24d6e
5
5
  SHA512:
6
- metadata.gz: 5a53e1b2ffd61b54eabf8ad81559054dec6fd40f9023ebf22ec27aea56cc9c3cd884cc329d0055b133da54792e77bce23ea44119fceb67e87493e8e80d5e25af
7
- data.tar.gz: 10237fa32a8c41d9a9b9c7196d7ded272d5ec4c36ad2cb2d789956acb33ed66a70388d4368b4ac2ed2fc5024aca91381d682cb1ef9f73b226ee5a1c88a44e0bd
6
+ metadata.gz: 0ea2c7d6d09e1e5ad6a85ee3d7d3e90826018bee1afc7c1ce1a206ca8514b658c8ef29534dc5b4082bf185d1540a9292218eb3836f6c3405b44d3ff10d98b484
7
+ data.tar.gz: c06fa865de8acedba8ec7b2934094f7a03fb46e2e1d5a116fc2b12fa4febcf6874940771a843bb022f5f571d5b77e9d4f00d5b3f4930f4cd2f16aa44293a66dd
@@ -1,44 +1,59 @@
1
+ require 'aws-sdk-route53'
2
+
1
3
  module Dev
2
4
  class Aws
3
5
  # Class for performing Route53 functions
4
6
  class Route53
5
7
  attr_reader :client
6
8
 
7
- def initialize(domains)
9
+ def initialize(domains = nil)
8
10
  @client = ::Aws::Route53::Client.new
9
- @domains = domains
11
+ @domains = Array(domains || [])
10
12
  end
11
13
 
12
- private def zones
14
+ def zones(&)
13
15
  if @domains.empty?
14
- all_zones
16
+ each_zone(&)
15
17
  else
16
- zones_by_domain_names(@domains)
18
+ each_zone_by_domains(&)
17
19
  end
18
20
  end
19
21
 
20
- private def all_zones
21
- [].tap do |ary|
22
- Dev::Aws.each_page(client, :list_hosted_zones) do |response|
23
- response.hosted_zones&.each do |hosted_zone|
24
- ary << hosted_zone unless hosted_zone.config.private_zone
25
- end
22
+ private def each_zone
23
+ Dev::Aws.each_page(client, :list_hosted_zones) do |response|
24
+ response.hosted_zones&.each do |hosted_zone|
25
+ next if hosted_zone.config.private_zone
26
+
27
+ yield hosted_zone
26
28
  end
29
+ rescue ::Aws::Route53::Errors::Throttling
30
+ sleep(1)
31
+ retry
27
32
  end
28
33
  end
29
34
 
30
- private def zones_by_domain_names(domains)
31
- [].tap do |ary|
32
- domains.each do |domain_name|
33
- response = client.list_hosted_zones_by_name({dns_name: domain_name})
34
- target = response.hosted_zones.find { |it| it.name.chomp('.') == domain_name }
35
- raise "The #{domain_name} hosted zone not found." unless target
35
+ private def each_zone_by_domains(&)
36
+ @domains.each do |domain|
37
+ response = client.list_hosted_zones_by_name({dns_name: domain})
36
38
 
37
- ary << target
38
- end
39
+ # The 'list_hosted_zones_by_name' returns fuzzy matches (so "foo.com" would return both "bar.foo.com" and "foo.com"
40
+ # So we are only selecting domains that match exactly since that's what we really want here
41
+ targets = response.hosted_zones.select { |it| it.name.chomp('.') == domain }
42
+ raise "The #{domain} hosted zone not found." if targets.empty?
43
+
44
+ targets.each(&)
45
+ rescue ::Aws::Route53::Errors::Throttling
46
+ sleep(1)
47
+ retry
39
48
  end
40
49
  end
41
50
 
51
+ private def ip_address(domain)
52
+ Addrinfo.ip(domain.to_s.strip)&.ip_address
53
+ rescue SocketError
54
+ "Unable to resolve domain: #{domain}"
55
+ end
56
+
42
57
  private def target_config_id(zone_id)
43
58
  client.list_query_logging_configs(
44
59
  hosted_zone_id: zone_id,
@@ -46,61 +61,78 @@ module Dev
46
61
  ).query_logging_configs&.first&.id
47
62
  end
48
63
 
49
- private def pretty_puts(output)
50
- # Find the maximum length of the keys
51
- max_key_length = output.keys.map(&:to_s).max_by(&:length).length
64
+ # Get the hosted zone details for the zone id
65
+ private def details(zone_id)
66
+ response = client.get_hosted_zone(id: zone_id)
67
+ [response.hosted_zone, response.delegation_set]
68
+ end
69
+
70
+ def list_zone_details
71
+ zones do |zone|
72
+ puts
73
+ zone_details, delegation_set = details(zone.id)
74
+ dns_resource = Dev::Dns::Resource.new(zone_details.name)
52
75
 
53
- output.each do |key, value|
54
- puts "#{key.to_s.ljust(max_key_length)}\t=>\t#{value}"
76
+ puts "#{zone_details.name.chomp('.').light_white} (#{zone_details.id}):"
77
+ puts format(' %-50s %s', 'Delegation Set:', delegation_set.id)
78
+ puts format(' %-50s %s', 'Delegation Defined Nameservers:', delegation_set.name_servers.sort.join(', '))
79
+ puts format(' %-50s %s', 'DNS Reported Nameservers:', dns_resource.recursive_nameserver_lookup.sort.join(', '))
80
+ puts format(' %-50s %s', 'DNS Reported Nameserver IPs:', dns_resource.recursive_nameserver_lookup.sort.map { |it| dns_resource.recursive_a_lookup(it) }.join(', '))
81
+ puts format(' %-50s %s', 'Domain Apex IP Resolution:', dns_resource.recursive_a_lookup.sort.join(', '))
82
+ rescue ::Aws::Route53::Errors::Throttling
83
+ sleep(1)
84
+ retry
55
85
  end
86
+ puts
56
87
  end
57
88
 
58
89
  def list_query_configs
59
- output = {}
60
- zones.each do |zone|
90
+ zones do |zone|
61
91
  target_config_id = target_config_id(zone.id)
62
-
63
- output[zone.name] = if target_config_id
64
- "Config\t=>\t#{target_config_id}".colorize(:green)
65
- else
66
- 'No query logging config assigned.'.colorize(:red)
67
- end
92
+ message = if target_config_id
93
+ "Config\t=>\t#{target_config_id}".colorize(:green)
94
+ else
95
+ 'No query logging config assigned.'.colorize(:red)
96
+ end
97
+ puts format('%-50s => %s', zone.name, message)
98
+ rescue ::Aws::Route53::Errors::Throttling
99
+ sleep(1)
100
+ retry
68
101
  end
69
-
70
- pretty_puts(output)
71
102
  end
72
103
 
73
104
  def activate_query_logging(log_group)
74
- output = {}
75
-
76
- zones.each do |zone|
105
+ zones do |zone|
77
106
  response = client.create_query_logging_config(
78
107
  hosted_zone_id: zone.id,
79
108
  cloud_watch_logs_log_group_arn: log_group
80
109
  )
81
- output[zone.id] = response.location
110
+ puts format('%-50s => %s', zone.id, response.location)
111
+ rescue ::Aws::Route53::Errors::Throttling
112
+ sleep(1)
113
+ retry
82
114
  rescue ::Aws::Route53::Errors::ServiceError => e
83
115
  raise "Error: #{e.message}" unless e.instance_of?(::Aws::Route53::Errors::QueryLoggingConfigAlreadyExists)
84
116
 
85
- output[zone.id] = e.message
117
+ puts format('%-50s => %s', zone.id, e.message)
86
118
  end
87
- pretty_puts(output)
88
119
  end
89
120
 
90
121
  def deactivate_query_logging
91
- output = {}
92
- zones.each do |zone|
122
+ zones do |zone|
93
123
  target_config_id = target_config_id(zone.id)
94
124
  if target_config_id
95
125
  client.delete_query_logging_config(
96
126
  id: target_config_id
97
127
  )
98
- output[zone.id] = 'Query logging config removed.'.colorize(:green)
128
+ puts format('%-50s => %s', zone.id, 'Query logging config removed.'.colorize(:green))
99
129
  else
100
- output[zone.id] = 'No query logging config assigned.'.colorize(:red)
130
+ puts format('%-50s => %s', zone.id, 'No query logging config assigned.'.colorize(:red))
101
131
  end
132
+ rescue ::Aws::Route53::Errors::Throttling
133
+ sleep(1)
134
+ retry
102
135
  end
103
- pretty_puts(output)
104
136
  end
105
137
  end
106
138
  end
@@ -0,0 +1,83 @@
1
+ module Dev
2
+ class Dns
3
+ class Resource
4
+ attr_reader :domain
5
+
6
+ def initialize(domain)
7
+ @domain = domain
8
+ end
9
+
10
+ # Returns whether or not the given value is a valid IPv4 or IPv6 address
11
+ def self.ip?(value)
12
+ ipv4?(value) || ipv6?(value)
13
+ end
14
+
15
+ # Returns whether or not the given value is a valid IPv4 address
16
+ def self.ipv4?(value)
17
+ value.match?(Resolv::IPv4::Regex)
18
+ end
19
+
20
+ # Returns whether or not the given value is a valid IPv6 address
21
+ def self.ipv6?(value)
22
+ value.match?(Resolv::IPv6::Regex)
23
+ end
24
+
25
+ # Recursively determine the correct nameservers for the given domain.
26
+ # If nameservers are not found, strip subdomains off until we've reached the TLD
27
+ def recursive_nameserver_lookup(name = domain)
28
+ records = lookup(name, type: Resolv::DNS::Resource::IN::NS)
29
+
30
+ # Strip the subdomain and try again if we didn't find any nameservers (this can happen with wildcards)
31
+ return recursive_nameserver_lookup(name.split('.', 2).last) if records.empty?
32
+
33
+ # Look up the IPs for the nameservers
34
+ records
35
+ end
36
+
37
+ # Recursively attempt to find an A record for the given domain.
38
+ # If one isn't found, also check for CNAMEs continually until we have either found an IP or run out of things to check
39
+ def recursive_a_lookup(name = domain)
40
+ # Try looking up an A record first. If we find one, we are done.
41
+ records = lookup(name, type: Resolv::DNS::Resource::IN::A)
42
+ return records unless records.empty?
43
+
44
+ # Try looking up a CNAME record
45
+ records = lookup(name, type: Resolv::DNS::Resource::IN::CNAME)
46
+
47
+ # If we didn't find an A record _or_ a CNAME, just return empty
48
+ return records if records.empty?
49
+
50
+ # If we found more than one CNAME that is a DNS error
51
+ raise "Found more than one CNAME entry for #{name}. This is not allowed by DNS" if records.length > 1
52
+
53
+ recursive_a_lookup(records.first)
54
+ end
55
+
56
+ # Lookup the given name using the record type provided.
57
+ def lookup(name = domain, type: Resolv::DNS::Resource::IN::A)
58
+ # Validate the type
59
+ raise 'lookup type must be a Resolv::DNS::Resource' unless type.ancestors.include?(Resolv::DNS::Resource)
60
+
61
+ # If we were given a tld, return empty
62
+ return [] unless name.include?('.')
63
+
64
+ # Look up NS records for the given host
65
+ records = Resolv::DNS.new.getresources(name, type)
66
+
67
+ # Return the record names
68
+ records.map do |record|
69
+ if record.respond_to?(:address)
70
+ record.address.to_s
71
+ elsif record.respond_to?(:name)
72
+ record.name.to_s
73
+ else
74
+ ''
75
+ end
76
+ end
77
+ rescue
78
+ sleep(1)
79
+ retry
80
+ end
81
+ end
82
+ end
83
+ end
@@ -6,16 +6,21 @@ module Dev
6
6
  module Services
7
7
  # Class contains rake templates for managing your AWS settings and logging in
8
8
  class Route53 < Dev::Template::BaseInterface
9
- # Create the rake task which ensures active credentials are present
10
- def create_ensure_credentials_task!
9
+ def create_list_zone_details_task!
11
10
  # Have to set a local variable to be accessible inside of the instance_eval block
12
11
  exclude = @exclude
13
12
 
14
13
  DEV_COMMANDS_TOP_LEVEL.instance_eval do
15
- return if exclude.include?(:ensure_aws_credentials)
14
+ return if exclude.include?(:list_details)
16
15
 
17
- task ensure_aws_credentials: %w(init) do
18
- raise 'AWS Credentials not found / expired' unless Dev::Aws::Credentials.new.active?
16
+ namespace :aws do
17
+ namespace :hosted_zone do
18
+ desc 'print details for all hosted zones'
19
+ task list_details: %w(ensure_aws_credentials) do
20
+ route53 = Dev::Aws::Route53.new(ENV['DOMAINS'].to_s.strip.split(','))
21
+ route53.list_zone_details
22
+ end
23
+ end
19
24
  end
20
25
  end
21
26
  end
@@ -26,9 +31,9 @@ module Dev
26
31
  exclude = @exclude
27
32
 
28
33
  DEV_COMMANDS_TOP_LEVEL.instance_eval do
29
- namespace :aws do
30
- return if exclude.include?(:dns_logging)
34
+ return if exclude.include?(:dns_logging_activate)
31
35
 
36
+ namespace :aws do
32
37
  namespace :hosted_zone do
33
38
  namespace :dns_logging do
34
39
  desc 'Activates query logging for all hosted zones by default.' \
@@ -56,9 +61,9 @@ module Dev
56
61
  exclude = @exclude
57
62
 
58
63
  DEV_COMMANDS_TOP_LEVEL.instance_eval do
59
- namespace :aws do
60
- return if exclude.include?(:dns_logging)
64
+ return if exclude.include?(:dns_logging_deactivate)
61
65
 
66
+ namespace :aws do
62
67
  namespace :hosted_zone do
63
68
  namespace :dns_logging do
64
69
  desc 'Deactivates query logging for all hosted zones by default. ' \
@@ -81,9 +86,9 @@ module Dev
81
86
  exclude = @exclude
82
87
 
83
88
  DEV_COMMANDS_TOP_LEVEL.instance_eval do
84
- namespace :aws do
85
- return if exclude.include?(:dns_logging)
89
+ return if exclude.include?(:dns_logging_config)
86
90
 
91
+ namespace :aws do
87
92
  namespace :hosted_zone do
88
93
  namespace :dns_logging do
89
94
  desc 'Lists the current config for domain(s). ' \
@@ -6,6 +6,6 @@ module Dev
6
6
  # Use 'v.v.v.pre.alpha.v' for pre-release vesions
7
7
  # Use 'v.v.v.beta.v for beta versions
8
8
  # Use semantic versioning for any releases (https://semver.org/)
9
- VERSION = '2.2.2'.freeze
9
+ VERSION = '2.2.3'.freeze
10
10
  end
11
11
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: firespring_dev_commands
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.2
4
+ version: 2.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Firespring
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-06-28 00:00:00.000000000 Z
11
+ date: 2024-07-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -353,6 +353,7 @@ files:
353
353
  - lib/firespring_dev_commands/coverage/cobertura.rb
354
354
  - lib/firespring_dev_commands/coverage/none.rb
355
355
  - lib/firespring_dev_commands/daterange.rb
356
+ - lib/firespring_dev_commands/dns/resource.rb
356
357
  - lib/firespring_dev_commands/docker.rb
357
358
  - lib/firespring_dev_commands/docker/compose.rb
358
359
  - lib/firespring_dev_commands/docker/desktop.rb