Class: Dory::Dnsmasq

Inherits:
Object
  • Object
show all
Extended by:
DockerService
Defined in:
lib/dory/dnsmasq.rb

Constant Summary collapse

@@first_attempt_failed =

I really hate these globals. It would be great to refactor these out

false
@@handle_systemd_services =
[]

Class Method Summary collapse

Methods included from DockerService

container_exists?, delete, delete_container_if_exists, docker_installed?, docker_installed?, execute_run_command, handle_error, ps, run_postconditions, run_preconditions, running?, start, start_cmd, stop

Class Method Details

.address(addr) ⇒ Object



114
115
116
# File 'lib/dory/dnsmasq.rb', line 114

def self.address(addr)
  Dory::Dinghy.match?(addr) ? Dory::Dinghy.ip : addr
end

.answer_from_settingsObject



222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/dory/dnsmasq.rb', line 222

def self.answer_from_settings
  # This `== true` is important because kill_others could be
  # 'no' which would be a truthy value despite the fact that it
  # should be falsey
  if self.kill_others == true || self.kill_others =~ /yes/i
    'Y'
  elsif self.kill_others == false || self.kill_others =~ /no/i
    'N'
  else
    nil
  end
end

.ask_about_killing?Boolean

Returns:

  • (Boolean)


214
215
216
# File 'lib/dory/dnsmasq.rb', line 214

def self.ask_about_killing?
  !self.answer_from_settings
end

.container_nameObject



98
99
100
# File 'lib/dory/dnsmasq.rb', line 98

def self.container_name
  Dory::Config.settings[:dory][:dnsmasq][:container_name]
end

.dnsmasq_image_nameObject



13
14
15
16
# File 'lib/dory/dnsmasq.rb', line 13

def self.dnsmasq_image_name
  setting = Dory::Config.settings[:dory][:dnsmasq][:image]
  setting ? setting : 'freedomben/dory-dnsmasq:1.1.0'
end

.domain_addr_arg_stringObject



118
119
120
121
122
123
124
125
126
# File 'lib/dory/dnsmasq.rb', line 118

def self.domain_addr_arg_string
  if self.old_domain
    "#{Shellwords.escape(self.old_domain)} #{Shellwords.escape(self.address(self.old_address))}"
  else
    self.domains.map do |domain|
      "#{Shellwords.escape(domain[:domain])} #{Shellwords.escape(self.address(domain[:address]))}"
    end.join(" ")
  end
end

.domainsObject



102
103
104
# File 'lib/dory/dnsmasq.rb', line 102

def self.domains
  Dory::Config.settings[:dory][:dnsmasq][:domains]
end

.down_systemd_servicesObject



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/dory/dnsmasq.rb', line 170

def self.down_systemd_services
  puts "[DEBUG] Putting systemd services down" if Dory::Config.debug?

  conf = if ask_about_killing?
           puts "You have some systemd services running that will race against us \n" \
                "to bind to port 53 (and usually they win):".yellow
           puts "\n     #{self.systemd_services.join(', ')}\n".yellow
           puts "If we don't stop these services temporarily while putting up the \n" \
                "dnsmasq container, starting it will likely fail.".yellow
           print "Would you like me to put them down while we start dns \n" \
                 "(I'll put them back up when finished)? (Y/N): ".yellow
           STDIN.gets.chomp
         else
           answer_from_settings
         end
  if conf =~ /y/i
    if self.systemd_services.all? { |service|
      Dory::Systemd.set_systemd_service(service: service, up: false)
    }
      puts "Putting down services succeeded".green
    else
      puts "One or more services failed to stop".red
    end
  else
    puts 'OK, not putting down the services'.yellow
    set_systemd_services([])
  end
end

.first_attempt_failed?Boolean

Returns:

  • (Boolean)


18
19
20
21
# File 'lib/dory/dnsmasq.rb', line 18

def self.first_attempt_failed?
  @@first_attempt_failed ||= false if @first_attempt_failed.nil?
  @@first_attempt_failed
end

.handle_error(_command_output) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/dory/dnsmasq.rb', line 67

def self.handle_error(_command_output)
  puts "[DEBUG] handling dnsmasq start error" if Dory::Config.debug?
  # If we've already tried to handle failure, prevent infinite recursion
  if first_attempt_failed?
    puts "[DEBUG] Attempt to kill conflicting service failed" if Dory::Config.debug?
    return false
  else
    if Dory::Config.debug?
      puts "[DEBUG] First attempt to start dnsmasq failed." \
           "There is probably a conflicting service present"
    end
    set_first_attempt_failed(true)
    self.start(handle_error: false)
  end
end

.has_services_that_block_dnsmasq?Boolean

Returns:

  • (Boolean)


160
161
162
# File 'lib/dory/dnsmasq.rb', line 160

def self.has_services_that_block_dnsmasq?
  !self.running_services_that_block_dnsmasq.empty?
end

.ip_from_dinghy?Boolean

Returns:

  • (Boolean)


83
84
85
86
# File 'lib/dory/dnsmasq.rb', line 83

def self.ip_from_dinghy?
  Dory::Dinghy.match?(self.address(self.old_address)) ||
    self.domains.any?{ |domain| Dory::Dinghy.match?(self.address(domain[:address])) }
end

.kill_othersObject



218
219
220
# File 'lib/dory/dnsmasq.rb', line 218

def self.kill_others
  Dory::Config.settings[:dory][:dnsmasq][:kill_others]
end

.offer_to_kill(listener_list, answer: nil) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/dory/dnsmasq.rb', line 135

def self.offer_to_kill(listener_list, answer: nil)
  listener_list.each do |process|
    puts "Process '#{process.command}' with PID '#{process.pid}' is listening on #{process.node} port #{self.port}.".yellow
  end
  pids = listener_list.uniq(&:pid).map(&:pid)
  pidstr = pids.join(' and ')
  print "This interferes with Dory's dnsmasq container.  Would you like me to kill PID #{pidstr}? (Y/N): ".yellow
  conf = answer ? answer : answer_from_settings
  conf = STDIN.gets.chomp unless conf
  if conf =~ /y/i
    puts "Requesting sudo to kill PID #{pidstr}".green
    return Sh.run_command("sudo kill #{pids.join(' ')}").success?
  else
    puts "OK, not killing PID #{pidstr}.  Please kill manually and try starting dory again.".red
    return false
  end
end

.old_addressObject



110
111
112
# File 'lib/dory/dnsmasq.rb', line 110

def self.old_address
  Dory::Config.settings[:dory][:dnsmasq][:address]
end

.old_domainObject



106
107
108
# File 'lib/dory/dnsmasq.rb', line 106

def self.old_domain
  Dory::Config.settings[:dory][:dnsmasq][:domain]
end

.portObject



88
89
90
91
92
# File 'lib/dory/dnsmasq.rb', line 88

def self.port
  return 53 unless Os.macos?
  p = Dory::Config.settings[:dory][:dnsmasq][:port]
  p.nil? || p == 0 ? 19323 : self.sanitize_port(p)
end

.run_commandObject



128
129
130
131
132
133
# File 'lib/dory/dnsmasq.rb', line 128

def self.run_command
  "docker run -d -p #{self.port}:#{self.port}/tcp -p #{self.port}:#{self.port}/udp " \
  "--name=#{Shellwords.escape(self.container_name)} " \
  "--cap-add=NET_ADMIN #{Shellwords.escape(self.dnsmasq_image_name)} " \
  "#{self.domain_addr_arg_string}"
end

.run_postconditionsObject



62
63
64
65
# File 'lib/dory/dnsmasq.rb', line 62

def self.run_postconditions
  puts "[DEBUG] dnsmasq service running postconditions" if Dory::Config.debug?
  self.up_systemd_services if self.systemd_services?
end

.run_preconditionsObject



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/dory/dnsmasq.rb', line 41

def self.run_preconditions
  puts "[DEBUG] dnsmasq service running preconditions" if Dory::Config.debug?

  # we don't want to hassle the user with checking the port unless necessary
  if first_attempt_failed?
    self.set_systemd_services(self.running_services_that_block_dnsmasq)
    self.down_systemd_services if self.systemd_services?

    puts "[DEBUG] First attempt failed.  Checking port #{self.port}" if Dory::Config.debug?
    listener_list = Dory::PortUtils.check_port(self.port)
    unless listener_list.empty?
      return self.offer_to_kill(listener_list)
    end

    return false
  else
    puts "[DEBUG] Skipping preconditions on first run" if Dory::Config.debug?
    return true
  end
end

.running_services_that_block_dnsmasqObject



164
165
166
167
168
# File 'lib/dory/dnsmasq.rb', line 164

def self.running_services_that_block_dnsmasq
  self.services_that_block_dnsmasq.select do |service|
    Dory::Systemd.systemd_service_running?(service)
  end
end

.sanitize_port(port) ⇒ Object



94
95
96
# File 'lib/dory/dnsmasq.rb', line 94

def self.sanitize_port(port)
  port.to_s.gsub(/\D/, '').to_i
end

.services_that_block_dnsmasqObject



153
154
155
156
157
158
# File 'lib/dory/dnsmasq.rb', line 153

def self.services_that_block_dnsmasq
  %w[
    NetworkManager.service
    systemd-resolved.service
  ]
end

.set_first_attempt_failed(failed) ⇒ Object



23
24
25
# File 'lib/dory/dnsmasq.rb', line 23

def self.set_first_attempt_failed(failed)
  @@first_attempt_failed = failed
end

.set_systemd_services(services) ⇒ Object



37
38
39
# File 'lib/dory/dnsmasq.rb', line 37

def self.set_systemd_services(services)
  @@systemd_services = services
end

.systemd_servicesObject



32
33
34
35
# File 'lib/dory/dnsmasq.rb', line 32

def self.systemd_services
  @@systemd_services ||= []
  @@systemd_services
end

.systemd_services?Boolean

Returns:

  • (Boolean)


27
28
29
30
# File 'lib/dory/dnsmasq.rb', line 27

def self.systemd_services?
  return false unless self.systemd_services
  self.systemd_services.count > 0
end

.up_systemd_servicesObject



199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/dory/dnsmasq.rb', line 199

def self.up_systemd_services
  if self.systemd_services?
    puts "[DEBUG] Putting systemd services back up: #{self.systemd_services.join(', ')}" if Dory::Config.debug?
    if self.systemd_services.reverse.all? { |service|
      Dory::Systemd.set_systemd_service(service: service, up: true)
    }
      puts "#{self.systemd_services.join(', ')} were successfully restarted".green
    else
      puts "#{self.systemd_services.join(', ')} failed to restart".red
    end
  else
    puts "[DEBUG] Not putting systemd services back up cause array was empty " if Dory::Config.debug?
  end
end