Merge ~ilasc/turnip:add-repack-data-notify into turnip:master

Proposed by Ioana Lasc
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)
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

To post a comment you must log in.
Revision history for this message
Ioana Lasc (ilasc) wrote :

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.

Revision history for this message
Colin Watson (cjwatson) wrote :

Use the cwd= argument to subprocess.check_output.

Revision history for this message
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.

Revision history for this message
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/pack/hooks/hook.py) and then send the relevant parts of the answer in the notify_push RPC call. The hook already runs with the repository in question as its current directory.

Revision history for this message
Ioana Lasc (ilasc) wrote :

Oh fantastic, out of the 3 avenues I was pursuing this sounds like the best, thanks Colin!

Revision history for this message
Ioana Lasc (ilasc) wrote :

Changes implemented, MP now ready for review.

Revision history for this message
Ioana Lasc (ilasc) wrote :

End to end local tests now passing, MP ready for review.

Revision history for this message
Colin Watson (cjwatson) :
review: Needs Fixing
Revision history for this message
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_set_symbolic_ref.

Revision history for this message
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.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/turnip/pack/git.py b/turnip/pack/git.py
2index 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)
25diff --git a/turnip/pack/helpers.py b/turnip/pack/helpers.py
26index 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."""
56diff --git a/turnip/pack/hookrpc.py b/turnip/pack/hookrpc.py
57index 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)
98diff --git a/turnip/pack/hooks/hook.py b/turnip/pack/hooks/hook.py
99index 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)
124diff --git a/turnip/pack/tests/fake_servers.py b/turnip/pack/tests/fake_servers.py
125index 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))
140diff --git a/turnip/pack/tests/test_functional.py b/turnip/pack/tests/test_functional.py
141index 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):
172diff --git a/turnip/pack/tests/test_helpers.py b/turnip/pack/tests/test_helpers.py
173index 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)
231diff --git a/turnip/pack/tests/test_hookrpc.py b/turnip/pack/tests/test_hookrpc.py
232index 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)

Subscribers

People subscribed via source and target branches