Merge ~logrotate-charmers/charm-logrotated:bugs_1833095_1833093 into charm-logrotated:master

Proposed by Jeremy Lounder
Status: Merged
Approved by: Paul Goins
Approved revision: 6d391ef597fad5552bfdee132025c4aa603a21d9
Merged at revision: b2e03efd1888e6644d178af2f5f098ee326e9df5
Proposed branch: ~logrotate-charmers/charm-logrotated:bugs_1833095_1833093
Merge into: charm-logrotated:master
Diff against target: 674 lines (+151/-132)
10 files modified
actions/actions.py (+12/-21)
dev/null (+0/-13)
lib/lib_cron.py (+38/-32)
lib/lib_logrotate.py (+20/-25)
reactive/logrotate.py (+20/-8)
tests/functional/conftest.py (+14/-9)
tests/functional/juju_tools.py (+14/-13)
tests/functional/test_logrotate.py (+5/-2)
tests/unit/conftest.py (+11/-0)
tests/unit/test_logrotate.py (+17/-9)
Reviewer Review Type Date Requested Status
Paul Goins Approve
Canonical IS Reviewers Pending
Review via email: mp+378375@code.launchpad.net

Commit message

LP#1833095 LP#1833093 Fixes linting and unit test issues.

To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Unable to determine commit message from repository - please click "Set commit message" and enter the commit message manually.

Revision history for this message
Paul Goins (vultaire) wrote :

Looks pretty good. I've suggested a few small improvements, although functionally the code is fine as-is without them. However, removal of the copyright blocks concerns me - I think those need to be restored.

review: Needs Fixing
Revision history for this message
Paul Goins (vultaire) wrote :

Adding one more comment

Revision history for this message
Paul Goins (vultaire) wrote :

Created bug regarding this code review: https://pad.lv/1864572

As this code is already merged into the current released charm, am merging this as-is.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/actions/__init__.py b/actions/__init__.py
0deleted file mode 1007550deleted file mode 100755
index 9b088de..0000000
--- a/actions/__init__.py
+++ /dev/null
@@ -1,13 +0,0 @@
1# Copyright 2016 Canonical Ltd
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
diff --git a/actions/actions.py b/actions/actions.py
index aee6bb7..8178850 100755
--- a/actions/actions.py
+++ b/actions/actions.py
@@ -1,45 +1,36 @@
1#!/usr/bin/env python31#!/usr/bin/env python3
2#2"""Actions module."""
3# Copyright 2016 Canonical Ltd
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
163
17import os4import os
18import sys5import sys
196
20sys.path.insert(0, os.path.join(os.environ['CHARM_DIR'], 'lib'))7from charmhelpers.core import hookenv
21from charmhelpers.core import (
22 hookenv,
23 host,
24)
258
26from lib_logrotate import LogrotateHelper
27from lib_cron import CronHelper9from lib_cron import CronHelper
2810
11from lib_logrotate import LogrotateHelper
12
13sys.path.insert(0, os.path.join(os.environ['CHARM_DIR'], 'lib'))
14
29hooks = hookenv.Hooks()15hooks = hookenv.Hooks()
30logrotate = LogrotateHelper()16logrotate = LogrotateHelper()
31cron = CronHelper()17cron = CronHelper()
3218
19
33@hooks.hook("update-logrotate-files")20@hooks.hook("update-logrotate-files")
34def update_logrotate_files():21def update_logrotate_files():
22 """Update the logrotate files."""
35 logrotate.read_config()23 logrotate.read_config()
36 logrotate.modify_configs()24 logrotate.modify_configs()
3725
26
38@hooks.hook("update-cronjob")27@hooks.hook("update-cronjob")
39def update_cronjob():28def update_cronjob():
29 """Update the cronjob file."""
40 cron.read_config()30 cron.read_config()
41 cron.install_cronjob()31 cron.install_cronjob()
4232
33
43if __name__ == "__main__":34if __name__ == "__main__":
35 """Main function."""
44 hooks.execute(sys.argv)36 hooks.execute(sys.argv)
45
diff --git a/lib/lib_cron.py b/lib/lib_cron.py
index 15bcdaf..f560084 100644
--- a/lib/lib_cron.py
+++ b/lib/lib_cron.py
@@ -1,24 +1,27 @@
1"""Cron helper module."""
1import os2import os
2import re3
3from lib_logrotate import LogrotateHelper4from lib_logrotate import LogrotateHelper
45
6
5class CronHelper:7class CronHelper:
6 """Helper class for logrotate charm."""8 """Helper class for logrotate charm."""
79
8 @classmethod
9 def __init__(self):10 def __init__(self):
10 """Init function"""11 """Init function."""
11 self.cronjob_check_paths = [ "hourly", "daily", "weekly", "monthly" ]12 self.cronjob_base_path = "/etc/cron."
13 self.cronjob_etc_config = "/etc/logrotate_cronjob_config"
14 self.cronjob_check_paths = ["hourly", "daily", "weekly", "monthly"]
12 self.cronjob_logrotate_cron_file = "charm-logrotate"15 self.cronjob_logrotate_cron_file = "charm-logrotate"
1316
14
15 @classmethod
16 def read_config(self):17 def read_config(self):
17 """Config changed/install hooks dumps config out to disk,18 """Read the configuration from the file.
18 Here we read that config to update the cronjob"""
1919
20 config_file = open("/etc/logrotate_cronjob_config", "r")20 Config changed/install hooks dumps config out to disk,
21 lines = config_file.read() 21 Here we read that config to update the cronjob.
22 """
23 config_file = open(self.cronjob_etc_config, "r")
24 lines = config_file.read()
22 lines = lines.split('\n')25 lines = lines.split('\n')
2326
24 if lines[0] == 'True':27 if lines[0] == 'True':
@@ -28,51 +31,54 @@ class CronHelper:
2831
29 self.cronjob_frequency = int(self.cronjob_check_paths.index(lines[1]))32 self.cronjob_frequency = int(self.cronjob_check_paths.index(lines[1]))
3033
31
32 @classmethod
33 def install_cronjob(self):34 def install_cronjob(self):
34 """If logrotate-cronjob config option is set to True 35 """Install the cron job task.
35 install cronjob. Otherwise cleanup"""
3636
37 If logrotate-cronjob config option is set to True install cronjob,
38 otherwise cleanup.
39 """
37 clean_up_file = self.cronjob_frequency if self.cronjob_enabled else -140 clean_up_file = self.cronjob_frequency if self.cronjob_enabled else -1
3841
39 if self.cronjob_enabled is True:42 if self.cronjob_enabled is True:
40 path_to_lib = os.path.realpath(__file__)43 path_to_lib = os.path.realpath(__file__)
41 cron_file_path = "/etc/cron." + self.cronjob_check_paths[clean_up_file] \44 cron_file_path = self.cronjob_base_path\
42 + "/" + self.cronjob_logrotate_cron_file45 + self.cronjob_check_paths[clean_up_file]\
46 + "/" + self.cronjob_logrotate_cron_file
4347
44 # upgrade to template if logic increases48 # upgrade to template if logic increases
45 cron_file = open(cron_file_path, 'w')49 cron_file = open(cron_file_path, 'w')
46 cron_file.write("#!/bin/sh\n/usr/bin/python3 " + path_to_lib + "\n\n")50 cron_file.write("#!/bin/sh\n/usr/bin/python3 " + path_to_lib + "\n\n")
47 cron_file.close()51 cron_file.close()
48 os.chmod(cron_file_path,700)52 os.chmod(cron_file_path, 700)
49
50 self.cleanup_cronjob(clean_up_file)
5153
54 self.cleanup_cronjob(clean_up_file)
5255
53 @classmethod56 def cleanup_cronjob(self, frequency=-1):
54 def cleanup_cronjob(self, frequency = -1):57 """Cleanup previous config."""
55 """Cleanup previous config"""58 if frequency == -1:
56 for i in range(4):59 for check_path in self.cronjob_check_paths:
57 if frequency != i:60 path = self.cronjob_base_path \
58 path = "/etc/cron." + self.cronjob_check_paths[i] + "/" +\61 + check_path \
59 self.cronjob_logrotate_cron_file62 + "/" \
63 + self.cronjob_logrotate_cron_file
60 if os.path.exists(path):64 if os.path.exists(path):
61 os.remove(path)65 os.remove(path)
66 if os.path.exists(self.cronjob_etc_config):
67 os.remove(self.cronjob_etc_config)
6268
63 @classmethod
64 def update_logrotate_etc(self):69 def update_logrotate_etc(self):
65 """Run logrotate update config"""70 """Run logrotate update config."""
66 logrotate = LogrotateHelper()71 logrotate = LogrotateHelper()
67 logrotate.read_config()72 logrotate.read_config()
68 logrotate.modify_configs()73 logrotate.modify_configs()
6974
7075
71def main():76def main():
72 cronHelper = CronHelper()77 """Ran by cron."""
73 cronHelper.read_config()78 cronhelper = CronHelper()
74 cronHelper.update_logrotate_etc()79 cronhelper.read_config()
75 cronHelper.install_cronjob()80 cronhelper.update_logrotate_etc()
81 cronhelper.install_cronjob()
7682
7783
78if __name__ == '__main__':84if __name__ == '__main__':
diff --git a/lib/lib_logrotate.py b/lib/lib_logrotate.py
index 145a41b..67245cb 100644
--- a/lib/lib_logrotate.py
+++ b/lib/lib_logrotate.py
@@ -1,6 +1,8 @@
1"""Logrotate module."""
1import os2import os
2import re3import re
34
5from charmhelpers.core import hookenv
46
5LOGROTATE_DIR = "/etc/logrotate.d/"7LOGROTATE_DIR = "/etc/logrotate.d/"
68
@@ -8,27 +10,24 @@ LOGROTATE_DIR = "/etc/logrotate.d/"
8class LogrotateHelper:10class LogrotateHelper:
9 """Helper class for logrotate charm."""11 """Helper class for logrotate charm."""
1012
11 @classmethod
12 def __init__(self):13 def __init__(self):
13 """Init function"""14 """Init function."""
14 pass15 self.retention = hookenv.config('logrotate-retention')
1516
16 @classmethod
17 def read_config(self):17 def read_config(self):
18 """Config changed/install hooks dumps config out to disk,18 """Read changes from disk.
19 Here we read that config to update the cronjob"""
2019
20 Config changed/install hooks dumps config out to disk,
21 Here we read that config to update the cronjob
22 """
21 config_file = open("/etc/logrotate_cronjob_config", "r")23 config_file = open("/etc/logrotate_cronjob_config", "r")
22 lines = config_file.read()24 lines = config_file.read()
23 lines = lines.split('\n')25 lines = lines.split('\n')
2426
25 self.retention = int(lines[2])27 self.retention = int(lines[2])
2628
27
28 @classmethod
29 def modify_configs(self):29 def modify_configs(self):
30 """Modify the logrotate config files."""30 """Modify the logrotate config files."""
31
32 for config_file in os.listdir(LOGROTATE_DIR):31 for config_file in os.listdir(LOGROTATE_DIR):
33 file_path = LOGROTATE_DIR + config_file32 file_path = LOGROTATE_DIR + config_file
3433
@@ -44,11 +43,8 @@ class LogrotateHelper:
44 logrotate_file.write(mod_contents)43 logrotate_file.write(mod_contents)
45 logrotate_file.close()44 logrotate_file.close()
4645
47
48 @classmethod
49 def modify_content(self, content):46 def modify_content(self, content):
50 """Helper function to edit the content of a logrotate file."""47 """Edit the content of a logrotate file."""
51
52 # Split the contents in a logrotate file in separate entries (if48 # Split the contents in a logrotate file in separate entries (if
53 # multiple are found in the file) and put in a list for further49 # multiple are found in the file) and put in a list for further
54 # processing50 # processing
@@ -66,7 +62,7 @@ class LogrotateHelper:
66 # the rotate option to the appropriate value62 # the rotate option to the appropriate value
67 results = []63 results = []
68 for item in items:64 for item in items:
69 count = self.calculate_count(item)65 count = self.calculate_count(item, self.retention)
70 rotate = 'rotate {}'.format(count)66 rotate = 'rotate {}'.format(count)
71 result = re.sub(r'rotate \d+\.?[0-9]*', rotate, item)67 result = re.sub(r'rotate \d+\.?[0-9]*', rotate, item)
72 results.append(result)68 results.append(result)
@@ -75,12 +71,9 @@ class LogrotateHelper:
7571
76 return results72 return results
7773
78 @classmethod
79 def modify_header(self, content):74 def modify_header(self, content):
80 """Helper function to add Juju headers to the file."""75 """Add Juju headers to the file."""
8176 header = "# Configuration file maintained by Juju. Local changes may be overwritten"
82 header = ("# Configuration file maintained by Juju. "
83 "Local changes may be overwritten")
8477
85 split = content.split('\n')78 split = content.split('\n')
86 if split[0].startswith(header):79 if split[0].startswith(header):
@@ -91,20 +84,22 @@ class LogrotateHelper:
91 return result84 return result
9285
93 @classmethod86 @classmethod
94 def calculate_count(self, item):87 def calculate_count(cls, item, retention):
95 """Calculate rotate based on rotation interval. Always round up."""88 """Calculate rotate based on rotation interval. Always round up."""
9689 # Fallback to default lowest retention - days
90 # better to keep the logs than lose them
91 count = retention
97 # Daily 1:1 to configuration retention period (in days)92 # Daily 1:1 to configuration retention period (in days)
98 if 'daily' in item:93 if 'daily' in item:
99 count = self.retention94 count = retention
100 # Weekly rounding up, as weeks are 7 days95 # Weekly rounding up, as weeks are 7 days
101 if 'weekly' in item:96 if 'weekly' in item:
102 count = int(round(self.retention/7))97 count = int(round(retention/7))
103 # Monthly default 30 days and round up because of 28/31 days months98 # Monthly default 30 days and round up because of 28/31 days months
104 if 'monthly' in item:99 if 'monthly' in item:
105 count = int(round(self.retention/30))100 count = int(round(retention/30))
106 # For every 360 days - add 1 year101 # For every 360 days - add 1 year
107 if 'yearly' in item:102 if 'yearly' in item:
108 count = self.retention // 360 + 1 if self.retention > 360 else 1103 count = retention // 360 + 1 if retention > 360 else 1
109104
110 return count105 return count
diff --git a/reactive/logrotate.py b/reactive/logrotate.py
index 4a8d5c1..1810707 100644
--- a/reactive/logrotate.py
+++ b/reactive/logrotate.py
@@ -1,36 +1,48 @@
1from lib_logrotate import LogrotateHelper1"""Reactive charm hooks."""
2from lib_cron import CronHelper
3from charmhelpers.core import hookenv2from charmhelpers.core import hookenv
3
4from charms.reactive import set_flag, when, when_not4from charms.reactive import set_flag, when, when_not
55
6from lib_cron import CronHelper
7
8from lib_logrotate import LogrotateHelper
9
10
11hooks = hookenv.Hooks()
6logrotate = LogrotateHelper()12logrotate = LogrotateHelper()
7cron = CronHelper()13cron = CronHelper()
814
15
9@when_not('logrotate.installed')16@when_not('logrotate.installed')
10def install_logrotate():17def install_logrotate():
11 dump_config_to_disk();18 """Install the logrotate charm."""
19 dump_config_to_disk()
12 logrotate.read_config()20 logrotate.read_config()
13 cron.read_config()21 cron.read_config()
14 logrotate.modify_configs()22 logrotate.modify_configs()
15 hookenv.status_set('active', 'Unit is ready.')23 hookenv.status_set('active', 'Unit is ready.')
16 set_flag('logrotate.installed')24 set_flag('logrotate.installed')
17 cron.install_cronjob();25 cron.install_cronjob()
26
1827
19@when('config.changed')28@when('config.changed')
20def config_changed():29def config_changed():
30 """Run when configuration changes."""
21 dump_config_to_disk()31 dump_config_to_disk()
22 cron.read_config()32 cron.read_config()
23 logrotate.read_config()33 logrotate.read_config()
24 hookenv.status_set('maintenance', 'Modifying configs.')34 hookenv.status_set('maintenance', 'Modifying configs.')
25 logrotate.modify_configs()35 logrotate.modify_configs()
26 hookenv.status_set('active', 'Unit is ready.')36 hookenv.status_set('active', 'Unit is ready.')
27 cron.install_cronjob();37 cron.install_cronjob()
38
2839
29def dump_config_to_disk():40def dump_config_to_disk():
41 """Dump configurations to disk."""
30 cronjob_enabled = hookenv.config('logrotate-cronjob')42 cronjob_enabled = hookenv.config('logrotate-cronjob')
31 cronjob_frequency = hookenv.config('logrotate-cronjob-frequency')43 cronjob_frequency = hookenv.config('logrotate-cronjob-frequency')
32 logrotate_retention = hookenv.config('logrotate-retention')44 logrotate_retention = hookenv.config('logrotate-retention')
33 with open('/etc/logrotate_cronjob_config', 'w+') as cronjob_config_file:45 with open('/etc/logrotate_cronjob_config', 'w+') as cronjob_config_file:
34 cronjob_config_file.write(str(cronjob_enabled) + '\n')46 cronjob_config_file.write(str(cronjob_enabled) + '\n')
35 cronjob_config_file.write(str(cronjob_frequency) + '\n')47 cronjob_config_file.write(str(cronjob_frequency) + '\n')
36 cronjob_config_file.write(str(logrotate_retention) + '\n')48 cronjob_config_file.write(str(logrotate_retention) + '\n')
diff --git a/tests/functional/conftest.py b/tests/functional/conftest.py
index 56925ff..1b22cb8 100644
--- a/tests/functional/conftest.py
+++ b/tests/functional/conftest.py
@@ -1,28 +1,32 @@
1#!/usr/bin/python31#!/usr/bin/python3
2'''2"""
3Reusable pytest fixtures for functional testing3Reusable pytest fixtures for functional testing.
44
5Environment variables5Environment variables
6---------------------6---------------------
77
8test_preserve_model:8test_preserve_model:
9if set, the testing model won't be torn down at the end of the testing session9if set, the testing model won't be torn down at the end of the testing session
10'''10"""
1111
12import asyncio12import asyncio
13import os13import os
14import uuid
15import pytest
16import subprocess14import subprocess
15import uuid
1716
18from juju.controller import Controller17from juju.controller import Controller
18
19from juju_tools import JujuTools19from juju_tools import JujuTools
2020
21import pytest
22
2123
22@pytest.fixture(scope='module')24@pytest.fixture(scope='module')
23def event_loop():25def event_loop():
24 '''Override the default pytest event loop to allow for fixtures using a26 """Override the default pytest event loop.
25 broader scope'''27
28 Do this too allow for fixtures using a broader scope.
29 """
26 loop = asyncio.get_event_loop_policy().new_event_loop()30 loop = asyncio.get_event_loop_policy().new_event_loop()
27 asyncio.set_event_loop(loop)31 asyncio.set_event_loop(loop)
28 loop.set_debug(True)32 loop.set_debug(True)
@@ -33,7 +37,7 @@ def event_loop():
3337
34@pytest.fixture(scope='module')38@pytest.fixture(scope='module')
35async def controller():39async def controller():
36 '''Connect to the current controller'''40 """Connect to the current controller."""
37 _controller = Controller()41 _controller = Controller()
38 await _controller.connect_current()42 await _controller.connect_current()
39 yield _controller43 yield _controller
@@ -42,7 +46,7 @@ async def controller():
4246
43@pytest.fixture(scope='module')47@pytest.fixture(scope='module')
44async def model(controller):48async def model(controller):
45 '''This model lives only for the duration of the test'''49 """Live only for the duration of the test."""
46 model_name = "functest-{}".format(str(uuid.uuid4())[-12:])50 model_name = "functest-{}".format(str(uuid.uuid4())[-12:])
47 _model = await controller.add_model(model_name,51 _model = await controller.add_model(model_name,
48 cloud_name=os.getenv('PYTEST_CLOUD_NAME'),52 cloud_name=os.getenv('PYTEST_CLOUD_NAME'),
@@ -62,5 +66,6 @@ async def model(controller):
6266
63@pytest.fixture(scope='module')67@pytest.fixture(scope='module')
64async def jujutools(controller, model):68async def jujutools(controller, model):
69 """Juju tools."""
65 tools = JujuTools(controller, model)70 tools = JujuTools(controller, model)
66 return tools71 return tools
diff --git a/tests/functional/juju_tools.py b/tests/functional/juju_tools.py
index 2fd501d..5e4a1cc 100644
--- a/tests/functional/juju_tools.py
+++ b/tests/functional/juju_tools.py
@@ -1,22 +1,26 @@
1"""Juju tools."""
2import base64
1import pickle3import pickle
4
2import juju5import juju
3import base64
46
5# from juju.errors import JujuError7# from juju.errors import JujuError
68
79
8class JujuTools:10class JujuTools:
11 """Juju tools."""
12
9 def __init__(self, controller, model):13 def __init__(self, controller, model):
14 """Init."""
10 self.controller = controller15 self.controller = controller
11 self.model = model16 self.model = model
1217
13 async def run_command(self, cmd, target):18 async def run_command(self, cmd, target):
14 '''19 """Run a command on a unit.
15 Runs a command on a unit.
1620
17 :param cmd: Command to be run21 :param cmd: Command to be run
18 :param unit: Unit object or unit name string22 :param unit: Unit object or unit name string
19 '''23 """
20 unit = (24 unit = (
21 target25 target
22 if isinstance(target, juju.unit.Unit)26 if isinstance(target, juju.unit.Unit)
@@ -26,13 +30,12 @@ class JujuTools:
26 return action.results30 return action.results
2731
28 async def remote_object(self, imports, remote_cmd, target):32 async def remote_object(self, imports, remote_cmd, target):
29 '''33 """Run command on target machine and returns a python object of the result.
30 Runs command on target machine and returns a python object of the result
3134
32 :param imports: Imports needed for the command to run35 :param imports: Imports needed for the command to run
33 :param remote_cmd: The python command to execute36 :param remote_cmd: The python command to execute
34 :param target: Unit object or unit name string37 :param target: Unit object or unit name string
35 '''38 """
36 python3 = "python3 -c '{}'"39 python3 = "python3 -c '{}'"
37 python_cmd = ('import pickle;'40 python_cmd = ('import pickle;'
38 'import base64;'41 'import base64;'
@@ -44,12 +47,11 @@ class JujuTools:
44 return pickle.loads(base64.b64decode(bytes(results['Stdout'][2:-1], 'utf8')))47 return pickle.loads(base64.b64decode(bytes(results['Stdout'][2:-1], 'utf8')))
4548
46 async def file_stat(self, path, target):49 async def file_stat(self, path, target):
47 '''50 """Run stat on a file.
48 Runs stat on a file
4951
50 :param path: File path52 :param path: File path
51 :param target: Unit object or unit name string53 :param target: Unit object or unit name string
52 '''54 """
53 imports = 'import os;'55 imports = 'import os;'
54 python_cmd = ('os.stat("{}")'56 python_cmd = ('os.stat("{}")'
55 .format(path))57 .format(path))
@@ -57,12 +59,11 @@ class JujuTools:
57 return await self.remote_object(imports, python_cmd, target)59 return await self.remote_object(imports, python_cmd, target)
5860
59 async def file_contents(self, path, target):61 async def file_contents(self, path, target):
60 '''62 """Return the contents of a file.
61 Returns the contents of a file
6263
63 :param path: File path64 :param path: File path
64 :param target: Unit object or unit name string65 :param target: Unit object or unit name string
65 '''66 """
66 cmd = 'cat {}'.format(path)67 cmd = 'cat {}'.format(path)
67 result = await self.run_command(cmd, target)68 result = await self.run_command(cmd, target)
68 return result['Stdout']69 return result['Stdout']
diff --git a/tests/functional/test_logrotate.py b/tests/functional/test_logrotate.py
index b4402be..100690f 100644
--- a/tests/functional/test_logrotate.py
+++ b/tests/functional/test_logrotate.py
@@ -1,6 +1,8 @@
1#!/usr/bin/python3.61#!/usr/bin/python3.6
2"""Main module for functional testing."""
23
3import os4import os
5
4import pytest6import pytest
57
6pytestmark = pytest.mark.asyncio8pytestmark = pytest.mark.asyncio
@@ -16,7 +18,7 @@ SERIES = ['xenial',
16@pytest.fixture(scope='module',18@pytest.fixture(scope='module',
17 params=SERIES)19 params=SERIES)
18async def deploy_app(request, model):20async def deploy_app(request, model):
19 '''Deploys the logrotate charm as a subordinate of ubuntu'''21 """Deploy the logrotate charm as a subordinate of ubuntu."""
20 release = request.param22 release = request.param
2123
22 await model.deploy(24 await model.deploy(
@@ -42,7 +44,7 @@ async def deploy_app(request, model):
4244
43@pytest.fixture(scope='module')45@pytest.fixture(scope='module')
44async def unit(deploy_app):46async def unit(deploy_app):
45 '''Returns the logrotate unit we've deployed'''47 """Return the logrotate unit we've deployed."""
46 return deploy_app.units.pop()48 return deploy_app.units.pop()
4749
48#########50#########
@@ -51,4 +53,5 @@ async def unit(deploy_app):
5153
5254
53async def test_deploy(deploy_app):55async def test_deploy(deploy_app):
56 """Tst the deployment."""
54 assert deploy_app.status == 'active'57 assert deploy_app.status == 'active'
diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py
index 534e654..8520709 100644
--- a/tests/unit/conftest.py
+++ b/tests/unit/conftest.py
@@ -1,11 +1,15 @@
1#!/usr/bin/python31#!/usr/bin/python3
2"""Configurations for tests."""
3
2import mock4import mock
5
3import pytest6import pytest
47
5# If layer options are used, add this to ${fixture}8# If layer options are used, add this to ${fixture}
6# and import layer in logrotate9# and import layer in logrotate
7@pytest.fixture10@pytest.fixture
8def mock_layers(monkeypatch):11def mock_layers(monkeypatch):
12 """Layers mock."""
9 import sys13 import sys
10 sys.modules['charms.layer'] = mock.Mock()14 sys.modules['charms.layer'] = mock.Mock()
11 sys.modules['reactive'] = mock.Mock()15 sys.modules['reactive'] = mock.Mock()
@@ -21,8 +25,10 @@ def mock_layers(monkeypatch):
2125
22 monkeypatch.setattr('lib_logrotate.layer.options', options)26 monkeypatch.setattr('lib_logrotate.layer.options', options)
2327
28
24@pytest.fixture29@pytest.fixture
25def mock_hookenv_config(monkeypatch):30def mock_hookenv_config(monkeypatch):
31 """Hookenv mock."""
26 import yaml32 import yaml
2733
28 def mock_config():34 def mock_config():
@@ -39,17 +45,22 @@ def mock_hookenv_config(monkeypatch):
3945
40 monkeypatch.setattr('lib_logrotate.hookenv.config', mock_config)46 monkeypatch.setattr('lib_logrotate.hookenv.config', mock_config)
4147
48
42@pytest.fixture49@pytest.fixture
43def mock_remote_unit(monkeypatch):50def mock_remote_unit(monkeypatch):
51 """Remote unit mock."""
44 monkeypatch.setattr('lib_logrotate.hookenv.remote_unit', lambda: 'unit-mock/0')52 monkeypatch.setattr('lib_logrotate.hookenv.remote_unit', lambda: 'unit-mock/0')
4553
4654
47@pytest.fixture55@pytest.fixture
48def mock_charm_dir(monkeypatch):56def mock_charm_dir(monkeypatch):
57 """Charm dir mock."""
49 monkeypatch.setattr('lib_logrotate.hookenv.charm_dir', lambda: '/mock/charm/dir')58 monkeypatch.setattr('lib_logrotate.hookenv.charm_dir', lambda: '/mock/charm/dir')
5059
60
51@pytest.fixture61@pytest.fixture
52def logrotate(tmpdir, mock_hookenv_config, mock_charm_dir, monkeypatch):62def logrotate(tmpdir, mock_hookenv_config, mock_charm_dir, monkeypatch):
63 """Logrotate fixture."""
53 from lib_logrotate import LogrotateHelper64 from lib_logrotate import LogrotateHelper
54 helper = LogrotateHelper65 helper = LogrotateHelper
5566
diff --git a/tests/unit/test_logrotate.py b/tests/unit/test_logrotate.py
index 1f0ed9b..b01c7b5 100644
--- a/tests/unit/test_logrotate.py
+++ b/tests/unit/test_logrotate.py
@@ -1,37 +1,45 @@
1from unittest.mock import patch1"""Main unit test module."""
2
23
3class TestLogrotateHelper():4class TestLogrotateHelper():
5 """Main test class."""
6
4 def test_pytest(self):7 def test_pytest(self):
8 """Simple pytest."""
5 assert True9 assert True
610
7
8 def test_daily_retention_count(self, logrotate):11 def test_daily_retention_count(self, logrotate):
12 """Test daily retention count."""
9 logrotate.retention = 9013 logrotate.retention = 90
10 contents = '/var/log/some.log {\n rotate 123\n daily\n}'14 contents = '/var/log/some.log {\n rotate 123\n daily\n}'
11 count = logrotate.calculate_count(contents)15 count = logrotate.calculate_count(contents, logrotate.retention)
12 assert count == 9016 assert count == 90
1317
14 def test_weekly_retention_count(self, logrotate):18 def test_weekly_retention_count(self, logrotate):
19 """Test weekly retention count."""
15 logrotate.retention = 2120 logrotate.retention = 21
16 contents = '/var/log/some.log {\n rotate 123\n weekly\n}'21 contents = '/var/log/some.log {\n rotate 123\n weekly\n}'
17 count = logrotate.calculate_count(contents)22 count = logrotate.calculate_count(contents, logrotate.retention)
18 assert count == 323 assert count == 3
1924
20 def test_monthly_retention_count(self, logrotate):25 def test_monthly_retention_count(self, logrotate):
26 """Test monthly retention count."""
21 logrotate.retention = 6027 logrotate.retention = 60
22 contents = '/var/log/some.log {\n rotate 123\n monthly\n}'28 contents = '/var/log/some.log {\n rotate 123\n monthly\n}'
23 count = logrotate.calculate_count(contents)29 count = logrotate.calculate_count(contents, logrotate.retention)
24 assert count == 230 assert count == 2
2531
26 def test_yearly_retention_count(self, logrotate):32 def test_yearly_retention_count(self, logrotate):
33 """Test yearly retention count."""
27 logrotate.retention = 18034 logrotate.retention = 180
28 contents = '/var/log/some.log {\n rotate 123\n yearly\n}'35 contents = '/var/log/some.log {\n rotate 123\n yearly\n}'
29 count = logrotate.calculate_count(contents)36 count = logrotate.calculate_count(contents, logrotate.retention)
30 assert count == 137 assert count == 1
3138
32 def test_modify_content(self, logrotate):39 def test_modify_content(self, logrotate):
40 """Test the modify_content method."""
33 logrotate.retention = 4241 logrotate.retention = 42
34 contents = '/var/log/some.log {\n rotate 123\n daily\n}\n/var/log/other.log {\n rotate 456\n weekly\n}'42 contents = '/log/some.log {\n rotate 123\n daily\n}\n/log/other.log {\n rotate 456\n weekly\n}'
35 mod_contents = logrotate.modify_content(contents)43 mod_contents = logrotate.modify_content(logrotate, contents)
36 expected_contents = '/var/log/some.log {\n rotate 42\n daily\n}\n\n/var/log/other.log {\n rotate 6\n weekly\n}\n'44 expected_contents = '/log/some.log {\n rotate 42\n daily\n}\n\n/log/other.log {\n rotate 6\n weekly\n}\n'
37 assert mod_contents == expected_contents45 assert mod_contents == expected_contents

Subscribers

People subscribed via source and target branches

to all changes: