diff --git a/Algorithmia/CLI.py b/Algorithmia/CLI.py index 3368915..3acc6ae 100644 --- a/Algorithmia/CLI.py +++ b/Algorithmia/CLI.py @@ -2,7 +2,6 @@ import os from Algorithmia.errors import DataApiError from Algorithmia.algo_response import AlgoResponse -from Algorithmia.util import md5_for_file, md5_for_str import json, re, requests, six import toml import shutil @@ -12,8 +11,7 @@ class CLI: def __init__(self): self.client = Algorithmia.client() # algo auth - - def auth(self, apikey, apiaddress, cacert="", profile="default"): + def auth(self, apiaddress, apikey="", cacert="", profile="default", bearer=""): # store api key in local config file and read from it each time a client needs to be created key = self.getconfigfile() @@ -24,20 +22,17 @@ def auth(self, apikey, apiaddress, cacert="", profile="default"): config['profiles'][profile]['api_key'] = apikey config['profiles'][profile]['api_server'] = apiaddress config['profiles'][profile]['ca_cert'] = cacert + config['profiles'][profile]['bearer_token'] = bearer else: - config['profiles'][profile] = {'api_key': apikey, 'api_server': apiaddress, 'ca_cert': cacert} + config['profiles'][profile] = {'api_key':apikey,'api_server':apiaddress,'ca_cert':cacert,'bearer_token':bearer} else: - config['profiles'] = {profile: {'api_key': apikey, 'api_server': apiaddress, 'ca_cert': cacert}} + config['profiles'] = {profile:{'api_key':apikey,'api_server':apiaddress,'ca_cert':cacert,'bearer_token':bearer }} with open(key, "w") as key: - toml.dump(config, key) - client = Algorithmia.client( - api_key=self.getAPIkey(profile), - api_address=self.getAPIaddress(profile), - ca_cert=self.getCert(profile) - ) - self.ls(path=None, client=client) + toml.dump(config,key) + + self.ls(path = None,client = CLI().getClient(profile)) # algo run run the the specified algo def runalgo(self, options, client): @@ -248,28 +243,7 @@ def cat(self, path, client): # algo freeze def freezeAlgo(self, client, manifest_path="model_manifest.json"): - if os.path.exists(manifest_path): - with open(manifest_path, 'r') as f: - manifest_file = json.load(f) - manifest_file['timestamp'] = str(time()) - required_files = manifest_file['required_files'] - optional_files = manifest_file['optional_files'] - for i in range(len(required_files)): - uri = required_files[i]['source_uri'] - local_file = client.file(uri).getFile(as_path=True) - md5_checksum = md5_for_file(local_file) - required_files[i]['md5_checksum'] = md5_checksum - for i in range(len(optional_files)): - uri = required_files[i]['source_uri'] - local_file = client.file(uri).getFile(as_path=True) - md5_checksum = md5_for_file(local_file) - required_files[i]['md5_checksum'] = md5_checksum - lock_md5_checksum = md5_for_str(str(manifest_file)) - manifest_file['lock_checksum'] = lock_md5_checksum - with open('model_manifest.json.freeze', 'w') as f: - json.dump(manifest_file, f) - else: - print("Expected to find a model_manifest.json file, none was discovered in working directory") + client.freeze(manifest_path) # algo cp def cp(self, src, dest, client): @@ -366,6 +340,7 @@ def getconfigfile(self): file.write("api_key = ''\n") file.write("api_server = ''\n") file.write("ca_cert = ''\n") + file.write("bearer_token = ''\n") key = keyPath + keyFile @@ -383,6 +358,16 @@ def getAPIkey(self, profile): return config_dict['profiles'][profile]['api_key'] else: return None + + def getBearerToken(self,profile): + key = self.getconfigfile() + config_dict = toml.load(key) + if 'profiles' in config_dict and profile in config_dict['profiles'] and \ + config_dict['profiles'][profile]['bearer_token'] != "": + return config_dict['profiles'][profile]['bearer_token'] + else: + return None + def getAPIaddress(self, profile): key = self.getconfigfile() @@ -401,3 +386,14 @@ def getCert(self, profile): return config_dict['profiles'][profile]['ca_cert'] else: return None + + def getClient(self,profile): + apiAddress = self.getAPIaddress(profile) + apiKey = self.getAPIkey(profile) + caCert = self.getCert(profile) + bearer = None + + if apiKey is None: + bearer = self.getBearerToken(profile) + + return Algorithmia.client(api_key=apiKey,api_address=apiAddress,ca_cert=caCert,bearer_token = bearer) diff --git a/Algorithmia/__init__.py b/Algorithmia/__init__.py index 05ed6dc..38e7ed6 100644 --- a/Algorithmia/__init__.py +++ b/Algorithmia/__init__.py @@ -23,8 +23,8 @@ def file(dataUrl): def dir(dataUrl): return getDefaultClient().dir(dataUrl) -def client(api_key=None, api_address=None, ca_cert=None): - return Client(api_key, api_address, ca_cert) +def client(api_key=None, api_address=None, ca_cert=None, bearer_token=None): + return Client(api_key, api_address, ca_cert, bearer_token) def handler(apply_func, load_func=lambda: None): return Handler(apply_func, load_func) diff --git a/Algorithmia/__main__.py b/Algorithmia/__main__.py index 9e67c5c..1b5f7b5 100644 --- a/Algorithmia/__main__.py +++ b/Algorithmia/__main__.py @@ -6,6 +6,7 @@ import six from Algorithmia.CLI import CLI import argparse +import re #bind input to raw input try: @@ -145,27 +146,26 @@ def main(): APIkey = input("enter API key: ") CACert = input('(optional) enter path to custom CA certificate: ') - if len(APIkey) == 28 and APIkey.startswith("sim"): - if APIaddress == "" or not APIaddress.startswith("https://api./"): - APIaddress = "https://api.algorithmia.com/" - - CLI().auth(apikey=APIkey, apiaddress=APIaddress, cacert=CACert, profile=args.profile) + if APIaddress == "" or not APIaddress.startswith("https://api./"): + print("invalid API address") else: - print("invalid api key") - + if len(APIkey) == 28 and APIkey.startswith("sim"): + CLI().auth(apikey=APIkey, apiaddress=APIaddress, cacert=CACert, profile=args.profile) + else: + jwt = re.compile(r"^([a-zA-Z0-9_=]+)\.([a-zA-Z0-9_=]+)\.([a-zA-Z0-9_\-\+\/=]*)") + Bearer = input("enter JWT token: ") + if jwt.match(Bearer): + CLI().auth(apikey=APIkey, bearer=Bearer, apiaddress=APIaddress, cacert=CACert, profile=args.profile) + else: + print("invalid authentication") + + + if args.cmd == 'help': parser.parse_args(['-h']) #create a client with the appropreate api address and key - client = Algorithmia.client() - if len(CLI().getAPIaddress(args.profile)) > 1: - client = Algorithmia.client(CLI().getAPIkey(args.profile), CLI().getAPIaddress(args.profile)) - elif len(CLI().getAPIaddress(args.profile)) > 1 and len(CLI().getCert(args.profile)) > 1: - client = Algorithmia.client(CLI().getAPIkey(args.profile), CLI().getAPIaddress(args.profile),CLI().getCert(args.profile)) - elif len(CLI().getAPIaddress(args.profile)) < 1 and len(CLI().getCert(args.profile)) > 1: - client = Algorithmia.client(CLI().getAPIkey(args.profile), CLI().getAPIaddress(args.profile),CLI().getCert(args.profile)) - else: - client = Algorithmia.client(CLI().getAPIkey(args.profile)) + client = CLI().getClient(args.profile) if args.cmd == 'run': diff --git a/Algorithmia/algorithm.py b/Algorithmia/algorithm.py index 85a6f85..378e1c0 100644 --- a/Algorithmia/algorithm.py +++ b/Algorithmia/algorithm.py @@ -8,10 +8,12 @@ from Algorithmia.errors import ApiError, ApiInternalError, raiseAlgoApiError from enum import Enum from algorithmia_api_client.rest import ApiException -from algorithmia_api_client import CreateRequest, UpdateRequest, VersionRequest, Details, Settings, SettingsMandatory, SettingsPublish, \ +from algorithmia_api_client import CreateRequest, UpdateRequest, VersionRequest, Details, Settings, SettingsMandatory, \ + SettingsPublish, \ CreateRequestVersionInfo, VersionInfo, VersionInfoPublish -OutputType = Enum('OutputType','default raw void') +OutputType = Enum('OutputType', 'default raw void') + class Algorithm(object): def __init__(self, client, algoRef): @@ -32,7 +34,7 @@ def __init__(self, client, algoRef): raise ValueError('Invalid algorithm URI: ' + algoRef) def set_options(self, timeout=300, stdout=False, output=OutputType.default, **query_parameters): - self.query_parameters = {'timeout':timeout, 'stdout':stdout} + self.query_parameters = {'timeout': timeout, 'stdout': stdout} self.output_type = output self.query_parameters.update(query_parameters) return self @@ -42,7 +44,8 @@ def create(self, details={}, settings={}, version_info={}): detailsObj = Details(**details) settingsObj = SettingsMandatory(**settings) createRequestVersionInfoObj = CreateRequestVersionInfo(**version_info) - create_parameters = {"name": self.algoname, "details": detailsObj, "settings": settingsObj, "version_info": createRequestVersionInfoObj} + create_parameters = {"name": self.algoname, "details": detailsObj, "settings": settingsObj, + "version_info": createRequestVersionInfoObj} create_request = CreateRequest(**create_parameters) try: # Create Algorithm @@ -57,7 +60,8 @@ def update(self, details={}, settings={}, version_info={}): detailsObj = Details(**details) settingsObj = Settings(**settings) createRequestVersionInfoObj = CreateRequestVersionInfo(**version_info) - update_parameters = {"details": detailsObj, "settings": settingsObj, "version_info": createRequestVersionInfoObj} + update_parameters = {"details": detailsObj, "settings": settingsObj, + "version_info": createRequestVersionInfoObj} update_request = UpdateRequest(**update_parameters) try: # Update Algorithm @@ -70,9 +74,10 @@ def update(self, details={}, settings={}, version_info={}): # Publish an algorithm def publish(self, details={}, settings={}, version_info={}): publish_parameters = {"details": details, "settings": settings, "version_info": version_info} - url = "/v1/algorithms/"+self.username+"/"+self.algoname + "/versions" + url = "/v1/algorithms/" + self.username + "/" + self.algoname + "/versions" print(publish_parameters) - api_response = self.client.postJsonHelper(url, publish_parameters, parse_response_as_json=True, **self.query_parameters) + api_response = self.client.postJsonHelper(url, publish_parameters, parse_response_as_json=True, + **self.query_parameters) return api_response # except ApiException as e: # error_message = json.loads(e.body) @@ -81,7 +86,8 @@ def publish(self, details={}, settings={}, version_info={}): def builds(self, limit=56, marker=None): try: if marker is not None: - api_response = self.client.manageApi.get_algorithm_builds(self.username, self.algoname, limit=limit, marker=marker) + api_response = self.client.manageApi.get_algorithm_builds(self.username, self.algoname, limit=limit, + marker=marker) else: api_response = self.client.manageApi.get_algorithm_builds(self.username, self.algoname, limit=limit) return api_response @@ -109,11 +115,10 @@ def get_build_logs(self, build_id): raise raiseAlgoApiError(error_message) def build_logs(self): - url = '/v1/algorithms/'+self.username+'/'+self.algoname+'/builds' + url = '/v1/algorithms/' + self.username + '/' + self.algoname + '/builds' response = json.loads(self.client.getHelper(url).content.decode('utf-8')) return response - def get_scm_status(self): try: api_response = self.client.manageApi.get_algorithm_scm_connection_status(self.username, self.algoname) @@ -157,7 +162,6 @@ def versions(self, limit=None, marker=None, published=None, callable=None): error_message = json.loads(e.body) raise raiseAlgoApiError(error_message) - # Compile an algorithm def compile(self): try: @@ -176,25 +180,26 @@ def pipe(self, input1): elif self.output_type == OutputType.void: return self._postVoidOutput(input1) else: - return AlgoResponse.create_algo_response(self.client.postJsonHelper(self.url, input1, **self.query_parameters)) + return AlgoResponse.create_algo_response( + self.client.postJsonHelper(self.url, input1, **self.query_parameters)) def _postRawOutput(self, input1): - # Don't parse response as json - self.query_parameters['output'] = 'raw' - response = self.client.postJsonHelper(self.url, input1, parse_response_as_json=False, **self.query_parameters) - # Check HTTP code and throw error as needed - if response.status_code == 400: - # Bad request - raise ApiError(response.text) - elif response.status_code == 500: - raise ApiInternalError(response.text) - else: - return response.text + # Don't parse response as json + self.query_parameters['output'] = 'raw' + response = self.client.postJsonHelper(self.url, input1, parse_response_as_json=False, **self.query_parameters) + # Check HTTP code and throw error as needed + if response.status_code == 400: + # Bad request + raise ApiError(response.text) + elif response.status_code == 500: + raise ApiInternalError(response.text) + else: + return response.text def _postVoidOutput(self, input1): - self.query_parameters['output'] = 'void' - responseJson = self.client.postJsonHelper(self.url, input1, **self.query_parameters) - if 'error' in responseJson: - raise ApiError(responseJson['error']['message']) - else: - return AsyncResponse(responseJson) + self.query_parameters['output'] = 'void' + responseJson = self.client.postJsonHelper(self.url, input1, **self.query_parameters) + if 'error' in responseJson: + raise ApiError(responseJson['error']['message']) + else: + return AsyncResponse(responseJson) diff --git a/Algorithmia/client.py b/Algorithmia/client.py index ccea0ca..7247376 100644 --- a/Algorithmia/client.py +++ b/Algorithmia/client.py @@ -6,12 +6,13 @@ from Algorithmia.datafile import DataFile, LocalDataFile, AdvancedDataFile from Algorithmia.datadirectory import DataDirectory, LocalDataDirectory, AdvancedDataDirectory from algorithmia_api_client import Configuration, DefaultApi, ApiClient - +from Algorithmia.util import md5_for_file, md5_for_str from tempfile import mkstemp import atexit import json, re, requests, six, certifi import tarfile import os +from time import time class Client(object): @@ -21,13 +22,20 @@ class Client(object): apiKey = None apiAddress = None requestSession = None + bearerToken = None + - def __init__(self, apiKey=None, apiAddress=None, caCert=None): + def __init__(self, apiKey = None, apiAddress = None, caCert = None, bearerToken=None): # Override apiKey with environment variable config = None self.requestSession = requests.Session() if apiKey is None and 'ALGORITHMIA_API_KEY' in os.environ: apiKey = os.environ['ALGORITHMIA_API_KEY'] + if apiKey is None: + if bearerToken is None and 'ALGORITHMIA_BEARER_TOKEN' in os.environ: + bearerToken = os.environ['ALGORITHMIA_BEARER_TOKEN'] + self.bearerToken = bearerToken + self.apiKey = apiKey if apiAddress is not None: self.apiAddress = apiAddress @@ -217,6 +225,8 @@ def postJsonHelper(self, url, input_object, parse_response_as_json=True, **query headers = {} if self.apiKey is not None: headers['Authorization'] = self.apiKey + else: + headers['Authorization'] = "Bearer "+ self.bearerToken input_json = None if input_object is None: @@ -244,18 +254,24 @@ def getHelper(self, url, **query_parameters): headers = {} if self.apiKey is not None: headers['Authorization'] = self.apiKey + else: + headers['Authorization'] = 'Bearer '+ self.bearerToken return self.requestSession.get(self.apiAddress + url, headers=headers, params=query_parameters) def getStreamHelper(self, url, **query_parameters): headers = {} if self.apiKey is not None: headers['Authorization'] = self.apiKey + else: + headers['Authorization'] = 'Bearer '+ self.bearerToken return self.requestSession.get(self.apiAddress + url, headers=headers, params=query_parameters, stream=True) def patchHelper(self, url, params): headers = {'content-type': 'application/json'} if self.apiKey is not None: headers['Authorization'] = self.apiKey + else: + headers['Authorization'] = 'Bearer '+ self.bearerToken return self.requestSession.patch(self.apiAddress + url, headers=headers, data=json.dumps(params)) # Used internally to get http head result @@ -263,6 +279,8 @@ def headHelper(self, url): headers = {} if self.apiKey is not None: headers['Authorization'] = self.apiKey + else: + headers['Authorization'] = 'Bearer '+ self.bearerToken return self.requestSession.head(self.apiAddress + url, headers=headers) # Used internally to http put a file @@ -270,6 +288,8 @@ def putHelper(self, url, data): headers = {} if self.apiKey is not None: headers['Authorization'] = self.apiKey + else: + headers['Authorization'] = 'Bearer '+ self.bearerToken if isJson(data): headers['Content-Type'] = 'application/json' @@ -283,6 +303,8 @@ def deleteHelper(self, url): headers = {} if self.apiKey is not None: headers['Authorization'] = self.apiKey + else: + headers['Authorization'] = 'Bearer '+ self.bearerToken response = self.requestSession.delete(self.apiAddress + url, headers=headers) if response.reason == "No Content": return response @@ -322,6 +344,30 @@ def exit_handler(self): except OSError as e: print(e) + # Used by CI/CD automation for freezing model manifest files, and by the CLI for manual freezing + def freeze(self, manifest_path, manifest_output_dir="."): + if os.path.exists(manifest_path): + with open(manifest_path, 'r') as f: + manifest_file = json.load(f) + manifest_file['timestamp'] = str(time()) + required_files = manifest_file['required_files'] + optional_files = manifest_file['optional_files'] + for i in range(len(required_files)): + uri = required_files[i]['source_uri'] + local_file = self.file(uri).getFile(as_path=True) + md5_checksum = md5_for_file(local_file) + required_files[i]['md5_checksum'] = md5_checksum + for i in range(len(optional_files)): + uri = required_files[i]['source_uri'] + local_file = self.file(uri).getFile(as_path=True) + md5_checksum = md5_for_file(local_file) + required_files[i]['md5_checksum'] = md5_checksum + lock_md5_checksum = md5_for_str(str(manifest_file)) + manifest_file['lock_checksum'] = lock_md5_checksum + with open(manifest_output_dir+'/'+'model_manifest.json.freeze', 'w') as f: + json.dump(manifest_file, f) + else: + print("Expected to find a model_manifest.json file, none was discovered in working directory") def isJson(myjson): try: diff --git a/README.md b/README.md index a7f3214..59a80dc 100644 --- a/README.md +++ b/README.md @@ -72,9 +72,9 @@ If the algorithm output is text, then the `result` field of the response will be ```python algo = client.algo('demo/Hello/0.1.1') response = algo.pipe("HAL 9000") -print response.result # Hello, world! -print response.metadata # Metadata(content_type='text',duration=0.0002127) -print response.metadata.duration # 0.0002127 +print(response.result) # Hello, world! +print(response.metadata) # Metadata(content_type='text',duration=0.0002127) +print(response.metadata.duration) # 0.0002127 ``` ### JSON input/output @@ -119,7 +119,7 @@ This includes support for changing the timeout or indicating that the API should ```python from Algorithmia.algorithm import OutputType response = client.algo('util/echo').set_options(timeout=60, stdout=False) -print response.metadata.stdout +print(response.metadata.stdout) ``` Note: `stdout=True` is only supported if you have access to the algorithm source. @@ -186,15 +186,15 @@ foo = client.dir("data://.my/foo") # List files in "foo" for file in foo.files(): - print file.path + " at URL: " + file.url + " last modified " + file.last_modified + print(file.path + " at URL: " + file.url + " last modified " + file.last_modified) # List directories in "foo" for file in foo.dirs(): - print dir.path + " at URL: " + file.url + print(dir.path + " at URL: " + file.url) # List everything in "foo" for entry in foo.list(): - print entry.path + " at URL: " + entry.url + print(entry.path + " at URL: " + entry.url) ``` ### Manage directory permissions @@ -230,7 +230,7 @@ $ algo auth Configuring authentication for profile: 'default' Enter API Endpoint [https://api.algorithmia.com]: Enter API Key: -(optional) enter path to custom CA certificate: +(optional) enter path to custom CA certificate: Profile is ready to use. Test with 'algo ls' ``` @@ -332,7 +332,7 @@ algo auth --profile second_user Configuring authentication for profile: 'second_user' Enter API Endpoint [https://api.algorithmia.com]: Enter API Key: -(optional) enter path to custom CA certificate: +(optional) enter path to custom CA certificate: ``` Now you may use `algo ls --profile second_user` to list files in your `second_user` account. For more information, see the auth command help with `algo auth --help`. @@ -342,7 +342,7 @@ Now you may use `algo ls --profile second_user` to list files in your `second_us When running commands, the Algorithmia CLI will use the default profile unless otherwise specified with the `--profile ` option. See the following example: ```text -$ algo run kenny/factor -d 17 --profile second_user +$ algo run kenny/factor -d 17 --profile second_user [17] ``` diff --git a/Test/CLI_test.py b/Test/CLI_test.py index ae1d546..99f662e 100644 --- a/Test/CLI_test.py +++ b/Test/CLI_test.py @@ -17,6 +17,7 @@ class CLIDummyTest(unittest.TestCase): @classmethod def setUpClass(cls): cls.client = Algorithmia.client(api_address="http://localhost:8080/", api_key="simabcd123") + cls.bearerClient = Algorithmia.client(api_address="http://localhost:8080/", bearer_token="simabcd123.token.token") def test_run(self): name = "util/Echo" @@ -52,6 +53,40 @@ def test_run(self): result = CLI().runalgo(args, self.client) self.assertEqual(result, inputs) + def test_run_token(self): + name = "util/Echo" + inputs = "test" + + parser = argparse.ArgumentParser('CLI for interacting with Algorithmia') + + subparsers = parser.add_subparsers(help='sub cmd', dest='subparser_name') + parser_run = subparsers.add_parser('run', help='algo run [input options] [output options]') + + parser_run.add_argument('algo') + parser_run.add_argument('-d', '--data', action='https://github.com/store', help='detect input type', default=None) + parser_run.add_argument('-t', '--text', action='https://github.com/store', help='treat input as text', default=None) + parser_run.add_argument('-j', '--json', action='https://github.com/store', help='treat input as json data', default=None) + parser_run.add_argument('-b', '--binary', action='https://github.com/store', help='treat input as binary data', default=None) + parser_run.add_argument('-D', '--data-file', action='https://github.com/store', help='specify a path to an input file', + default=None) + parser_run.add_argument('-T', '--text-file', action='https://github.com/store', help='specify a path to a text file', + default=None) + parser_run.add_argument('-J', '--json-file', action='https://github.com/store', help='specify a path to a json file', + default=None) + parser_run.add_argument('-B', '--binary-file', action='https://github.com/store', help='specify a path to a binary file', + default=None) + parser_run.add_argument('--timeout', action='https://github.com/store', type=int, default=300, + help='specify a timeout (seconds)') + parser_run.add_argument('--debug', action='https://github.com/store_true', + help='print the stdout from the algo ') + parser_run.add_argument('--profile', action='https://github.com/store', type=str, default='default') + parser_run.add_argument('-o', '--output', action='https://github.com/store', default=None, type=str) + + args = parser.parse_args(['run', name, '-d', inputs]) + + result = CLI().runalgo(args, self.bearerClient) + self.assertEqual(result, inputs) + class CLIMainTest(unittest.TestCase): def setUp(self): @@ -156,7 +191,7 @@ def test_auth(self): key = os.getenv('ALGORITHMIA_API_KEY') address = 'https://api.algorithmia.com/' profile = 'default' - CLI().auth(key, address, profile=profile) + CLI().auth(address, key, profile=profile) resultK = CLI().getAPIkey(profile) resultA = CLI().getAPIaddress(profile) self.assertEqual(resultK, key) @@ -176,13 +211,24 @@ def test_auth_cert(self): cacert = localfile profile = 'test' - CLI().auth(key, address, cacert, profile) + CLI().auth(address, key, cacert=cacert, profile=profile) resultK = CLI().getAPIkey(profile) resultA = CLI().getAPIaddress(profile) resultC = CLI().getCert(profile) self.assertEqual(resultK, key) self.assertEqual(resultA, address) self.assertEqual(resultC, cacert) + + def test_auth_token(self): + address = 'https://api.algorithmia.com/' + bearer = 'testtokenabcd' + profile = 'test' + + CLI().auth(apiaddress=address, bearer=bearer, profile=profile) + resultA = CLI().getAPIaddress(profile) + resultT = CLI().getBearerToken(profile) + self.assertEqual(resultA, address) + self.assertEqual(resultT, bearer) def test_get_environment(self): result = CLI().get_environment_by_language("python2", self.client) @@ -230,7 +276,7 @@ def test_get_template(self): def test_api_address_auth(self): api_key = os.getenv('ALGORITHMIA_TEST_API_KEY') api_address = "https://api.test.algorithmia.com/" - CLI().auth(api_key, api_address) + CLI().auth(api_address, api_key) profile = "default" client = Algorithmia.client(CLI().getAPIkey(profile), CLI().getAPIaddress(profile), CLI().getCert(profile)) diff --git a/Test/client_test.py b/Test/client_test.py index a254c45..0519266 100644 --- a/Test/client_test.py +++ b/Test/client_test.py @@ -397,6 +397,10 @@ def test_algorithm_programmatic_create_process(self): self.assertEqual(response.version_info.semantic_version, "0.1.0", "information is incorrect") + def test_algo_freeze(self): + self.regular_client.freeze("Test/resources/manifests/example_manifest.json", "Test/resources/manifests") + + if __name__ == '__main__': unittest.main() diff --git a/Test/resources/manifests/example_manifest.json b/Test/resources/manifests/example_manifest.json new file mode 100644 index 0000000..ba6cbf5 --- /dev/null +++ b/Test/resources/manifests/example_manifest.json @@ -0,0 +1,29 @@ +{ + "required_files" : [ + { "name": "squeezenet", + "source_uri": "data://AlgorithmiaSE/image_cassification_demo/squeezenet1_1-f364aa15.pth", + "fail_on_tamper": true, + "metadata": { + "dataset_md5_checksum": "46a44d32d2c5c07f7f66324bef4c7266" + } + }, + { + "name": "labels", + "source_uri": "data://AlgorithmiaSE/image_cassification_demo/imagenet_class_index.json", + "fail_on_tamper": true, + "metadata": { + "dataset_md5_checksum": "46a44d32d2c5c07f7f66324bef4c7266" + } + } + ], + "optional_files": [ + { + "name": "mobilenet", + "source_uri": "data://AlgorithmiaSE/image_cassification_demo/mobilenet_v2-b0353104.pth", + "fail_on_tamper": false, + "metadata": { + "dataset_md5_checksum": "46a44d32d2c5c07f7f66324bef4c7266" + } + } + ] +} \ No newline at end of file