Created
May 20, 2015 03:52
-
-
Save natefinch/e377eacd6b2316b2a884 to your computer and use it in GitHub Desktop.
CI test for logrotation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
from __future__ import print_function | |
__metaclass__ = type | |
from argparse import ArgumentParser | |
import os | |
import re | |
import subprocess | |
import sys | |
from time import sleep | |
import datetime import datetime | |
from jujupy import ( | |
CannotConnectEnv, | |
Environment, | |
yaml_loads | |
) | |
from substrate import ( | |
LIBVIRT_DOMAIN_RUNNING, | |
start_libvirt_domain, | |
stop_libvirt_domain, | |
verify_libvirt_domain, | |
) | |
def init_log_rotation(environment, debug): | |
""""Deploy a test charm in the specified environment. The charm will write | |
gobs of data to the unit log file, which should then rotate. | |
:param environment: The name of the desired environment. | |
returns the environment | |
""" | |
env = Environment.from_config(environment) | |
env.client.debug = debug | |
running_domains = dict() | |
# Clean up any leftover junk | |
env.destroy_environment() | |
env.bootstrap() | |
try: | |
# wait for status info.... | |
try: | |
try: | |
env.get_status() | |
except CannotConnectEnv: | |
print("Status got Unable to connect to env. Retrying...") | |
env.get_status() | |
env.wait_for_started() | |
env.deploy('local:{}/fill-logs'.format(env.config.get( | |
'default-series', 'trusty'))) | |
env.wait_for_started() | |
test_unit_rotation(env) | |
except subprocess.CalledProcessError as e: | |
if getattr(e, 'stderr', None) is not None: | |
sys.stderr.write(e.stderr) | |
raise | |
finally: | |
env.destroy_environment() | |
def test_unit_rotation(env): | |
# the rotation point should be 300 megs, so let's make sure we hit that.hit | |
# we'll obviously already have some data in the logs, so adding exactly 300megs | |
# should do the trick. | |
env.action_do("fill-logs/0", "fill-unit", "megs=300") | |
output = env.action_do_fetch("fill-logs/0", "unit-size") | |
obj = yaml_loads(output) | |
# Now we should have one primary log file, and one backup log file. | |
# The backup should be approximately 300 megs. | |
# The primary should be below 300. | |
check_unit_log0(obj) | |
check_unit_backup("log1", obj) | |
# we should only have one backup, not two. | |
log2 = obj["results"]["result-map"]["log2"] | |
if log2 is not None: | |
raise Exception("Extra backup unit log after rotation: " + log2["name"]) | |
# do it all again, this should generate a second backup. | |
env.action_do("fill-logs/0", "fill-unit", "megs=300") | |
output = env.action_do_fetch("fill-logs/0", "unit-size") | |
obj = yaml_loads(output) | |
check_unit_log0(obj) | |
check_unit_backup("log1", obj) | |
check_unit_backup("log2", obj) | |
log3 = obj["results"]["result-map"]["log3"] | |
if log3 is not None: | |
raise Exception("Extra backup unit log after second rotation: " + log2["name"]) | |
# one more time... we should still only have 2 backups and primary | |
env.action_do("fill-logs/0", "fill-unit", "megs=300") | |
output = env.action_do_fetch("fill-logs/0", "unit-size") | |
obj = yaml_loads(output) | |
check_unit_log0(obj) | |
check_unit_backup("log1", obj) | |
check_unit_backup("log2", obj) | |
log3 = obj["results"]["result-map"]["log3"] | |
if log3 is not None: | |
raise Exception("Extra backup unit log after second rotation: " + log2["name"]) | |
def check_unit_backup(logname, yaml_obj): | |
log = yaml_obj["results"]["result-map"][logname] | |
if log is None: | |
raise Exception(format("Missing backup unit log '{}'' after rotation.", logname)) | |
backup_pattern_string = "/var/log/juju/unit-fill-logs-0(.+?)\.log" | |
backup_pattern = re.compile(backup_pattern_string) | |
log_name = log["name"] | |
matches = re.match(backup_pattern, log_name) | |
if len(matches) < 2: | |
raise Exception(format("Rotated unit log name '{}' does not match pattern '{}'.", log_name, backup_pattern_string) | |
size = int(log["size"]) | |
if size < 300 || size > 301: | |
raise Exception(format("Unit log name '{}' should be close to 300MB, but is {}MB.", size) | |
dt = matches[1] | |
dt_pattern = "%Y-%m-%dT%H-%M-%S.%f" | |
try: | |
# note - we have to use datetime's strptime because time's doesn't | |
# support partial seconds. | |
dt = datetime.strptime(dt, dt_pattern) | |
except Exception as e: | |
raise Exception(format("Rotated unit log name for {} has invalid datetime appended: {}", logname, dt)) | |
def check_unit_log0(obj): | |
log0 = obj["results"]["result-map"]["log0"] | |
if log1 is None: | |
raise Exception("No unit log returned from unit-size action.") | |
expected = "/var/log/juju/unit-fill-logs-0.log" | |
name = log0["name"] | |
if name != expected: | |
raise Exception(format("Wrong unit name from action result. Expected: {}, actual: {}", expected, name) | |
size = int(log0["size"]) | |
if size > 300: | |
raise Exception(format("Unit log not rolled. Expected size < 300MB, got: {}MB", size) | |
def main(): | |
parser = ArgumentParser('Test log rotation') | |
parser.add_argument('env', help='The juju environment to test') | |
args = parser.parse_args() | |
debug = bool(os.environ.get('DEBUG') == 'true') | |
try: | |
log_rotation(args.env, debug) | |
except Exception as e: | |
print('%s: %s' % (type(e), e)) | |
sys.exit(1) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment