sapling/eden/fs/service/client.py
Wez Furlong 9c634a9ce2 readlink .eden/socket before connecting
Summary:
The eden mount may be in a deep tree (for instance, in the
watchman integration tests) and be too long to fit in the unix domain
socket address.

The failure that we see in that case mistakenly informs the user that
eden is not running, rather than that we have a broken address.

Reviewed By: simpkins

Differential Revision: D5948497

fbshipit-source-id: 3bde8b83bf08b82bdd08758382ac0e218cc12829
2017-10-02 11:30:11 -07:00

90 lines
2.9 KiB
Python

# Copyright (c) 2016-present, Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from facebook.eden import EdenService
from thrift.protocol.THeaderProtocol import THeaderProtocol
from thrift.transport.THeaderTransport import THeaderTransport
from thrift.transport.TSocket import TSocket
from thrift.transport.TTransport import TTransportException
import os
SOCKET_PATH = 'socket'
class EdenNotRunningError(Exception):
def __init__(self, eden_dir):
msg = 'edenfs daemon does not appear to be running'
super(EdenNotRunningError, self).__init__(msg)
self.eden_dir = eden_dir
# Monkey-patch EdenService.EdenError's __str__() behavior to just return the
# error message. By default it returns the same data as __repr__(), which is
# ugly to show to users.
def _eden_thrift_error_str(ex):
return ex.message
EdenService.EdenError.__str__ = _eden_thrift_error_str
class EdenClient(EdenService.Client):
'''
EdenClient is a subclass of EdenService.Client that provides
a few additional conveniences:
- Smarter constructor
- Implement the context manager __enter__ and __exit__ methods, so it can
be used in with statements.
'''
def __init__(self, eden_dir=None, mounted_path=None):
self._eden_dir = eden_dir
if mounted_path:
sock_path = os.readlink(os.path.join(mounted_path, '.eden', 'socket'))
else:
sock_path = os.path.join(self._eden_dir, SOCKET_PATH)
self._socket = TSocket(unix_socket=sock_path)
self._socket.setTimeout(60000) # in milliseconds
self._transport = THeaderTransport(self._socket)
self._protocol = THeaderProtocol(self._transport)
super(EdenClient, self).__init__(self._protocol)
def __enter__(self):
self.open()
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
self.close()
def open(self):
try:
self._transport.open()
except TTransportException as ex:
self._transport.close()
if ex.type == TTransportException.NOT_OPEN:
raise EdenNotRunningError(self._eden_dir)
def close(self):
if self._transport is not None:
self._transport.close()
self._transport = None
def create_thrift_client(eden_dir=None, mounted_path=None):
'''Construct a thrift client to speak to the running eden server
instance associated with the specified mount point.
@return Returns a context manager for EdenService.Client.
'''
return EdenClient(eden_dir=eden_dir, mounted_path=mounted_path)