gigantum
Advanced tools
| from urllib.parse import urljoin, urlparse | ||
| import os | ||
| import json | ||
| import glob | ||
| import requests | ||
| from gigantumcli.utilities import ExitCLI | ||
| class ServerConfig: | ||
| def __init__(self, working_dir): | ||
| self.working_dir = os.path.expanduser(working_dir) | ||
| self.servers_dir = os.path.join(self.working_dir, '.labmanager', 'servers') | ||
| @staticmethod | ||
| def _discover_server(url: str): | ||
| """Method to load the server's discovery data | ||
| Args: | ||
| url(str): URL/domain to the server's root (users may not be precise here, so we'll try to be smart) | ||
| Returns: | ||
| dict: discovery data returned from the server | ||
| """ | ||
| url_parts = urlparse(url) | ||
| if not url_parts.scheme: | ||
| url_parts = urlparse("https://" + url) | ||
| team_url = urljoin("https://" + url_parts.netloc, 'gigantum/.well-known/discover.json') | ||
| enterprise_url = urljoin("https://" + url_parts.netloc, '.well-known/discover.json') | ||
| try: | ||
| response = requests.get(team_url) | ||
| except requests.exceptions.ConnectionError: | ||
| raise ExitCLI("Failed to discover configuration for server located at" | ||
| " {}. Check server URL and try again.".format(url)) | ||
| data = None | ||
| if response.status_code == 200: | ||
| try: | ||
| # If a 200, make sure you get valid JSON back in case you were routed to some other 200 response. | ||
| data = response.json() | ||
| except json.JSONDecodeError: | ||
| pass | ||
| if not data: | ||
| response = requests.get(enterprise_url) | ||
| if response.status_code == 200: | ||
| try: | ||
| # If a 200, make sure you get valid JSON back in case you were routed to some other 200 response. | ||
| data = response.json() | ||
| except json.JSONDecodeError: | ||
| pass | ||
| if not data: | ||
| raise ExitCLI("Failed to discover configuration for server located at" | ||
| " {} ({}). Check server URL and try again.".format(url, response.status_code)) | ||
| return data | ||
| def add_server(self, url): | ||
| """Method to discover a server's configuration and add it to the local configured servers | ||
| Args: | ||
| url: URL/domain to the server's root (users may not be precise here, so we'll try to be smart) | ||
| Returns: | ||
| str: id for the server | ||
| """ | ||
| server_data = self._discover_server(url) | ||
| # Ensure core URLS have trailing slashes to standardize within codebase | ||
| server_data['git_url'] = server_data['git_url'] if server_data['git_url'][-1] == '/' \ | ||
| else server_data['git_url'] + '/' | ||
| server_data['hub_api_url'] = server_data['hub_api_url'] if server_data['hub_api_url'][-1] == '/' \ | ||
| else server_data['hub_api_url'] + '/' | ||
| server_data['object_service_url'] = server_data['object_service_url'] \ | ||
| if server_data['object_service_url'][-1] == '/' \ | ||
| else server_data['object_service_url'] + '/' | ||
| # Verify Server is not already configured | ||
| server_data_file = os.path.join(self.servers_dir, server_data['id'] + ".json") | ||
| if os.path.exists(server_data_file): | ||
| raise ValueError("The server `{}` located at {} is already configured.".format(server_data['name'], url)) | ||
| # Fetch Auth configuration | ||
| response = requests.get(server_data['auth_config_url']) | ||
| if response.status_code != 200: | ||
| raise ExitCLI("Failed to load auth configuration " | ||
| "for server located at {}: {}".format(url, response.status_code)) | ||
| auth_data = response.json() | ||
| # Create servers dir if it is missing (maybe this user has never started the client) | ||
| if not os.path.isdir(self.servers_dir): | ||
| os.makedirs(self.servers_dir, exist_ok=True) | ||
| # Save configuration data | ||
| save_data = {"server": server_data, | ||
| "auth": auth_data} | ||
| with open(server_data_file, 'wt') as f: | ||
| json.dump(save_data, f, indent=2) | ||
| # Create directory for server's projects/datasets | ||
| user_storage_dir = os.path.join(self.working_dir, 'servers', server_data['id']) | ||
| os.makedirs(user_storage_dir, exist_ok=True) | ||
| return server_data['id'] | ||
| def list_servers(self, should_print: bool = False): | ||
| """Method to list configured servers, optionally printing a table | ||
| Args: | ||
| should_print(bool): flag indicating if the results should be printed | ||
| Returns: | ||
| list | ||
| """ | ||
| configured_servers = list() | ||
| for server_file in glob.glob(os.path.join(self.servers_dir, "*.json")): | ||
| with open(server_file, 'rt') as f: | ||
| data = json.load(f) | ||
| hub_url_parts = urlparse(data['server']['hub_api_url']) | ||
| server_url = hub_url_parts.scheme + "://" + hub_url_parts.netloc | ||
| configured_servers.append((data['server']['id'], data['server']['name'], server_url)) | ||
| if should_print: | ||
| print('%-30s' % "Server ID", '%-30s' % "Server Name", '%-30s' % "Server Location") | ||
| for server in configured_servers: | ||
| print('%-30s' % server[0], '%-30s' % server[1], '%-30s' % server[2]) | ||
| print("\n") | ||
| return configured_servers |
| import pytest | ||
| import tempfile | ||
| import uuid | ||
| import os | ||
| import shutil | ||
| import responses | ||
| from gigantumcli.server import ServerConfig | ||
| from gigantumcli.utilities import ExitCLI | ||
| @pytest.fixture | ||
| def server_config(): | ||
| """Fixture to create a Build instance with a test image name that does not exist and cleanup after""" | ||
| unit_test_working_dir = os.path.join(tempfile.gettempdir(), uuid.uuid4().hex) | ||
| os.mkdir(unit_test_working_dir) | ||
| yield ServerConfig(working_dir=unit_test_working_dir) | ||
| shutil.rmtree(unit_test_working_dir) | ||
| class TestServerConfig(object): | ||
| @responses.activate | ||
| def test_server_discovery_fails(self, server_config): | ||
| responses.add(responses.GET, 'https://test2.gigantum.com/gigantum/.well-known/discover.json', | ||
| json={}, | ||
| status=404) | ||
| responses.add(responses.GET, 'https://test2.gigantum.com/.well-known/discover.json', | ||
| json={}, | ||
| status=404) | ||
| with pytest.raises(ExitCLI): | ||
| server_config.add_server("test2.gigantum.com") | ||
| @responses.activate | ||
| def test_auth_discovery_fails(self, server_config): | ||
| responses.add(responses.GET, 'https://test2.gigantum.com/gigantum/.well-known/discover.json', | ||
| json={}, | ||
| status=404) | ||
| responses.add(responses.GET, 'https://test2.gigantum.com/.well-known/discover.json', | ||
| json={"id": 'another-server', | ||
| "name": "Another server", | ||
| "git_url": "https://test2.repo.gigantum.com/", | ||
| "git_server_type": "gitlab", | ||
| "hub_api_url": "https://test2.gigantum.com/api/v1/", | ||
| "object_service_url": "https://test2.api.gigantum.com/object-v1/", | ||
| "user_search_url": "https://user-search2.us-east-1.cloudsearch.amazonaws.com", | ||
| "lfs_enabled": True, | ||
| "auth_config_url": "https://test2.gigantum.com/.well-known/auth.json"}, | ||
| status=200) | ||
| responses.add(responses.GET, 'https://test2.gigantum.com/.well-known/auth.json', | ||
| json={}, | ||
| status=404) | ||
| with pytest.raises(ExitCLI): | ||
| server_config.add_server("https://test2.gigantum.com/") | ||
| with pytest.raises(ExitCLI): | ||
| server_config.add_server("https://thiswillneverwork.gigantum.com/") | ||
| @responses.activate | ||
| def test_add_server(self, server_config): | ||
| responses.add(responses.GET, 'https://test2.gigantum.com/gigantum/.well-known/discover.json', | ||
| json={"id": 'another-server', | ||
| "name": "Another server", | ||
| "git_url": "https://test2.repo.gigantum.com/", | ||
| "git_server_type": "gitlab", | ||
| "hub_api_url": "https://test2.gigantum.com/api/v1/", | ||
| "object_service_url": "https://test2.api.gigantum.com/object-v1/", | ||
| "user_search_url": "https://user-search2.us-east-1.cloudsearch.amazonaws.com", | ||
| "lfs_enabled": True, | ||
| "auth_config_url": "https://test2.gigantum.com/gigantum/.well-known/auth.json"}, | ||
| status=200) | ||
| responses.add(responses.GET, 'https://test2.gigantum.com/gigantum/.well-known/auth.json', | ||
| json={"audience": "test2.api.gigantum.io", | ||
| "issuer": "https://test2-auth.gigantum.com", | ||
| "signing_algorithm": "RS256", | ||
| "public_key_url": "https://test2-auth.gigantum.com/.well-known/jwks.json", | ||
| "login_url": "https://test2.gigantum.com/client/login", | ||
| "login_type": "auth0", | ||
| "auth0_client_id": "0000000000000000"}, | ||
| status=200) | ||
| server_id = server_config.add_server("https://test2.gigantum.com/") | ||
| assert server_id == 'another-server' | ||
| assert os.path.isfile(os.path.join(server_config.servers_dir, 'another-server.json')) | ||
| assert os.path.isdir(os.path.join(server_config.working_dir, 'servers', 'another-server')) | ||
| @responses.activate | ||
| def test_add_server_already_configured(self, server_config): | ||
| responses.add(responses.GET, 'https://test2.gigantum.com/gigantum/.well-known/discover.json', | ||
| json={"id": 'another-server', | ||
| "name": "Another server", | ||
| "git_url": "https://test2.repo.gigantum.com/", | ||
| "git_server_type": "gitlab", | ||
| "hub_api_url": "https://test2.gigantum.com/api/v1/", | ||
| "object_service_url": "https://test2.api.gigantum.com/object-v1/", | ||
| "user_search_url": "https://user-search2.us-east-1.cloudsearch.amazonaws.com", | ||
| "lfs_enabled": True, | ||
| "auth_config_url": "https://test2.gigantum.com/gigantum/.well-known/auth.json"}, | ||
| status=200) | ||
| responses.add(responses.GET, 'https://test2.gigantum.com/gigantum/.well-known/auth.json', | ||
| json={"audience": "test2.api.gigantum.io", | ||
| "issuer": "https://test2-auth.gigantum.com", | ||
| "signing_algorithm": "RS256", | ||
| "public_key_url": "https://test2-auth.gigantum.com/.well-known/jwks.json", | ||
| "login_url": "https://test2.gigantum.com/client/login", | ||
| "login_type": "auth0", | ||
| "auth0_client_id": "0000000000000000"}, | ||
| status=200) | ||
| responses.add(responses.GET, 'https://test2.gigantum.com/gigantum/.well-known/discover.json', | ||
| json={"id": 'another-server', | ||
| "name": "Another server", | ||
| "git_url": "https://test2.repo.gigantum.com/", | ||
| "git_server_type": "gitlab", | ||
| "hub_api_url": "https://test2.gigantum.com/api/v1/", | ||
| "object_service_url": "https://test2.api.gigantum.com/object-v1/", | ||
| "user_search_url": "https://user-search2.us-east-1.cloudsearch.amazonaws.com", | ||
| "lfs_enabled": True, | ||
| "auth_config_url": "https://test2.gigantum.com/gigantum/.well-known/auth.json"}, | ||
| status=200) | ||
| server_id = server_config.add_server("https://test2.gigantum.com/") | ||
| assert server_id == 'another-server' | ||
| assert os.path.isfile(os.path.join(server_config.servers_dir, 'another-server.json')) | ||
| assert os.path.isdir(os.path.join(server_config.working_dir, 'servers', 'another-server')) | ||
| with pytest.raises(ValueError): | ||
| server_config.add_server("https://test2.gigantum.com/") | ||
| @responses.activate | ||
| def test_list_servers(self, server_config): | ||
| responses.add(responses.GET, 'https://test2.gigantum.com/gigantum/.well-known/discover.json', | ||
| json={"id": 'another-server', | ||
| "name": "Another server", | ||
| "git_url": "https://test2.repo.gigantum.com/", | ||
| "git_server_type": "gitlab", | ||
| "hub_api_url": "https://test2.gigantum.com/api/v1/", | ||
| "object_service_url": "https://test2.api.gigantum.com/object-v1/", | ||
| "user_search_url": "https://user-search2.us-east-1.cloudsearch.amazonaws.com", | ||
| "lfs_enabled": True, | ||
| "auth_config_url": "https://test2.gigantum.com/gigantum/.well-known/auth.json"}, | ||
| status=200) | ||
| responses.add(responses.GET, 'https://test2.gigantum.com/gigantum/.well-known/auth.json', | ||
| json={"audience": "test2.api.gigantum.io", | ||
| "issuer": "https://test2-auth.gigantum.com", | ||
| "signing_algorithm": "RS256", | ||
| "public_key_url": "https://test2-auth.gigantum.com/.well-known/jwks.json", | ||
| "login_url": "https://test2.gigantum.com/client/login", | ||
| "login_type": "auth0", | ||
| "auth0_client_id": "0000000000000000"}, | ||
| status=200) | ||
| responses.add(responses.GET, 'https://test3.gigantum.com/gigantum/.well-known/discover.json', | ||
| json={"id": 'my-server', | ||
| "name": "My Server 1", | ||
| "git_url": "https://test3.repo.gigantum.com/", | ||
| "git_server_type": "gitlab", | ||
| "hub_api_url": "https://test3.gigantum.com/api/v1/", | ||
| "object_service_url": "https://test3.api.gigantum.com/object-v1/", | ||
| "user_search_url": "https://user-search3.us-east-1.cloudsearch.amazonaws.com", | ||
| "lfs_enabled": True, | ||
| "auth_config_url": "https://test3.gigantum.com/gigantum/.well-known/auth.json"}, | ||
| status=200) | ||
| responses.add(responses.GET, 'https://test3.gigantum.com/gigantum/.well-known/auth.json', | ||
| json={"audience": "test3.api.gigantum.io", | ||
| "issuer": "https://test3-auth.gigantum.com", | ||
| "signing_algorithm": "RS256", | ||
| "public_key_url": "https://test3-auth.gigantum.com/.well-known/jwks.json", | ||
| "login_url": "https://test3.gigantum.com/client/login", | ||
| "login_type": "auth0", | ||
| "auth0_client_id": "0000000000000000"}, | ||
| status=200) | ||
| server_id = server_config.add_server("https://test2.gigantum.com/") | ||
| assert server_id == 'another-server' | ||
| assert os.path.isfile(os.path.join(server_config.servers_dir, 'another-server.json')) | ||
| assert os.path.isdir(os.path.join(server_config.working_dir, 'servers', 'another-server')) | ||
| server_id = server_config.add_server("https://test3.gigantum.com/") | ||
| assert server_id == 'my-server' | ||
| assert os.path.isfile(os.path.join(server_config.servers_dir, 'my-server.json')) | ||
| assert os.path.isdir(os.path.join(server_config.working_dir, 'servers', 'my-server')) | ||
| server_list = server_config.list_servers(should_print=True) | ||
| assert len(server_list) == 2 |
| Metadata-Version: 2.1 | ||
| Name: gigantum | ||
| Version: 1.1.3 | ||
| Version: 1.2.0 | ||
| Summary: CLI for the Gigantum Platform | ||
@@ -5,0 +5,0 @@ Home-page: https://github.com/gigantum/gigantum-cli |
@@ -17,2 +17,3 @@ LICENSE | ||
| gigantumcli/dockerinterface.py | ||
| gigantumcli/server.py | ||
| gigantumcli/utilities.py | ||
@@ -22,2 +23,3 @@ gigantumcli/tests/__init__.py | ||
| gigantumcli/tests/test_changelog.py | ||
| gigantumcli/tests/test_cleanup_containers.py | ||
| gigantumcli/tests/test_cleanup_containers.py | ||
| gigantumcli/tests/test_server.py |
| # Gigantum CLI Version | ||
| __version__ = "1.1.3" | ||
| __version__ = "1.2.0" | ||
@@ -15,2 +15,3 @@ import sys | ||
| from gigantumcli.utilities import ask_question, ExitCLI, is_running_as_admin, get_nvidia_driver_version | ||
| from gigantumcli.server import ServerConfig | ||
@@ -355,2 +356,29 @@ # Temporary fix due to docker 2.5.0.0 and docker-py failing when container doesn't exist | ||
| def add_server(working_dir: str = "~/gigantum"): | ||
| """Method to add a server to this Client's configuration | ||
| Args: | ||
| working_dir(str): Working dir for the client | ||
| Returns: | ||
| None | ||
| """ | ||
| print("\n\nEnter the server URL to add (e.g. https://gigantum.mycompany.com): ") | ||
| server_url = input() | ||
| server_config = ServerConfig(working_dir=working_dir) | ||
| server_config.add_server(server_url) | ||
| print("\nServer successfully added. The server will now be available on your Client login page.") | ||
| def list_servers(working_dir: str = "~/gigantum"): | ||
| """Method to list servers this Client is configured to use | ||
| Args: | ||
| working_dir(str): Working dir for the client | ||
| Returns: | ||
| None | ||
| """ | ||
| server_config = ServerConfig(working_dir=working_dir) | ||
| server_config.list_servers(should_print=True) | ||
| def feedback(): | ||
@@ -357,0 +385,0 @@ """Method to throw up a browser to provide feedback |
| import argparse | ||
| from gigantumcli.actions import install, update, start, stop, feedback, ExitCLI | ||
| from gigantumcli.actions import install, update, start, stop, feedback, list_servers, add_server, ExitCLI | ||
| import sys | ||
@@ -12,2 +12,4 @@ | ||
| "stop": "Stop the Client", | ||
| "add-server": "Add a new Team or Enterprise server to this Client installation", | ||
| "list-servers": "List the available servers for this Client installation", | ||
| "feedback": "Open a web page to provide feedback" | ||
@@ -73,2 +75,6 @@ } | ||
| feedback() | ||
| elif args.action == "add-server": | ||
| add_server(working_dir=args.working_dir) | ||
| elif args.action == "list-servers": | ||
| list_servers(working_dir=args.working_dir) | ||
| else: | ||
@@ -75,0 +81,0 @@ raise ValueError("Unsupported action `{}` provided. Available actions: {}".format(args.action, |
| import pytest | ||
| import mock | ||
| from unittest import mock | ||
| import shutil | ||
@@ -4,0 +4,0 @@ import os |
@@ -1,20 +0,1 @@ | ||
| # Copyright (c) 2017 FlashX, LLC | ||
| # | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to deal | ||
| # in the Software without restriction, including without limitation the rights | ||
| # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| # copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # | ||
| # The above copyright notice and this permission notice shall be included in all | ||
| # copies or substantial portions of the Software. | ||
| # | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| # SOFTWARE. | ||
| import pytest | ||
@@ -24,3 +5,3 @@ from pkg_resources import resource_filename | ||
| import json | ||
| from mock import patch | ||
| from unittest.mock import patch | ||
@@ -27,0 +8,0 @@ from gigantumcli.changelog import ChangeLog |
| import pytest | ||
| from pkg_resources import resource_filename | ||
| import os | ||
| import json | ||
| from mock import patch | ||
@@ -7,0 +3,0 @@ from gigantumcli.dockerinterface import DockerInterface |
+1
-1
| Metadata-Version: 2.1 | ||
| Name: gigantum | ||
| Version: 1.1.3 | ||
| Version: 1.2.0 | ||
| Summary: CLI for the Gigantum Platform | ||
@@ -5,0 +5,0 @@ Home-page: https://github.com/gigantum/gigantum-cli |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
100470
19.49%25
8.7%1251
28.31%