Class: Landrush::Server

Inherits:
Object
  • Object
show all
Extended by:
Util::ProcessHelper
Defined in:
lib/landrush/server.rb

Constant Summary collapse

Name =
Resolv::DNS::Name
IN =
Resolv::DNS::Resource::IN

Class Attribute Summary collapse

Class Method Summary collapse

Methods included from Util::ProcessHelper

delete_pid_file, ensure_path_exits, process_status, read_pid, terminate_process, write_pid

Class Attribute Details

.gems_dirObject

Returns the value of attribute gems_dir.



16
17
18
# File 'lib/landrush/server.rb', line 16

def gems_dir
  @gems_dir
end

.portObject



42
43
44
45
46
47
48
49
50
51
# File 'lib/landrush/server.rb', line 42

def port
  @port unless @port.nil?
  if (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM).nil?
    # Default Landrush port for non Windows OS
    100_53
  else
    # On Windows we need to use the default DNS port, since there seems to be no way to configure it otherwise
    53
  end
end

.uiObject

Returns the value of attribute ui.



22
23
24
# File 'lib/landrush/server.rb', line 22

def ui
  @ui
end

Class Method Details

.check_a_record(host, transaction) ⇒ Object



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/landrush/server.rb', line 257

def self.check_a_record(host, transaction)
  value = Store.hosts.get(host)
  return if value.nil?

  if begin
        IPAddr.new(value)
      rescue
        nil
      end
    name = transaction.name =~ /#{host}/ ? transaction.name : host
    transaction.respond!(value, ttl: 0, name: name)
  else
    transaction.respond!(Name.create(value), resource_class: IN::CNAME, ttl: 0)
    check_a_record(value, transaction)
  end
end

.interfacesObject



69
70
71
72
73
74
# File 'lib/landrush/server.rb', line 69

def self.interfaces
  [
    [:udp, '0.0.0.0', port],
    [:tcp, '0.0.0.0', port]
  ]
end

.log_directoryObject



56
57
58
# File 'lib/landrush/server.rb', line 56

def self.log_directory
  File.join(working_dir, 'log')
end

.log_file_pathObject



60
61
62
# File 'lib/landrush/server.rb', line 60

def self.log_file_path
  File.join(log_directory, 'landrush.log')
end

.pidObject



166
167
168
169
170
# File 'lib/landrush/server.rb', line 166

def self.pid
  IO.read(pid_file).to_i
rescue
  nil
end

.pid_fileObject



274
275
276
# File 'lib/landrush/server.rb', line 274

def self.pid_file
  File.join(working_dir, 'run', 'landrush.pid')
end

.restartObject



161
162
163
164
# File 'lib/landrush/server.rb', line 161

def self.restart
  stop
  start
end

.run(port, working_dir) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/landrush/server.rb', line 204

def self.run(port, working_dir)
  server = self
  server.port = port
  server.working_dir = working_dir

  ensure_path_exits(log_file_path)
  log_file = File.open(log_file_path, 'w')
  log_file.sync = true
  @logger = Logger.new(log_file)
  @logger.level = Logger::INFO

  # Start the DNS server
  run_dns_server(listen: interfaces, logger: @logger) do
    match(/.*/, IN::A) do |transaction|
      host = Store.hosts.find(transaction.name)
      if host
        server.check_a_record(host, transaction)
      else
        transaction.passthrough!(server.upstream)
      end
    end

    match(/.*/, IN::PTR) do |transaction|
      host = Store.hosts.find(transaction.name)
      if host
        transaction.respond!(Name.create(Store.hosts.get(host)))
      else
        transaction.passthrough!(server.upstream)
      end
    end

    # Default DNS handler
    otherwise do |transaction|
      # @logger.info "Passing on to upstream: #{transaction.to_s}"
      transaction.passthrough!(server.upstream)
    end
  end
end

.run_dns_server(options = {}, &block) ⇒ Object



243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/landrush/server.rb', line 243

def self.run_dns_server(options = {}, &block)
  server = RubyDNS::RuleBasedServer.new(options, &block)

  EventMachine.run do
    trap('INT') do
      EventMachine.stop
    end

    server.run(options)
  end

  server.fire(:stop)
end

.running?Boolean

Returns:

  • (Boolean)


172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/landrush/server.rb', line 172

def self.running?
  pid = read_pid(pid_file)
  return false if pid.nil?
  if Vagrant::Util::Platform.windows?
    begin
      Process.get_exitcode(pid).nil?
    # Need to handle this explicitly since this error gets thrown in case we call get_exitcode with a stale pid
    rescue SystemCallError => e
      raise e unless e.class.name.start_with?('Errno::ENXIO')
    end
  else
    begin
      !!Process.kill(0, pid)
    rescue
      false
    end
  end
end

.startObject

Used to start the Landrush DNS server as a child process using ChildProcess gem



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/landrush/server.rb', line 81

def self.start
  # On a machine with just Vagrant installed there might be no other Ruby except the
  # one bundled with Vagrant. Let's make sure the embedded bin directory containing
  # the Ruby executable is added to the PATH.
  Landrush::Util::Path.ensure_ruby_on_path

  ruby_bin = Landrush::Util::Path.embedded_vagrant_ruby.nil? ? 'ruby' : Landrush::Util::Path.embedded_vagrant_ruby
  start_server_script = Pathname(__dir__).join('start_server.rb').to_s
  @ui.detail("[landrush] '#{ruby_bin} #{start_server_script} #{port} #{working_dir} #{gems_dir}'") unless @ui.nil?
  if Vagrant::Util::Platform.windows?
    # Need to handle Windows differently. Kernel.spawn fails to work, if
    # the shell creating the process is closed.
    # See https://github.com/vagrant-landrush/landrush/issues/199
    #
    # Note to the Future: Windows does not have a
    # file handle inheritance issue like Linux and Mac (see:
    # https://github.com/vagrant-landrush/landrush/issues/249)
    #
    # On windows, if no filehandle is passed then no files get
    # inherited by default, but if any filehandle is passed to
    # a spawned process then all files that are
    # set as inheritable will get inherited. In another project this
    # created a problem (see: https://github.com/dustymabe/vagrant-sshfs/issues/41).
    #
    # Today we don't pass any filehandles, so it isn't a problem.
    # Future self, make sure this doesn't become a problem.
    info = Process.create(command_line:    "#{ruby_bin} #{start_server_script} #{port} #{working_dir} #{gems_dir}",
                          creation_flags:  Process::DETACHED_PROCESS,
                          process_inherit: false,
                          thread_inherit:  true,
                          cwd:             working_dir.to_path)
    pid = info.process_id
  else
    # Fix https://github.com/vagrant-landrush/landrush/issues/249)
    # by turning of filehandle inheritance with :close_others => true
    # and by explicitly closing STDIN, STDOUT, and STDERR
    pid = spawn(ruby_bin, start_server_script, port.to_s, working_dir.to_s, gems_dir.to_s,
                in:           :close,
                out:          :close,
                err:          :close,
                close_others: true,
                chdir:        working_dir.to_path,
                pgroup:       true)
    Process.detach pid
  end

  write_pid(pid, pid_file)
  # As of Vagrant 1.8.6 this additonal sleep is needed, otherwise the child process dies!?
  sleep 1
end

.statusObject



191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/landrush/server.rb', line 191

def self.status
  case process_status(pid_file)
  when :running
    puts "Daemon status: running pid=#{read_pid(pid_file)}"
  when :stopped
    puts 'Daemon status: stopped'
  else
    puts 'Daemon status: unknown'
    puts "#{pid_file} exists, but process is not running"
    puts "Check log file: #{log_file_path}"
  end
end

.stopObject



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/landrush/server.rb', line 132

def self.stop
  puts 'Stopping daemon...'

  # Check if the pid file exists...
  unless File.file?(pid_file)
    puts "Pid file #{pid_file} not found. Is the daemon running?"
    return
  end

  pid = read_pid(pid_file)

  # Check if the daemon is already stopped...
  unless running?
    puts "Pid #{pid} is not running. Has daemon crashed?"
    return
  end

  terminate_process pid

  # If after doing our best the daemon is still running (pretty odd)...
  if running?
    puts 'Daemon appears to be still running!'
    return
  end

  # Otherwise the daemon has been stopped.
  delete_pid_file(pid_file)
end

.upstreamObject



76
77
78
# File 'lib/landrush/server.rb', line 76

def self.upstream
  @upstream ||= RubyDNS::Resolver.new(upstream_servers)
end

.upstream_serversObject



64
65
66
67
# File 'lib/landrush/server.rb', line 64

def self.upstream_servers
  # Doing collect to cast protocol to symbol because JSON store doesn't know about symbols
  @upstream_servers ||= Store.config.get('upstream').collect { |i| [i[0].to_sym, i[1], i[2]] }
end

.working_dirObject



25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/landrush/server.rb', line 25

def working_dir
  # TODO, https://github.com/vagrant-landrush/landrush/issues/178
  # Due to the fact that the whole server is just a bunch of static methods,
  # there is no initalize method to ensure that the working directory is
  # set prior to making calls to this method. Things work, since at the appropriate
  # Vagrant plugin integration points (e.g. setup.rb) we set the working dir based
  # on the enviroment passed to us.
  if @working_dir.nil?
    raise 'The Server\s working directory needs to be explicitly set prior to calling this method'
  end
  @working_dir
end

.working_dir=(working_dir) ⇒ Object



38
39
40
# File 'lib/landrush/server.rb', line 38

def working_dir=(working_dir)
  @working_dir = Pathname(working_dir).tap(&:mkpath)
end