Skip to content

Commit

Permalink
Merge branch master into topic-monit-systemd
Browse files Browse the repository at this point in the history
  • Loading branch information
sjones4 committed Sep 26, 2019
2 parents f7f320c + a3c5cfb commit 917c335
Show file tree
Hide file tree
Showing 119 changed files with 4,725 additions and 5,340 deletions.
1,460 changes: 0 additions & 1,460 deletions AdminServer/appscale/admin/__init__.py

Large diffs are not rendered by default.

1,461 changes: 1,461 additions & 0 deletions AdminServer/appscale/admin/admin_server.py

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions AdminServer/appscale/admin/instance_manager/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,6 @@ def http_response(self, request, response):

# The ZooKeeper node that keeps track of running AppServers by version.
VERSION_REGISTRATION_NODE = '/appscale/instances_by_version'

# The port Hermes listens on.
HERMES_PORT = 4378
8 changes: 5 additions & 3 deletions AdminServer/appscale/admin/instance_manager/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ def create_java_start_cmd(app_name, port, load_balancer_port, load_balancer_host
'--nginx_host={}'.format(load_balancer_host),
'--xmpp_path={}'.format(load_balancer_host),
'--uaserver_path={}'.format(
':'.join([options.db_proxy, str(UA_SERVER_PORT)])),
':'.join([load_balancer_host, str(UA_SERVER_PORT)])),
'--external_api_port={}'.format(api_server_port)
]

Expand Down Expand Up @@ -190,7 +190,8 @@ def create_python_api_start_cmd(app_name, login_ip, port,
'--nginx_host', login_ip,
'--enable_sendmail',
'--xmpp_path', options.load_balancer_ip,
'--uaserver_path', '{}:{}'.format(options.db_proxy, UA_SERVER_PORT),
'--uaserver_path', '{}:{}'.format(options.load_balancer_ip,
UA_SERVER_PORT),
'--datastore_path', '{}:{}'.format(options.db_proxy, DB_SERVER_PORT),
'--external_api_port', str(api_server_port)
]
Expand Down Expand Up @@ -247,7 +248,8 @@ def create_python27_start_cmd(app_name, login_ip, port, pidfile, revision_key,
'--xmpp_path', options.load_balancer_ip,
'--php_executable_path=' + str(PHP_CGI_LOCATION),
'--max_module_instances', "{}:1".format(service_id),
'--uaserver_path', '{}:{}'.format(options.db_proxy, UA_SERVER_PORT),
'--uaserver_path', '{}:{}'.format(options.load_balancer_ip,
UA_SERVER_PORT),
'--datastore_path', '{}:{}'.format(options.db_proxy, DB_SERVER_PORT),
'--host', options.private_ip,
'--automatic_restart', 'no',
Expand Down
4 changes: 2 additions & 2 deletions AdminServer/appscale/admin/instance_manager/routing_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
from tornado import gen
from tornado.httpclient import AsyncHTTPClient

from appscale.admin.instance_manager.constants import VERSION_REGISTRATION_NODE
from appscale.admin.instance_manager.constants import VERSION_REGISTRATION_NODE, \
HERMES_PORT
from appscale.admin.instance_manager.instance import Instance
from appscale.common import appscale_info
from appscale.common.constants import GAE_PREFIX, VERSION_PATH_SEPARATOR
from appscale.hermes.constants import HERMES_PORT

logger = logging.getLogger(__name__)

Expand Down
12 changes: 11 additions & 1 deletion AdminServer/appscale/admin/instance_manager/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,17 @@ def setup_logrotate(app_name, log_size):
notifempty
copytruncate
}}
""".format(log_prefix=log_prefix, size=log_size)
/opt/appscale/logserver/requests-{app_name}*.log {{
size {size}
missingok
rotate 3
compress
delaycompress
notifempty
copytruncate
}}
""".format(log_prefix=log_prefix, app_name=app_name, size=log_size)
logger.debug("Logrotate file: {} - Contents:\n{}".
format(app_logrotate_script, contents))

Expand Down
174 changes: 110 additions & 64 deletions AdminServer/appscale/admin/routing/haproxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,77 @@
import monotonic
import os
import pkgutil
import signal
import subprocess

from tornado import gen

from appscale.common.appscale_info import get_private_ip

logger = logging.getLogger('appscale-admin')
logger = logging.getLogger(__name__)

# The directory that contains HAProxy config files.
CONFIG_DIR = os.path.join('/', 'etc', 'haproxy')

# The location of the combined HAProxy config file for AppServer instances.
APP_CONFIG = os.path.join(CONFIG_DIR, 'app-haproxy.cfg')

# The location of the combined HAProxy config file for AppScale services.
SERVICE_CONFIG = os.path.join(CONFIG_DIR, 'service-haproxy.cfg')

# The location of the pidfile for instance-related HAProxy processes.
APP_PID = os.path.join('/', 'run', 'appscale', 'app-haproxy.pid')

# The location of the pidfile for service-related HAProxy processes.
SERVICE_PID = os.path.join('/', 'run', 'appscale', 'service-haproxy.pid')

# The location of the unix socket used for reporting application stats.
APP_STATS_SOCKET = os.path.join(CONFIG_DIR, 'stats')

# The location of the unix socket used for reporting service stats.
SERVICE_STATS_SOCKET = os.path.join(CONFIG_DIR, 'service-stats')


class InvalidConfig(Exception):
""" Indicates that a given HAProxy configuration cannot be enforced. """
pass


class HAProxyAppVersion(object):
""" Represents a version's HAProxy configuration. """
class HAProxyListenBlock(object):
""" Represents an HAProxy configuration block. """

# The template for a server config line.
SERVER_TEMPLATE = ('server gae_{version}-{server} {server} '
SERVER_TEMPLATE = ('server {block_id}-{location} {location} '
'maxconn {max_connections} check')

# The template for a version block.
VERSION_TEMPLATE = pkgutil.get_data('appscale.admin.routing',
'templates/version.cfg')
# The template for a listen block.
BLOCK_TEMPLATE = pkgutil.get_data('appscale.admin.routing',
'templates/listen_block.cfg')

def __init__(self, version_key, port, max_connections):
""" Creates a new HAProxyAppVersion instance.
def __init__(self, block_id, port, max_connections, servers=()):
""" Creates a new HAProxyListenBlock instance.
Args:
version_key: A string specifying a version
block_id: A string specifying the name of the listen block.
port: An integer specifying the listen port.
max_connections: An integer specifying the max number of connections.
servers: An iterable specifying server locations.
"""
self.version_key = version_key
self.block_id = block_id
self.port = port
self.max_connections = max_connections
self.servers = []
self.servers = servers

self._private_ip = get_private_ip()

def __repr__(self):
""" Returns a print-friendly representation of the version config. """
return 'HAProxyAppVersion<{}:{}, maxconn:{}, servers:{}>'.format(
self.version_key, self.port, self.max_connections, self.servers)
return 'HAProxyListenBlock({!r}, {!r}, {!r}, {!r})'.format(
self.block_id, self.port, self.max_connections, self.servers)

@property
def block(self):
""" Represents the version as a configuration block.
""" Generates the configuration block.
Returns:
A string containing the configuration block or None.
Expand All @@ -61,27 +83,18 @@ def block(self):
return None

server_lines = [
self.SERVER_TEMPLATE.format(version=self.version_key, server=server,
self.SERVER_TEMPLATE.format(block_id=self.block_id, location=server,
max_connections=self.max_connections)
for server in self.servers]
server_lines.sort()
bind_location = ':'.join([self._private_ip, str(self.port)])
return self.VERSION_TEMPLATE.format(
version=self.version_key, bind_location=bind_location,
return self.BLOCK_TEMPLATE.format(
block_id=self.block_id, bind_location=bind_location,
servers='\n '.join(server_lines))


class HAProxy(object):
""" Manages HAProxy operations. """

# The location of the combined HAProxy config file for AppServer instances.
APP_CONFIG = os.path.join(CONFIG_DIR, 'app-haproxy.cfg')

# The location of the pidfile for instance-related HAProxy processes.
APP_PID = os.path.join('/', 'run', 'appscale', 'app-haproxy.pid')

# The location of the unix socket used for reporting stats.
APP_STATS_SOCKET = os.path.join(CONFIG_DIR, 'stats')
""" Manages an HAProxy process. """

# The template for the configuration file.
BASE_TEMPLATE = pkgutil.get_data('appscale.admin.routing',
Expand All @@ -99,14 +112,18 @@ class HAProxy(object):
# The minimum number of seconds to wait between each reload operation.
RELOAD_COOLDOWN = .1

def __init__(self):
def __init__(self, config_location, pid_location, stats_socket):
""" Creates a new HAProxy operator. """
self.connect_timeout_ms = self.DEFAULT_CONNECT_TIMEOUT * 1000
self.client_timeout_ms = self.DEFAULT_CLIENT_TIMEOUT * 1000
self.server_timeout_ms = self.DEFAULT_SERVER_TIMEOUT * 1000
self.versions = {}
self.blocks = {}
self.reload_future = None

self._config_location = config_location
self._pid_location = pid_location
self._stats_socket = stats_socket

# Given the arbitrary base of the monotonic clock, it doesn't make sense
# for outside functions to access this attribute.
self._last_reload = monotonic.monotonic()
Expand All @@ -119,22 +136,25 @@ def config(self):
A string containing a complete HAProxy configuration.
"""
unique_ports = set()
for version in self.versions.values():
if version.port in unique_ports:
for block in self.blocks.values():
if block.port in unique_ports:
raise InvalidConfig('Port {} is used by more than one '
'version'.format(version.port))
'block'.format(block.port))

unique_ports.add(block.port)

unique_ports.add(version.port)
listen_blocks = [self.blocks[key].block
for key in sorted(self.blocks.keys())
if self.blocks[key].block]
if not listen_blocks:
return None

version_blocks = [self.versions[key].block
for key in sorted(self.versions.keys())
if self.versions[key].block]
return self.BASE_TEMPLATE.format(
stats_socket=self.APP_STATS_SOCKET,
stats_socket=self._stats_socket,
connect_timeout=self.connect_timeout_ms,
client_timeout=self.client_timeout_ms,
server_timeout=self.server_timeout_ms,
versions='\n'.join(version_blocks))
listen_blocks='\n'.join(listen_blocks))

@gen.coroutine
def reload(self):
Expand All @@ -144,6 +164,44 @@ def reload(self):

yield self.reload_future

def _get_pid(self):
try:
with open(self._pid_location) as pid_file:
pid = int(pid_file.read())
except IOError as error:
if error.errno != errno.ENOENT:
raise

pid = None

# Check if the process is running.
if pid is not None:
try:
os.kill(pid, 0)
except OSError as error:
if error.errno == errno.ESRCH:
pid = None
else:
logger.warning('Encountered unexpected error when checking haproxy '
'process: {}'.format(str(error)))

return pid

def _stop(self):
pid = self._get_pid()
if pid is not None:
try:
os.kill(pid, signal.SIGUSR1)
except OSError as error:
if error.errno != errno.ESRCH:
logger.error('Unable to stop haproxy process')

try:
os.remove(self._config_location)
except OSError as error:
if error.errno != errno.ENOENT:
raise

@gen.coroutine
def _reload(self):
""" Updates the routing entries if they've changed. """
Expand All @@ -158,9 +216,13 @@ def _reload(self):
logger.error(str(error))
return

# Ensure process is not running if there is nothing to route.
if new_content is None:
self._stop()

try:
with open(self.APP_CONFIG, 'r') as app_config_file:
existing_content = app_config_file.read()
with open(self._config_location, 'r') as config_file:
existing_content = config_file.read()
except IOError as error:
if error.errno != errno.ENOENT:
raise
Expand All @@ -170,31 +232,15 @@ def _reload(self):
if new_content == existing_content:
return

with open(self.APP_CONFIG, 'w') as app_config_file:
app_config_file.write(new_content)

try:
with open(self.APP_PID) as pid_file:
pid = int(pid_file.read())

except IOError as error:
if error.errno != errno.ENOENT:
raise

pid = None

# Check if the process is running.
if pid is not None:
try:
os.kill(pid, 0)
except OSError:
pid = None
with open(self._config_location, 'w') as config_file:
config_file.write(new_content)

pid = self._get_pid()
if pid is None:
subprocess.check_call(['haproxy', '-f', self.APP_CONFIG, '-D',
'-p', self.APP_PID])
subprocess.check_call(['haproxy', '-f', self._config_location, '-D',
'-p', self._pid_location])
else:
subprocess.check_call(['haproxy', '-f', self.APP_CONFIG, '-D',
'-p', self.APP_PID, '-sf', str(pid)])
subprocess.check_call(['haproxy', '-f', self._config_location, '-D',
'-p', self._pid_location, '-sf', str(pid)])

logger.info('Updated HAProxy config')
Loading

0 comments on commit 917c335

Please sign in to comment.