Allow the connection timeout to be configured on a per-server basis. Fixes #3388

pull/14/head
Akshay Joshi 2018-06-19 19:58:46 -04:00 committed by Dave Page
parent 9821e28da5
commit 7a06acb678
14 changed files with 138 additions and 13 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -14,6 +14,7 @@ Features
| `Feature #2686 <https://redmine.postgresql.org/issues/2686>`_ - Add an option to auto-complete keywords in upper case
| `Feature #3204 <https://redmine.postgresql.org/issues/3204>`_ - Add support for LISTEN/NOTIFY in the query tool
| `Feature #3362 <https://redmine.postgresql.org/issues/3362>`_ - Function and procedure support for PG11
| `Feature #3388 <https://redmine.postgresql.org/issues/3388>`_ - Allow the connection timeout to be configured on a per-server basis
Bug fixes
*********

View File

@ -85,6 +85,7 @@ Use the fields in the *Advanced* tab to configure a connection:
* Specify the IP address of the server host in the *Host address* field. Using this field to specify the host IP address may save time by avoiding a DNS lookup on connection, but it may be useful to specify both a host name and address when using Kerberos, GSSAPI, or SSPI authentication methods, as well as for verify-full SSL certificate verification.
* Use the *DB restriction* field to provide a SQL restriction that will be used against the pg_database table to limit the databases that you see. For example, you might enter: *live_db test_db* so that only live_db and test_db are shown in the pgAdmin browser. Separate entries with a comma or tab as you type.
* Use the *Password File* field to specify the location of a password file (.pgpass). A .pgpass file allows a user to login without providing a password when they connect. For more information, see `Section 33.15 of the Postgres documentation <http://www.postgresql.org/docs/current/static/libpq-pgpass.html>`_.
* Use the *Connection timeout* field to specify the maximum wait for connection, in seconds. Zero or not specified means wait indefinitely. It is not recommended to use a timeout of less than 2 seconds.
*NOTE:* The password file option is only supported when pgAdmin is using libpq v10.0 or later to connect to the server.

View File

@ -0,0 +1,28 @@
"""empty message
Revision ID: 493cd3e39c0c
Revises: 7c56ea250085
Create Date: 2018-06-18 11:26:33.285037
"""
from alembic import op
import sqlalchemy as sa
from pgadmin.model import db
# revision identifiers, used by Alembic.
revision = '493cd3e39c0c'
down_revision = '7c56ea250085'
branch_labels = None
depends_on = None
def upgrade():
db.engine.execute(
'ALTER TABLE server ADD COLUMN connect_timeout INTEGER DEFAULT 0'
)
def downgrade():
pass

View File

@ -480,6 +480,7 @@ class ServerNode(PGChildNodeView):
'bgcolor': 'bgcolor',
'fgcolor': 'fgcolor',
'service': 'service',
'connect_timeout': 'connect_timeout',
'use_ssh_tunnel': 'use_ssh_tunnel',
'tunnel_host': 'tunnel_host',
'tunnel_port': 'tunnel_port',
@ -672,6 +673,8 @@ class ServerNode(PGChildNodeView):
'sslcompression': True if is_ssl and server.sslcompression
else False,
'service': server.service if server.service else None,
'connect_timeout':
server.connect_timeout if server.connect_timeout else 0,
'use_ssh_tunnel': server.use_ssh_tunnel
if server.use_ssh_tunnel else 0,
'tunnel_host': server.tunnel_host if server.tunnel_host
@ -755,6 +758,7 @@ class ServerNode(PGChildNodeView):
bgcolor=data.get('bgcolor', None),
fgcolor=data.get('fgcolor', None),
service=data.get('service', None),
connect_timeout=data.get('connect_timeout', 0),
use_ssh_tunnel=data.get('use_ssh_tunnel', 0),
tunnel_host=data.get('tunnel_host', None),
tunnel_port=data.get('tunnel_port', 22),

View File

@ -627,6 +627,7 @@ define('pgadmin.node.server', [
tunnel_identity_file: undefined,
tunnel_password: undefined,
tunnel_authentication: 0,
connect_timeout: 0,
},
// Default values!
initialize: function(attrs, args) {
@ -888,6 +889,11 @@ define('pgadmin.node.server', [
id: 'service', label: gettext('Service'), type: 'text',
mode: ['properties', 'edit', 'create'], disabled: 'isConnected',
group: gettext('Connection'),
},{
id: 'connect_timeout', label: gettext('Connection timeout (seconds)'),
type: 'int', group: gettext('Advanced'),
mode: ['properties', 'edit', 'create'], disabled: 'isConnected',
min: 0,
}],
validate: function() {
const validateModel = new modelValidation.ModelValidation(this);

View File

@ -0,0 +1,47 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import json
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
class ServersWithConnectTimeoutAddTestCase(BaseTestGenerator):
""" This class will add the servers under default server group. """
scenarios = [
# Fetch the default url for server object
(
'Default Server Node url', dict(
url='/browser/server/obj/'
)
)
]
def setUp(self):
pass
def runTest(self):
""" This function will add the server under default server group."""
url = "{0}{1}/".format(self.url, utils.SERVER_GROUP)
# Add service name in the config
self.server['connect_timeout'] = 5
response = self.tester.post(
url,
data=json.dumps(self.server),
content_type='html/json'
)
self.assertEquals(response.status_code, 200)
response_data = json.loads(response.data.decode('utf-8'))
self.server_id = response_data['node']['_id']
def tearDown(self):
"""This function delete the server from SQLite """
utils.delete_server_with_api(self.tester, self.server_id)

View File

@ -173,8 +173,8 @@ define([
id: s.id,
label: s.label,
type: s.type,
min: s.min || undefined,
max: s.max || undefined,
min: !_.isUndefined(s.min) ? s.min : undefined,
max: !_.isUndefined(s.max) ? s.max : undefined,
};
break;
default:
@ -767,9 +767,9 @@ define([
min_value = field.min,
max_value = field.max;
if (min_value && value < min_value) {
if (!_.isUndefined(min_value) && value < min_value) {
return S(pgAdmin.Browser.messages.MUST_GR_EQ).sprintf(label, min_value).value();
} else if (max_value && value > max_value) {
} else if (!_.isUndefined(max_value) && value > max_value) {
return S(pgAdmin.Browser.messages.MUST_LESS_EQ).sprintf(label, max_value).value();
}
return null;
@ -1301,4 +1301,4 @@ define([
pgBrowser.Events = _.extend({}, Backbone.Events);
return pgBrowser;
});
});

View File

@ -29,7 +29,7 @@ from flask_sqlalchemy import SQLAlchemy
#
##########################################################################
SCHEMA_VERSION = 16
SCHEMA_VERSION = 17
##########################################################################
#
@ -145,6 +145,7 @@ class Server(db.Model):
bgcolor = db.Column(db.Text(10), nullable=True)
fgcolor = db.Column(db.Text(10), nullable=True)
service = db.Column(db.Text(), nullable=True)
connect_timeout = db.Column(db.Integer(), nullable=False)
use_ssh_tunnel = db.Column(
db.Integer(),
db.CheckConstraint('use_ssh_tunnel >= 0 AND use_ssh_tunnel <= 1'),

View File

@ -334,7 +334,15 @@ def create_backup_job(sid):
cmd=utility, args=args
)
manager.export_password_env(p.id)
p.set_env_variables(server)
# Check for connection timeout and if it is greater than 0 then
# set the environment variable PGCONNECT_TIMEOUT.
if manager.connect_timeout > 0:
env = dict()
env['PGCONNECT_TIMEOUT'] = str(manager.connect_timeout)
p.set_env_variables(server, env=env)
else:
p.set_env_variables(server)
p.start()
jid = p.id
except Exception as e:
@ -499,7 +507,15 @@ def create_backup_objects_job(sid):
cmd=utility, args=args
)
manager.export_password_env(p.id)
p.set_env_variables(server)
# Check for connection timeout and if it is greater than 0 then
# set the environment variable PGCONNECT_TIMEOUT.
if manager.connect_timeout > 0:
env = dict()
env['PGCONNECT_TIMEOUT'] = str(manager.connect_timeout)
p.set_env_variables(server, env=env)
else:
p.set_env_variables(server)
p.start()
jid = p.id
except Exception as e:

View File

@ -240,7 +240,15 @@ def create_maintenance_job(sid, did):
cmd=utility, args=args
)
manager.export_password_env(p.id)
p.set_env_variables(server)
# Check for connection timeout and if it is greater than 0 then
# set the environment variable PGCONNECT_TIMEOUT.
if manager.connect_timeout > 0:
env = dict()
env['PGCONNECT_TIMEOUT'] = str(manager.connect_timeout)
p.set_env_variables(server, env=env)
else:
p.set_env_variables(server)
p.start()
jid = p.id
except Exception as e:

View File

@ -342,7 +342,15 @@ def create_restore_job(sid):
cmd=utility, args=args
)
manager.export_password_env(p.id)
p.set_env_variables(server)
# Check for connection timeout and if it is greater than 0 then
# set the environment variable PGCONNECT_TIMEOUT.
if manager.connect_timeout > 0:
env = dict()
env['PGCONNECT_TIMEOUT'] = str(manager.connect_timeout)
p.set_env_variables(server, env=env)
else:
p.set_env_variables(server)
p.start()
jid = p.id
except Exception as e:

View File

@ -307,7 +307,8 @@ class Connection(BaseConnection):
sslrootcert=get_complete_file_path(manager.sslrootcert),
sslcrl=get_complete_file_path(manager.sslcrl),
sslcompression=True if manager.sslcompression else False,
service=manager.service
service=manager.service,
connect_timeout=manager.connect_timeout
)
# If connection is asynchronous then we will have to wait
@ -1234,7 +1235,8 @@ WHERE
sslrootcert=get_complete_file_path(manager.sslrootcert),
sslcrl=get_complete_file_path(manager.sslcrl),
sslcompression=True if manager.sslcompression else False,
service=manager.service
service=manager.service,
connect_timeout=manager.connect_timeout
)
except psycopg2.Error as e:
@ -1519,7 +1521,8 @@ Failed to reset the connection to the server due to following error:
sslcrl=get_complete_file_path(self.manager.sslcrl),
sslcompression=True if self.manager.sslcompression
else False,
service=self.manager.service
service=self.manager.service,
connect_timeout=self.manager.connect_timeout
)
# Get the cursor and run the query

View File

@ -74,6 +74,8 @@ class ServerManager(object):
self.sslcrl = server.sslcrl
self.sslcompression = True if server.sslcompression else False
self.service = server.service
self.connect_timeout = \
server.connect_timeout if server.connect_timeout else 0
if config.SUPPORT_SSH_TUNNEL:
self.use_ssh_tunnel = server.use_ssh_tunnel
self.tunnel_host = server.tunnel_host