Class: Bindan::Emulator::FirestoreController
- Inherits:
-
Object
- Object
- Bindan::Emulator::FirestoreController
- Defined in:
- lib/bindan/emulators/firestore_controller.rb
Overview
Represents and controls a single instance of the Google Cloud Firestore emulator process. This class encapsulates the logic for starting, stopping, and waiting for the emulator, separating process management from the test execution flow.
Constant Summary collapse
- WAIT_TIMEOUT =
seconds
15- INITIAL_BACKOFF_KEY_SEC =
seconds
0.1- BACKOFF_MULTIPLIER =
1.5
Class Method Summary collapse
- .host_and_port(host = nil, port = nil) ⇒ Array<String, Integer>
- .kill_process_if_already_exists(host:, port:, with_message: true) ⇒ Object
-
.start(import: nil, export: nil, host: nil, port: nil, close_io: true) ⇒ FirestoreEmulatorController
Initiates the emulator process and returns a new instance.
-
.stop(pid, with_message: true) ⇒ void
kill process group.
Instance Method Summary collapse
-
#initialize(pid, host, port) ⇒ FirestoreController
constructor
A new instance of FirestoreController.
- #stop(with_message: true) ⇒ void
-
#wait_available(timeout: WAIT_TIMEOUT, backoff: INITIAL_BACKOFF_KEY_SEC, multiplier: BACKOFF_MULTIPLIER, with_message: true) ⇒ void
waits to accessable.
Constructor Details
#initialize(pid, host, port) ⇒ FirestoreController
Returns a new instance of FirestoreController.
108 109 110 111 112 |
# File 'lib/bindan/emulators/firestore_controller.rb', line 108 def initialize(pid, host, port) @pid = pid @host = host @port = port end |
Class Method Details
.host_and_port(host = nil, port = nil) ⇒ Array<String, Integer>
24 25 26 27 28 29 30 31 |
# File 'lib/bindan/emulators/firestore_controller.rb', line 24 def host_and_port(host = nil, port = nil) host, port = ENV["FIRESTORE_EMULATOR_HOST"].to_s.split(":") if ENV["FIRESTORE_EMULATOR_HOST"] host ||= ENV["CI"] ? "0.0.0.0" : "localhost" port ||= 8080 [host, port] end |
.kill_process_if_already_exists(host:, port:, with_message: true) ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/bindan/emulators/firestore_controller.rb', line 88 def kill_process_if_already_exists(host:, port:, with_message: true) TCPSocket.new(host, port).close # `lsof -Fg -i:PORT` returns PIDs of the process listening on the port. process_ids, = Open3.capture3(*"lsof -Fg -g -s TCP:LISTEN -i :#{port}".split(" ")) process_group = process_ids.lines.map(&:chomp).find { |id| id =~ /^g([0-9]+)/ } if process_group id = process_group[1..].to_i # strip first letter puts " Found emulator process group #{id} on port #{port}. Terminating." if stop(id, with_message: false) end rescue Errno::ECONNREFUSED # noop end |
.start(import: nil, export: nil, host: nil, port: nil, close_io: true) ⇒ FirestoreEmulatorController
Initiates the emulator process and returns a new instance.
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/bindan/emulators/firestore_controller.rb', line 44 def start(import: nil, export: nil, host: nil, port: nil, close_io: true) host, port = host_and_port(host, port) puts " Starting Firestore emulator ..." unless close_io kill_process_if_already_exists(host: host, port: port, with_message: !close_io) # # This command is known to launch a background Java process and then exit. # Trying keep process info with process group # cmd = "gcloud emulators firestore start --host-port=#{host}:#{port}" cmd += " --import-data=#{import}" if import cmd += " --export-on-exit=#{export}" if export opts = close_io ? {out: "/dev/null", err: "/dev/null"} : {} pid = Process.spawn(*cmd, pgroup: true, **opts) # steep:ignore puts " Emulator process initiated." unless close_io new(pid, host, port) end |
.stop(pid, with_message: true) ⇒ void
This method returns an undefined value.
kill process group
71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/bindan/emulators/firestore_controller.rb', line 71 def stop(pid, with_message: true) puts "\n=> Stopping Firestore emulator..." if begin Process.kill("TERM", -pid) Process.wait(-pid) rescue Errno::ESRCH, Errno::ECHILD # Process was already killed or doesn't exist, which is fine. end puts " Emulator process terminated." if end |
Instance Method Details
#stop(with_message: true) ⇒ void
This method returns an undefined value.
147 148 149 |
# File 'lib/bindan/emulators/firestore_controller.rb', line 147 def stop(with_message: true) self.class.stop(@pid, with_message: ) end |
#wait_available(timeout: WAIT_TIMEOUT, backoff: INITIAL_BACKOFF_KEY_SEC, multiplier: BACKOFF_MULTIPLIER, with_message: true) ⇒ void
This method returns an undefined value.
waits to accessable
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/bindan/emulators/firestore_controller.rb', line 123 def wait_available(timeout: WAIT_TIMEOUT, backoff: INITIAL_BACKOFF_KEY_SEC, multiplier: BACKOFF_MULTIPLIER, with_message: true) last_rescue_error = nil puts " Waiting for emulator to be ready on #{@host}:#{@port}..." if count = 1 Timeout.timeout(timeout) do loop do TCPSocket.new(@host, @port).close puts " Emulator is up and listening." if return rescue Errno::ECONNREFUSED, Errno::EADDRNOTAVAIL => e last_rescue_error = e sleep backoff count += 1 backoff *= multiplier**count end end rescue Timeout::Error raise last_rescue_error # steep:ignore end |