From d55bfa7fb1cc15cc5c6ece3e2cfd9302a37e0eec Mon Sep 17 00:00:00 2001 From: Clay Gerrard Date: Tue, 30 Mar 2021 13:16:07 -0500 Subject: [PATCH] s-m-s-r: read shard_ranges from stdin Change-Id: I874e9c65e6209d727fa089be9d31462faf0d0c0b --- swift/cli/manage_shard_ranges.py | 13 ++++- test/unit/cli/test_manage_shard_ranges.py | 71 +++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/swift/cli/manage_shard_ranges.py b/swift/cli/manage_shard_ranges.py index 7a43cd0267..c8fc54acad 100644 --- a/swift/cli/manage_shard_ranges.py +++ b/swift/cli/manage_shard_ranges.py @@ -161,6 +161,7 @@ import json import os.path import sys import time +from contextlib import contextmanager from six.moves import input @@ -207,12 +208,22 @@ def _print_shard_range(sr, level=0): print(indent + ' state: %9s upper: %r' % (sr.state_text, sr.upper_str)) +@contextmanager +def _open_input(args): + if args.input == '-': + args.input = '' + yield sys.stdin + else: + with open(args.input, 'r') as fd: + yield fd + + def _load_and_validate_shard_data(args, require_index=True): required_keys = ['lower', 'upper', 'object_count'] if require_index: required_keys.append('index') try: - with open(args.input, 'r') as fd: + with _open_input(args) as fd: try: data = json.load(fd) if not isinstance(data, list): diff --git a/test/unit/cli/test_manage_shard_ranges.py b/test/unit/cli/test_manage_shard_ranges.py index d72ec5b482..7ed3318f1d 100644 --- a/test/unit/cli/test_manage_shard_ranges.py +++ b/test/unit/cli/test_manage_shard_ranges.py @@ -526,6 +526,77 @@ class TestManageShardRanges(unittest.TestCase): [(data['lower'], data['upper']) for data in self.shard_data], [(sr.lower_str, sr.upper_str) for sr in broker.get_shard_ranges()]) + def test_analyze_stdin(self): + out = StringIO() + err = StringIO() + stdin = StringIO() + stdin.write(json.dumps([])) # empty but valid json + stdin.seek(0) + with mock.patch('sys.stdout', out), mock.patch('sys.stderr', err), \ + mock.patch('sys.stdin', stdin): + main(['-', 'analyze']) + expected = [ + 'Found no complete sequence of shard ranges.', + 'Repairs necessary to fill gaps.', + 'Gap filling not supported by this tool. No repairs performed.', + ] + + self.assertEqual(expected, out.getvalue().splitlines()) + broker = self._make_broker() + broker.update_metadata({'X-Container-Sysmeta-Sharding': + (True, Timestamp.now().internal)}) + shard_ranges = [ + dict(sr, state=ShardRange.STATES[sr.state]) + for sr in make_shard_ranges(broker, self.shard_data, '.shards_') + ] + out = StringIO() + err = StringIO() + stdin = StringIO() + stdin.write(json.dumps(shard_ranges)) + stdin.seek(0) + with mock.patch('sys.stdout', out), mock.patch('sys.stderr', err), \ + mock.patch('sys.stdin', stdin): + main(['-', 'analyze']) + expected = [ + 'Found one complete sequence of 10 shard ranges ' + 'and no overlapping shard ranges.', + 'No repairs necessary.', + ] + self.assertEqual(expected, out.getvalue().splitlines()) + + def test_analyze_stdin_with_overlaps(self): + broker = self._make_broker() + broker.set_sharding_sysmeta('Quoted-Root', 'a/c') + with mock_timestamp_now(next(self.ts_iter)): + shard_ranges = make_shard_ranges( + broker, self.shard_data, '.shards_') + with mock_timestamp_now(next(self.ts_iter)): + overlap_shard_ranges_1 = make_shard_ranges( + broker, self.overlap_shard_data_1, '.shards_') + broker.merge_shard_ranges(shard_ranges + overlap_shard_ranges_1) + shard_ranges = [ + dict(sr, state=ShardRange.STATES[sr.state]) + for sr in broker.get_shard_ranges() + ] + out = StringIO() + err = StringIO() + stdin = StringIO() + stdin.write(json.dumps(shard_ranges)) + stdin.seek(0) + with mock.patch('sys.stdout', out), mock.patch('sys.stderr', err), \ + mock.patch('sys.stdin', stdin): + main(['-', 'analyze']) + expected = [ + 'Repairs necessary to remove overlapping shard ranges.', + 'Chosen a complete sequence of 10 shard ranges with ' + 'current total of 100 object records to accept object records ' + 'from 10 overlapping donor shard ranges.', + 'Once applied to the broker these changes will result in:', + ' 10 shard ranges being removed.', + ' 10 object records being moved to the chosen shard ranges.', + ] + self.assertEqual(expected, out.getvalue().splitlines()) + def _assert_enabled(self, broker, epoch): own_sr = broker.get_own_shard_range() self.assertEqual(ShardRange.SHARDING, own_sr.state)