| # |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| # |
| |
| import io |
| import unittest |
| from functools import reduce |
| from typing import List, Any, Callable, Dict, cast |
| from unittest.mock import patch, MagicMock |
| |
| from apache_polaris.cli.command import Command |
| from apache_polaris.cli.command.policies import PoliciesCommand |
| from apache_polaris.cli.options.parser import Parser |
| from apache_polaris.sdk.management import PolarisDefaultApi |
| |
| INVALID_ARGS = 2 |
| |
| |
| class TestCliParsing(unittest.TestCase): |
| def test_invalid_commands(self) -> None: |
| with self.assertRaises(SystemExit) as cm: |
| Parser.parse(["not-real-command!", "list"]) |
| self.assertEqual(cm.exception.code, INVALID_ARGS) |
| |
| with self.assertRaises(SystemExit) as cm: |
| Parser.parse(["catalogs", "not-real-subcommand"]) |
| self.assertEqual(cm.exception.code, INVALID_ARGS) |
| |
| with self.assertRaises(SystemExit) as cm: |
| Parser.parse(["catalogs", "create"]) # missing required input |
| self.assertEqual(cm.exception.code, INVALID_ARGS) |
| |
| with self.assertRaises(SystemExit) as cm: |
| Parser.parse( |
| ["catalogs", "create", "catalog_name", "--type", "BANANA"] |
| ) # invalid catalog type |
| self.assertEqual(cm.exception.code, INVALID_ARGS) |
| |
| with self.assertRaises(SystemExit) as cm: |
| Parser.parse( |
| ["catalogs", "create", "catalog_name", "--set-property", "foo=bar"] |
| ) # can't use --set-property on create |
| self.assertEqual(cm.exception.code, INVALID_ARGS) |
| |
| with self.assertRaises(SystemExit) as cm: |
| Parser.parse(["catalogs", "get", "catalog_name", "--fake-flag"]) |
| self.assertEqual(cm.exception.code, INVALID_ARGS) |
| |
| with self.assertRaises(SystemExit) as cm: |
| Parser.parse( |
| ["catalogs", "update", "catalog_name", "--property", "foo=bar"] |
| ) |
| self.assertEqual(cm.exception.code, INVALID_ARGS) |
| |
| with self.assertRaises(SystemExit) as cm: |
| Parser.parse( |
| [ |
| "catalogs", |
| "create", |
| "catalog_name", |
| "--type", |
| "EXTERNAL", |
| "--remote-url", |
| "gone", |
| ] |
| ) # remote-url deprecated |
| self.assertEqual(cm.exception.code, INVALID_ARGS) |
| |
| with self.assertRaises(SystemExit) as cm: |
| Parser.parse(["principals", "create", "name", "--type", "bad"]) |
| self.assertEqual(cm.exception.code, INVALID_ARGS) |
| |
| with self.assertRaises(SystemExit) as cm: |
| Parser.parse(["principals", "update", "name", "--client-id", "something"]) |
| self.assertEqual(cm.exception.code, INVALID_ARGS) |
| |
| with self.assertRaises(SystemExit) as cm: |
| Parser.parse( |
| [ |
| "privileges", |
| "catalog", |
| "--catalog", |
| "c", |
| "--catalog-role", |
| "r", |
| "privilege", |
| "grant", |
| ] |
| ) |
| self.assertEqual(cm.exception.code, INVALID_ARGS) |
| |
| with self.assertRaises(SystemExit) as cm: |
| Parser.parse( |
| [ |
| "privileges", |
| "--catalog", |
| "c", |
| "--catalog-role", |
| "r", |
| "catalog", |
| "grant", |
| "privilege", |
| "--namespace", |
| "unexpected!", |
| ] |
| ) |
| self.assertEqual(cm.exception.code, INVALID_ARGS) |
| |
| def _check_usage_output(self, f: Callable[[], Any], needle: str = "usage:") -> None: |
| with ( |
| patch("sys.stdout", new_callable=io.StringIO) as mock_stdout, |
| patch("sys.stderr", new_callable=io.StringIO), |
| ): |
| with self.assertRaises(SystemExit) as cm: |
| f() |
| self.assertEqual(cm.exception.code, 0) |
| help_output = str(mock_stdout.getvalue()) |
| self.assertIn("usage:", help_output) |
| print(help_output) |
| |
| def test_usage(self) -> None: |
| self._check_usage_output(lambda: Parser.parse(["--help"])) |
| self._check_usage_output(lambda: Parser.parse(["catalogs", "--help"])) |
| self._check_usage_output(lambda: Parser.parse(["catalogs", "create", "--help"])) |
| self._check_usage_output( |
| lambda: Parser.parse(["catalogs", "create", "something", "--help"]) |
| ) |
| |
| def test_extended_usage(self) -> None: |
| self._check_usage_output( |
| lambda: Parser._build_parser().parse_args(["--help"], "input:") |
| ) |
| self._check_usage_output( |
| lambda: Parser._build_parser().parse_args(["catalogs", "--help"], "input:") |
| ) |
| self._check_usage_output( |
| lambda: Parser._build_parser().parse_args( |
| ["catalogs", "create", "--help"], "input:" |
| ) |
| ) |
| self._check_usage_output( |
| lambda: Parser._build_parser().parse_args( |
| ["catalogs", "create", "c", "--help"], "input:" |
| ) |
| ) |
| self._check_usage_output( |
| lambda: Parser._build_parser().parse_args( |
| ["privileges", "table", "grant", "--help"], "input:" |
| ) |
| ) |
| self._check_usage_output( |
| lambda: Parser.parse(["catalogs", "create", "something", "--help"]), |
| "input:", |
| ) |
| |
| def test_parsing_valid_commands(self) -> None: |
| Parser.parse(["catalogs", "create", "catalog_name"]) |
| Parser.parse(["catalogs", "create", "catalog_name", "--type", "internal"]) |
| Parser.parse(["catalogs", "create", "catalog_name", "--type", "INTERNAL"]) |
| Parser.parse(["catalogs", "list"]) |
| Parser.parse(["catalogs", "get", "catalog_name"]) |
| Parser.parse(["principals", "list"]) |
| Parser.parse(["--host", "some-host", "catalogs", "list"]) |
| Parser.parse( |
| ["--base-url", "https://customservice.com/subpath", "catalogs", "list"] |
| ) |
| Parser.parse( |
| [ |
| "privileges", |
| "catalog", |
| "grant", |
| "--catalog", |
| "foo", |
| "--catalog-role", |
| "bar", |
| "TABLE_READ_DATA", |
| ] |
| ) |
| Parser.parse( |
| [ |
| "privileges", |
| "table", |
| "grant", |
| "--catalog", |
| "foo", |
| "--catalog-role", |
| "bar", |
| "--namespace", |
| "n", |
| "--table", |
| "t", |
| "TABLE_READ_DATA", |
| ] |
| ) |
| Parser.parse( |
| [ |
| "privileges", |
| "table", |
| "revoke", |
| "--catalog", |
| "foo", |
| "--catalog-role", |
| "bar", |
| "--namespace", |
| "n", |
| "--table", |
| "t", |
| "TABLE_READ_DATA", |
| ] |
| ) |
| |
| # These commands are valid for parsing, but may cause errors within the command itself |
| def test_parse_argparse_valid_commands(self) -> None: |
| Parser.parse(["catalogs", "create", "catalog_name", "--type", "internal"]) |
| Parser.parse( |
| [ |
| "privileges", |
| "table", |
| "grant", |
| "--namespace", |
| "n", |
| "--table", |
| "t", |
| "TABLE_READ_DATA", |
| ] |
| ) |
| Parser.parse( |
| [ |
| "privileges", |
| "catalog", |
| "grant", |
| "--catalog", |
| "c", |
| "--catalog-role", |
| "r", |
| "fake-privilege", |
| ] |
| ) |
| |
| def test_commands(self) -> None: |
| def build_mock_client() -> MagicMock: |
| client = MagicMock(spec=PolarisDefaultApi) |
| client.call_tracker = dict() |
| |
| def capture_method(method_name: str) -> Callable[..., None]: |
| def _capture(*args: Any, **kwargs: Any) -> None: |
| client.call_tracker["_method"] = method_name |
| for i, arg in enumerate(args): |
| client.call_tracker[i] = arg |
| |
| return _capture |
| |
| for method_name in dir(client): |
| if callable( |
| getattr(client, method_name) |
| ) and not method_name.startswith("__"): |
| setattr( |
| client, |
| method_name, |
| MagicMock( |
| name=method_name, side_effect=capture_method(method_name) |
| ), |
| ) |
| return client |
| |
| mock_client = build_mock_client() |
| |
| def mock_execute(input: List[str]) -> Dict[Any, Any]: |
| mock_client.call_tracker = dict() |
| |
| # Assuming Parser and Command are used to parse input and generate commands |
| options = Parser.parse(input) |
| command = Command.from_options(options) |
| |
| try: |
| command.execute(mock_client) |
| except AttributeError as e: |
| # Some commands may fail due to the mock, but the results should still match expectations |
| print(f"Suppressed error: {e}") |
| return mock_client.call_tracker |
| |
| def check_exception(f: Callable[[], Any], exception_str: str) -> None: |
| throws = True |
| try: |
| f() |
| throws = False |
| except Exception as e: |
| self.assertIn(exception_str, str(e)) |
| self.assertTrue(throws, "Exception should be raised") |
| |
| def check_arguments( |
| result: Dict[Any, Any], method_name: str, args: Dict[Any, Any] = {} |
| ) -> None: |
| self.assertEqual(method_name, result["_method"]) |
| |
| def get(obj: Any, arg_string: str) -> Any: |
| attributes = arg_string.split(".") |
| return reduce(getattr, attributes, obj) |
| |
| for arg, value in args.items(): |
| index, path = arg |
| if path is not None: |
| self.assertEqual(value, get(result[index], path)) |
| else: |
| self.assertEqual(value, result[index]) |
| |
| # Test various failing commands: |
| check_exception( |
| lambda: mock_execute(["catalogs", "create", "my-catalog"]), "--storage-type" |
| ) |
| check_exception( |
| lambda: mock_execute( |
| ["catalogs", "create", "my-catalog", "--storage-type", "gcs"] |
| ), |
| "--default-base-location", |
| ) |
| check_exception( |
| lambda: mock_execute(["catalog-roles", "get", "foo"]), "--catalog" |
| ) |
| check_exception( |
| lambda: mock_execute( |
| ["catalogs", "update", "foo", "--set-property", "bad-format"] |
| ), |
| "bad-format", |
| ) |
| check_exception( |
| lambda: mock_execute( |
| [ |
| "privileges", |
| "catalog", |
| "grant", |
| "--catalog", |
| "foo", |
| "--catalog-role", |
| "bar", |
| "TABLE_READ_MORE_BOOKS", |
| ] |
| ), |
| "catalog privilege: TABLE_READ_MORE_BOOKS", |
| ) |
| check_exception( |
| lambda: mock_execute( |
| [ |
| "catalogs", |
| "create", |
| "my-catalog", |
| "--storage-type", |
| "gcs", |
| "--allowed-location", |
| "a", |
| "--allowed-location", |
| "b", |
| "--role-arn", |
| "ra", |
| "--default-base-location", |
| "x", |
| ] |
| ), |
| "gcs", |
| ) |
| check_exception( |
| lambda: mock_execute( |
| [ |
| "catalogs", |
| "create", |
| "my-catalog", |
| "--type", |
| "external", |
| "--storage-type", |
| "file", |
| "--default-base-location", |
| "dbl", |
| "--catalog-connection-type", |
| "hive", |
| "--catalog-authentication-type", |
| "implicit", |
| ] |
| ), |
| "--hive-warehouse", |
| ) |
| |
| # Test various correct commands: |
| check_arguments( |
| mock_execute( |
| [ |
| "catalogs", |
| "create", |
| "my-catalog", |
| "--storage-type", |
| "gcs", |
| "--default-base-location", |
| "x", |
| ] |
| ), |
| "create_catalog", |
| { |
| (0, "catalog.name"): "my-catalog", |
| (0, "catalog.storage_config_info.storage_type"): "GCS", |
| (0, "catalog.properties.default_base_location"): "x", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "catalogs", |
| "create", |
| "my-catalog", |
| "--type", |
| "external", |
| "--storage-type", |
| "gcs", |
| "--default-base-location", |
| "dbl", |
| ] |
| ), |
| "create_catalog", |
| { |
| (0, "catalog.name"): "my-catalog", |
| (0, "catalog.type"): "EXTERNAL", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "catalogs", |
| "create", |
| "my-catalog", |
| "--storage-type", |
| "s3", |
| "--allowed-location", |
| "a", |
| "--allowed-location", |
| "b", |
| "--role-arn", |
| "ra", |
| "--external-id", |
| "ei", |
| "--default-base-location", |
| "x", |
| ] |
| ), |
| "create_catalog", |
| { |
| (0, "catalog.name"): "my-catalog", |
| (0, "catalog.storage_config_info.storage_type"): "S3", |
| (0, "catalog.properties.default_base_location"): "x", |
| (0, "catalog.storage_config_info.allowed_locations"): ["a", "b"], |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "catalogs", |
| "create", |
| "my-catalog", |
| "--storage-type", |
| "s3", |
| "--allowed-location", |
| "a", |
| "--role-arn", |
| "ra", |
| "--region", |
| "us-west-2", |
| "--external-id", |
| "ei", |
| "--default-base-location", |
| "x", |
| ] |
| ), |
| "create_catalog", |
| { |
| (0, "catalog.name"): "my-catalog", |
| (0, "catalog.storage_config_info.storage_type"): "S3", |
| (0, "catalog.properties.default_base_location"): "x", |
| (0, "catalog.storage_config_info.allowed_locations"): ["a"], |
| (0, "catalog.storage_config_info.region"): "us-west-2", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "catalogs", |
| "create", |
| "my-catalog", |
| "--storage-type", |
| "gcs", |
| "--allowed-location", |
| "a", |
| "--allowed-location", |
| "b", |
| "--service-account", |
| "sa", |
| "--default-base-location", |
| "x", |
| ] |
| ), |
| "create_catalog", |
| { |
| (0, "catalog.name"): "my-catalog", |
| (0, "catalog.storage_config_info.storage_type"): "GCS", |
| (0, "catalog.properties.default_base_location"): "x", |
| (0, "catalog.storage_config_info.allowed_locations"): ["a", "b"], |
| (0, "catalog.storage_config_info.gcs_service_account"): "sa", |
| }, |
| ) |
| check_arguments(mock_execute(["catalogs", "list"]), "list_catalogs") |
| check_arguments( |
| mock_execute( |
| ["--base-url", "https://customservice.com/subpath", "catalogs", "list"] |
| ), |
| "list_catalogs", |
| ) |
| check_arguments( |
| mock_execute(["catalogs", "delete", "foo"]), |
| "delete_catalog", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute(["catalogs", "get", "foo"]), |
| "get_catalog", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute(["catalogs", "update", "foo", "--default-base-location", "x"]), |
| "get_catalog", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute(["catalogs", "update", "foo", "--set-property", "key=value"]), |
| "get_catalog", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| ["catalogs", "update", "foo", "--set-property", "listkey=k1=v1,k2=v2"] |
| ), |
| "get_catalog", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute(["catalogs", "update", "foo", "--remove-property", "key"]), |
| "get_catalog", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "catalogs", |
| "update", |
| "foo", |
| "--set-property", |
| "key=value", |
| "--default-base-location", |
| "x", |
| ] |
| ), |
| "get_catalog", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "catalogs", |
| "update", |
| "foo", |
| "--set-property", |
| "key=value", |
| "--default-base-location", |
| "x", |
| "--region", |
| "us-west-1", |
| ] |
| ), |
| "get_catalog", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute(["principals", "create", "foo", "--property", "key=value"]), |
| "create_principal", |
| { |
| (0, "principal.name"): "foo", |
| (0, "principal.client_id"): None, |
| (0, "principal.properties"): {"key": "value"}, |
| }, |
| ) |
| check_arguments( |
| mock_execute(["principals", "delete", "foo"]), |
| "delete_principal", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute(["principals", "get", "foo"]), |
| "get_principal", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments(mock_execute(["principals", "list"]), "list_principals") |
| check_arguments( |
| mock_execute(["principals", "rotate-credentials", "foo"]), |
| "rotate_credentials", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| ["principals", "update", "foo", "--set-property", "key=value"] |
| ), |
| "get_principal", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute(["principals", "update", "foo", "--remove-property", "key"]), |
| "get_principal", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute(["principal-roles", "create", "foo"]), |
| "create_principal_role", |
| { |
| (0, "principal_role.name"): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute(["principal-roles", "delete", "foo"]), |
| "delete_principal_role", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute(["principal-roles", "delete", "foo"]), |
| "delete_principal_role", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute(["principal-roles", "get", "foo"]), |
| "get_principal_role", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute(["principal-roles", "get", "foo"]), |
| "get_principal_role", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute(["principal-roles", "list"]), "list_principal_roles" |
| ) |
| check_arguments( |
| mock_execute(["principal-roles", "list", "--principal", "foo"]), |
| "list_principal_roles_assigned", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| ["principal-roles", "update", "foo", "--set-property", "key=value"] |
| ), |
| "get_principal_role", |
| {(0, None): "foo"}, |
| ) |
| check_arguments( |
| mock_execute( |
| ["principal-roles", "update", "foo", "--remove-property", "key"] |
| ), |
| "get_principal_role", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute(["principal-roles", "grant", "bar", "--principal", "foo"]), |
| "assign_principal_role", |
| { |
| (0, None): "foo", |
| (1, "principal_role.name"): "bar", |
| }, |
| ) |
| check_arguments( |
| mock_execute(["principal-roles", "revoke", "bar", "--principal", "foo"]), |
| "revoke_principal_role", |
| { |
| (0, None): "foo", |
| (1, None): "bar", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "catalog-roles", |
| "create", |
| "foo", |
| "--catalog", |
| "bar", |
| "--property", |
| "key=value", |
| ] |
| ), |
| "create_catalog_role", |
| { |
| (0, None): "bar", |
| (1, "catalog_role.name"): "foo", |
| (1, "catalog_role.properties"): {"key": "value"}, |
| }, |
| ) |
| check_arguments( |
| mock_execute(["catalog-roles", "delete", "foo", "--catalog", "bar"]), |
| "delete_catalog_role", |
| { |
| (0, None): "bar", |
| (1, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute(["catalog-roles", "get", "foo", "--catalog", "bar"]), |
| "get_catalog_role", |
| { |
| (0, None): "bar", |
| (1, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute(["catalog-roles", "list", "foo"]), |
| "list_catalog_roles", |
| { |
| (0, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute(["catalog-roles", "list", "foo", "--principal-role", "bar"]), |
| "list_catalog_roles_for_principal_role", |
| { |
| (0, None): "bar", |
| (1, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "catalog-roles", |
| "update", |
| "foo", |
| "--catalog", |
| "bar", |
| "--set-property", |
| "key=value", |
| ] |
| ), |
| "get_catalog_role", |
| { |
| (0, None): "bar", |
| (1, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "catalog-roles", |
| "update", |
| "foo", |
| "--catalog", |
| "bar", |
| "--remove-property", |
| "key", |
| ] |
| ), |
| "get_catalog_role", |
| { |
| (0, None): "bar", |
| (1, None): "foo", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "catalog-roles", |
| "grant", |
| "--principal-role", |
| "foo", |
| "--catalog", |
| "bar", |
| "baz", |
| ] |
| ), |
| "assign_catalog_role_to_principal_role", |
| { |
| (0, None): "foo", |
| (1, None): "bar", |
| (2, "catalog_role.name"): "baz", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "catalog-roles", |
| "revoke", |
| "--principal-role", |
| "foo", |
| "--catalog", |
| "bar", |
| "baz", |
| ] |
| ), |
| "revoke_catalog_role_from_principal_role", |
| { |
| (0, None): "foo", |
| (1, None): "bar", |
| (2, None): "baz", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "privileges", |
| "catalog", |
| "grant", |
| "--catalog", |
| "foo", |
| "--catalog-role", |
| "bar", |
| "TABLE_READ_DATA", |
| ] |
| ), |
| "add_grant_to_catalog_role", |
| { |
| (0, None): "foo", |
| (1, None): "bar", |
| (2, "grant.privilege.value"): "TABLE_READ_DATA", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "privileges", |
| "catalog", |
| "revoke", |
| "--catalog", |
| "foo", |
| "--catalog-role", |
| "bar", |
| "TABLE_READ_DATA", |
| ] |
| ), |
| "revoke_grant_from_catalog_role", |
| { |
| (0, None): "foo", |
| (1, None): "bar", |
| (2, None): False, |
| (3, "grant.privilege.value"): "TABLE_READ_DATA", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "privileges", |
| "namespace", |
| "grant", |
| "--namespace", |
| "a.b.c", |
| "--catalog", |
| "foo", |
| "--catalog-role", |
| "bar", |
| "TABLE_READ_DATA", |
| ] |
| ), |
| "add_grant_to_catalog_role", |
| { |
| (0, None): "foo", |
| (1, None): "bar", |
| (2, "grant.privilege.value"): "TABLE_READ_DATA", |
| (2, "grant.namespace"): ["a", "b", "c"], |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "privileges", |
| "table", |
| "grant", |
| "--namespace", |
| "a.b.c", |
| "--table", |
| "t", |
| "--catalog", |
| "foo", |
| "--catalog-role", |
| "bar", |
| "TABLE_READ_DATA", |
| ] |
| ), |
| "add_grant_to_catalog_role", |
| { |
| (0, None): "foo", |
| (1, None): "bar", |
| (2, "grant.privilege.value"): "TABLE_READ_DATA", |
| (2, "grant.namespace"): ["a", "b", "c"], |
| (2, "grant.table_name"): "t", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "privileges", |
| "table", |
| "revoke", |
| "--namespace", |
| "a.b.c", |
| "--catalog", |
| "foo", |
| "--catalog-role", |
| "bar", |
| "--table", |
| "t", |
| "--cascade", |
| "TABLE_READ_DATA", |
| ] |
| ), |
| "revoke_grant_from_catalog_role", |
| { |
| (0, None): "foo", |
| (1, None): "bar", |
| (2, None): True, |
| (3, "grant.privilege.value"): "TABLE_READ_DATA", |
| (3, "grant.namespace"): ["a", "b", "c"], |
| (3, "grant.table_name"): "t", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "privileges", |
| "view", |
| "grant", |
| "--namespace", |
| "a.b.c", |
| "--catalog", |
| "foo", |
| "--catalog-role", |
| "bar", |
| "--view", |
| "v", |
| "VIEW_FULL_METADATA", |
| ] |
| ), |
| "add_grant_to_catalog_role", |
| { |
| (0, None): "foo", |
| (1, None): "bar", |
| (2, "grant.privilege.value"): "VIEW_FULL_METADATA", |
| (2, "grant.namespace"): ["a", "b", "c"], |
| (2, "grant.view_name"): "v", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "catalogs", |
| "create", |
| "my-catalog", |
| "--type", |
| "external", |
| "--storage-type", |
| "gcs", |
| "--default-base-location", |
| "dbl", |
| "--catalog-connection-type", |
| "iceberg-rest", |
| "--iceberg-remote-catalog-name", |
| "i", |
| "--catalog-uri", |
| "u", |
| "--catalog-authentication-type", |
| "bearer", |
| "--catalog-bearer-token", |
| "b", |
| ] |
| ), |
| "create_catalog", |
| { |
| (0, "catalog.name"): "my-catalog", |
| (0, "catalog.type"): "EXTERNAL", |
| (0, "catalog.connection_config_info.connection_type"): "ICEBERG_REST", |
| (0, "catalog.connection_config_info.remote_catalog_name"): "i", |
| (0, "catalog.connection_config_info.uri"): "u", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "catalogs", |
| "create", |
| "my-catalog", |
| "--type", |
| "external", |
| "--storage-type", |
| "gcs", |
| "--default-base-location", |
| "dbl", |
| "--catalog-connection-type", |
| "iceberg-rest", |
| "--iceberg-remote-catalog-name", |
| "c", |
| "--catalog-uri", |
| "u", |
| "--catalog-authentication-type", |
| "oauth", |
| "--catalog-token-uri", |
| "u", |
| "--catalog-client-id", |
| "i", |
| "--catalog-client-secret", |
| "k", |
| "--catalog-client-scope", |
| "s1", |
| "--catalog-client-scope", |
| "s2", |
| ] |
| ), |
| "create_catalog", |
| { |
| (0, "catalog.name"): "my-catalog", |
| (0, "catalog.type"): "EXTERNAL", |
| (0, "catalog.connection_config_info.connection_type"): "ICEBERG_REST", |
| (0, "catalog.connection_config_info.remote_catalog_name"): "c", |
| (0, "catalog.connection_config_info.uri"): "u", |
| ( |
| 0, |
| "catalog.connection_config_info.authentication_parameters.authentication_type", |
| ): "OAUTH", |
| ( |
| 0, |
| "catalog.connection_config_info.authentication_parameters.token_uri", |
| ): "u", |
| ( |
| 0, |
| "catalog.connection_config_info.authentication_parameters.client_id", |
| ): "i", |
| ( |
| 0, |
| "catalog.connection_config_info.authentication_parameters.scopes", |
| ): ["s1", "s2"], |
| (0, "catalog.storage_config_info.storage_type"): "GCS", |
| (0, "catalog.properties.default_base_location"): "dbl", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "catalogs", |
| "create", |
| "my-catalog", |
| "--type", |
| "external", |
| "--storage-type", |
| "gcs", |
| "--default-base-location", |
| "dbl", |
| "--catalog-connection-type", |
| "iceberg-rest", |
| "--iceberg-remote-catalog-name", |
| "i", |
| "--catalog-uri", |
| "u", |
| "--catalog-authentication-type", |
| "sigv4", |
| "--catalog-role-arn", |
| "a", |
| "--catalog-signing-region", |
| "s", |
| ] |
| ), |
| "create_catalog", |
| { |
| (0, "catalog.name"): "my-catalog", |
| (0, "catalog.type"): "EXTERNAL", |
| (0, "catalog.connection_config_info.connection_type"): "ICEBERG_REST", |
| (0, "catalog.connection_config_info.remote_catalog_name"): "i", |
| (0, "catalog.connection_config_info.uri"): "u", |
| ( |
| 0, |
| "catalog.connection_config_info.authentication_parameters.role_arn", |
| ): "a", |
| ( |
| 0, |
| "catalog.connection_config_info.authentication_parameters.signing_region", |
| ): "s", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "catalogs", |
| "create", |
| "my-catalog", |
| "--type", |
| "external", |
| "--storage-type", |
| "gcs", |
| "--default-base-location", |
| "dbl", |
| "--catalog-connection-type", |
| "iceberg-rest", |
| "--iceberg-remote-catalog-name", |
| "i", |
| "--catalog-uri", |
| "u", |
| "--catalog-authentication-type", |
| "sigv4", |
| "--catalog-role-arn", |
| "a", |
| "--catalog-signing-region", |
| "s", |
| "--catalog-role-session-name", |
| "n", |
| "--catalog-external-id", |
| "i", |
| "--catalog-signing-name", |
| "g", |
| ] |
| ), |
| "create_catalog", |
| { |
| (0, "catalog.name"): "my-catalog", |
| (0, "catalog.type"): "EXTERNAL", |
| (0, "catalog.connection_config_info.connection_type"): "ICEBERG_REST", |
| (0, "catalog.connection_config_info.remote_catalog_name"): "i", |
| (0, "catalog.connection_config_info.uri"): "u", |
| ( |
| 0, |
| "catalog.connection_config_info.authentication_parameters.role_arn", |
| ): "a", |
| ( |
| 0, |
| "catalog.connection_config_info.authentication_parameters.signing_region", |
| ): "s", |
| ( |
| 0, |
| "catalog.connection_config_info.authentication_parameters.role_session_name", |
| ): "n", |
| ( |
| 0, |
| "catalog.connection_config_info.authentication_parameters.external_id", |
| ): "i", |
| ( |
| 0, |
| "catalog.connection_config_info.authentication_parameters.signing_name", |
| ): "g", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "catalogs", |
| "create", |
| "my-catalog", |
| "--type", |
| "external", |
| "--storage-type", |
| "file", |
| "--default-base-location", |
| "dbl", |
| "--catalog-connection-type", |
| "hadoop", |
| "--hadoop-warehouse", |
| "h", |
| "--catalog-authentication-type", |
| "implicit", |
| "--catalog-uri", |
| "u", |
| ] |
| ), |
| "create_catalog", |
| { |
| (0, "catalog.name"): "my-catalog", |
| (0, "catalog.type"): "EXTERNAL", |
| (0, "catalog.connection_config_info.connection_type"): "HADOOP", |
| (0, "catalog.connection_config_info.warehouse"): "h", |
| ( |
| 0, |
| "catalog.connection_config_info.authentication_parameters.authentication_type", |
| ): "IMPLICIT", |
| (0, "catalog.connection_config_info.uri"): "u", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "catalogs", |
| "create", |
| "my-catalog", |
| "--type", |
| "external", |
| "--storage-type", |
| "file", |
| "--default-base-location", |
| "dbl", |
| "--catalog-connection-type", |
| "hive", |
| "--hive-warehouse", |
| "h", |
| "--catalog-authentication-type", |
| "implicit", |
| "--catalog-uri", |
| "u", |
| ] |
| ), |
| "create_catalog", |
| { |
| (0, "catalog.name"): "my-catalog", |
| (0, "catalog.type"): "EXTERNAL", |
| (0, "catalog.connection_config_info.connection_type"): "HIVE", |
| (0, "catalog.connection_config_info.warehouse"): "h", |
| ( |
| 0, |
| "catalog.connection_config_info.authentication_parameters.authentication_type", |
| ): "IMPLICIT", |
| (0, "catalog.connection_config_info.uri"): "u", |
| }, |
| ) |
| check_arguments( |
| mock_execute( |
| [ |
| "catalogs", |
| "create", |
| "my-catalog", |
| "--type", |
| "external", |
| "--storage-type", |
| "file", |
| "--default-base-location", |
| "dbl", |
| "--catalog-connection-type", |
| "hive", |
| "--hive-warehouse", |
| "/warehouse/path", |
| "--catalog-authentication-type", |
| "oauth", |
| "--catalog-uri", |
| "thrift://hive-metastore:9083", |
| "--catalog-token-uri", |
| "http://auth-server/token", |
| "--catalog-client-id", |
| "test-client", |
| "--catalog-client-secret", |
| "test-secret", |
| "--catalog-client-scope", |
| "read", |
| "--catalog-client-scope", |
| "write", |
| ] |
| ), |
| "create_catalog", |
| { |
| (0, "catalog.name"): "my-catalog", |
| (0, "catalog.type"): "EXTERNAL", |
| (0, "catalog.connection_config_info.connection_type"): "HIVE", |
| (0, "catalog.connection_config_info.warehouse"): "/warehouse/path", |
| ( |
| 0, |
| "catalog.connection_config_info.authentication_parameters.authentication_type", |
| ): "OAUTH", |
| ( |
| 0, |
| "catalog.connection_config_info.uri", |
| ): "thrift://hive-metastore:9083", |
| }, |
| ) |
| |
| check_arguments( |
| mock_execute( |
| [ |
| "principals", |
| "reset", |
| "test", |
| "--new-client-id", |
| "e469c048cf866df1", |
| "--new-client-secret", |
| "e469c048cf866dfae469c048cf866df1", |
| ] |
| ), |
| "reset_credentials", |
| { |
| (0, None): "test", |
| (1, "client_id"): "e469c048cf866df1", |
| (1, "client_secret"): "e469c048cf866dfae469c048cf866df1", |
| }, |
| ) |
| |
| check_arguments( |
| mock_execute(["principals", "reset", "test"]), |
| "reset_credentials", |
| { |
| (0, None): "test", |
| (1, None): None, |
| }, |
| ) |
| |
| check_arguments( |
| mock_execute( |
| ["principals", "reset", "test", "--new-client-id", "e469c048cf866df1"] |
| ), |
| "reset_credentials", |
| { |
| (0, None): "test", |
| (1, "client_id"): "e469c048cf866df1", |
| (1, "client_secret"): None, |
| }, |
| ) |
| |
| check_arguments( |
| mock_execute( |
| [ |
| "principals", |
| "reset", |
| "test", |
| "--new-client-secret", |
| "e469c048cf866dfae469c048cf866df1", |
| ] |
| ), |
| "reset_credentials", |
| { |
| (0, None): "test", |
| (1, "client_id"): None, |
| (1, "client_secret"): "e469c048cf866dfae469c048cf866df1", |
| }, |
| ) |
| |
| def test_policies_attach_parameters_parsed_to_dict(self) -> None: |
| options = Parser.parse( |
| [ |
| "policies", |
| "attach", |
| "policy-name", |
| "--catalog", |
| "cat", |
| "--attachment-type", |
| "catalog", |
| "--parameters", |
| "key=value", |
| ] |
| ) |
| command = Command.from_options(options) |
| command = cast(PoliciesCommand, command) |
| self.assertIsInstance(command.parameters, dict) |
| self.assertEqual({"key": "value"}, command.parameters) |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |