Merge ~ilasc/turnip:add-repack-data-notify into turnip:master
- Git
- lp:~ilasc/turnip
- add-repack-data-notify
- Merge into master
Status: | Merged |
---|---|
Approved by: | Ioana Lasc |
Approved revision: | 021a655ab2c38444a01fbe4425cd191c549f1d9f |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~ilasc/turnip:add-repack-data-notify |
Merge into: | turnip:master |
Diff against target: |
271 lines (+103/-15) 8 files modified
turnip/pack/git.py (+4/-1) turnip/pack/helpers.py (+13/-0) turnip/pack/hookrpc.py (+12/-4) turnip/pack/hooks/hook.py (+7/-1) turnip/pack/tests/fake_servers.py (+3/-2) turnip/pack/tests/test_functional.py (+10/-4) turnip/pack/tests/test_helpers.py (+33/-0) turnip/pack/tests/test_hookrpc.py (+21/-3) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Colin Watson (community) | Approve | ||
Review via email: mp+397972@code.launchpad.net |
Commit message
Add repack data to notify call
Description of the change
Ioana Lasc (ilasc) wrote : | # |
Colin Watson (cjwatson) wrote : | # |
Use the cwd= argument to subprocess.
Ioana Lasc (ilasc) wrote : | # |
Thanks Colin, the difficulty I'm having is finding the full path in notifyPush to cd into, the path returned by translatedPath is the repo ID, I need to go from that to the full path on disk, or pass it in somehow from somewhere else.
Colin Watson (cjwatson) wrote : | # |
OK, I saw the commented-out os.chdir and assumed that the problem was at that level.
Rather than calling git count-objects in hookrpc, I think you should call it in the hook itself (turnip/
Ioana Lasc (ilasc) wrote : | # |
Oh fantastic, out of the 3 avenues I was pursuing this sounds like the best, thanks Colin!
Ioana Lasc (ilasc) wrote : | # |
Changes implemented, MP now ready for review.
Ioana Lasc (ilasc) wrote : | # |
End to end local tests now passing, MP ready for review.
Colin Watson (cjwatson) : | # |
Ioana Lasc (ilasc) wrote : | # |
Agreed with all comments, thanks Colin!
Moved the repack_data method to helpers and covered the call to it from processEnded in test_turnip_
Colin Watson (cjwatson) wrote : | # |
Looks pretty good now, thanks! Just a few details, but after that feel free to land and see how it goes on qastaging.
Preview Diff
1 | diff --git a/turnip/pack/git.py b/turnip/pack/git.py |
2 | index e871091..a74f1e0 100644 |
3 | --- a/turnip/pack/git.py |
4 | +++ b/turnip/pack/git.py |
5 | @@ -42,6 +42,7 @@ from turnip.pack.helpers import ( |
6 | ensure_config, |
7 | ensure_hooks, |
8 | INCOMPLETE_PKT, |
9 | + get_repack_data, |
10 | translate_xmlrpc_fault, |
11 | ) |
12 | |
13 | @@ -679,8 +680,10 @@ class PackBackendProtocol(PackServerProtocol): |
14 | if self.command == b'turnip-set-symbolic-ref': |
15 | if reason.check(error.ProcessDone): |
16 | try: |
17 | + loose_object_count, pack_count = get_repack_data() |
18 | yield self.factory.hookrpc_handler.notify( |
19 | - self.raw_pathname) |
20 | + self.raw_pathname, loose_object_count, pack_count, |
21 | + self.factory.hookrpc_handler.auth_params) |
22 | self.sendPacket(b'ACK %s\n' % self.symbolic_ref_name) |
23 | except Exception as e: |
24 | message = str(e) |
25 | diff --git a/turnip/pack/helpers.py b/turnip/pack/helpers.py |
26 | index d23f12f..d6fd344 100644 |
27 | --- a/turnip/pack/helpers.py |
28 | +++ b/turnip/pack/helpers.py |
29 | @@ -12,6 +12,7 @@ import enum |
30 | import hashlib |
31 | import os.path |
32 | import re |
33 | +import subprocess |
34 | import stat |
35 | import sys |
36 | from tempfile import ( |
37 | @@ -204,6 +205,18 @@ def ensure_hooks(repo_root): |
38 | pass |
39 | |
40 | |
41 | +def get_repack_data(): |
42 | + output = subprocess.check_output( |
43 | + ['git', 'count-objects', '-v'], universal_newlines=True) |
44 | + if not output: |
45 | + return None, None |
46 | + match = re.search(r'^packs: (.*)', output, flags=re.M) |
47 | + packs = int(match.group(1)) if match else None |
48 | + match = re.search(r'^count: (.*)', output, flags=re.M) |
49 | + objects = int(match.group(1)) if match else None |
50 | + return objects, packs |
51 | + |
52 | + |
53 | @enum.unique |
54 | class TurnipFaultCode(enum.Enum): |
55 | """An internal vocabulary of possible faults from the virtinfo service.""" |
56 | diff --git a/turnip/pack/hookrpc.py b/turnip/pack/hookrpc.py |
57 | index 15f5187..9ae29cb 100644 |
58 | --- a/turnip/pack/hookrpc.py |
59 | +++ b/turnip/pack/hookrpc.py |
60 | @@ -1,4 +1,4 @@ |
61 | -# Copyright 2015-2018 Canonical Ltd. This software is licensed under the |
62 | +# Copyright 2015-2021 Canonical Ltd. This software is licensed under the |
63 | # GNU Affero General Public License version 3 (see the file LICENSE). |
64 | |
65 | """RPC server for Git hooks. |
66 | @@ -229,9 +229,13 @@ class HookRPCHandler(object): |
67 | for ref in paths}) |
68 | |
69 | @defer.inlineCallbacks |
70 | - def notify(self, path): |
71 | + def notify(self, path, loose_object_count, pack_count, auth_params): |
72 | proxy = xmlrpc.Proxy(self.virtinfo_url, allowNone=True) |
73 | - yield proxy.callRemote('notify', six.ensure_str(path)).addTimeout( |
74 | + statistics = dict({("loose_object_count", loose_object_count), |
75 | + ("pack_count", pack_count)}) |
76 | + yield proxy.callRemote('notify', six.ensure_str(path), |
77 | + statistics, |
78 | + auth_params).addTimeout( |
79 | self.virtinfo_timeout, self.reactor) |
80 | |
81 | @defer.inlineCallbacks |
82 | @@ -239,10 +243,14 @@ class HookRPCHandler(object): |
83 | """Notify the virtinfo service about a push.""" |
84 | log_context = HookRPCLogContext(self.auth_params[args['key']]) |
85 | path = self.ref_paths[args['key']] |
86 | + auth_params = self.auth_params[args['key']] |
87 | + loose_object_count = args.get('loose_object_count') |
88 | + pack_count = args.get('pack_count') |
89 | log_context.log.info( |
90 | "notifyPush request received: ref_path={path}", path=path) |
91 | try: |
92 | - yield self.notify(path) |
93 | + yield self.notify(path, loose_object_count, |
94 | + pack_count, auth_params) |
95 | except defer.TimeoutError: |
96 | log_context.log.info( |
97 | "notifyPush timed out: ref_path={path}", path=path) |
98 | diff --git a/turnip/pack/hooks/hook.py b/turnip/pack/hooks/hook.py |
99 | index e0257b2..983a022 100755 |
100 | --- a/turnip/pack/hooks/hook.py |
101 | +++ b/turnip/pack/hooks/hook.py |
102 | @@ -19,6 +19,7 @@ import sys |
103 | import six |
104 | |
105 | from turnip.compat.files import fd_buffer |
106 | +from turnip.pack.helpers import get_repack_data |
107 | |
108 | # XXX twom 2018-10-23 This should be a pygit2 import, but |
109 | # that currently causes CFFI warnings to be returned to the client. |
110 | @@ -212,7 +213,12 @@ if __name__ == '__main__': |
111 | # Details of the changes aren't currently included. |
112 | lines = stdin.readlines() |
113 | if lines: |
114 | - rpc_invoke(sock, 'notify_push', {'key': rpc_key}) |
115 | + loose_object_count, pack_count = get_repack_data() |
116 | + rpc_invoke(sock, |
117 | + 'notify_push', |
118 | + {'key': rpc_key, |
119 | + 'loose_object_count': loose_object_count, |
120 | + 'pack_count': pack_count}) |
121 | if len(lines) == 1: |
122 | send_mp_url(lines[0]) |
123 | sys.exit(0) |
124 | diff --git a/turnip/pack/tests/fake_servers.py b/turnip/pack/tests/fake_servers.py |
125 | index 92b77b9..458c317 100644 |
126 | --- a/turnip/pack/tests/fake_servers.py |
127 | +++ b/turnip/pack/tests/fake_servers.py |
128 | @@ -114,8 +114,9 @@ class FakeVirtInfoService(xmlrpc.XMLRPC): |
129 | self.authentications.append((username, password)) |
130 | return {'user': username} |
131 | |
132 | - def xmlrpc_notify(self, path): |
133 | - self.push_notifications.append(path) |
134 | + def xmlrpc_notify(self, path, statistics, auth_params): |
135 | + self.push_notifications.append((path, statistics, |
136 | + auth_params)) |
137 | |
138 | def xmlrpc_checkRefPermissions(self, path, ref_paths, auth_params): |
139 | self.ref_permissions_checks.append((path, ref_paths, auth_params)) |
140 | diff --git a/turnip/pack/tests/test_functional.py b/turnip/pack/tests/test_functional.py |
141 | index 02bf771..be972b2 100644 |
142 | --- a/turnip/pack/tests/test_functional.py |
143 | +++ b/turnip/pack/tests/test_functional.py |
144 | @@ -731,8 +731,8 @@ class FrontendFunctionalTestMixin(FunctionalTestMixin): |
145 | yield self.assertCommandSuccess( |
146 | (b'git', b'push', b'origin', b'master'), path=clone1) |
147 | self.assertEqual( |
148 | - [six.ensure_text(self.internal_name)], |
149 | - self.virtinfo.push_notifications) |
150 | + six.ensure_text(self.internal_name), |
151 | + self.virtinfo.push_notifications[0][0]) |
152 | |
153 | @defer.inlineCallbacks |
154 | def test_unicode_fault(self): |
155 | @@ -849,8 +849,14 @@ class TestSmartHTTPFrontendFunctional(FrontendFunctionalTestMixin, TestCase): |
156 | head_target = yield self.get_symbolic_ref(repo, b'HEAD') |
157 | self.assertEqual(b'refs/heads/new-head', head_target) |
158 | self.assertEqual( |
159 | - [six.ensure_text(self.internal_name)], |
160 | - self.virtinfo.push_notifications) |
161 | + six.ensure_text(self.internal_name), |
162 | + self.virtinfo.push_notifications[0][0]) |
163 | + self.assertNotEqual( |
164 | + 0, |
165 | + self.virtinfo.push_notifications[0][1].get('pack_count')) |
166 | + self.assertNotEqual( |
167 | + 0, |
168 | + self.virtinfo.push_notifications[0][1].get('loose_object_count')) |
169 | |
170 | @defer.inlineCallbacks |
171 | def test_turnip_set_symbolic_ref_error(self): |
172 | diff --git a/turnip/pack/tests/test_helpers.py b/turnip/pack/tests/test_helpers.py |
173 | index bc66977..2ffba15 100644 |
174 | --- a/turnip/pack/tests/test_helpers.py |
175 | +++ b/turnip/pack/tests/test_helpers.py |
176 | @@ -16,10 +16,13 @@ import sys |
177 | import tempfile |
178 | from textwrap import dedent |
179 | import time |
180 | +import uuid |
181 | |
182 | from fixtures import TempDir |
183 | from pygit2 import ( |
184 | Config, |
185 | + GIT_FILEMODE_BLOB, |
186 | + IndexEntry, |
187 | init_repository, |
188 | ) |
189 | import six |
190 | @@ -33,6 +36,7 @@ from turnip.pack import helpers |
191 | from turnip.pack.helpers import ( |
192 | encode_packet, |
193 | get_capabilities_advertisement, |
194 | + get_repack_data, |
195 | ) |
196 | import turnip.pack.hooks |
197 | from turnip.version_info import version_info |
198 | @@ -414,3 +418,32 @@ class MockStatsd(): |
199 | |
200 | def get_client(self): |
201 | return self |
202 | + |
203 | + |
204 | +class TestGetRepackData(TestCase): |
205 | + """Test get repack indicators for repository.""" |
206 | + |
207 | + def setUp(self): |
208 | + super(TestGetRepackData, self).setUp() |
209 | + self.repo_dir = self.useFixture(TempDir()).path |
210 | + self.repo = init_repository(self.repo_dir, bare=False) |
211 | + |
212 | + def test_get_repack_data(self): |
213 | + curdir = os.getcwd() |
214 | + os.chdir(self.repo_dir) |
215 | + # create a test file |
216 | + blob_content = ( |
217 | + b'commit ' + 'file content'.encode('ascii') + b' - ' |
218 | + + uuid.uuid1().hex.encode('ascii')) |
219 | + test_file = 'test.txt' |
220 | + # stage the changes |
221 | + self.repo.index.add(IndexEntry( |
222 | + test_file, self.repo.create_blob(blob_content), |
223 | + GIT_FILEMODE_BLOB)) |
224 | + self.repo.index.write_tree() |
225 | + |
226 | + objects, packs = get_repack_data() |
227 | + os.chdir(curdir) |
228 | + |
229 | + self.assertIsNotNone(objects) |
230 | + self.assertIsNotNone(packs) |
231 | diff --git a/turnip/pack/tests/test_hookrpc.py b/turnip/pack/tests/test_hookrpc.py |
232 | index a4b0d19..eea5d08 100644 |
233 | --- a/turnip/pack/tests/test_hookrpc.py |
234 | +++ b/turnip/pack/tests/test_hookrpc.py |
235 | @@ -327,15 +327,33 @@ class TestHookRPCHandler(TestCase): |
236 | @defer.inlineCallbacks |
237 | def test_notifyPush(self): |
238 | with self.registeredKey('/translated') as key: |
239 | - yield self.hookrpc_handler.notifyPush(None, {'key': key}) |
240 | - self.assertEqual(['/translated'], self.virtinfo.push_notifications) |
241 | + yield self.hookrpc_handler.notifyPush( |
242 | + None, |
243 | + {'key': key, 'loose_object_count': 19, 'pack_count': 7}) |
244 | + |
245 | + # notify will now return in this format: |
246 | + # ('/translated', {'loose_object_count': 19, 'pack_count': 7}, {}) |
247 | + # with the numbers being different of course for each |
248 | + # repository state |
249 | + self.assertEqual('/translated', |
250 | + self.virtinfo.push_notifications[0][0]) |
251 | + self.assertEqual( |
252 | + 19, |
253 | + self.virtinfo.push_notifications[0][1].get( |
254 | + 'loose_object_count')) |
255 | + self.assertEqual( |
256 | + 7, |
257 | + self.virtinfo.push_notifications[0][1].get( |
258 | + 'pack_count')) |
259 | |
260 | def test_notifyPush_timeout(self): |
261 | clock = task.Clock() |
262 | self.hookrpc_handler = hookrpc.HookRPCHandler( |
263 | self.virtinfo_url, 15, reactor=clock) |
264 | with self.registeredKey('/translated') as key: |
265 | - d = self.hookrpc_handler.notifyPush(None, {'key': key}) |
266 | + d = self.hookrpc_handler.notifyPush( |
267 | + None, |
268 | + {'key': key, 'loose_object_count': 9, 'pack_count': 7}) |
269 | clock.advance(1) |
270 | self.assertFalse(d.called) |
271 | clock.advance(15) |
This is still work in progress, opened for visibility only.
I working on figuring out how how to 'cd' into the correct directory on the backend before calling git count-objects as that needs to be performed in the actual repo directory.