class SnapshotCommand(ProjectCommand): def __init__(self, cli_helper): super(SnapshotCommand, self).__init__(cli_helper) def usage(self): self.cli_helper.echo(__("argparser", "cli.snapshot.usage")) def snapshot(self): self.parse(["snapshot", "--help"]) return True @Helper.notify_no_project_found def create(self, **kwargs): self.snapshot_controller = SnapshotController() self.cli_helper.echo(__("info", "cli.snapshot.create")) run_id = kwargs.get("run_id", None) # creating snapshot with task id if it exists if run_id is not None: excluded_args = [ "environment_id", "environment_paths", "paths", "config_filepath", "config_filename", "stats_filepath", "stats_filename" ] for arg in excluded_args: if arg in kwargs and kwargs[arg] is not None: raise SnapshotCreateFromTaskArgs( "error", "cli.snapshot.create.run.args", arg) message = kwargs.get("message", None) label = kwargs.get("label", None) # Create a new core snapshot object snapshot_task_obj = self.snapshot_controller.create_from_task( message, run_id, label=label) self.cli_helper.echo( "Created snapshot id: %s" % snapshot_task_obj.id) return snapshot_task_obj else: # creating snapshot without task id snapshot_dict = {"visible": True} # Environment if kwargs.get("environment_id", None) or kwargs.get( "environment_paths", None): mutually_exclusive_args = [ "environment_id", "environment_paths" ] mutually_exclusive(mutually_exclusive_args, kwargs, snapshot_dict) # File if kwargs.get("paths", None): snapshot_dict['paths'] = kwargs['paths'] # Config if kwargs.get("config_filepath", None) or kwargs.get( "config_filename", None) or kwargs.get("config", None): mutually_exclusive_args = [ "config_filepath", "config_filename", "config" ] mutually_exclusive(mutually_exclusive_args, kwargs, snapshot_dict) # parsing config if "config" in snapshot_dict: config = {} config_list = snapshot_dict["config"] for item in config_list: item_parsed_dict = parse_cli_key_value(item, 'config') config.update(item_parsed_dict) snapshot_dict["config"] = config # Stats if kwargs.get("stats_filepath", None) or kwargs.get( "stats_filename", None) or kwargs.get("config", None): mutually_exclusive_args = [ "stats_filepath", "stats_filename", "stats" ] mutually_exclusive(mutually_exclusive_args, kwargs, snapshot_dict) # parsing stats if "stats" in snapshot_dict: stats = {} stats_list = snapshot_dict["stats"] for item in stats_list: item_parsed_dict = parse_cli_key_value(item, 'stats') stats.update(item_parsed_dict) snapshot_dict["stats"] = stats optional_args = ["message", "label"] for arg in optional_args: if arg in kwargs and kwargs[arg] is not None: snapshot_dict[arg] = kwargs[arg] snapshot_obj = self.snapshot_controller.create(snapshot_dict) # Because snapshots may be invisible to the user, this function ensures that by the end # the user can monitor the snapshot on the CLI, but making it visible snapshot_obj = self.snapshot_controller.update( snapshot_obj.id, visible=True) self.cli_helper.echo( __("info", "cli.snapshot.create.success", snapshot_obj.id)) return snapshot_obj @Helper.notify_no_project_found def delete(self, **kwargs): self.snapshot_controller = SnapshotController() self.cli_helper.echo(__("info", "cli.snapshot.delete")) snapshot_id = kwargs.get('id') result = self.snapshot_controller.delete(snapshot_id) self.cli_helper.echo( __("info", "cli.snapshot.delete.success", snapshot_id)) return result @Helper.notify_no_project_found def update(self, **kwargs): self.snapshot_controller = SnapshotController() self.cli_helper.echo(__("info", "cli.snapshot.update")) snapshot_id = kwargs.get('id') # getting previous saved config and stats snapshot_obj = self.snapshot_controller.get(snapshot_id) config = snapshot_obj.config stats = snapshot_obj.stats # extracting config update_config_list = kwargs.get('config', None) if update_config_list: update_config = {} for item in update_config_list: item_parsed_dict = parse_cli_key_value(item, 'config') update_config.update(item_parsed_dict) # updating config config.update(update_config) # extracting stats update_stats_list = kwargs.get('stats', None) if update_stats_list: update_stats = {} for item in update_stats_list: item_parsed_dict = parse_cli_key_value(item, 'stats') update_stats.update(item_parsed_dict) # updating stats stats.update(update_stats) # extracting message message = kwargs.get('message', None) # extracting label label = kwargs.get('label', None) result = self.snapshot_controller.update( snapshot_id, config=config, stats=stats, message=message, label=label) self.cli_helper.echo( __("info", "cli.snapshot.update.success", snapshot_id)) return result @Helper.notify_no_project_found def ls(self, **kwargs): self.snapshot_controller = SnapshotController() detailed_info = kwargs.get('details', None) show_all = kwargs.get('show_all', None) print_format = kwargs.get('format', "table") download = kwargs.get('download', None) download_path = kwargs.get('download_path', None) current_snapshot_obj = self.snapshot_controller.current_snapshot() current_snapshot_id = current_snapshot_obj.id if current_snapshot_obj else None if show_all: snapshot_objs = self.snapshot_controller.list( sort_key="created_at", sort_order="descending") else: snapshot_objs = self.snapshot_controller.list( visible=True, sort_key="created_at", sort_order="descending") item_dict_list = [] if detailed_info: header_list = [ "id", "created at", "config", "stats", "message", "label", "code id", "environment id", "file collection id" ] for snapshot_obj in snapshot_objs: snapshot_config_printable = printable_object( snapshot_obj.config) snapshot_stats_printable = printable_object(snapshot_obj.stats) snapshot_message = printable_object(snapshot_obj.message) snapshot_label = printable_object(snapshot_obj.label) printable_snapshot_id = snapshot_obj.id if current_snapshot_id is not None and \ snapshot_obj.id != current_snapshot_id\ else "(current) " + snapshot_obj.id item_dict_list.append({ "id": printable_snapshot_id, "created at": prettify_datetime(snapshot_obj.created_at), "config": snapshot_config_printable, "stats": snapshot_stats_printable, "message": snapshot_message, "label": snapshot_label, "code id": snapshot_obj.code_id, "environment id": snapshot_obj.environment_id, "file collection id": snapshot_obj.file_collection_id }) else: header_list = [ "id", "created at", "config", "stats", "message", "label" ] for snapshot_obj in snapshot_objs: snapshot_config_printable = printable_object( snapshot_obj.config) snapshot_stats_printable = printable_object(snapshot_obj.stats) snapshot_message = printable_object(snapshot_obj.message) snapshot_label = printable_object(snapshot_obj.label) printable_snapshot_id = snapshot_obj.id if current_snapshot_id is not None and \ snapshot_obj.id != current_snapshot_id \ else "(current) " + snapshot_obj.id item_dict_list.append({ "id": printable_snapshot_id, "created at": prettify_datetime(snapshot_obj.created_at), "config": snapshot_config_printable, "stats": snapshot_stats_printable, "message": snapshot_message, "label": snapshot_label, }) if download: if not download_path: # download to current working directory with timestamp current_time = datetime.utcnow() epoch_time = datetime.utcfromtimestamp(0) current_time_unix_time_ms = ( current_time - epoch_time).total_seconds() * 1000.0 download_path = os.path.join( self.snapshot_controller.home, "snapshot_ls_" + str(current_time_unix_time_ms)) self.cli_helper.print_items( header_list, item_dict_list, print_format=print_format, output_path=download_path) return snapshot_objs self.cli_helper.print_items( header_list, item_dict_list, print_format=print_format) return snapshot_objs @Helper.notify_no_project_found def checkout(self, **kwargs): self.snapshot_controller = SnapshotController() snapshot_id = kwargs.get('id') checkout_success = self.snapshot_controller.checkout(snapshot_id) if checkout_success: self.cli_helper.echo( __("info", "cli.snapshot.checkout.success", snapshot_id)) return self.snapshot_controller.checkout(snapshot_id) @Helper.notify_no_project_found def diff(self, **kwargs): self.snapshot_controller = SnapshotController() snapshot_id_1 = kwargs.get("id_1", None) snapshot_id_2 = kwargs.get("id_2", None) snapshot_obj_1 = self.snapshot_controller.get(snapshot_id_1) snapshot_obj_2 = self.snapshot_controller.get(snapshot_id_2) comparison_attributes = [ "id", "created_at", "message", "label", "code_id", "environment_id", "file_collection_id", "config", "stats" ] table_data = [["Attributes", "Snapshot 1", "", "Snapshot 2"], ["", "", "", ""]] for attribute in comparison_attributes: value_1 = getattr(snapshot_obj_1, attribute) if getattr( snapshot_obj_1, attribute) else "N/A" value_2 = getattr(snapshot_obj_2, attribute) if getattr( snapshot_obj_2, attribute) else "N/A" if isinstance(value_1, datetime): value_1 = prettify_datetime(value_1) if isinstance(value_2, datetime): value_2 = prettify_datetime(value_2) if attribute in ["config", "stats"]: alldict = [] if isinstance(value_1, dict): alldict.append(value_1) if isinstance(value_2, dict): alldict.append(value_2) allkey = set().union(*alldict) for key in allkey: key_value_1 = "%s: %s" % (key, value_1[key]) if value_1 != "N/A" and value_1.get(key, None) \ else "N/A" key_value_2 = "%s: %s" % (key, value_2[key]) if value_2 != "N/A" and value_2.get(key, None) \ else "N/A" table_data.append( [attribute, key_value_1, "->", key_value_2]) else: table_data.append([attribute, value_1, "->", value_2]) output = format_table(table_data) self.cli_helper.echo(output) return output @Helper.notify_no_project_found def inspect(self, **kwargs): self.snapshot_controller = SnapshotController() snapshot_id = kwargs.get("id", None) snapshot_obj = self.snapshot_controller.get(snapshot_id) output = str(snapshot_obj) self.cli_helper.echo(output) return output
class TestSnapshotController(): def setup_method(self): self.temp_dir = tempfile.mkdtemp(dir=test_datmo_dir) Config().set_home(self.temp_dir) self.environment_ids = [] def teardown_method(self): if not check_docker_inactive(test_datmo_dir, Config().datmo_directory_name): self.__setup() self.environment_controller = EnvironmentController() for env_id in list(set(self.environment_ids)): if not self.environment_controller.delete(env_id): raise Exception def __setup(self): Config().set_home(self.temp_dir) self.project_controller = ProjectController() self.project_controller.init("test", "test description") self.task_controller = TaskController() self.snapshot_controller = SnapshotController() def test_init_fail_project_not_init(self): Config().set_home(self.temp_dir) failed = False try: SnapshotController() except ProjectNotInitialized: failed = True assert failed def test_init_fail_invalid_path(self): test_home = "some_random_dir" Config().set_home(test_home) failed = False try: SnapshotController() except InvalidProjectPath: failed = True assert failed def test_current_snapshot(self): self.__setup() # Test failure for unstaged changes failed = False try: self.snapshot_controller.current_snapshot() except UnstagedChanges: failed = True assert failed # Test success after snapshot created snapshot_obj = self.__default_create() current_snapshot_obj = self.snapshot_controller.current_snapshot() assert current_snapshot_obj == snapshot_obj def test_create_fail_no_message(self): self.__setup() # Test no message failed = False try: self.snapshot_controller.create({}) except RequiredArgumentMissing: failed = True assert failed def test_create_success_no_code(self): self.__setup() # Test default values for snapshot, fail due to code result = self.snapshot_controller.create( {"message": "my test snapshot"}) assert result def test_create_success_no_code_environment(self): self.__setup() # Create environment definition env_def_path = os.path.join( self.project_controller.environment_driver. environment_directory_path, "Dockerfile") with open(env_def_path, "wb") as f: f.write(to_bytes("FROM python:3.5-alpine")) # test must pass when there is file present in root project folder result = self.snapshot_controller.create( {"message": "my test snapshot"}) assert result def test_create_success_no_code_environment_files(self): self.__setup() # Create environment definition env_def_path = os.path.join( self.project_controller.environment_driver. environment_directory_path, "Dockerfile") with open(env_def_path, "wb") as f: f.write(to_bytes("FROM python:3.5-alpine")) test_file = os.path.join( self.project_controller.file_driver.files_directory, "test.txt") with open(test_file, "wb") as f: f.write(to_bytes(str("hello"))) # test must pass when there is file present in root project folder result = self.snapshot_controller.create( {"message": "my test snapshot"}) assert result def test_create_no_environment_detected_in_file(self): self.__setup() # Test default values for snapshot, fail due to no environment from file self.snapshot_controller.file_driver.create("filepath1") snapshot_obj_0 = self.snapshot_controller.create( {"message": "my test snapshot"}) assert isinstance(snapshot_obj_0, Snapshot) assert snapshot_obj_0.code_id assert snapshot_obj_0.environment_id assert snapshot_obj_0.file_collection_id assert snapshot_obj_0.config == {} assert snapshot_obj_0.stats == {} def test_create_success_default_detected_in_file(self): self.__setup() # Test default values for snapshot when there is no environment test_filepath = os.path.join(self.snapshot_controller.home, "script.py") with open(test_filepath, "wb") as f: f.write(to_bytes("import os\n")) f.write(to_bytes("import sys\n")) f.write(to_bytes("print('hello')\n")) snapshot_obj_1 = self.snapshot_controller.create( {"message": "my test snapshot"}) assert isinstance(snapshot_obj_1, Snapshot) assert snapshot_obj_1.code_id assert snapshot_obj_1.environment_id assert snapshot_obj_1.file_collection_id assert snapshot_obj_1.config == {} assert snapshot_obj_1.stats == {} def test_create_success_default_env_def(self): self.__setup() # Create environment definition env_def_path = os.path.join( self.project_controller.environment_driver. environment_directory_path, "Dockerfile") with open(env_def_path, "wb") as f: f.write(to_bytes("FROM python:3.5-alpine")) # Creating a file in project folder test_filepath = os.path.join(self.snapshot_controller.home, "script.py") with open(test_filepath, "wb") as f: f.write(to_bytes("import numpy\n")) f.write(to_bytes("import sklearn\n")) f.write(to_bytes("print('hello')\n")) # Test default values for snapshot, success snapshot_obj = self.snapshot_controller.create( {"message": "my test snapshot"}) assert isinstance(snapshot_obj, Snapshot) assert snapshot_obj.code_id assert snapshot_obj.environment_id assert snapshot_obj.file_collection_id assert snapshot_obj.config == {} assert snapshot_obj.stats == {} def test_create_success_with_environment(self): self.__setup() # Create environment definition env_def_path = os.path.join( self.project_controller.environment_driver. environment_directory_path, "Dockerfile") with open(env_def_path, "wb") as f: f.write(to_bytes("FROM python:3.5-alpine")) # creating a file in project folder test_filepath = os.path.join(self.snapshot_controller.home, "script.py") with open(test_filepath, "wb") as f: f.write(to_bytes("import numpy\n")) f.write(to_bytes("import sklearn\n")) f.write(to_bytes("print('hello')\n")) # Test default values for snapshot, success snapshot_obj = self.snapshot_controller.create( {"message": "my test snapshot"}) assert isinstance(snapshot_obj, Snapshot) assert snapshot_obj.code_id assert snapshot_obj.environment_id assert snapshot_obj.file_collection_id assert snapshot_obj.config == {} assert snapshot_obj.stats == {} def test_create_success_env_paths(self): self.__setup() # Create environment definition random_dir = os.path.join(self.snapshot_controller.home, "random_dir") os.makedirs(random_dir) env_def_path = os.path.join(random_dir, "randomDockerfile") with open(env_def_path, "wb") as f: f.write(to_bytes("FROM python:3.5-alpine")) environment_paths = [env_def_path + ">Dockerfile"] # Test default values for snapshot, success snapshot_obj = self.snapshot_controller.create({ "message": "my test snapshot", "environment_paths": environment_paths }) assert isinstance(snapshot_obj, Snapshot) assert snapshot_obj.code_id assert snapshot_obj.environment_id assert snapshot_obj.file_collection_id assert snapshot_obj.config == {} assert snapshot_obj.stats == {} def test_create_success_default_env_def_duplicate(self): self.__setup() # Test 2 snapshots with same parameters # Create environment definition env_def_path = os.path.join( self.project_controller.environment_driver. environment_directory_path, "Dockerfile") with open(env_def_path, "wb") as f: f.write(to_bytes("FROM python:3.5-alpine")) # Creating a file in project folder test_filepath = os.path.join(self.snapshot_controller.home, "script.py") with open(test_filepath, "wb") as f: f.write(to_bytes("import numpy\n")) f.write(to_bytes("import sklearn\n")) f.write(to_bytes("print('hello')\n")) snapshot_obj = self.snapshot_controller.create( {"message": "my test snapshot"}) snapshot_obj_1 = self.snapshot_controller.create( {"message": "my test snapshot"}) # Should return the same object back assert snapshot_obj_1.id == snapshot_obj.id assert snapshot_obj_1.code_id == snapshot_obj.code_id assert snapshot_obj_1.environment_id == \ snapshot_obj.environment_id assert snapshot_obj_1.file_collection_id == \ snapshot_obj.file_collection_id assert snapshot_obj_1.config == \ snapshot_obj.config assert snapshot_obj_1.stats == \ snapshot_obj.stats def test_create_success_given_files_env_def_config_file_stats_file(self): self.__setup() # Create environment definition env_def_path = os.path.join( self.project_controller.environment_driver. environment_directory_path, "Dockerfile") with open(env_def_path, "wb") as f: f.write(to_bytes("FROM python:3.5-alpine")) # Creating a file in project folder test_filepath = os.path.join(self.snapshot_controller.home, "script.py") with open(test_filepath, "wb") as f: f.write(to_bytes("import numpy\n")) f.write(to_bytes("import sklearn\n")) f.write(to_bytes("print('hello')\n")) snapshot_obj = self.snapshot_controller.create( {"message": "my test snapshot"}) # Create files to add _, files_directory_name = os.path.split( self.project_controller.file_driver.files_directory) files_directory_relative_path = os.path.join( self.project_controller.file_driver.datmo_directory_name, files_directory_name) self.snapshot_controller.file_driver.create(os.path.join( files_directory_relative_path, "dirpath1"), directory=True) self.snapshot_controller.file_driver.create(os.path.join( files_directory_relative_path, "dirpath2"), directory=True) self.snapshot_controller.file_driver.create( os.path.join(files_directory_relative_path, "filepath1")) # Create config config_filepath = os.path.join(self.snapshot_controller.home, "config.json") with open(config_filepath, "wb") as f: f.write(to_bytes(str('{"foo":"bar"}'))) # Create stats stats_filepath = os.path.join(self.snapshot_controller.home, "stats.json") with open(stats_filepath, "wb") as f: f.write(to_bytes(str('{"foo":"bar"}'))) input_dict = { "message": "my test snapshot", "config_filepath": config_filepath, "stats_filepath": stats_filepath, } # Create snapshot in the project snapshot_obj_4 = self.snapshot_controller.create(input_dict) assert snapshot_obj_4 != snapshot_obj assert snapshot_obj_4.code_id != snapshot_obj.code_id assert snapshot_obj_4.environment_id == \ snapshot_obj.environment_id assert snapshot_obj_4.file_collection_id != \ snapshot_obj.file_collection_id assert snapshot_obj_4.config == {"foo": "bar"} assert snapshot_obj_4.stats == {"foo": "bar"} def test_create_success_given_files_env_def_different_config_stats(self): self.__setup() # Create environment definition env_def_path = os.path.join( self.project_controller.environment_driver. environment_directory_path, "Dockerfile") with open(env_def_path, "wb") as f: f.write(to_bytes("FROM python:3.5-alpine")) # Creating a file in project folder test_filepath = os.path.join(self.snapshot_controller.home, "script.py") with open(test_filepath, "wb") as f: f.write(to_bytes("import numpy\n")) f.write(to_bytes("import sklearn\n")) f.write(to_bytes("print('hello')\n")) snapshot_obj = self.snapshot_controller.create( {"message": "my test snapshot"}) # Create files to add _, files_directory_name = os.path.split( self.project_controller.file_driver.files_directory) files_directory_relative_path = os.path.join( self.project_controller.file_driver.datmo_directory_name, files_directory_name) self.snapshot_controller.file_driver.create(os.path.join( files_directory_relative_path, "dirpath1"), directory=True) self.snapshot_controller.file_driver.create(os.path.join( files_directory_relative_path, "dirpath2"), directory=True) self.snapshot_controller.file_driver.create( os.path.join(files_directory_relative_path, "filepath1")) # Create config config_filepath = os.path.join(self.snapshot_controller.home, "config.json") with open(config_filepath, "wb") as f: f.write(to_bytes(str('{"foo":"bar"}'))) # Create stats stats_filepath = os.path.join(self.snapshot_controller.home, "stats.json") with open(stats_filepath, "wb") as f: f.write(to_bytes(str('{"foo":"bar"}'))) # Test different config and stats inputs input_dict = { "message": "my test snapshot", "config_filename": "different_name", "stats_filename": "different_name", } # Create snapshot in the project snapshot_obj_1 = self.snapshot_controller.create(input_dict) assert snapshot_obj_1 != snapshot_obj assert snapshot_obj_1.config == {} assert snapshot_obj_1.stats == {} def test_create_success_given_files_env_def_direct_config_stats(self): self.__setup() # Create environment definition env_def_path = os.path.join( self.project_controller.environment_driver. environment_directory_path, "Dockerfile") with open(env_def_path, "wb") as f: f.write(to_bytes("FROM python:3.5-alpine")) # Create files to add _, files_directory_name = os.path.split( self.project_controller.file_driver.files_directory) files_directory_relative_path = os.path.join( self.project_controller.file_driver.datmo_directory_name, files_directory_name) self.snapshot_controller.file_driver.create(os.path.join( files_directory_relative_path, "dirpath1"), directory=True) self.snapshot_controller.file_driver.create(os.path.join( files_directory_relative_path, "dirpath2"), directory=True) self.snapshot_controller.file_driver.create( os.path.join(files_directory_relative_path, "filepath1")) # Creating a file in project folder test_filepath = os.path.join(self.snapshot_controller.home, "script.py") with open(test_filepath, "wb") as f: f.write(to_bytes("import numpy\n")) f.write(to_bytes("import sklearn\n")) f.write(to_bytes("print('hello')\n")) # Test different config and stats inputs input_dict = { "message": "my test snapshot", "config": { "foo": "bar" }, "stats": { "foo": "bar" }, } # Create snapshot in the project snapshot_obj_6 = self.snapshot_controller.create(input_dict) assert snapshot_obj_6.config == {"foo": "bar"} assert snapshot_obj_6.stats == {"foo": "bar"} @pytest_docker_environment_failed_instantiation(test_datmo_dir) def test_create_from_task(self): self.__setup() # 0) Test if fails with TaskNotComplete error # 1) Test if success with empty task files, results # 2) Test if success with task files, results, and message # 3) Test if success with message, label, config and stats # 4) Test if success with updated stats from after_snapshot_id and task_results # Create task in the project task_obj = self.task_controller.create() # 0) Test option 0 failed = False try: _ = self.snapshot_controller.create_from_task( message="my test snapshot", task_id=task_obj.id) except TaskNotComplete: failed = True assert failed # 1) Test option 1 # Create task_dict task_command = ["sh", "-c", "echo test"] task_dict = {"command_list": task_command} # Create environment definition env_def_path = os.path.join(self.project_controller.home, "Dockerfile") with open(env_def_path, "wb") as f: f.write(to_bytes("FROM python:3.5-alpine")) updated_task_obj = self.task_controller.run(task_obj.id, task_dict=task_dict) after_snapshot_obj = self.task_controller.dal.snapshot.get_by_id( updated_task_obj.after_snapshot_id) environment_obj = self.task_controller.dal.environment.get_by_id( after_snapshot_obj.environment_id) self.environment_ids.append(environment_obj.id) snapshot_obj = self.snapshot_controller.create_from_task( message="my test snapshot", task_id=updated_task_obj.id) assert isinstance(snapshot_obj, Snapshot) assert snapshot_obj.id == updated_task_obj.after_snapshot_id assert snapshot_obj.message == "my test snapshot" assert snapshot_obj.stats == updated_task_obj.results assert snapshot_obj.visible == True # Create new task and corresponding dict task_obj = self.task_controller.create() task_command = ["sh", "-c", "echo accuracy:0.45"] task_dict = {"command_list": task_command} # Create environment definition env_def_path = os.path.join(self.project_controller.home, "Dockerfile") with open(env_def_path, "wb") as f: f.write(to_bytes("FROM python:3.5-alpine")) # Test the default values updated_task_obj = self.task_controller.run(task_obj.id, task_dict=task_dict) after_snapshot_obj = self.task_controller.dal.snapshot.get_by_id( updated_task_obj.after_snapshot_id) environment_obj = self.task_controller.dal.environment.get_by_id( after_snapshot_obj.environment_id) self.environment_ids.append(environment_obj.id) # 2) Test option 2 snapshot_obj = self.snapshot_controller.create_from_task( message="my test snapshot", task_id=updated_task_obj.id) assert isinstance(snapshot_obj, Snapshot) assert snapshot_obj.id == updated_task_obj.after_snapshot_id assert snapshot_obj.message == "my test snapshot" assert snapshot_obj.stats == updated_task_obj.results assert snapshot_obj.visible == True # 3) Test option 3 test_config = {"algo": "regression"} test_stats = {"accuracy": 0.9} snapshot_obj = self.snapshot_controller.create_from_task( message="my test snapshot", task_id=updated_task_obj.id, label="best", config=test_config, stats=test_stats) assert isinstance(snapshot_obj, Snapshot) assert snapshot_obj.id == updated_task_obj.after_snapshot_id assert snapshot_obj.message == "my test snapshot" assert snapshot_obj.label == "best" assert snapshot_obj.config == test_config assert snapshot_obj.stats == test_stats assert snapshot_obj.visible == True # 4) Test option 4 test_config = {"algo": "regression"} test_stats = {"new_key": 0.9} task_obj_2 = self.task_controller.create() updated_task_obj_2 = self.task_controller.run(task_obj_2.id, task_dict=task_dict, snapshot_dict={ "config": test_config, "stats": test_stats }) after_snapshot_obj = self.task_controller.dal.snapshot.get_by_id( updated_task_obj_2.after_snapshot_id) environment_obj = self.task_controller.dal.environment.get_by_id( after_snapshot_obj.environment_id) self.environment_ids.append(environment_obj.id) snapshot_obj = self.snapshot_controller.create_from_task( message="my test snapshot", task_id=updated_task_obj_2.id, label="best") updated_stats_dict = {} updated_stats_dict.update(test_stats) updated_stats_dict.update(updated_task_obj.results) assert isinstance(snapshot_obj, Snapshot) assert snapshot_obj.id == updated_task_obj_2.after_snapshot_id assert snapshot_obj.message == "my test snapshot" assert snapshot_obj.label == "best" assert snapshot_obj.stats == updated_stats_dict assert snapshot_obj.visible == True def __default_create(self): # Create files to add _, files_directory_name = os.path.split( self.project_controller.file_driver.files_directory) files_directory_relative_path = os.path.join( self.project_controller.file_driver.datmo_directory_name, files_directory_name) self.snapshot_controller.file_driver.create(os.path.join( files_directory_relative_path, "dirpath1"), directory=True) self.snapshot_controller.file_driver.create(os.path.join( files_directory_relative_path, "dirpath2"), directory=True) self.snapshot_controller.file_driver.create( os.path.join(files_directory_relative_path, "filepath1")) self.snapshot_controller.file_driver.create("filepath2") with open(os.path.join(self.snapshot_controller.home, "filepath2"), "wb") as f: f.write(to_bytes(str("import sys\n"))) # Create environment_driver definition env_def_path = os.path.join( self.project_controller.environment_driver. environment_directory_path, "Dockerfile") with open(env_def_path, "wb") as f: f.write(to_bytes("FROM python:3.5-alpine")) # Create config config_filepath = os.path.join(self.snapshot_controller.home, "config.json") with open(config_filepath, "wb") as f: f.write(to_bytes(str("{}"))) # Create stats stats_filepath = os.path.join(self.snapshot_controller.home, "stats.json") with open(stats_filepath, "wb") as f: f.write(to_bytes(str("{}"))) input_dict = { "message": "my test snapshot", "config_filename": config_filepath, "stats_filename": stats_filepath, } # Create snapshot in the project return self.snapshot_controller.create(input_dict) def test_check_unstaged_changes(self): self.__setup() # Check unstaged changes failed = False try: self.snapshot_controller.check_unstaged_changes() except UnstagedChanges: failed = True assert failed # Check no unstaged changes _ = self.__default_create() result = self.snapshot_controller.check_unstaged_changes() assert result == False def test_checkout(self): self.__setup() # Create snapshot snapshot_obj_1 = self.__default_create() # Create duplicate snapshot in project self.snapshot_controller.file_driver.create("test") snapshot_obj_2 = self.__default_create() assert snapshot_obj_2 != snapshot_obj_1 # Checkout to snapshot 1 using snapshot id result = self.snapshot_controller.checkout(snapshot_obj_1.id) # TODO: Check for which snapshot we are on assert result == True def test_list(self): self.__setup() # Create file to add to snapshot test_filepath_1 = os.path.join(self.snapshot_controller.home, "test.txt") with open(test_filepath_1, "wb") as f: f.write(to_bytes(str("test"))) # Create snapshot in the project snapshot_obj_1 = self.__default_create() # Create file to add to second snapshot test_filepath_2 = os.path.join(self.snapshot_controller.home, "test2.txt") with open(test_filepath_2, "wb") as f: f.write(to_bytes(str("test2"))) # Create second snapshot in the project snapshot_obj_2 = self.__default_create() # List all snapshots and ensure they exist result = self.snapshot_controller.list() assert len(result) == 2 and \ snapshot_obj_1 in result and \ snapshot_obj_2 in result # List all tasks regardless of filters in ascending result = self.snapshot_controller.list(sort_key='created_at', sort_order='ascending') assert len(result) == 2 and \ snapshot_obj_1 in result and \ snapshot_obj_2 in result assert result[0].created_at <= result[-1].created_at # List all tasks regardless of filters in descending result = self.snapshot_controller.list(sort_key='created_at', sort_order='descending') assert len(result) == 2 and \ snapshot_obj_1 in result and \ snapshot_obj_2 in result assert result[0].created_at >= result[-1].created_at # Wrong order being passed in failed = False try: _ = self.snapshot_controller.list(sort_key='created_at', sort_order='wrong_order') except InvalidArgumentType: failed = True assert failed # Wrong key and order being passed in failed = False try: _ = self.snapshot_controller.list(sort_key='wrong_key', sort_order='wrong_order') except InvalidArgumentType: failed = True assert failed # wrong key and right order being passed in expected_result = self.snapshot_controller.list(sort_key='created_at', sort_order='ascending') result = self.snapshot_controller.list(sort_key='wrong_key', sort_order='ascending') expected_ids = [item.id for item in expected_result] ids = [item.id for item in result] assert set(expected_ids) == set(ids) # List snapshots with visible filter result = self.snapshot_controller.list(visible=False) assert len(result) == 0 result = self.snapshot_controller.list(visible=True) assert len(result) == 2 and \ snapshot_obj_1 in result and \ snapshot_obj_2 in result def test_update(self): self.__setup() test_config = {"config_foo": "bar"} test_stats = {"stats_foo": "bar"} test_message = 'test_message' test_label = 'test_label' # Updating all config, stats, message and label # Create snapshot in the project snapshot_obj = self.__default_create() # Update snapshot in the project self.snapshot_controller.update(snapshot_obj.id, config=test_config, stats=test_stats, message=test_message, label=test_label) # Get the updated snapshot obj updated_snapshot_obj = self.snapshot_controller.dal.snapshot.get_by_id( snapshot_obj.id) assert updated_snapshot_obj.config == test_config assert updated_snapshot_obj.stats == test_stats assert updated_snapshot_obj.message == test_message assert updated_snapshot_obj.label == test_label # Updating config, stats # Create snapshot in the project snapshot_obj = self.__default_create() # Update snapshot in the project self.snapshot_controller.update(snapshot_obj.id, config=test_config, stats=test_stats) # Get the updated snapshot obj updated_snapshot_obj = self.snapshot_controller.dal.snapshot.get_by_id( snapshot_obj.id) assert updated_snapshot_obj.config == test_config assert updated_snapshot_obj.stats == test_stats # Updating both message and label # Create snapshot in the project snapshot_obj = self.__default_create() # Update snapshot in the project self.snapshot_controller.update(snapshot_obj.id, message=test_message, label=test_label) # Get the updated snapshot obj updated_snapshot_obj = self.snapshot_controller.dal.snapshot.get_by_id( snapshot_obj.id) assert updated_snapshot_obj.message == test_message assert updated_snapshot_obj.label == test_label # Updating only message # Create snapshot in the project snapshot_obj_1 = self.__default_create() # Update snapshot in the project self.snapshot_controller.update(snapshot_obj_1.id, message=test_message) # Get the updated snapshot obj updated_snapshot_obj_1 = self.snapshot_controller.dal.snapshot.get_by_id( snapshot_obj_1.id) assert updated_snapshot_obj_1.message == test_message # Updating only label # Create snapshot in the project snapshot_obj_2 = self.__default_create() # Update snapshot in the project self.snapshot_controller.update(snapshot_obj_2.id, label=test_label) # Get the updated snapshot obj updated_snapshot_obj_2 = self.snapshot_controller.dal.snapshot.get_by_id( snapshot_obj_2.id) assert updated_snapshot_obj_2.label == test_label def test_get(self): self.__setup() # Test failure for no snapshot failed = False try: self.snapshot_controller.get("random") except DoesNotExist: failed = True assert failed # Test success for snapshot snapshot_obj = self.__default_create() snapshot_obj_returned = self.snapshot_controller.get(snapshot_obj.id) assert snapshot_obj == snapshot_obj_returned def test_get_files(self): self.__setup() # Test failure case failed = False try: self.snapshot_controller.get_files("random") except DoesNotExist: failed = True assert failed # Test success case snapshot_obj = self.__default_create() result = self.snapshot_controller.get_files(snapshot_obj.id) file_collection_obj = self.task_controller.dal.file_collection.get_by_id( snapshot_obj.file_collection_id) file_names = [item.name for item in result] assert len(result) == 1 for item in result: assert isinstance(item, TextIOWrapper) assert item.mode == "r" assert os.path.join(self.task_controller.home, ".datmo", "collections", file_collection_obj.filehash, "filepath1") in file_names result = self.snapshot_controller.get_files(snapshot_obj.id, mode="a") assert len(result) == 1 for item in result: assert isinstance(item, TextIOWrapper) assert item.mode == "a" assert os.path.join(self.task_controller.home, ".datmo", "collections", file_collection_obj.filehash, "filepath1") in file_names def test_delete(self): self.__setup() # Create snapshot in the project snapshot_obj = self.__default_create() # Delete snapshot in the project result = self.snapshot_controller.delete(snapshot_obj.id) # Check if snapshot retrieval throws error thrown = False try: self.snapshot_controller.dal.snapshot.get_by_id(snapshot_obj.id) except EntityNotFound: thrown = True assert result == True and \ thrown == True
class TestSnapshotController(): def setup_method(self): # provide mountable tmp directory for docker tempfile.tempdir = "/tmp" if not platform.system( ) == "Windows" else None test_datmo_dir = os.environ.get('TEST_DATMO_DIR', tempfile.gettempdir()) self.temp_dir = tempfile.mkdtemp(dir=test_datmo_dir) self.project = ProjectController(self.temp_dir) self.project.init("test", "test description") self.task = TaskController(self.temp_dir) self.snapshot = SnapshotController(self.temp_dir) def teardown_method(self): pass def test_create_fail_no_message(self): # Test no message failed = False try: self.snapshot.create({}) except RequiredArgumentMissing: failed = True assert failed def test_create_fail_no_code(self): # Test default values for snapshot, fail due to code failed = False try: self.snapshot.create({"message": "my test snapshot"}) except GitCommitDoesNotExist: failed = True assert failed def test_create_fail_no_environment_with_language(self): # Test default values for snapshot, fail due to environment with other than default self.snapshot.file_driver.create("filepath1") failed = False try: self.snapshot.create({ "message": "my test snapshot", "language": "java" }) except EnvironmentDoesNotExist: failed = True assert failed def test_create_fail_no_environment_detected_in_file(self): # Test default values for snapshot, fail due to no environment from file self.snapshot.file_driver.create("filepath1") failed = False try: self.snapshot.create({ "message": "my test snapshot", }) except EnvironmentDoesNotExist: failed = True assert failed def test_create_success_default_detected_in_file(self): # Test default values for snapshot when there is no environment test_filepath = os.path.join(self.snapshot.home, "script.py") with open(test_filepath, "w") as f: f.write(to_unicode("import numpy\n")) f.write(to_unicode("import sklearn\n")) f.write(to_unicode("print('hello')\n")) snapshot_obj_1 = self.snapshot.create({"message": "my test snapshot"}) assert snapshot_obj_1 assert snapshot_obj_1.code_id assert snapshot_obj_1.environment_id assert snapshot_obj_1.file_collection_id assert snapshot_obj_1.config == {} assert snapshot_obj_1.stats == {} def test_create_success_default_env_def(self): # Create environment definition env_def_path = os.path.join(self.snapshot.home, "Dockerfile") with open(env_def_path, "w") as f: f.write(to_unicode(str("FROM datmo/xgboost:cpu"))) # Test default values for snapshot, success snapshot_obj = self.snapshot.create({"message": "my test snapshot"}) assert snapshot_obj assert snapshot_obj.code_id assert snapshot_obj.environment_id assert snapshot_obj.file_collection_id assert snapshot_obj.config == {} assert snapshot_obj.stats == {} def test_create_success_default_env_def_duplicate(self): # Test 2 snapshots with same parameters # Create environment definition env_def_path = os.path.join(self.snapshot.home, "Dockerfile") with open(env_def_path, "w") as f: f.write(to_unicode(str("FROM datmo/xgboost:cpu"))) snapshot_obj = self.snapshot.create({"message": "my test snapshot"}) snapshot_obj_1 = self.snapshot.create({"message": "my test snapshot"}) # Should return the same object back assert snapshot_obj_1 == snapshot_obj assert snapshot_obj_1.code_id == snapshot_obj.code_id assert snapshot_obj_1.environment_id == \ snapshot_obj.environment_id assert snapshot_obj_1.file_collection_id == \ snapshot_obj.file_collection_id assert snapshot_obj_1.config == \ snapshot_obj.config assert snapshot_obj_1.stats == \ snapshot_obj.stats def test_create_success_given_files_env_def_config_file_stats_file(self): # Create environment definition env_def_path = os.path.join(self.snapshot.home, "Dockerfile") with open(env_def_path, "w") as f: f.write(to_unicode(str("FROM datmo/xgboost:cpu"))) snapshot_obj = self.snapshot.create({"message": "my test snapshot"}) # Create files to add self.snapshot.file_driver.create("dirpath1", directory=True) self.snapshot.file_driver.create("dirpath2", directory=True) self.snapshot.file_driver.create("filepath1") # Create config config_filepath = os.path.join(self.snapshot.home, "config.json") with open(config_filepath, "w") as f: f.write(to_unicode(str('{"foo":"bar"}'))) # Create stats stats_filepath = os.path.join(self.snapshot.home, "stats.json") with open(stats_filepath, "w") as f: f.write(to_unicode(str('{"foo":"bar"}'))) input_dict = { "message": "my test snapshot", "filepaths": [ os.path.join(self.snapshot.home, "dirpath1"), os.path.join(self.snapshot.home, "dirpath2"), os.path.join(self.snapshot.home, "filepath1") ], "environment_definition_filepath": env_def_path, "config_filepath": config_filepath, "stats_filepath": stats_filepath, } # Create snapshot in the project snapshot_obj_4 = self.snapshot.create(input_dict) assert snapshot_obj_4 != snapshot_obj assert snapshot_obj_4.code_id != snapshot_obj.code_id assert snapshot_obj_4.environment_id == \ snapshot_obj.environment_id assert snapshot_obj_4.file_collection_id != \ snapshot_obj.file_collection_id assert snapshot_obj_4.config == {"foo": "bar"} assert snapshot_obj_4.stats == {"foo": "bar"} def test_create_success_given_files_env_def_different_config_stats(self): # Create environment definition env_def_path = os.path.join(self.snapshot.home, "Dockerfile") with open(env_def_path, "w") as f: f.write(to_unicode(str("FROM datmo/xgboost:cpu"))) snapshot_obj = self.snapshot.create({"message": "my test snapshot"}) # Create files to add self.snapshot.file_driver.create("dirpath1", directory=True) self.snapshot.file_driver.create("dirpath2", directory=True) self.snapshot.file_driver.create("filepath1") # Create config config_filepath = os.path.join(self.snapshot.home, "config.json") with open(config_filepath, "w") as f: f.write(to_unicode(str('{"foo":"bar"}'))) # Create stats stats_filepath = os.path.join(self.snapshot.home, "stats.json") with open(stats_filepath, "w") as f: f.write(to_unicode(str('{"foo":"bar"}'))) # Test different config and stats inputs input_dict = { "message": "my test snapshot", "filepaths": [ os.path.join(self.snapshot.home, "dirpath1"), os.path.join(self.snapshot.home, "dirpath2"), os.path.join(self.snapshot.home, "filepath1") ], "environment_definition_filepath": env_def_path, "config_filename": "different_name", "stats_filename": "different_name", } # Create snapshot in the project snapshot_obj_1 = self.snapshot.create(input_dict) assert snapshot_obj_1 != snapshot_obj assert snapshot_obj_1.config == {} assert snapshot_obj_1.stats == {} def test_create_success_given_files_env_def_direct_config_stats(self): # Create environment definition env_def_path = os.path.join(self.snapshot.home, "Dockerfile") with open(env_def_path, "w") as f: f.write(to_unicode(str("FROM datmo/xgboost:cpu"))) # Create files to add self.snapshot.file_driver.create("dirpath1", directory=True) self.snapshot.file_driver.create("dirpath2", directory=True) self.snapshot.file_driver.create("filepath1") # Test different config and stats inputs input_dict = { "message": "my test snapshot", "filepaths": [ os.path.join(self.snapshot.home, "dirpath1"), os.path.join(self.snapshot.home, "dirpath2"), os.path.join(self.snapshot.home, "filepath1") ], "environment_definition_filepath": env_def_path, "config": { "foo": "bar" }, "stats": { "foo": "bar" }, } # Create snapshot in the project snapshot_obj_6 = self.snapshot.create(input_dict) assert snapshot_obj_6.config == {"foo": "bar"} assert snapshot_obj_6.stats == {"foo": "bar"} def test_create_from_task(self): # 1) Test if fails with TaskNotComplete error # 2) Test if success with task files, results, and message # Setup task task_command = ["sh", "-c", "echo accuracy:0.45"] input_dict = {"command": task_command} # Create task in the project task_obj = self.task.create(input_dict) # 1) Test option 1 failed = False try: _ = self.snapshot.create_from_task(message="my test snapshot", task_id=task_obj.id) except TaskNotComplete: failed = True assert failed # Create environment definition env_def_path = os.path.join(self.project.home, "Dockerfile") with open(env_def_path, "w") as f: f.write(to_unicode(str("FROM datmo/xgboost:cpu"))) # Test the default values updated_task_obj = self.task.run(task_obj.id) # 2) Test option 2 snapshot_obj = self.snapshot.create_from_task( message="my test snapshot", task_id=updated_task_obj.id) assert snapshot_obj.id == updated_task_obj.after_snapshot_id assert snapshot_obj.message == "my test snapshot" assert snapshot_obj.stats == updated_task_obj.results assert snapshot_obj.visible == True def __default_create(self): # Create files to add self.snapshot.file_driver.create("dirpath1", directory=True) self.snapshot.file_driver.create("dirpath2", directory=True) self.snapshot.file_driver.create("filepath1") # Create environment_driver definition env_def_path = os.path.join(self.snapshot.home, "Dockerfile") with open(env_def_path, "w") as f: f.write(to_unicode(str("FROM datmo/xgboost:cpu"))) # Create config config_filepath = os.path.join(self.snapshot.home, "config.json") with open(config_filepath, "w") as f: f.write(to_unicode(str("{}"))) # Create stats stats_filepath = os.path.join(self.snapshot.home, "stats.json") with open(stats_filepath, "w") as f: f.write(to_unicode(str("{}"))) input_dict = { "message": "my test snapshot", "filepaths": [ os.path.join(self.snapshot.home, "dirpath1"), os.path.join(self.snapshot.home, "dirpath2"), os.path.join(self.snapshot.home, "filepath1") ], "environment_definition_filepath": env_def_path, "config_filename": config_filepath, "stats_filename": stats_filepath, } # Create snapshot in the project return self.snapshot.create(input_dict) def test_checkout(self): # Create snapshot snapshot_obj_1 = self.__default_create() code_obj_1 = self.snapshot.dal.code.get_by_id(snapshot_obj_1.code_id) # Create duplicate snapshot in project self.snapshot.file_driver.create("test") snapshot_obj_2 = self.__default_create() assert snapshot_obj_2 != snapshot_obj_1 # Checkout to snapshot 1 using snapshot id result = self.snapshot.checkout(snapshot_obj_1.id) # Snapshot directory in user directory snapshot_obj_1_path = os.path.join(self.snapshot.home, "datmo_snapshots", snapshot_obj_1.id) assert result == True and \ self.snapshot.code_driver.latest_commit() == code_obj_1.commit_id and \ os.path.isdir(snapshot_obj_1_path) def test_list(self): # Check for error if incorrect session given failed = False try: self.snapshot.list(session_id="does_not_exist") except SessionDoesNotExistException: failed = True assert failed # Create file to add to snapshot test_filepath_1 = os.path.join(self.snapshot.home, "test.txt") with open(test_filepath_1, "w") as f: f.write(to_unicode(str("test"))) # Create snapshot in the project snapshot_obj_1 = self.__default_create() # Create file to add to second snapshot test_filepath_2 = os.path.join(self.snapshot.home, "test2.txt") with open(test_filepath_2, "w") as f: f.write(to_unicode(str("test2"))) # Create second snapshot in the project snapshot_obj_2 = self.__default_create() # List all snapshots and ensure they exist result = self.snapshot.list() assert len(result) == 2 and \ snapshot_obj_1 in result and \ snapshot_obj_2 in result # List all snapshots with session filter result = self.snapshot.list(session_id=self.project.current_session.id) assert len(result) == 2 and \ snapshot_obj_1 in result and \ snapshot_obj_2 in result # List snapshots with visible filter result = self.snapshot.list(visible=False) assert len(result) == 0 result = self.snapshot.list(visible=True) assert len(result) == 2 and \ snapshot_obj_1 in result and \ snapshot_obj_2 in result def test_delete(self): # Create snapshot in the project snapshot_obj = self.__default_create() # Delete snapshot in the project result = self.snapshot.delete(snapshot_obj.id) # Check if snapshot retrieval throws error thrown = False try: self.snapshot.dal.snapshot.get_by_id(snapshot_obj.id) except EntityNotFound: thrown = True assert result == True and \ thrown == True
class RunCommand(ProjectCommand): def __init__(self, cli_helper): super(RunCommand, self).__init__(cli_helper) @Helper.notify_environment_active(TaskController) @Helper.notify_no_project_found def run(self, **kwargs): self.cli_helper.echo(__("info", "cli.run.run")) # Create input dictionaries snapshot_dict = {} # Environment if kwargs.get("environment_id", None) or kwargs.get( "environment_paths", None): mutually_exclusive_args = ["environment_id", "environment_paths"] mutually_exclusive(mutually_exclusive_args, kwargs, snapshot_dict) task_dict = { "ports": kwargs['ports'], "interactive": kwargs['interactive'], "mem_limit": kwargs['mem_limit'] } if not isinstance(kwargs['cmd'], list): if platform.system() == "Windows": task_dict['command'] = kwargs['cmd'] elif isinstance(kwargs['cmd'], basestring): task_dict['command_list'] = shlex.split(kwargs['cmd']) else: task_dict['command_list'] = kwargs['cmd'] data_paths = kwargs['data'] # Run task and return Task object result task_obj = self.task_run_helper(task_dict, snapshot_dict, "cli.run.run", data_paths=data_paths) if not task_obj: return False # Creating the run object run_obj = Run(task_obj) return run_obj @Helper.notify_no_project_found def ls(self, **kwargs): print_format = kwargs.get('format', "table") download = kwargs.get('download', None) download_path = kwargs.get('download_path', None) # Get all task meta information self.task_controller = TaskController() task_objs = self.task_controller.list(sort_key="created_at", sort_order="descending") header_list = [ "id", "command", "type", "status", "config", "results", "created at" ] item_dict_list = [] run_obj_list = [] for task_obj in task_objs: # Create a new Run Object from Task Object run_obj = Run(task_obj) task_results_printable = printable_object(run_obj.results) snapshot_config_printable = printable_object(run_obj.config) item_dict_list.append({ "id": run_obj.id, "command": run_obj.command, "type": run_obj.type, "status": run_obj.status, "config": snapshot_config_printable, "results": task_results_printable, "created at": prettify_datetime(run_obj.created_at) }) run_obj_list.append(run_obj) if download: if not download_path: # download to current working directory with timestamp current_time = datetime.utcnow() epoch_time = datetime.utcfromtimestamp(0) current_time_unix_time_ms = ( current_time - epoch_time).total_seconds() * 1000.0 download_path = os.path.join( os.getcwd(), "run_ls_" + str(current_time_unix_time_ms)) self.cli_helper.print_items(header_list, item_dict_list, print_format=print_format, output_path=download_path) return task_objs self.cli_helper.print_items(header_list, item_dict_list, print_format=print_format) return run_obj_list @Helper.notify_environment_active(TaskController) @Helper.notify_no_project_found def rerun(self, **kwargs): self.task_controller = TaskController() # Get task id task_id = kwargs.get("id", None) self.cli_helper.echo(__("info", "cli.run.rerun", task_id)) # Create the task_obj task_obj = self.task_controller.get(task_id) # Create the run obj run_obj = Run(task_obj) # Select the initial snapshot if it's a script else the final snapshot initial = True if run_obj.type == 'script' else False environment_id = run_obj.environment_id command = task_obj.command_list snapshot_id = run_obj.core_snapshot_id if not initial else run_obj.before_snapshot_id # Checkout to the core snapshot id before rerunning the task self.snapshot_controller = SnapshotController() try: checkout_success = self.snapshot_controller.checkout(snapshot_id) except Exception: self.cli_helper.echo(__("error", "cli.snapshot.checkout.failure")) sys.exit(1) if checkout_success: self.cli_helper.echo( __("info", "cli.snapshot.checkout.success", snapshot_id)) # Rerunning the task # Create input dictionary for the new task snapshot_dict = {} snapshot_dict["environment_id"] = environment_id task_dict = { "ports": task_obj.ports, "interactive": task_obj.interactive, "mem_limit": task_obj.mem_limit, "command_list": command, "data_file_path_map": task_obj.data_file_path_map, "data_directory_path_map": task_obj.data_directory_path_map, "workspace": task_obj.workspace } # Run task and return Task object result new_task_obj = self.task_run_helper(task_dict, snapshot_dict, "cli.run.run") if not new_task_obj: return False # Creating the run object new_run_obj = Run(new_task_obj) return new_run_obj @Helper.notify_environment_active(TaskController) @Helper.notify_no_project_found def stop(self, **kwargs): self.task_controller = TaskController() input_dict = {} mutually_exclusive(["id", "all"], kwargs, input_dict) if "id" in input_dict: self.cli_helper.echo(__("info", "cli.run.stop", input_dict['id'])) elif "all" in input_dict: self.cli_helper.echo(__("info", "cli.run.stop.all")) else: raise RequiredArgumentMissing() try: if "id" in input_dict: result = self.task_controller.stop(task_id=input_dict['id']) if not result: self.cli_helper.echo( __("error", "cli.run.stop", input_dict['id'])) else: self.cli_helper.echo( __("info", "cli.run.stop.success", input_dict['id'])) if "all" in input_dict: result = self.task_controller.stop(all=input_dict['all']) if not result: self.cli_helper.echo(__("error", "cli.run.stop.all")) else: self.cli_helper.echo(__("info", "cli.run.stop.all.success")) return result except Exception: if "id" in input_dict: self.cli_helper.echo( __("error", "cli.run.stop", input_dict['id'])) if "all" in input_dict: self.cli_helper.echo(__("error", "cli.run.stop.all")) return False @Helper.notify_environment_active(TaskController) @Helper.notify_no_project_found def delete(self, **kwargs): self.task_controller = TaskController() task_id = kwargs.get("id", None) if task_id: self.cli_helper.echo(__("info", "cli.run.delete", task_id)) else: raise RequiredArgumentMissing() try: # Delete the task for the run result = self.task_controller.delete(task_id) if result: self.cli_helper.echo( __("info", "cli.run.delete.success", task_id)) return result except Exception: self.cli_helper.echo(__("error", "cli.run.delete", task_id)) return False
class SnapshotCommand(ProjectCommand): def __init__(self, home, cli_helper): super(SnapshotCommand, self).__init__(home, cli_helper) # dest="subcommand" argument will populate a "subcommand" property with the subparsers name # example "subcommand"="create" or "subcommand"="ls" self.snapshot_controller = SnapshotController(home=home) def usage(self): self.cli_helper.echo(__("argparser", "cli.snapshot.usage")) def snapshot(self): self.parse(["snapshot", "--help"]) return True def create(self, **kwargs): self.cli_helper.echo(__("info", "cli.snapshot.create")) task_id = kwargs.get("task_id", None) # creating snapshot with task id if it exists if task_id is not None: excluded_args = [ "code_id", "commit_id", "environment_id", "environment_definition_filepath", "file_collection_id", "filepaths", "config_filepath", "config_filename", "stats_filepath", "stats_filename" ] for arg in excluded_args: if arg in kwargs and kwargs[arg] is not None: raise SnapshotCreateFromTaskArgs( "error", "cli.snapshot.create.task.args", arg) message = kwargs.get("message", None) label = kwargs.get("label", None) # Create a new core snapshot object snapshot_task_obj = self.snapshot_controller.create_from_task( message, task_id, label=label) self.cli_helper.echo("Created snapshot id: %s" % snapshot_task_obj.id) return snapshot_task_obj.id else: # creating snapshot without task id snapshot_dict = {"visible": True} # Code if kwargs.get("code_id", None) or kwargs.get("commit_id", None): mutually_exclusive_args = ["code_id", "commit_id"] mutually_exclusive(mutually_exclusive_args, kwargs, snapshot_dict) # Environment if kwargs.get("environment_id", None) or kwargs.get( "environment_definition_filepath", None): mutually_exclusive_args = [ "environment_id", "environment_definition_filepath" ] mutually_exclusive(mutually_exclusive_args, kwargs, snapshot_dict) # File if kwargs.get("file_collection_id", None) or kwargs.get( "filepaths", None): mutually_exclusive_args = ["file_collection_id", "filepaths"] mutually_exclusive(mutually_exclusive_args, kwargs, snapshot_dict) # Config if kwargs.get("config_filepath", None) or kwargs.get( "config_filename", None): mutually_exclusive_args = [ "config_filepath", "config_filename" ] mutually_exclusive(mutually_exclusive_args, kwargs, snapshot_dict) # Stats if kwargs.get("stats_filepath", None) or kwargs.get( "stats_filename", None): mutually_exclusive_args = ["stats_filepath", "stats_filename"] mutually_exclusive(mutually_exclusive_args, kwargs, snapshot_dict) optional_args = ["session_id", "message", "label"] for arg in optional_args: if arg in kwargs and kwargs[arg] is not None: snapshot_dict[arg] = kwargs[arg] snapshot_obj = self.snapshot_controller.create(snapshot_dict) self.cli_helper.echo( __("info", "cli.snapshot.create.success", snapshot_obj.id)) return snapshot_obj.id def delete(self, **kwargs): self.cli_helper.echo(__("info", "cli.snapshot.delete")) snapshot_id = kwargs.get("id", None) self.cli_helper.echo( __("info", "cli.snapshot.delete.success", snapshot_id)) return self.snapshot_controller.delete(snapshot_id) def ls(self, **kwargs): session_id = kwargs.get('session_id', self.snapshot_controller.current_session.id) # Get all snapshot meta information detailed_info = kwargs.get('details', None) # List of ids shown listed_snapshot_ids = [] snapshot_objs = self.snapshot_controller.list(session_id=session_id, visible=True, sort_key='created_at', sort_order='descending') if detailed_info: header_list = [ "id", "created at", "config", "stats", "message", "label", "code id", "environment id", "file collection id" ] t = prettytable.PrettyTable(header_list) for snapshot_obj in snapshot_objs: t.add_row([ snapshot_obj.id, snapshot_obj.created_at.strftime("%Y-%m-%d %H:%M:%S"), snapshot_obj.config, snapshot_obj.stats, snapshot_obj.message, snapshot_obj.label, snapshot_obj.code_id, snapshot_obj.environment_id, snapshot_obj.file_collection_id ]) listed_snapshot_ids.append(snapshot_obj.id) else: header_list = [ "id", "created at", "config", "stats", "message", "label" ] t = prettytable.PrettyTable(header_list) for snapshot_obj in snapshot_objs: t.add_row([ snapshot_obj.id, snapshot_obj.created_at.strftime("%Y-%m-%d %H:%M:%S"), snapshot_obj.config, snapshot_obj.stats, snapshot_obj.message, snapshot_obj.label ]) listed_snapshot_ids.append(snapshot_obj.id) self.cli_helper.echo(t) return listed_snapshot_ids def checkout(self, **kwargs): snapshot_id = kwargs.get("id", None) checkout_success = self.snapshot_controller.checkout(snapshot_id) if checkout_success: self.cli_helper.echo( __("info", "cli.snapshot.checkout.success", snapshot_id)) return self.snapshot_controller.checkout(snapshot_id)
class SnapshotCommand(ProjectCommand): def __init__(self, home, cli_helper): super(SnapshotCommand, self).__init__(home, cli_helper) # dest="subcommand" argument will populate a "subcommand" property with the subparsers name # example "subcommand"="create" or "subcommand"="ls" snapshot_parser = self.subparsers.add_parser("snapshot", help="Snapshot module") subcommand_parsers = snapshot_parser.add_subparsers( title="subcommands", dest="subcommand") create = subcommand_parsers.add_parser("create", help="create snapshot") create.add_argument("--message", "-m", dest="message", default=None, help="message to describe snapshot") create.add_argument("--label", "-l", dest="label", default=None, help="Label snapshots with a category (e.g. best)") create.add_argument("--session-id", dest="session_id", default=None, help="user given session id") create.add_argument("--task-id", dest="task_id", default=None, help="Specify task id to pull information from") create.add_argument("--code-id", dest="code_id", default=None, help="code id from code object") create.add_argument("--commit-id", dest="commit_id", default=None, help="commit id from source control") create.add_argument("--environment-id", dest="environment_id", default=None, help="environment id from environment object") create.add_argument( "--environment-def-path", dest="environment_def_path", default=None, help= "absolute filepath to environment definition file (e.g. /path/to/Dockerfile)" ) create.add_argument( "--file-collection-id", dest="file_collection_id", default=None, help="file collection id for file collection object") create.add_argument( "--filepaths", dest="filepaths", default=None, nargs="*", help= "absolute paths to files or folders to include within the files of the snapshot" ) create.add_argument( "--config-filename", dest="config_filename", default=None, help="filename to use to search for configuration JSON") create.add_argument( "--config-filepath", dest="config_filepath", default=None, help="absolute filepath to use to search for configuration JSON") create.add_argument("--stats-filename", dest="stats_filename", default=None, help="filename to use to search for metrics JSON") create.add_argument( "--stats-filepath", dest="stats_filepath", default=None, help="absolute filepath to use to search for metrics JSON") delete = subcommand_parsers.add_parser("delete", help="Delete a snapshot by id") delete.add_argument("--id", dest="id", help="snapshot id to delete") ls = subcommand_parsers.add_parser("ls", help="List snapshots") ls.add_argument("--session-id", dest="session_id", default=None, help="Session ID to filter") ls.add_argument("--all", "-a", dest="details", action="store_true", help="Show detailed snapshot information") checkout = subcommand_parsers.add_parser( "checkout", help="Checkout a snapshot by id") checkout.add_argument("--id", dest="id", default=None, help="Snapshot ID") self.snapshot_controller = SnapshotController(home=home) def create(self, **kwargs): self.cli_helper.echo(__("info", "cli.snapshot.create")) snapshot_dict = {} # Code mutually_exclusive_args = ["code_id", "commit_id"] mutually_exclusive(mutually_exclusive_args, kwargs, snapshot_dict) # Environment mutually_exclusive_args = ["environment_id", "environment_def_path"] mutually_exclusive(mutually_exclusive_args, kwargs, snapshot_dict) # File mutually_exclusive_args = ["file_collection_id", "filepaths"] mutually_exclusive(mutually_exclusive_args, kwargs, snapshot_dict) # Config mutually_exclusive_args = ["config_filepath", "config_filename"] mutually_exclusive(mutually_exclusive_args, kwargs, snapshot_dict) # Stats mutually_exclusive_args = ["stats_filepath", "stats_filename"] mutually_exclusive(mutually_exclusive_args, kwargs, snapshot_dict) optional_args = ["session_id", "task_id", "message", "label"] for arg in optional_args: if arg in kwargs and kwargs[arg] is not None: snapshot_dict[arg] = kwargs[arg] snapshot_obj = self.snapshot_controller.create(snapshot_dict) return snapshot_obj.id def delete(self, **kwargs): self.cli_helper.echo(__("info", "cli.snapshot.delete")) snapshot_id = kwargs.get("id", None) return self.snapshot_controller.delete(snapshot_id) def ls(self, **kwargs): session_id = kwargs.get('session_id', self.snapshot_controller.current_session.id) # Get all snapshot meta information detailed_info = kwargs.get('details', None) if detailed_info: header_list = [ "id", "created at", "config", "stats", "message", "label", "code id", "environment id", "file collection id" ] t = prettytable.PrettyTable(header_list) snapshot_objs = self.snapshot_controller.list( session_id=session_id, visible=True) for snapshot_obj in snapshot_objs: t.add_row([ snapshot_obj.id, snapshot_obj.created_at.strftime("%Y-%m-%d %H:%M:%S"), snapshot_obj.config, snapshot_obj.stats, snapshot_obj.message, snapshot_obj.label, snapshot_obj.code_id, snapshot_obj.environment_id, snapshot_obj.file_collection_id ]) else: header_list = [ "id", "created at", "config", "stats", "message", "label" ] t = prettytable.PrettyTable(header_list) snapshot_objs = self.snapshot_controller.list( session_id=session_id, visible=True) for snapshot_obj in snapshot_objs: t.add_row([ snapshot_obj.id, snapshot_obj.created_at.strftime("%Y-%m-%d %H:%M:%S"), snapshot_obj.config, snapshot_obj.stats, snapshot_obj.message, snapshot_obj.label ]) self.cli_helper.echo(t) return True def checkout(self, **kwargs): snapshot_id = kwargs.get("id", None) return self.snapshot_controller.checkout(snapshot_id)