From 716f762dc459cf853526f55110e583b4efce83e6 Mon Sep 17 00:00:00 2001 From: jamesd-db Date: Tue, 8 Aug 2023 12:45:26 -0700 Subject: [PATCH] Framework for creating and using the Unity Catalog connections API (#647) * First changes to create a connections CLI * Fixed create commands and added for all other connection types, added get, list, and delete commands as well * Added oauth CLI flow * addition of oauth connectors and documentation for use --- databricks_cli/unity_catalog/README.md | 29 + databricks_cli/unity_catalog/api.py | 17 + databricks_cli/unity_catalog/cli.py | 2 + .../unity_catalog/connection_cli.py | 622 ++++++++++++++++++ databricks_cli/unity_catalog/response.html | 26 + .../snowflake_oauth_creation.png | Bin 0 -> 81377 bytes databricks_cli/unity_catalog/uc_service.py | 27 + tests/unity_catalog/test_con_cli.py | 111 ++++ 8 files changed, 834 insertions(+) create mode 100644 databricks_cli/unity_catalog/README.md create mode 100644 databricks_cli/unity_catalog/connection_cli.py create mode 100644 databricks_cli/unity_catalog/response.html create mode 100644 databricks_cli/unity_catalog/snowflake_oauth_creation.png create mode 100644 tests/unity_catalog/test_con_cli.py diff --git a/databricks_cli/unity_catalog/README.md b/databricks_cli/unity_catalog/README.md new file mode 100644 index 00000000..21cd9747 --- /dev/null +++ b/databricks_cli/unity_catalog/README.md @@ -0,0 +1,29 @@ +## READ BEFORE USING THE CONNECTIONS API FOR OAUTH 2.0 CONNECTIONS + +### Requirements +Install and use a cert utility like `mkcert` in order to establish self-signed local certificates to allow the local server to recieve authorization codes from authorization servers via TLS/SSL. + +For example: + +``` +brew install mkcert +mkcert localhost +``` +Ensure these certificates are in the directory of wherever you are running the databricks-cli from. For example if you are calling databricks-cli from `~`, then you should have your `.pem` keys in `~` + +### Before Using the CLI for OAuth +* You must first create an OAuth connection object in the provider that you wish to use Lakehouse Federation with. For example in Snowflake you need to create a security integration by running a command like the one below: +![Snowflake login image](./snowflake_oauth_creation.png) + +You can then show the client id and client secret using the command: +``` +SELECT SYSTEM$SHOW_OAUTH_CLIENT_SECRETS('SNOWFLAKE_DEMO') +``` +* You can read more about the OAuth settings for security integrations in Snowflake [here](https://docs.snowflake.com/en/sql-reference/sql/create-security-integration-oauth-snowflake) + + +* You can perform a similar operation in Salesforce by going to the App Manager dashboard and creating a Connected Application and enabling OAuth settings with the redirect URL of `https://localhost:8331`. You can read more about using OAuth in salesforce [here](https://help.salesforce.com/s/articleView?id=sf.connected_app_create_api_integration.htm&type=5). + +### When using the CLI you MUST set your redirect URL to `https://localhost:8331` + +* After this configuration you should be able to use the connection as normal, using the `--help` flag on commands to see which arguments are available. \ No newline at end of file diff --git a/databricks_cli/unity_catalog/api.py b/databricks_cli/unity_catalog/api.py index b491b995..43cf14b3 100644 --- a/databricks_cli/unity_catalog/api.py +++ b/databricks_cli/unity_catalog/api.py @@ -83,6 +83,23 @@ def delete_external_location(self, name, force): def validate_external_location(self, validation_spec): return self.client.validate_external_location(validation_spec) + # Connections APIs + + def create_connection(self, con_spec): + return self.client.create_connection(con_spec) + + def list_connections(self): + return self.client.list_connections() + + def get_connection(self, name): + return self.client.get_connection(name) + + def update_connnection(self, name, con_spec): + return self.client.update_connection(name, con_spec) + + def delete_connection(self, name): + return self.client.delete_connection(name) + # Data Access Configuration APIs def create_dac(self, metastore_id, dac_spec, skip_validation): diff --git a/databricks_cli/unity_catalog/cli.py b/databricks_cli/unity_catalog/cli.py index b7a3ad95..1f40afb5 100644 --- a/databricks_cli/unity_catalog/cli.py +++ b/databricks_cli/unity_catalog/cli.py @@ -35,6 +35,7 @@ from databricks_cli.unity_catalog.delta_sharing_cli import register_delta_sharing_commands from databricks_cli.unity_catalog.perms_cli import register_perms_commands from databricks_cli.unity_catalog.lineage_cli import register_lineage_commands +from databricks_cli.unity_catalog.connection_cli import register_connection_commands @click.group(context_settings=CONTEXT_SETTINGS) @@ -56,3 +57,4 @@ def unity_catalog_group(): # pragma: no cover register_delta_sharing_commands(unity_catalog_group) register_perms_commands(unity_catalog_group) register_lineage_commands(unity_catalog_group) +register_connection_commands(unity_catalog_group) diff --git a/databricks_cli/unity_catalog/connection_cli.py b/databricks_cli/unity_catalog/connection_cli.py new file mode 100644 index 00000000..39431fe8 --- /dev/null +++ b/databricks_cli/unity_catalog/connection_cli.py @@ -0,0 +1,622 @@ +# Databricks CLI +# Copyright 2022 Databricks, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"), except +# that the use of services to which certain application programming +# interfaces (each, an "API") connect requires that the user first obtain +# a license for the use of the APIs from Databricks, Inc. ("Databricks"), +# by creating an account at www.databricks.com and agreeing to either (a) +# the Community Edition Terms of Service, (b) the Databricks Terms of +# Service, or (c) another written agreement between Licensee and Databricks +# for the use of the APIs. +# +# 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 functools +import http.server +import logging +import os +import socketserver +import ssl +import webbrowser +import oauthlib.oauth2 +from oauthlib.common import generate_token + + +import click + +from databricks_cli.click_types import JsonClickType +from databricks_cli.configure.config import provide_api_client, profile_option, debug_option +from databricks_cli.unity_catalog.api import UnityCatalogApi +from databricks_cli.unity_catalog.utils import hide, json_file_help, json_string_help, \ + mc_pretty_format +from databricks_cli.utils import eat_exceptions, CONTEXT_SETTINGS, json_cli_base + +# These two options are shared among create and updates, so they are very common + +logging.getLogger("http.server").setLevel(logging.WARNING) + +def create_update_common_options(f): + @click.option('--read-only/--no-read-only', is_flag=True, default=None, + help='Whether the connection is read-only') + @click.option('--comment', default=None, + help='Free-form text description.') + @functools.wraps(f) + def wrapper(*args, **kwargs): + f(*args, **kwargs) + return wrapper + +# These args show up in most create operations +def common_create_args(f): + @click.option('--name', default=None, + help='Name of new connection') + @click.option('--host', default=None, + help='Host of new connection') + @click.option('--port', default=None, + help='Port of new connection') + @functools.wraps(f) + def wrapper(*args, **kwargs): + f(*args, **kwargs) + return wrapper + +def json_options(f): + @click.option('--json-file', default=None, type=click.Path(), + help=json_file_help(method='POST', path='/connections'), + ) + @click.option('--json', default=None, type=JsonClickType(), + help=json_string_help(method='POST', path='/connections'), + ) + @functools.wraps(f) + def wrapper(*args, **kwargs): + f(*args, **kwargs) + return wrapper + + +redirect_uri = 'https://localhost:8331' +snowflake_path = '/oauth/authorize' +salesforce_path = '/services/oauth2/authorize' +return_query_res = "" + +class AccessCodeRequestHandler(http.server.SimpleHTTPRequestHandler): + def do_GET(self): + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + + script_path = os.path.abspath(__file__) + html_file_path = os.path.join(os.path.dirname(script_path), 'response.html') + + with open(html_file_path, 'rb') as file: + self.wfile.write(file.read()) + + global return_query_res + return_query_res = self.path + +def run_oauth_response_server(): + server_address = ('', 8331) + httpd = socketserver.TCPServer(server_address, AccessCodeRequestHandler) + + ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + ssl_context.load_cert_chain(certfile="localhost.pem", keyfile="localhost-key.pem") + httpd.socket = ssl_context.wrap_socket(httpd.socket, server_side=True) + + try: + httpd.handle_request() + except KeyboardInterrupt: + pass + +def get_auth_code(host, client_id, scope, path): + oauth = oauthlib.oauth2.WebApplicationClient(client_id) + state = generate_token() + verifier = oauth.create_code_verifier(96) + challenge = oauth.create_code_challenge(verifier, 'S256') + + authorization_url = oauth.prepare_request_uri( + 'https://' + host + path, redirect_uri = redirect_uri, + scope = scope, state = state, code_challenge = challenge, + code_challenge_method = 'S256') + + webbrowser.open_new(authorization_url) + run_oauth_response_server() + parsed_result = oauth.parse_request_uri_response(redirect_uri + return_query_res, + state = state) + parsed_result['code_verifier'] = verifier + return parsed_result + + +@click.command(context_settings=CONTEXT_SETTINGS, + short_help='Create mysql connection with CLI flags.') +@common_create_args +@create_update_common_options +@click.option('--user', default=None, + help='Username for authorization of new connection') +@click.option( + "--password", prompt=True, hide_input=True, + confirmation_prompt=True +) +@debug_option +@eat_exceptions +@profile_option +@provide_api_client +def create_mysql_cli(api_client, name, host, port, user, + read_only, comment, password): + """ + Create new mysql connection. + """ + if (name is None) or (host is None) or (port is None) or (user is None): + raise ValueError('Must provide all required connection parameters') + data = { + 'name': name, + 'connection_type': 'MYSQL', + 'options': {'host': host, 'port': port, 'user': user, 'password': password}, + 'read_only': read_only, + 'comment': comment, + } + con_json = UnityCatalogApi(api_client).create_connection(data) + click.echo(mc_pretty_format(con_json)) + + +@click.command(context_settings=CONTEXT_SETTINGS, + short_help='Create postgresql connection with CLI flags.') +@common_create_args +@create_update_common_options +@click.option('--user', default=None, + help='Username for authorization of new connection') +@click.option( + "--password", prompt=True, hide_input=True, + confirmation_prompt=True +) +@debug_option +@eat_exceptions +@profile_option +@provide_api_client +def create_postgresql_cli(api_client, name, host, port, user, + read_only, comment, password): + """ + Create new postgresql connection. + """ + if (name is None) or (host is None) or (port is None) or (user is None): + raise ValueError('Must provide all required connection parameters') + data = { + 'name': name, + 'connection_type': 'POSTGRESQL', + 'options': {'host': host, 'port': port, 'user': user, 'password': password}, + 'read_only': read_only, + 'comment': comment, + } + con_json = UnityCatalogApi(api_client).create_connection(data) + click.echo(mc_pretty_format(con_json)) + +@click.command(context_settings=CONTEXT_SETTINGS, + short_help='Create snowflake connection with CLI flags.') +@common_create_args +@create_update_common_options +@click.option('--sfwarehouse', default=None, + help='Snowflake warehouse name of new connection') +@click.option('--user', default=None, + help='Username for authorization of new connection') +@click.option( + "--password", prompt=True, hide_input=True, + confirmation_prompt=True +) +@debug_option +@eat_exceptions +@profile_option +@provide_api_client +def create_snowflake_cli(api_client, name, host, port, user, sfwarehouse, + read_only, comment, password): + """ + Create new snowflake connection. + """ + if (name is None) or (host is None) or (port is None) or (user is None) or \ + (sfwarehouse is None): + raise ValueError('Must provide all required connection parameters') + data = { + 'name': name, + 'connection_type': 'SNOWFLAKE', + 'options': {'host': host, 'port': port, 'user': user, 'password': password, + 'sfWarehouse': sfwarehouse}, + 'read_only': read_only, + 'comment': comment, + } + con_json = UnityCatalogApi(api_client).create_connection(data) + click.echo(mc_pretty_format(con_json)) + + +@click.command(context_settings=CONTEXT_SETTINGS, + short_help='Create redshift connection with CLI flags.') +@common_create_args +@create_update_common_options +@click.option('--user', default=None, + help='Username for authorization of new connection') +@click.option( + "--password", prompt=True, hide_input=True, + confirmation_prompt=True +) +@debug_option +@eat_exceptions +@profile_option +@provide_api_client +def create_redshift_cli(api_client, name, host, port, user, + read_only, comment, password): + """ + Create new redshift connection. + """ + if (name is None) or (host is None) or (port is None) or (user is None): + raise ValueError('Must provide all required connection parameters') + data = { + 'name': name, + 'connection_type': 'REDSHIFT', + 'options': {'host': host, 'port': port, 'user': user, 'password': password}, + 'read_only': read_only, + 'comment': comment, + } + con_json = UnityCatalogApi(api_client).create_connection(data) + click.echo(mc_pretty_format(con_json)) + + +@click.command(context_settings=CONTEXT_SETTINGS, + short_help='Create sqldw connection with CLI flags.') +@common_create_args +@create_update_common_options +@click.option('--trustservercert', is_flag=True, default=None, + help='Trust the server provided certificate') +@click.option('--user', default=None, + help='Username for authorization of new connection') +@click.option( + "--password", prompt=True, hide_input=True, + confirmation_prompt=True +) +@debug_option +@eat_exceptions +@profile_option +@provide_api_client +def create_sqldw_cli(api_client, name, host, port, user, trustservercert, + read_only, comment, password): + """ + Create new sqldw connection. + """ + if (name is None) or (host is None) or (port is None) or (user is None): + raise ValueError('Must provide all required connection parameters') + data = { + 'name': name, + 'connection_type': 'SQLDW', + 'options': {'host': host, 'port': port, 'user': user, 'password': password}, + 'read_only': read_only, + 'comment': comment, + } + if trustservercert is not None: + data['options']['trustServerCertificate'] = trustservercert + con_json = UnityCatalogApi(api_client).create_connection(data) + click.echo(mc_pretty_format(con_json)) + + +@click.command(context_settings=CONTEXT_SETTINGS, + short_help='Create sqlserver connection with CLI flags.') +@common_create_args +@create_update_common_options +@click.option('--trustservercert', is_flag=True, default=None, + help='Trust the server provided certificate') +@click.option('--user', default=None, + help='Username for authorization of new connection') +@click.option( + "--password", prompt=True, hide_input=True, + confirmation_prompt=True +) +@debug_option +@eat_exceptions +@profile_option +@provide_api_client +def create_sqlserver_cli(api_client, name, host, port, user, trustservercert, + read_only, comment, password): + """ + Create new sqlserver connection. + """ + if (name is None) or (host is None) or (port is None) or (user is None): + raise ValueError('Must provide all required connection parameters') + data = { + 'name': name, + 'connection_type': 'SQLSERVER', + 'options': {'host': host, 'port': port, 'user': user, 'password': password}, + 'read_only': read_only, + 'comment': comment, + } + if trustservercert is not None: + data['options']['trustServerCertificate'] = trustservercert + con_json = UnityCatalogApi(api_client).create_connection(data) + click.echo(mc_pretty_format(con_json)) + + +@click.command(context_settings=CONTEXT_SETTINGS, + short_help='Create databricks connection with CLI flags.') +@create_update_common_options +@click.option('--name', default=None, + help='Name of new connection') +@click.option('--host', default=None, + help='Host of new connection') +@click.option('--httppath', default=None, + help='HTTP path of new connection') +@click.option( + "--token", prompt=True, hide_input=True, + confirmation_prompt=True +) +@debug_option +@eat_exceptions +@profile_option +@provide_api_client +def create_databricks_cli(api_client, name, host, httppath, token, + read_only, comment): + """ + Create new databricks connection. + """ + if (name is None) or (host is None) or (httppath is None) or (token is None): + raise ValueError('Must provide all required connection parameters') + data = { + 'name': name, + 'connection_type': 'DATABRICKS', + 'options': {'host': host, 'httpPath': httppath, 'personalAccessToken': token}, + 'read_only': read_only, + 'comment': comment, + } + con_json = UnityCatalogApi(api_client).create_connection(data) + click.echo(mc_pretty_format(con_json)) + +#OAuth CLIs below + +@click.command(context_settings=CONTEXT_SETTINGS, + short_help='Create snowflake oauth connection with CLI flags.') +@common_create_args +@create_update_common_options +@click.option('--user', default=None, + help='Username for authorization of new connection') +@click.option('--sfwarehouse', default=None, + help='Snowflake warehouse name of new connection') +@click.option('--scope', default=None, + help='Scope of new OAuth connection. Should be a single \ + quoted string with separate options separated by spaces') +@click.option('--client-id', default=None, + help='Client ID for new connection') +@click.option( + "--client-secret", prompt=True, hide_input=True) +@debug_option +@eat_exceptions +@profile_option +@provide_api_client +def create_snowflake_oauth_cli(api_client, name, host, port, client_id, sfwarehouse, + read_only, comment, scope, client_secret, user): + """ + Create new snowflake U2M oauth connection. + """ + missing_args = (name is None) or (host is None) or (port is None) or (client_id is None) or \ + (client_secret is None) or (sfwarehouse is None) or (user is None) + + if missing_args: + raise ValueError('Must provide all required oauth connection parameters') + + code_dict = get_auth_code(host, client_id, scope, snowflake_path) + data = { + 'name': name, + 'connection_type': 'SNOWFLAKE', + 'options': {'host': host, 'port': port, 'user': user, 'client_id': client_id, + 'client_secret': client_secret, 'oauth_scope': scope, + 'authorization_code': code_dict['code'], 'sfWarehouse': sfwarehouse, + 'oauth_redirect_uri': redirect_uri, + 'pkce_verifier': code_dict['code_verifier']}, + 'read_only': read_only, + 'comment': comment + } + con_json = UnityCatalogApi(api_client).create_connection(data) + click.echo(mc_pretty_format(con_json)) + + +@click.command(context_settings=CONTEXT_SETTINGS, + short_help='Create salesforce oauth connection with CLI flags.') +@create_update_common_options +@click.option('--scope', default=None, + help='Scope of new OAuth connection. Should be a single \ + quoted string with separate options separated by spaces') +@click.option('--client-id', default=None, + help='Client ID for new connection') +@click.option('--is-sandbox', is_flag=True, default=False, + help='Client ID for new connection') +@click.option('--name', default=None, + help='Name of new connection') +@click.option( + "--client-secret", prompt=True, hide_input=True) +@debug_option +@eat_exceptions +@profile_option +@provide_api_client +def create_salesforce_oauth_cli(api_client, name, client_id, is_sandbox, + read_only, comment, scope, client_secret): + """ + Create new salesforce refresh token oauth connection. + """ + missing_args = (name is None) or (client_id is None) or \ + (client_secret is None) or (is_sandbox is None) + + if missing_args: + raise ValueError('Must provide all required oauth connection parameters') + + code_dict = get_auth_code('login.salesforce.com', client_id, scope, salesforce_path) + data = { + 'name': name, + 'connection_type': 'SALESFORCE', + 'options': {'client_id': client_id, + 'client_secret': client_secret, 'oauth_scope': scope, + 'authorization_code': code_dict['code'], 'is_sandbox': is_sandbox, + 'oauth_redirect_uri': redirect_uri, + 'pkce_verifier': code_dict['code_verifier']}, + 'read_only': read_only, + 'comment': comment + } + con_json = UnityCatalogApi(api_client).create_connection(data) + click.echo(mc_pretty_format(con_json)) + + +@click.command(context_settings=CONTEXT_SETTINGS, + short_help='Create connection with a JSON input.') +@json_options +@debug_option +@eat_exceptions +@profile_option +@provide_api_client +def create_json(api_client, json_file, json): + ''' + Create new connection with an inline JSON or JSON file path. + ''' + if (json is None) and (json_file is None): + raise ValueError('Must either provide inline JSON or JSON file.') + json_cli_base(json_file, json, + lambda json: + UnityCatalogApi(api_client).create_connection(json), + encode_utf8=True) + + + +@click.command(context_settings=CONTEXT_SETTINGS, + short_help='List connections.') +@debug_option +@profile_option +@eat_exceptions +@provide_api_client +def list_connections_cli(api_client): + """ + List connections. + """ + locs_json = UnityCatalogApi(api_client).list_connections() + click.echo(mc_pretty_format(locs_json)) + + +@click.command(context_settings=CONTEXT_SETTINGS, + short_help='Get a connection.') +@click.option('--name', required=True, + help='Name of the connection to get.') +@debug_option +@profile_option +@eat_exceptions +@provide_api_client +def get_connection_cli(api_client, name): + """ + Get a connection. + """ + loc_json = UnityCatalogApi(api_client).get_connection(name) + click.echo(mc_pretty_format(loc_json)) + + +@click.command(context_settings=CONTEXT_SETTINGS, + short_help='Delete a connection.') +@click.option('--name', required=True, + help='Name of the connection to delete.') +@debug_option +@profile_option +@eat_exceptions +@provide_api_client +def delete_connection_cli(api_client, name): + """ + Delete a connection. + """ + UnityCatalogApi(api_client).delete_connection(name) + + +@click.command(context_settings=CONTEXT_SETTINGS, + short_help='Update a connection.') +@click.option('--name', required=True, + help='Name of the connection to update.') +@click.option('--new-name', default=None, help='New name of the connection.') +@create_update_common_options +@click.option('--owner', default=None, + help='Owner of the connection.') + +@click.option('--json-file', default=None, type=click.Path(), + help=json_file_help(method='PATCH', path='/connections/{name}')) +@click.option('--json', default=None, type=JsonClickType(), + help=json_string_help(method='PATCH', path='/connections/{name}')) +@debug_option +@profile_option +@eat_exceptions +@provide_api_client +def update_connection_cli(api_client, name, new_name, read_only, + comment, owner, json_file, json): + """ + Update an connection. + + The public specification for the JSON request is in development. + """ + if ((new_name is not None) or + (read_only is not None) or (comment is not None)): + if (json_file is not None) or (json is not None): + raise ValueError('Cannot specify JSON if any other update flags are specified') + data = { + 'name': new_name, + 'read_only': read_only, + 'comment': comment, + 'owner': owner + } + loc_json = UnityCatalogApi(api_client).update_connnection( + name, data) + click.echo(mc_pretty_format(loc_json)) + else: + json_cli_base(json_file, json, + lambda json: UnityCatalogApi(api_client).update_connnection(name, json), + encode_utf8=True) + +@click.group() +def create_group(): # pragma: no cover + pass + +@click.group() +def connections_group(): # pragma: no cover + pass + + +def register_connection_commands(cmd_group): + # Register deprecated "verb-noun" commands for backward compatibility. + cmd_group.add_command(hide(create_json), name='create-connection-json') + cmd_group.add_command(hide(create_mysql_cli), name='create-mysql-connection') + cmd_group.add_command(hide(create_postgresql_cli), name='create-postgresql-connection') + cmd_group.add_command(hide(create_snowflake_cli), name='create-snowflake-connection') + cmd_group.add_command(hide(create_snowflake_oauth_cli), + name='create-snowflake-oauth-connection') + cmd_group.add_command(hide(create_salesforce_oauth_cli), + name='create-salesforce-oauth-connection') + cmd_group.add_command(hide(create_redshift_cli), name='create-redshift-connection') + cmd_group.add_command(hide(create_sqldw_cli), name='create-sqldw-connection') + cmd_group.add_command(hide(create_sqlserver_cli), name='create-sqlserver-connection') + cmd_group.add_command(hide(create_databricks_cli), name='create-databricks-connection') + cmd_group.add_command(hide(list_connections_cli), name='list-connections') + cmd_group.add_command(hide(get_connection_cli), name='get-connection') + cmd_group.add_command(hide(delete_connection_cli), name='delete-connection') + cmd_group.add_command(hide(update_connection_cli), name='update-connection') + + + + + + # Register command group. + create_group.add_command(create_mysql_cli, name='mysql') + create_group.add_command(create_postgresql_cli, name='postgresql') + create_group.add_command(create_snowflake_cli, name='snowflake') + create_group.add_command(create_snowflake_oauth_cli, name='snowflake-oauth') + create_group.add_command(create_salesforce_oauth_cli, name='salesforce-oauth') + create_group.add_command(create_redshift_cli, name='redshift') + create_group.add_command(create_sqldw_cli, name='sqldw') + create_group.add_command(create_sqlserver_cli, name='sqlserver') + create_group.add_command(create_databricks_cli, name='databricks') + + connections_group.add_command(create_group, name='create') + connections_group.add_command(create_json, name='create-json') + connections_group.add_command(list_connections_cli, name='list') + connections_group.add_command(get_connection_cli, name='get') + connections_group.add_command(delete_connection_cli, name='delete') + connections_group.add_command(update_connection_cli, name='update') + cmd_group.add_command(connections_group, name='connection') diff --git a/databricks_cli/unity_catalog/response.html b/databricks_cli/unity_catalog/response.html new file mode 100644 index 00000000..0dece051 --- /dev/null +++ b/databricks_cli/unity_catalog/response.html @@ -0,0 +1,26 @@ + + + + + + + + + + diff --git a/databricks_cli/unity_catalog/snowflake_oauth_creation.png b/databricks_cli/unity_catalog/snowflake_oauth_creation.png new file mode 100644 index 0000000000000000000000000000000000000000..9e5d03bb59a0a2290d6b1dca6445c6273d35b46b GIT binary patch literal 81377 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 xUtX&LKD1do$WZac|6GRu|6k(&# literal 0 HcmV?d00001 diff --git a/databricks_cli/unity_catalog/uc_service.py b/databricks_cli/unity_catalog/uc_service.py index cbd2f056..b43ab656 100644 --- a/databricks_cli/unity_catalog/uc_service.py +++ b/databricks_cli/unity_catalog/uc_service.py @@ -143,6 +143,33 @@ def validate_external_location(self, validation_spec, headers=None): return self.client.perform_query('POST', '/unity-catalog/validate-storage-credentials', data=validation_spec, headers=headers) + + # Connection Operations + + def create_connection(self, con_spec, headers=None): + url = '/unity-catalog/connections' + return self.client.perform_query('POST', url, data=con_spec, headers=headers) + + def list_connections(self, headers=None): + _data = {} + return self.client.perform_query('GET', '/unity-catalog/connections', data=_data, + headers=headers) + + def get_connection(self, name, headers=None): + _data = {} + return self.client.perform_query('GET', '/unity-catalog/connections/%s' % (name), + data=_data, headers=headers) + + def update_connection(self, name, con_spec, headers=None): + _data = con_spec + return self.client.perform_query('PATCH', '/unity-catalog/connections/%s' % (name), + data=_data, headers=headers) + + def delete_connection(self, name, headers=None): + _data = {} + return self.client.perform_query('DELETE', '/unity-catalog/connections/%s' % (name), + data=_data, headers=headers) + # Data Access Configuration Operations def create_dac(self, metastore_id, dac_spec, skip_validation, headers=None): diff --git a/tests/unity_catalog/test_con_cli.py b/tests/unity_catalog/test_con_cli.py new file mode 100644 index 00000000..ad2fb26f --- /dev/null +++ b/tests/unity_catalog/test_con_cli.py @@ -0,0 +1,111 @@ +# Databricks CLI +# Copyright 2017 Databricks, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"), except +# that the use of services to which certain application programming +# interfaces (each, an "API") connect requires that the user first obtain +# a license for the use of the APIs from Databricks, Inc. ("Databricks"), +# by creating an account at www.databricks.com and agreeing to either (a) +# the Community Edition Terms of Service, (b) the Databricks Terms of +# Service, or (c) another written agreement between Licensee and Databricks +# for the use of the APIs. +# +# 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. + +# pylint:disable=redefined-outer-name + +import json +import mock +import pytest +from click.testing import CliRunner + +from databricks_cli.unity_catalog import connection_cli +from tests.utils import provide_conf + +CONNECTION_NAME = 'test_connection_name' +COMMENT = 'some_comment' + +TESTHOST = "test_postgresql.fakedb.com" +TESTPORT = "1234" +TEST_OPTIONS = { + "host": TESTHOST, + "port": TESTPORT, + "user": "user123", + "password": "password123" +} + +COMPLETE_OPTIONS = { + 'name': CONNECTION_NAME, + 'connection_type': 'MYSQL', + 'options': TEST_OPTIONS, + 'read_only': True, + 'comment': COMMENT, +} + +RETURN_OPTIONS = { + 'name': CONNECTION_NAME, + 'connection_type': 1, + 'options': {"host": TESTHOST, "port": TESTPORT,}, + 'read_only': True, + 'comment': COMMENT, +} + + + +CONNECTION_TYPES = ['mysql'] + +# CONNECTION_TYPES = ['mysql', 'postresql', 'snowflake', 'redshift', +# 'sqldw', 'sqlserver', 'databricks', 'online-catalog'] + +@pytest.fixture() +def api_mock(): + with mock.patch( + 'databricks_cli.unity_catalog.connection_cli.UnityCatalogApi') as uc_api_mock: + _connection_api_mock = mock.MagicMock() + uc_api_mock.return_value = _connection_api_mock + yield _connection_api_mock + + +@pytest.fixture() +def echo_mock(): + with mock.patch('databricks_cli.unity_catalog.connection_cli.click.echo') as echo_mock: + yield echo_mock + + +@provide_conf +def test_create_connection_cli(api_mock): + for con_type in CONNECTION_TYPES: + api_mock.create_connection.return_value = RETURN_OPTIONS + runner = CliRunner() + runner.invoke( + getattr(connection_cli, 'create_{0}_cli'.format(con_type)), + args=[ + '--name', CONNECTION_NAME, + '--host', TEST_OPTIONS['host'], + '--port', TEST_OPTIONS['port'], + '--user', TEST_OPTIONS['user'], + '--read-only', + '--comment', COMMENT, + ], input='{0}\n{0}\n'.format(TEST_OPTIONS['password'])) + api_mock.create_connection.assert_called_once_with(COMPLETE_OPTIONS) + + +@provide_conf +def test_create_connection_cli_json(api_mock): + api_mock.create_connection.return_value = RETURN_OPTIONS + runner = CliRunner() + runner.invoke( + connection_cli.create_json, + args=[ + '--json', json.dumps(COMPLETE_OPTIONS) + ]) + api_mock.create_connection.assert_called_once_with(COMPLETE_OPTIONS)