source: trunk/src/allmydata/test/cli/test_grid_manager.py

Last change on this file was b440065, checked in by Jean-Paul Calderone <exarkun@…>, at 2023-08-08T15:30:02Z

avoid trying to call os.getuid on windows

  • Property mode set to 100644
File size: 11.3 KB
Line 
1"""
2Tests for the grid manager CLI.
3"""
4
5import os
6from io import (
7    BytesIO,
8)
9from unittest import (
10    skipIf,
11)
12
13from twisted.trial.unittest import (
14    TestCase,
15)
16from allmydata.cli.grid_manager import (
17    grid_manager,
18)
19
20import click.testing
21
22# these imports support the tests for `tahoe *` subcommands
23from ..common_util import (
24    run_cli,
25)
26from ..common import (
27    superuser,
28)
29from twisted.internet.defer import (
30    inlineCallbacks,
31)
32from twisted.python.filepath import (
33    FilePath,
34)
35from twisted.python.runtime import (
36    platform,
37)
38from allmydata.util import jsonbytes as json
39
40class GridManagerCommandLine(TestCase):
41    """
42    Test the mechanics of the `grid-manager` command
43    """
44
45    def setUp(self):
46        self.runner = click.testing.CliRunner()
47        super(GridManagerCommandLine, self).setUp()
48
49    def invoke_and_check(self, *args, **kwargs):
50        """Invoke a command with the runner and ensure it succeeded."""
51        result = self.runner.invoke(*args, **kwargs)
52        if result.exception is not None:
53            raise result.exc_info[1].with_traceback(result.exc_info[2])
54        self.assertEqual(result.exit_code, 0, result)
55        return result
56
57    def test_create(self):
58        """
59        Create a new grid-manager
60        """
61        with self.runner.isolated_filesystem():
62            result = self.invoke_and_check(grid_manager, ["--config", "foo", "create"])
63            self.assertEqual(["foo"], os.listdir("."))
64            self.assertEqual(["config.json"], os.listdir("./foo"))
65            result = self.invoke_and_check(grid_manager, ["--config", "foo", "public-identity"])
66            self.assertTrue(result.output.startswith("pub-v0-"))
67
68    def test_load_invalid(self):
69        """
70        An invalid config is reported to the user
71        """
72        with self.runner.isolated_filesystem():
73            with open("config.json", "wb") as f:
74                f.write(json.dumps_bytes({"not": "valid"}))
75            result = self.runner.invoke(grid_manager, ["--config", ".", "public-identity"])
76            self.assertNotEqual(result.exit_code, 0)
77            self.assertIn(
78                "Error loading Grid Manager",
79                result.output,
80            )
81
82    def test_create_already(self):
83        """
84        It's an error to create a new grid-manager in an existing
85        directory.
86        """
87        with self.runner.isolated_filesystem():
88            result = self.invoke_and_check(grid_manager, ["--config", "foo", "create"])
89            result = self.runner.invoke(grid_manager, ["--config", "foo", "create"])
90            self.assertEqual(1, result.exit_code)
91            self.assertIn(
92                "Can't create",
93                result.stdout,
94            )
95
96    def test_create_stdout(self):
97        """
98        Create a new grid-manager with no files
99        """
100        with self.runner.isolated_filesystem():
101            result = self.invoke_and_check(grid_manager, ["--config", "-", "create"])
102            self.assertEqual([], os.listdir("."))
103            config = json.loads(result.output)
104            self.assertEqual(
105                {"private_key", "grid_manager_config_version"},
106                set(config.keys()),
107            )
108
109    def test_list_stdout(self):
110        """
111        Load Grid Manager without files (using 'list' subcommand, but any will do)
112        """
113        config = {
114            "storage_servers": {
115                "storage0": {
116                    "public_key": "pub-v0-cbq6hcf3pxcz6ouoafrbktmkixkeuywpcpbcomzd3lqbkq4nmfga"
117                }
118            },
119            "private_key": "priv-v0-6uinzyaxy3zvscwgsps5pxcfezhrkfb43kvnrbrhhfzyduyqnniq",
120            "grid_manager_config_version": 0
121        }
122        result = self.invoke_and_check(
123            grid_manager, ["--config", "-", "list"],
124            input=BytesIO(json.dumps_bytes(config)),
125        )
126        self.assertEqual(result.exit_code, 0)
127        self.assertEqual(
128            "storage0: pub-v0-cbq6hcf3pxcz6ouoafrbktmkixkeuywpcpbcomzd3lqbkq4nmfga\n",
129            result.output,
130        )
131
132    def test_add_and_sign(self):
133        """
134        Add a new storage-server and sign a certificate for it
135        """
136        pubkey = "pub-v0-cbq6hcf3pxcz6ouoafrbktmkixkeuywpcpbcomzd3lqbkq4nmfga"
137        with self.runner.isolated_filesystem():
138            self.invoke_and_check(grid_manager, ["--config", "foo", "create"])
139            self.invoke_and_check(grid_manager, ["--config", "foo", "add", "storage0", pubkey])
140            result = self.invoke_and_check(grid_manager, ["--config", "foo", "sign", "storage0", "10"])
141            sigcert = json.loads(result.output)
142            self.assertEqual({"certificate", "signature"}, set(sigcert.keys()))
143            cert = json.loads(sigcert['certificate'])
144            self.assertEqual(cert["public_key"], pubkey)
145
146    def test_add_and_sign_second_cert(self):
147        """
148        Add a new storage-server and sign two certificates.
149        """
150        pubkey = "pub-v0-cbq6hcf3pxcz6ouoafrbktmkixkeuywpcpbcomzd3lqbkq4nmfga"
151        with self.runner.isolated_filesystem():
152            self.invoke_and_check(grid_manager, ["--config", "foo", "create"])
153            self.invoke_and_check(grid_manager, ["--config", "foo", "add", "storage0", pubkey])
154            self.invoke_and_check(grid_manager, ["--config", "foo", "sign", "storage0", "10"])
155            self.invoke_and_check(grid_manager, ["--config", "foo", "sign", "storage0", "10"])
156            # we should now have two certificates stored
157            self.assertEqual(
158                set(FilePath("foo").listdir()),
159                {'storage0.cert.1', 'storage0.cert.0', 'config.json'},
160            )
161
162    def test_add_twice(self):
163        """
164        An error is reported trying to add an existing server
165        """
166        pubkey0 = "pub-v0-cbq6hcf3pxcz6ouoafrbktmkixkeuywpcpbcomzd3lqbkq4nmfga"
167        pubkey1 = "pub-v0-5ysc55trfvfvg466v46j4zmfyltgus3y2gdejifctv7h4zkuyveq"
168        with self.runner.isolated_filesystem():
169            self.invoke_and_check(grid_manager, ["--config", "foo", "create"])
170            self.invoke_and_check(grid_manager, ["--config", "foo", "add", "storage0", pubkey0])
171            result = self.runner.invoke(grid_manager, ["--config", "foo", "add", "storage0", pubkey1])
172            self.assertNotEquals(result.exit_code, 0)
173            self.assertIn(
174                "A storage-server called 'storage0' already exists",
175                result.output,
176            )
177
178    def test_add_list_remove(self):
179        """
180        Add a storage server, list it, remove it.
181        """
182        pubkey = "pub-v0-cbq6hcf3pxcz6ouoafrbktmkixkeuywpcpbcomzd3lqbkq4nmfga"
183        with self.runner.isolated_filesystem():
184            self.invoke_and_check(grid_manager, ["--config", "foo", "create"])
185            self.invoke_and_check(grid_manager, ["--config", "foo", "add", "storage0", pubkey])
186            self.invoke_and_check(grid_manager, ["--config", "foo", "sign", "storage0", "1"])
187
188            result = self.invoke_and_check(grid_manager, ["--config", "foo", "list"])
189            names = [
190                line.split(':')[0]
191                for line in result.output.strip().split('\n')
192                if not line.startswith("  ")  # "cert" lines start with whitespace
193            ]
194            self.assertEqual(names, ["storage0"])
195
196            self.invoke_and_check(grid_manager, ["--config", "foo", "remove", "storage0"])
197
198            result = self.invoke_and_check(grid_manager, ["--config", "foo", "list"])
199            self.assertEqual(result.output.strip(), "")
200
201    def test_remove_missing(self):
202        """
203        Error reported when removing non-existant server
204        """
205        with self.runner.isolated_filesystem():
206            self.invoke_and_check(grid_manager, ["--config", "foo", "create"])
207            result = self.runner.invoke(grid_manager, ["--config", "foo", "remove", "storage0"])
208            self.assertNotEquals(result.exit_code, 0)
209            self.assertIn(
210                "No storage-server called 'storage0' exists",
211                result.output,
212            )
213
214    def test_sign_missing(self):
215        """
216        Error reported when signing non-existant server
217        """
218        with self.runner.isolated_filesystem():
219            self.invoke_and_check(grid_manager, ["--config", "foo", "create"])
220            result = self.runner.invoke(grid_manager, ["--config", "foo", "sign", "storage0", "42"])
221            self.assertNotEquals(result.exit_code, 0)
222            self.assertIn(
223                "No storage-server called 'storage0' exists",
224                result.output,
225            )
226
227    @skipIf(platform.isWindows(), "We don't know how to set permissions on Windows.")
228    @skipIf(superuser, "cannot test as superuser with all permissions")
229    def test_sign_bad_perms(self):
230        """
231        Error reported if we can't create certificate file
232        """
233        pubkey = "pub-v0-cbq6hcf3pxcz6ouoafrbktmkixkeuywpcpbcomzd3lqbkq4nmfga"
234        with self.runner.isolated_filesystem():
235            self.invoke_and_check(grid_manager, ["--config", "foo", "create"])
236            self.invoke_and_check(grid_manager, ["--config", "foo", "add", "storage0", pubkey])
237            # make the directory un-writable (so we can't create a new cert)
238            os.chmod("foo", 0o550)
239            result = self.runner.invoke(grid_manager, ["--config", "foo", "sign", "storage0", "42"])
240            self.assertEquals(result.exit_code, 1)
241            self.assertIn(
242                "Permission denied",
243                result.output,
244            )
245
246
247class TahoeAddGridManagerCert(TestCase):
248    """
249    Test `tahoe admin add-grid-manager-cert` subcommand
250    """
251
252    @inlineCallbacks
253    def test_help(self):
254        """
255        some kind of help is printed
256        """
257        code, out, err = yield run_cli("admin", "add-grid-manager-cert")
258        self.assertEqual(err, "")
259        self.assertNotEqual(0, code)
260
261    @inlineCallbacks
262    def test_no_name(self):
263        """
264        error to miss --name option
265        """
266        code, out, err = yield run_cli(
267            "admin", "add-grid-manager-cert", "--filename", "-",
268            stdin=b"the cert",
269        )
270        self.assertIn(
271            "Must provide --name",
272            out
273        )
274
275    @inlineCallbacks
276    def test_no_filename(self):
277        """
278        error to miss --name option
279        """
280        code, out, err = yield run_cli(
281            "admin", "add-grid-manager-cert", "--name", "foo",
282            stdin=b"the cert",
283        )
284        self.assertIn(
285            "Must provide --filename",
286            out
287        )
288
289    @inlineCallbacks
290    def test_add_one(self):
291        """
292        we can add a certificate
293        """
294        nodedir = self.mktemp()
295        fake_cert = b"""{"certificate": "", "signature": ""}"""
296
297        code, out, err = yield run_cli(
298            "--node-directory", nodedir,
299            "admin", "add-grid-manager-cert", "-f", "-", "--name", "foo",
300            stdin=fake_cert,
301            ignore_stderr=True,
302        )
303        nodepath = FilePath(nodedir)
304        with nodepath.child("tahoe.cfg").open("r") as f:
305            config_data = f.read()
306
307        self.assertIn("tahoe.cfg", nodepath.listdir())
308        self.assertIn(
309            b"foo = foo.cert",
310            config_data,
311        )
312        self.assertIn("foo.cert", nodepath.listdir())
313        with nodepath.child("foo.cert").open("r") as f:
314            self.assertEqual(
315                json.load(f),
316                json.loads(fake_cert)
317            )
Note: See TracBrowser for help on using the repository browser.