mirror of https://github.com/ekimekim/wubloader
started on the segment_coverage service
parent
095e391b60
commit
929308f3e7
@ -0,0 +1,14 @@
|
|||||||
|
FROM alpine:3.7
|
||||||
|
# dependencies needed for compiling c extensions
|
||||||
|
# also busybox-extras for telnet for easier use of backdoor
|
||||||
|
RUN apk --update add py2-pip gcc python-dev musl-dev busybox-extras
|
||||||
|
|
||||||
|
# Install common lib first as it changes less
|
||||||
|
COPY common /tmp/common
|
||||||
|
RUN pip install /tmp/common && rm -r /tmp/common
|
||||||
|
|
||||||
|
# Install actual application
|
||||||
|
COPY segment_coverage /tmp/segment_coverage
|
||||||
|
RUN pip install /tmp/segment_coverage && rm -r /tmp/segment_coverage
|
||||||
|
|
||||||
|
ENTRYPOINT ["python2", "-m", "segment_coverage"]
|
@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
import gevent.monkey
|
||||||
|
gevent.monkey.patch_all()
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
import argh
|
||||||
|
|
||||||
|
from segment_coverage.main import main
|
||||||
|
|
||||||
|
LOG_FORMAT = "[%(asctime)s] %(levelname)8s %(name)s(%(module)s:%(lineno)d): %(message)s"
|
||||||
|
|
||||||
|
level = os.environ.get('WUBLOADER_LOG_LEVEL', 'INFO').upper()
|
||||||
|
logging.basicConfig(level=level, format=LOG_FORMAT)
|
||||||
|
argh.dispatch_command(main)
|
||||||
|
|
@ -0,0 +1,101 @@
|
|||||||
|
import glob
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
import argh
|
||||||
|
import gevent.backdoor
|
||||||
|
import prometheus_client as prom
|
||||||
|
|
||||||
|
import common
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class CoverageChecker(object):
|
||||||
|
"""Checks the segment coverage for a given channel in a a given directoy."""
|
||||||
|
|
||||||
|
CHECK_INTERVAL = 60 #seconds between checking coverage
|
||||||
|
|
||||||
|
def __init__(channel, qualities, base_dir, recent_cutoff):
|
||||||
|
"""Constructor for CoverageChecker.
|
||||||
|
|
||||||
|
Creates a checker for a given channel with specified qualities."""
|
||||||
|
self.base_dir = base_dir
|
||||||
|
self.channel = channel
|
||||||
|
self.qualities = qualities
|
||||||
|
self.recent_cutoff = recent_cutoff
|
||||||
|
self.stopping = gevent.event.Event()
|
||||||
|
self.logger = logging.getLogger('CoverageChecker({})'.format(channel))
|
||||||
|
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""Stop checking coverage."""
|
||||||
|
self.logger.info('Stopping')
|
||||||
|
self.stopping.set()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""Loop over available hours for each quality, checking segment coverage."""
|
||||||
|
self.logger.info('Starting')
|
||||||
|
|
||||||
|
while not self.stopping.is_set():
|
||||||
|
|
||||||
|
for quality in self.qualities:
|
||||||
|
hour_dirs = glob.glob('{}/{}/{}/????-??-??T??'.format(self.basedir, self.channel, quality))
|
||||||
|
for hour_dir in hour_dirs:
|
||||||
|
segments = glob.glob('{}/??:*-*-*.ts')
|
||||||
|
|
||||||
|
for segment in segments:
|
||||||
|
seg = common.parse_segment_path(segment)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@argh.arg('channels', nargs='*', help='Channels to check coverage of')
|
||||||
|
@argh.arg('--base-dir', help='Directory where segments are stored. Default is current working directory.')
|
||||||
|
@argh.arg('--qualities', help="Qualities of each channel to checked. Comma seperated if multiple. Default is 'source'.")
|
||||||
|
@argh.arg('--metrics-port', help='Port for Prometheus stats. Default is 8006.')
|
||||||
|
@argh.arg('--backdoor-port', help='Port for gevent.backdoor access. By default disabled.')
|
||||||
|
@argh.arg('--recent-cutoff', help='Ignore segments younger than this when computing coverage. Expressed as number of seconds.')
|
||||||
|
|
||||||
|
def main(channels, base_dir='.', qualities='source', metrics_port=8006,
|
||||||
|
backdoor_port=0, recent_cutoff=120):
|
||||||
|
"""Segment coverage service"""
|
||||||
|
qualities = qualities.split(',') if qualities else []
|
||||||
|
qualities = [quality.strip() for quality in qualities]
|
||||||
|
|
||||||
|
common.PromLogCountsHandler.install()
|
||||||
|
common.install_stacksampler()
|
||||||
|
prom.start_http_server(metrics_port)
|
||||||
|
|
||||||
|
managers = []
|
||||||
|
workers = []
|
||||||
|
for channel in channels:
|
||||||
|
logging.info('Starting coverage checks {} with {} as qualities in {}'.format(channel, ', '.join(qualities), base_dir))
|
||||||
|
manager = CoverageChecker(channel, qualities, base_dir, recent_cutoff)
|
||||||
|
managers.append(manager)
|
||||||
|
workers.append(gevent.spawn(manager.run))
|
||||||
|
|
||||||
|
def stop():
|
||||||
|
for manager in managers:
|
||||||
|
manager.stop()
|
||||||
|
|
||||||
|
gevent.signal(signal.SIGTERM, stop)
|
||||||
|
|
||||||
|
if backdoor_port:
|
||||||
|
gevent.backdoor.BackdoorServer(('127.0.0.1', backdoor_port), locals=locals()).start()
|
||||||
|
|
||||||
|
# Wait for any to die
|
||||||
|
gevent.wait(workers, count=1)
|
||||||
|
# If one has stopped, either:
|
||||||
|
# 1. stop() was called and all are stopping
|
||||||
|
# 2. one errored and we should stop all remaining and report the error
|
||||||
|
# Our behaviour in both cases is the same:
|
||||||
|
# 1. Tell all managers to gracefully stop
|
||||||
|
stop()
|
||||||
|
# 2. Wait (with timeout) until they've stopped
|
||||||
|
gevent.wait(workers)
|
||||||
|
# 3. Check if any of them failed. If they did, report it. If mulitple
|
||||||
|
# failed, we report one arbitrarily.
|
||||||
|
for worker in workers:
|
||||||
|
worker.get()
|
||||||
|
|
||||||
|
logging.info('Gracefully stopped')
|
@ -0,0 +1,14 @@
|
|||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name = 'wubloader-segment_coverage',
|
||||||
|
version = '0.0.0',
|
||||||
|
packages = find_packages(),
|
||||||
|
install_requires = [
|
||||||
|
'argh',
|
||||||
|
'gevent',
|
||||||
|
'prometheus-client',
|
||||||
|
'python-dateutil',
|
||||||
|
'wubloader-common',
|
||||||
|
],
|
||||||
|
)
|
Loading…
Reference in New Issue