Skip to content

Commit

Permalink
Merge 38a62bb into 065fea1
Browse files Browse the repository at this point in the history
  • Loading branch information
0golovatyi committed May 2, 2019
2 parents 065fea1 + 38a62bb commit e88c331
Show file tree
Hide file tree
Showing 20 changed files with 333 additions and 299 deletions.
2 changes: 2 additions & 0 deletions .vscode/settings.json
@@ -1,6 +1,8 @@
{
"git.enabled": true,
"files.exclude": {
"**/build": true,
"**/dist": true,
"**/__pycache__": true,
"**/.pytest_cache": true,
"**/*.egg-info": true,
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Expand Up @@ -45,6 +45,7 @@ be able to work on TabPy changes:
git clone https://github.com/tableau/TabPy.git
cd TabPy
```

Before making any code changes run environment setup script.
For Windows run this command from the repository root folder:

Expand Down
23 changes: 17 additions & 6 deletions docs/server-config.md
Expand Up @@ -163,21 +163,32 @@ With the feature on additional information is logged for HTTP requests: caller i
URL, client infomation (Tableau Desktop\Server), Tableau user name (for Tableau Server)
and TabPy user name as shown in the example below:

<!-- markdownlint-disable MD013 -->
<!-- markdownlint-disable MD040 -->

```
2019-04-17,15:20:37 [INFO] (evaluation_plane_handler.py:evaluation_plane_handler:86):
::1 calls POST http://localhost:9004/evaluate,
Client: Tableau Server 2019.2,
Tableau user: ogolovatyi,
TabPy user: user1
function to evaluate=def _user_script(tabpy, _arg1, _arg2):
2019-05-02,13:50:08 [INFO] (base_handler.py:base_handler:90): Call ID: 934073bd-0d29-46d3-b693-b1e4b1efa9e4, Caller: ::1, Method: POST, Resource: http://localhost:9004/evaluate, Client: Postman for manual testing, Tableau user: ogolovatyi
2019-05-02,13:50:08 [DEBUG] (base_handler.py:base_handler:120): Checking if need to handle authentication, <<
call ID: 934073bd-0d29-46d3-b693-b1e4b1efa9e4>>
2019-05-02,13:50:08 [DEBUG] (base_handler.py:base_handler:120): Handling authentication, <<call ID: 934073bd-
0d29-46d3-b693-b1e4b1efa9e4>>
2019-05-02,13:50:08 [DEBUG] (base_handler.py:base_handler:120): Checking request headers for authentication d
ata, <<call ID: 934073bd-0d29-46d3-b693-b1e4b1efa9e4>>
2019-05-02,13:50:08 [DEBUG] (base_handler.py:base_handler:120): Validating credentials for user name "user1",
<<call ID: 934073bd-0d29-46d3-b693-b1e4b1efa9e4>>
2019-05-02,13:50:08 [DEBUG] (state.py:state:484): Collecting Access-Control-Allow-Origin from state file...
2019-05-02,13:50:08 [INFO] (base_handler.py:base_handler:120): function to evaluate=def _user_script(tabpy, _
arg1, _arg2):
res = []
for i in range(len(_arg1)):
res.append(_arg1[i] * _arg2[i])
return res
, <<call ID: 934073bd-0d29-46d3-b693-b1e4b1efa9e4>>
```

<!-- markdownlint-enable MD040 -->
<!-- markdownlint-enable MD013 -->

No passwords are logged.

NOTE the request context details are logged with INFO level.
29 changes: 16 additions & 13 deletions tabpy-server/tabpy_server/app/app.py
Expand Up @@ -9,9 +9,7 @@
from tabpy_server import __version__
from tabpy_server.app.ConfigParameters import ConfigParameters
from tabpy_server.app.SettingsParameters import SettingsParameters
from tabpy_server.app.util import (
log_and_raise,
parse_pwd_file)
from tabpy_server.app.util import parse_pwd_file
from tabpy_server.management.state import TabPyState
from tabpy_server.management.util import _get_state_from_file
from tabpy_server.psws.callbacks import (init_model_evaluator, init_ps_server)
Expand Down Expand Up @@ -76,8 +74,9 @@ def run(self):
'keyfile': self.settings[SettingsParameters.KeyFile]
})
else:
log_and_raise(f'Unsupported transfer protocol {protocol}.',
RuntimeError)
msg = f'Unsupported transfer protocol {protocol}.'
logger.critical(msg)
raise RuntimeError(msg)

logger.info('Web service listening on port {}'.format(
str(self.settings[SettingsParameters.Port])))
Expand Down Expand Up @@ -244,9 +243,10 @@ def set_parameter(settings_key,
ConfigParameters.TABPY_PWD_FILE)
if ConfigParameters.TABPY_PWD_FILE in self.settings:
if not self._parse_pwd_file():
log_and_raise('Failed to read passwords file %s' %
self.settings[ConfigParameters.TABPY_PWD_FILE],
RuntimeError)
msg = ('Failed to read passwords file '
f'{self.settings[ConfigParameters.TABPY_PWD_FILE]}')
logger.critical(msg)
raise RuntimeError(msg)
else:
logger.info(
"Password file is not specified: "
Expand All @@ -268,17 +268,19 @@ def set_parameter(settings_key,

def _validate_transfer_protocol_settings(self):
if SettingsParameters.TransferProtocol not in self.settings:
log_and_raise(
'Missing transfer protocol information.', RuntimeError)
msg = 'Missing transfer protocol information.'
logger.critical(msg)
raise RuntimeError(msg)

protocol = self.settings[SettingsParameters.TransferProtocol]

if protocol == 'http':
return

if protocol != 'https':
log_and_raise('Unsupported transfer protocol: {}.'.format(
protocol), RuntimeError)
msg = f'Unsupported transfer protocol: {protocol}'
logger.critical(msg)
raise RuntimeError(msg)

self._validate_cert_key_state(
'The parameter(s) {} must be set.',
Expand Down Expand Up @@ -309,7 +311,8 @@ def _validate_cert_key_state(msg, cert_valid, key_valid):
err = https_error + msg.format(ConfigParameters.TABPY_KEY_FILE)

if err is not None:
log_and_raise(err, RuntimeError)
logger.critical(err)
raise RuntimeError(err)

def _parse_pwd_file(self):
succeeded, self.credentials = parse_pwd_file(
Expand Down
26 changes: 10 additions & 16 deletions tabpy-server/tabpy_server/app/util.py
Expand Up @@ -8,14 +8,6 @@
logger = logging.getLogger(__name__)


def log_and_raise(msg, exception_type):
'''
Log the message and raise an exception of specified type
'''
logger.fatal(msg)
raise exception_type(msg)


def validate_cert(cert_file_path):
with open(cert_file_path, 'r') as f:
cert_buf = f.read()
Expand All @@ -31,13 +23,15 @@ def validate_cert(cert_file_path):

https_error = 'Error using HTTPS: '
if now < not_before:
log_and_raise(https_error +
'The certificate provided is not valid until {}.'.format(
not_before), RuntimeError)
msg = (https_error +
f'The certificate provided is not valid until {not_before}.')
logger.critical(msg)
raise RuntimeError(msg)
if now > not_after:
log_and_raise(https_error +
f'The certificate provided expired on {not_after}.',
RuntimeError)
msg = (https_error +
f'The certificate provided expired on {not_after}.')
logger.critical(msg)
raise RuntimeError(msg)


def parse_pwd_file(pwd_file_name):
Expand All @@ -58,10 +52,10 @@ def parse_pwd_file(pwd_file_name):
credentials : dict
Credentials from the file. Empty if succeeded is False.
'''
logger.info('Parsing passwords file {}...'.format(pwd_file_name))
logger.info(f'Parsing passwords file {pwd_file_name}...')

if not os.path.isfile(pwd_file_name):
logger.fatal('Passwords file {} not found'.format(pwd_file_name))
logger.critical(f'Passwords file {pwd_file_name} not found')
return False, {}

credentials = {}
Expand Down
32 changes: 16 additions & 16 deletions tabpy-server/tabpy_server/common/endpoint_file_mgr.py
Expand Up @@ -13,25 +13,28 @@
import shutil
from re import compile as _compile

logger = logging.getLogger(__name__)

_name_checker = _compile(r'^[a-zA-Z0-9-_\s]+$')


def _check_endpoint_name(name):
def _check_endpoint_name(name, logger=logging.getLogger(__name__)):
"""Checks that the endpoint name is valid by comparing it with an RE and
checking that it is not reserved."""
if not isinstance(name, str):
log_and_raise("Endpoint name must be a string or unicode", TypeError)
msg = 'Endpoint name must be a string or unicode'
logger.log(logging.CRITICAL, msg)
raise TypeError(msg)

if name == '':
log_and_raise("Endpoint name cannot be empty", ValueError)
msg = 'Endpoint name cannot be empty'
logger.log(logging.CRITICAL, msg)
raise ValueError(msg)

if not _name_checker.match(name):
log_and_raise(
'Endpoint name can only contain: a-z, A-Z, 0-9,'
' underscore, hyphens and spaces.',
ValueError)
msg = ('Endpoint name can only contain: a-z, A-Z, 0-9,'
' underscore, hyphens and spaces.')
logger.log(logging.CRITICAL, msg)
raise ValueError(msg)


def grab_files(directory):
Expand All @@ -51,12 +54,9 @@ def grab_files(directory):
yield full_path


def get_local_endpoint_file_path(name, version, query_path):
_check_endpoint_name(name)
return os.path.join(query_path, name, str(version))


def cleanup_endpoint_files(name, query_path, retain_versions=None):
def cleanup_endpoint_files(name, query_path,
logger=logging.getLogger(__name__),
retain_versions=None):
'''
Cleanup the disk space a certain endpiont uses.
Expand All @@ -70,7 +70,7 @@ def cleanup_endpoint_files(name, query_path, retain_versions=None):
folder for the given version, otherwise, all files for that endpoint
are removed.
'''
_check_endpoint_name(name)
_check_endpoint_name(name, logger=logger)
local_dir = os.path.join(query_path, name)

# nothing to clean, this is true for state file path where we load
Expand All @@ -84,7 +84,7 @@ def cleanup_endpoint_files(name, query_path, retain_versions=None):
else:
retain_folders = [os.path.join(local_dir, str(version))
for version in retain_versions]
logger.info("Retain folder: %s" % retain_folders)
logger.log(logging.INFO, f'Retain folders: {retain_folders}')

for file_or_dir in os.listdir(local_dir):
candidate_dir = os.path.join(local_dir, file_or_dir)
Expand Down
8 changes: 0 additions & 8 deletions tabpy-server/tabpy_server/common/util.py
Expand Up @@ -5,11 +5,3 @@ def format_exception(e, context):
err_msg = "%s : " % e.__class__.__name__
err_msg += "%s" % str(e)
return err_msg


def format_exception_DEBUG(e, context, detail=0):
trace = traceback.format_exc()
err_msg = "Traceback\n %s\n" % trace
err_msg += "Error type : %s\n" % e.__class__.__name__
err_msg += "Error message : %s\n" % str(e)
return "Error when %s: %s" % (context, err_msg)

0 comments on commit e88c331

Please sign in to comment.