Merge lp:~bac/charms/precise/mongodb/logrotate into lp:charms/mongodb

Proposed by Brad Crittenden on 2013-12-11
Status: Merged
Merged at revision: 36
Proposed branch: lp:~bac/charms/precise/mongodb/logrotate
Merge into: lp:charms/mongodb
Diff against target: 306 lines (+167/-53)
6 files modified
Makefile (+18/-0)
config.yaml (+14/-2)
hooks/hooks.py (+89/-50)
revision (+1/-1)
tests/10-unit.test (+12/-0)
tests/test_write_log_rotate_config.py (+33/-0)
To merge this branch: bzr merge lp:~bac/charms/precise/mongodb/logrotate
Reviewer Review Type Date Requested Status
Juan L. Negron (community) 2013-12-11 Approve on 2013-12-11
Review via email: mp+198633@code.launchpad.net

Description of the change

Expose some logrotate variables in config.yaml and update the logrotate config file for mongodb-server in config-changed. The purpose is to make more informed choices about the size of log files we want to keep.

Motivating this change is charmworld generates about 5G/week of log files from mongo and this eventually uses all of the available storage for that instance. The Canonical webops have asked for the ability to rotate the logs and, if possible, make mongo log less data. This branch addresses the former but I cannot see how to do the latter. There is the --quiet option for starting mongo but the documentation says not to use it in production.

To post a comment you must log in.
Juan L. Negron (negronjl) wrote :

Reviewing this now...

-Juan

Juan L. Negron (negronjl) wrote :

Looks good ... Thanks Brad!

Approved ... Merging.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'Makefile'
2--- Makefile 1970-01-01 00:00:00 +0000
3+++ Makefile 2013-12-11 19:27:36 +0000
4@@ -0,0 +1,18 @@
5+# This file is part of the mongodb charm.
6+# Copyright (C) 2013 Canonical Ltd.
7+#
8+# This program is free software: you can redistribute it and/or modify it under
9+# the terms of the GNU Affero General Public License version 3, as published by
10+# the Free Software Foundation.
11+#
12+# This program is distributed in the hope that it will be useful, but WITHOUT
13+# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
14+# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+# Affero General Public License for more details.
16+#
17+# You should have received a copy of the GNU Affero General Public License
18+# along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
20+
21+unittest:
22+ tests/10-unit.test
23
24=== modified file 'config.yaml'
25--- config.yaml 2013-09-13 15:51:50 +0000
26+++ config.yaml 2013-12-11 19:27:36 +0000
27@@ -11,6 +11,18 @@
28 default: True
29 type: boolean
30 description: Append log entries to existing log file
31+ logrotate-frequency:
32+ default: daily
33+ type: string
34+ description: How often should the logs be rotated. Use values from logrotate.
35+ logrotate-rotate:
36+ default: 5
37+ type: int
38+ description: Number of log files to keep.
39+ logrotate-maxsize:
40+ default: 500M
41+ type: string
42+ description: Maximum log size before rotating.
43 bind_ip:
44 default: "all"
45 type: string
46@@ -154,7 +166,7 @@
47 backup_copies_kept:
48 default: 7
49 type: int
50- description: Number of backups to keep. Keeps one week's worth by default.
51+ description: "Number of backups to keep. Keeps one week's worth by default."
52 #------------------------------------------------------------------------
53 # Volume management
54 # volume-map, volume-dev_regexp are only used
55@@ -177,7 +189,7 @@
56 description: >
57 YAML map as e.g. "{ mongodb/0: vol-0000010, mongodb/1: vol-0000016 }".
58 Service units will raise a "configure-error" condition if no volume-map
59- value is set for it - it's expected a human to set it properly to resolve it.
60+ value is set for it - it expects a human to set it properly to resolve it.
61 volume-dev-regexp:
62 type: string
63 default: "/dev/vd[b-z]"
64
65=== modified file 'hooks/hooks.py'
66--- hooks/hooks.py 2013-11-25 19:48:00 +0000
67+++ hooks/hooks.py 2013-12-11 19:27:36 +0000
68@@ -21,6 +21,7 @@
69 from os import remove
70 from os.path import exists
71 from string import Template
72+from textwrap import dedent
73 from yaml.constructor import ConstructorError
74
75 ###############################################################################
76@@ -322,13 +323,6 @@
77 ###############################################################################
78 # Global variables
79 ###############################################################################
80-parser = argparse.ArgumentParser()
81-parser.add_argument('-H', '--hook_name', dest='hook_name', help='hook to call')
82-args = parser.parse_args()
83-if args.hook_name is not None:
84- hook_name = args.hook_name
85-else:
86- hook_name = os.path.basename(sys.argv[0])
87 default_mongodb_config = "/etc/mongodb.conf"
88 default_mongodb_init_config = "/etc/init/mongodb.conf"
89 default_mongos_list = "/etc/mongos.list"
90@@ -826,12 +820,15 @@
91 if os.path.exists('/var/lib/mongodb/mongod.lock'):
92 os.remove('/var/lib/mongodb/mongod.lock')
93
94- service('mongodb', 'start')
95+ if not service('mongodb', 'start'):
96+ return False
97
98- while service('mongodb', 'status') and \
99- not port_check(my_hostname, my_port) and \
100- current_try < max_tries:
101- juju_log("restart_mongod: Waiting for MongoDB to be ready ...")
102+ while (service('mongodb', 'status') and
103+ not port_check(my_hostname, my_port) and
104+ current_try < max_tries):
105+ juju_log(
106+ "restart_mongod: Waiting for MongoDB to be ready ({}/{})".format(
107+ current_try, max_tries))
108 time.sleep(wait_for)
109 current_try += 1
110
111@@ -970,6 +967,9 @@
112 # extra demon options
113 update_daemon_options(config_data['extra_daemon_options'])
114
115+ # write mongodb logrotate configuration file
116+ write_logrotate_config(config_data)
117+
118 # restart mongodb
119 restart_mongod()
120
121@@ -1429,44 +1429,83 @@
122 "Invalid volume storage configuration, not applying changes")
123 return False
124
125+
126+#------------------------------------------------------------------------------
127+# Write mongodb-server logrotate configuration
128+#------------------------------------------------------------------------------
129+def write_logrotate_config(config_data,
130+ conf_file = '/etc/logrotate.d/mongodb-server'):
131+
132+ juju_log('Writing {}.'.format(conf_file))
133+ contents = dedent("""
134+ {logpath} {{
135+ {logrotate-frequency}
136+ rotate {logrotate-rotate}
137+ maxsize {logrotate-maxsize}
138+ copytruncate
139+ delaycompress
140+ compress
141+ noifempty
142+ missingok
143+ }}""")
144+ contents = contents.format(**config_data)
145+ try:
146+ with open(conf_file, 'w') as f:
147+ f.write(contents)
148+ except IOError:
149+ juju_log('Could not write {}.'.format(conf_file))
150+ return False
151+ return True
152+
153+
154 ###############################################################################
155 # Main section
156 ###############################################################################
157-if hook_name == "install":
158- retVal = install_hook()
159-elif hook_name == "config-changed":
160- retVal = config_changed()
161-elif hook_name == "start":
162- retVal = start_hook()
163-elif hook_name == "stop":
164- retVal = stop_hook()
165-elif hook_name == "database-relation-joined":
166- retVal = database_relation_joined()
167-elif hook_name == "replica-set-relation-joined":
168- retVal = replica_set_relation_joined()
169-elif hook_name == "replica-set-relation-changed":
170- retVal = replica_set_relation_changed()
171-elif hook_name == "configsvr-relation-joined":
172- retVal = configsvr_relation_joined()
173-elif hook_name == "configsvr-relation-changed":
174- retVal = configsvr_relation_changed()
175-elif hook_name == "mongos-cfg-relation-joined":
176- retVal = mongos_relation_joined()
177-elif hook_name == "mongos-cfg-relation-changed":
178- retVal = mongos_relation_changed()
179-elif hook_name == "mongos-cfg-relation-broken":
180- retVal = mongos_relation_broken()
181-elif hook_name == "mongos-relation-joined":
182- retVal = mongos_relation_joined()
183-elif hook_name == "mongos-relation-changed":
184- retVal = mongos_relation_changed()
185-elif hook_name == "mongos-relation-broken":
186- retVal = mongos_relation_broken()
187-else:
188- print "Unknown hook"
189- retVal = False
190-
191-if retVal is True:
192- sys.exit(0)
193-else:
194- sys.exit(1)
195+if __name__ == '__main__':
196+ parser = argparse.ArgumentParser()
197+ parser.add_argument('-H', '--hook_name', dest='hook_name',
198+ help='hook to call')
199+ args = parser.parse_args()
200+ if args.hook_name is not None:
201+ hook_name = args.hook_name
202+ else:
203+ hook_name = os.path.basename(sys.argv[0])
204+
205+ if hook_name == "install":
206+ retVal = install_hook()
207+ elif hook_name == "config-changed":
208+ retVal = config_changed()
209+ elif hook_name == "start":
210+ retVal = start_hook()
211+ elif hook_name == "stop":
212+ retVal = stop_hook()
213+ elif hook_name == "database-relation-joined":
214+ retVal = database_relation_joined()
215+ elif hook_name == "replica-set-relation-joined":
216+ retVal = replica_set_relation_joined()
217+ elif hook_name == "replica-set-relation-changed":
218+ retVal = replica_set_relation_changed()
219+ elif hook_name == "configsvr-relation-joined":
220+ retVal = configsvr_relation_joined()
221+ elif hook_name == "configsvr-relation-changed":
222+ retVal = configsvr_relation_changed()
223+ elif hook_name == "mongos-cfg-relation-joined":
224+ retVal = mongos_relation_joined()
225+ elif hook_name == "mongos-cfg-relation-changed":
226+ retVal = mongos_relation_changed()
227+ elif hook_name == "mongos-cfg-relation-broken":
228+ retVal = mongos_relation_broken()
229+ elif hook_name == "mongos-relation-joined":
230+ retVal = mongos_relation_joined()
231+ elif hook_name == "mongos-relation-changed":
232+ retVal = mongos_relation_changed()
233+ elif hook_name == "mongos-relation-broken":
234+ retVal = mongos_relation_broken()
235+ else:
236+ print "Unknown hook"
237+ retVal = False
238+
239+ if retVal is True:
240+ sys.exit(0)
241+ else:
242+ sys.exit(1)
243
244=== modified file 'revision'
245--- revision 2013-04-25 15:58:45 +0000
246+++ revision 2013-12-11 19:27:36 +0000
247@@ -1,1 +1,1 @@
248-27
249+28
250
251=== added file 'tests/10-unit.test'
252--- tests/10-unit.test 1970-01-01 00:00:00 +0000
253+++ tests/10-unit.test 2013-12-11 19:27:36 +0000
254@@ -0,0 +1,12 @@
255+#!/usr/bin/python
256+
257+"""Unit test suite."""
258+
259+import os
260+import sys
261+import unittest
262+
263+runner = unittest.TextTestRunner(verbosity=2)
264+suite = unittest.TestLoader().discover(os.path.dirname(__file__))
265+result = runner.run(suite)
266+sys.exit(not result.wasSuccessful())
267
268=== added symlink 'tests/hooks.py'
269=== target is u'../hooks/hooks.py'
270=== added file 'tests/test_write_log_rotate_config.py'
271--- tests/test_write_log_rotate_config.py 1970-01-01 00:00:00 +0000
272+++ tests/test_write_log_rotate_config.py 2013-12-11 19:27:36 +0000
273@@ -0,0 +1,33 @@
274+import hooks
275+import mock
276+import os
277+import unittest
278+import tempfile
279+
280+
281+class TestWriteLogrotateConfigFile(unittest.TestCase):
282+
283+ def test_success(self):
284+ logpath = '/tmp/foo/foo.log'
285+ config_data = {
286+ 'logpath': logpath,
287+ 'logrotate-frequency': 'daily',
288+ 'logrotate-maxsize': '5G',
289+ 'logrotate-rotate': 5,
290+ }
291+ fd, temp_fn = tempfile.mkstemp()
292+ os.close(fd)
293+ with mock.patch('hooks.juju_log') as mock_juju_log:
294+ with mock.patch('hooks.open', create=True) as mock_open:
295+ mock_open.return_value = mock.MagicMock(spec=file)
296+ hooks.write_logrotate_config(config_data, temp_fn)
297+ os.unlink(temp_fn)
298+ mock_juju_log.assert_called_once_with('Writing {}.'.format(temp_fn))
299+ mock_open.assert_called_once_with(temp_fn, 'w')
300+ mock_file = mock_open().__enter__()
301+ call_args = mock_file.write.call_args[0][0]
302+ self.assertTrue(mock_file.write.called)
303+ self.assertIn(logpath, call_args)
304+ self.assertIn('daily', call_args)
305+ self.assertIn('maxsize 5G', call_args)
306+ self.assertIn('rotate 5', call_args)

Subscribers

People subscribed via source and target branches