Skip to content

Commit

Permalink
Include a host fingerprint in pantsd's identity (#11092)
Browse files Browse the repository at this point in the history
### Problem

`pantsd` uses only options values to decide which instances are potentially valid for use. But on a well-configured system, option values will be stable across virtual machines and docker images, which means that `pantsd` will fruitlessly attempt to connect to a pid/socket that exists only on a different host. See #10847.

### Solution

In two commits: clean up some unnecessary abstraction around process metadata management, and include a host fingerprint in process metadata.

### Result

Multiple docker or virtual machine instances running in the same workspace should ignore one another's instances of `pantsd`. If we're able to include the uptime in the fingerprint in the future, it would further strengthen this check, but the fact that `docker` generates a random hostname means that this is sufficient to fix #10847.
  • Loading branch information
stuhood authored Nov 2, 2020
1 parent bf5e789 commit 411e0dc
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 269 deletions.
31 changes: 11 additions & 20 deletions src/python/pants/pantsd/pants_daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,24 +170,20 @@ def _pantsd_logging(self) -> Iterator[None]:
self._logger.debug("Logging reinitialized in pantsd context")
yield

def _write_nailgun_port(self):
"""Write the nailgun port to a well known file."""
self.write_socket(self._server.port())

def _initialize_pid(self):
"""Writes out our pid and metadata.
def _initialize_metadata(self) -> None:
"""Writes out our pid and other metadata.
Once written, does a one-time read of the pid to confirm that we haven't raced another
process starting.
Order matters a bit here, because technically all that is necessary to connect is the port,
and Services are lazily initialized by the core when a connection is established. Our pid
needs to be on disk before that happens.
"""

# Write the pidfile. The SchedulerService will monitor it after a grace period.
pid = os.getpid()
self._logger.debug(f"pantsd running with PID: {pid}")
self.write_pid(pid=pid)
self.write_metadata_by_name(
"pantsd", self.FINGERPRINT_KEY, ensure_text(self.options_fingerprint)
)
self.write_pid()
self.write_process_name()
self.write_fingerprint(ensure_text(self.options_fingerprint))
self._logger.debug(f"pantsd running with PID: {self.pid}")
self.write_socket(self._server.port())

def run_sync(self):
"""Synchronously run pantsd."""
Expand All @@ -208,12 +204,7 @@ def run_sync(self):
# Set the process name in ps output to 'pantsd' vs './pants compile src/etc:: -ldebug'.
set_process_title(f"pantsd [{self._build_root}]")

# Write our pid and the server's port to .pids. Order matters a bit here, because
# technically all that is necessary to connect is the port, and Services are lazily
# initialized by the core when a connection is established. Our pid needs to be on
# disk before that happens.
self._initialize_pid()
self._write_nailgun_port()
self._initialize_metadata()

# Check periodically whether the core is valid, and exit if it is not.
while self._core.is_valid():
Expand Down
Loading

0 comments on commit 411e0dc

Please sign in to comment.