def get_command(command_id: str) -> Any: certs.cli_cert = certs.default_load(conf.make_master_url()) authentication.cli_auth = authentication.Authentication( conf.make_master_url(), try_reauth=True) r = api.get(conf.make_master_url(), "api/v1/commands/" + command_id) assert r.status_code == requests.codes.ok, r.text return r.json()["command"]
def __init__( self, master: Optional[str] = None, user: Optional[str] = None, password: Optional[str] = None, cert_path: Optional[str] = None, cert_name: Optional[str] = None, noverify: bool = False, ): master = master or util.get_default_master_address() cert = certs.default_load( master_url=master, explicit_path=cert_path, explicit_cert_name=cert_name, explicit_noverify=noverify, ) # TODO: This should probably be try_reauth=False, but it appears that would break the case # where the default credentials are available from the master and could be discovered by # a REST API call against the master. auth = authentication.Authentication(master, user, password, try_reauth=True, cert=cert) self._session = session.Session(master, user, auth, cert)
def create_test_session() -> session.Session: murl = conf.make_master_url() certs.cli_cert = certs.default_load(murl) authentication.cli_auth = authentication.Authentication(murl, try_reauth=True) return session.Session(murl, "determined", authentication.cli_auth, certs.cli_cert)
def trial_metrics(trial_id: int) -> Dict[str, Any]: certs.cli_cert = certs.default_load(conf.make_master_url()) authentication.cli_auth = authentication.Authentication( conf.make_master_url(), try_reauth=True) r = api.get(conf.make_master_url(), "trials/{}/metrics".format(trial_id)) json = r.json() # type: Dict[str, Any] return json
def num_experiments() -> int: certs.cli_cert = certs.default_load(conf.make_master_url()) authentication.cli_auth = authentication.Authentication( conf.make_master_url(), try_reauth=True) r = api.get(conf.make_master_url(), "experiments") assert r.status_code == requests.codes.ok, r.text return len(r.json())
def num_agents() -> int: authentication.cli_auth = authentication.Authentication( conf.make_master_url(), try_reauth=True) r = api.get(conf.make_master_url(), "agents") assert r.status_code == requests.codes.ok, r.text return len(r.json())
def test_streaming_observability_metrics_apis( framework_base_experiment: str, framework_timings_enabled: bool ) -> None: # TODO: refactor tests to not use cli singleton auth. certs.cli_cert = certs.default_load(conf.make_master_url()) authentication.cli_auth = authentication.Authentication(conf.make_master_url(), try_reauth=True) config_path = conf.tutorials_path(f"../{framework_base_experiment}/const.yaml") model_def_path = conf.tutorials_path(f"../{framework_base_experiment}") config_obj = conf.load_config(config_path) config_obj = conf.set_profiling_enabled(config_obj) with tempfile.NamedTemporaryFile() as tf: with open(tf.name, "w") as f: yaml.dump(config_obj, f) experiment_id = exp.create_experiment( tf.name, model_def_path, ) exp.wait_for_experiment_state(experiment_id, "COMPLETED") trials = exp.experiment_trials(experiment_id) trial_id = trials[0]["id"] gpu_enabled = conf.GPU_ENABLED request_profiling_metric_labels(trial_id, framework_timings_enabled, gpu_enabled) if gpu_enabled: request_profiling_system_metrics(trial_id, "gpu_util") if framework_timings_enabled: request_profiling_pytorch_timing_metrics(trial_id, "train_batch")
def get_num_running_commands() -> int: # TODO: refactor tests to not use cli singleton auth. certs.cli_cert = certs.default_load(conf.make_master_url()) authentication.cli_auth = authentication.Authentication(conf.make_master_url(), try_reauth=True) r = api.get(conf.make_master_url(), "api/v1/commands") assert r.status_code == requests.codes.ok, r.text return len([command for command in r.json()["commands"] if command["state"] == "STATE_RUNNING"])
def cancel_experiment_v1(experiment_id: int) -> None: certs.cli_cert = certs.default_load(conf.make_master_url()) authentication.cli_auth = authentication.Authentication( conf.make_master_url(), try_reauth=True) r = api.post(conf.make_master_url(), "/api/v1/experiments/{}/cancel".format(experiment_id)) r.raise_for_status() wait_for_experiment_state(experiment_id, "CANCELED")
def experiment_json(experiment_id: int) -> Dict[str, Any]: certs.cli_cert = certs.default_load(conf.make_master_url()) authentication.cli_auth = authentication.Authentication( conf.make_master_url(), try_reauth=True) r = api.get(conf.make_master_url(), "experiments/{}".format(experiment_id)) assert r.status_code == requests.codes.ok, r.text json = r.json() # type: Dict[str, Any] return json
def trial_logs(trial_id: int) -> List[str]: certs.cli_cert = certs.default_load(conf.make_master_url()) authentication.cli_auth = authentication.Authentication( conf.make_master_url(), try_reauth=True) return [ tl["message"] for tl in api.trial_logs(conf.make_master_url(), trial_id) ]
def experiment_has_active_workload(experiment_id: int) -> bool: certs.cli_cert = certs.default_load(conf.make_master_url()) authentication.cli_auth = authentication.Authentication( conf.make_master_url(), try_reauth=True) r = api.get(conf.make_master_url(), "tasks").json() for task in r.values(): if "Experiment {}".format(experiment_id) in task["name"] and len( task["containers"]) > 0: return True return False
def experiment_has_completed_workload(experiment_id: int) -> bool: certs.cli_cert = certs.default_load(conf.make_master_url()) authentication.cli_auth = authentication.Authentication( conf.make_master_url(), try_reauth=True) trials = experiment_trials(experiment_id) if not any(trials): return False return any( any(s["state"] == "COMPLETED" for s in t["steps"]) for t in trials)
def cluster_slots() -> Dict[str, Any]: """ cluster_slots returns a dict of slots that each agent has. :return: Dict[AgentID, List[Slot]] """ # TODO: refactor tests to not use cli singleton auth. certs.cli_cert = certs.default_load(conf.make_master_url()) authentication.cli_auth = authentication.Authentication(conf.make_master_url(), try_reauth=True) r = api.get(conf.make_master_url(), "agents") assert r.status_code == requests.codes.ok, r.text json = r.json() # type: Dict[str, Any] return {agent["id"]: agent["slots"].values() for agent in json.values()}
def change_experiment_state(experiment_id: int, new_state: str) -> None: # TODO(DET-5678): refactor tests to not use cli singleton auth. certs.cli_cert = certs.default_load(conf.make_master_url()) authentication.cli_auth = authentication.Authentication( conf.make_master_url(), try_reauth=True) r = api.patch( conf.make_master_url(), "experiments/{}".format(experiment_id), headers={"Content-Type": "application/merge-patch+json"}, body={"state": new_state}, ) assert r.status_code == requests.codes.no_content, r.text
def test_post_user_api(clean_auth: None) -> None: master_url = conf.make_master_url() authentication.cli_auth = authentication.Authentication( conf.make_master_url(), requested_user="******", password="", try_reauth=True) new_username = get_random_string() user = bindings.v1User(active=True, admin=False, username=new_username) body = bindings.v1PostUserRequest(password="", user=user) resp = bindings.post_PostUser(session.Session(master_url, "admin", authentication.cli_auth, None), body=body) assert resp.to_json()["user"]["username"] == new_username
def experiment_has_completed_workload(experiment_id: int) -> bool: certs.cli_cert = certs.default_load(conf.make_master_url()) authentication.cli_auth = authentication.Authentication( conf.make_master_url(), try_reauth=True) trials = experiment_trials(experiment_id) if not any(trials): return False for t in trials: for s in t.workloads: if (s.training is not None and s.training.state == determinedexperimentv1State.STATE_COMPLETED) or ( s.validation is not None and s.validation.state == determinedexperimentv1State.STATE_COMPLETED): return True return False
def wait_for_gc_to_finish(experiment_id: int) -> None: certs.cli_cert = certs.default_load(conf.make_master_url()) authentication.cli_auth = authentication.Authentication( conf.make_master_url(), try_reauth=True) saw_gc = False # Don't wait longer than 5 minutes (as 600 half-seconds to improve our sampling resolution). for _ in range(600): r = api.get(conf.make_master_url(), "tasks").json() names = [task["name"] for task in r.values()] gc_name = f"Checkpoint GC (Experiment {experiment_id})" if gc_name in names: saw_gc = True elif saw_gc: # We previously saw checkpoint gc but now we don't, so it must have finished. return time.sleep(0.5) # It's possible that it ran really fast and we missed it, so just log this. print("Did not observe checkpoint gc start or finish!", file=sys.stderr)
def test_task_logs(task_type: str, task_config: Dict[str, Any], log_regex: Any) -> None: # TODO: refactor tests to not use cli singleton auth. master_url = conf.make_master_url() certs.cli_cert = certs.default_load(conf.make_master_url()) authentication.cli_auth = authentication.Authentication( conf.make_master_url(), try_reauth=True) rps = bindings.get_GetResourcePools( session.Session(master_url, "determined", authentication.cli_auth, certs.cli_cert)) assert rps.resourcePools and len( rps.resourcePools) > 0, "missing resource pool" if (rps.resourcePools[0].type == bindings.v1ResourcePoolType.RESOURCE_POOL_TYPE_K8S and task_type == command.TaskTypeCommand): # TODO(DET-6712): Investigate intermittent slowness with K8s command logs. return body = {} if task_type == command.TaskTypeTensorBoard: exp_id = exp.run_basic_test( conf.fixtures_path("no_op/single.yaml"), conf.fixtures_path("no_op"), 1, ) body.update({"experiment_ids": [exp_id]}) resp = command.launch_command( master_url, f"api/v1/{command.RemoteTaskNewAPIs[task_type]}", task_config, "", default_body=body, ) task_id = resp[command.RemoteTaskName[task_type]]["id"] try: check_logs(master_url, task_id, log_regex, api.task_logs, api.task_log_fields) finally: command._kill(master_url, task_type, task_id)
def test_hp_importance_api() -> None: certs.cli_cert = certs.default_load(conf.make_master_url()) authentication.cli_auth = authentication.Authentication( conf.make_master_url(), try_reauth=True) pool = mp.pool.ThreadPool(processes=1) experiment_id = exp.create_experiment( conf.fixtures_path("mnist_pytorch/random.yaml"), conf.tutorials_path("mnist_pytorch"), ) hp_importance_thread = pool.apply_async(request_hp_importance, (experiment_id, )) hp_importance_results = hp_importance_thread.get() if hp_importance_results is not None: pytest.fail("hyperparameter-importance: %s. Results: %s" % hp_importance_results)
def test_change_displayname(clean_auth: None) -> None: u_patch = create_test_user(ADMIN_CREDENTIALS, False) original_name = u_patch.username master_url = conf.make_master_url() certs.cli_cert = certs.default_load(master_url) authentication.cli_auth = authentication.Authentication( conf.make_master_url(), requested_user=original_name, password="", try_reauth=True) sess = session.Session(master_url, original_name, authentication.cli_auth, certs.cli_cert) # Get API bindings object for the created test user all_users = bindings.get_GetUsers(sess).users assert all_users is not None current_user = list( filter(lambda u: u.username == original_name, all_users))[0] assert current_user is not None and current_user.id # Rename user using display name patch_user = bindings.v1PatchUser(displayName="renamed") bindings.patch_PatchUser(sess, body=patch_user, userId=current_user.id) modded_user = bindings.get_GetUser(sess, userId=current_user.id).user assert modded_user is not None assert modded_user.displayName == "renamed" # Avoid display name of 'admin' patch_user.displayName = "Admin" with pytest.raises(errors.APIException): bindings.patch_PatchUser(sess, body=patch_user, userId=current_user.id) # Clear display name (UI will show username) patch_user.displayName = "" bindings.patch_PatchUser(sess, body=patch_user, userId=current_user.id) modded_user = bindings.get_GetUser(sess, userId=current_user.id).user assert modded_user is not None assert modded_user.displayName == ""
def test_trial_logs() -> None: # TODO: refactor tests to not use cli singleton auth. master_url = conf.make_master_url() certs.cli_cert = certs.default_load(conf.make_master_url()) authentication.cli_auth = authentication.Authentication( conf.make_master_url(), try_reauth=True) experiment_id = exp.run_basic_test(conf.fixtures_path("no_op/single.yaml"), conf.fixtures_path("no_op"), 1) trial = exp.experiment_trials(experiment_id)[0].trial trial_id = trial.id task_id = trial.taskId assert task_id != "" log_regex = re.compile("^.*New trial runner.*$") # Trial-specific APIs should work just fine. check_logs(master_url, trial_id, log_regex, api.trial_logs, api.trial_log_fields) # And so should new task log APIs. check_logs(master_url, task_id, log_regex, api.task_logs, api.task_log_fields)
def _submit_experiment( config: Optional[Dict[str, Any]], context_dir: str, command: Optional[List[str]], test: bool = False, master_url: Optional[str] = None, ) -> int: if context_dir == "": raise errors.InvalidExperimentException( "Cannot specify the context directory to be empty.") context_path = pathlib.Path(context_dir) config = {**constants.DEFAULT_EXP_CFG, **(config or {})} config.setdefault("internal", {}) config["internal"]["native"] = { "command": _set_command_default(context_path, command) } logging.info(f"Creating an experiment with config: {config}") if master_url is None: master_url = util.get_default_master_address() exp_context = context.Context.from_local(context_path) # When a requested_user isn't specified to initialize_session(), the # authentication module will attempt to use the token store to grab the # current logged-in user. If there is no logged in user found, it will # default to constants.DEFAULT_DETERMINED_USER. # TODO: refactor this to use the python sdk rather than using the cli_auth singleton. authentication.cli_auth = authentication.Authentication( master_url, requested_user=None, try_reauth=True) if test: return api.create_test_experiment_and_follow_logs( master_url, config, exp_context) else: return api.create_experiment_and_follow_logs(master_url, config, exp_context)
def test_streaming_metrics_api() -> None: # TODO: refactor tests to not use cli singleton auth. certs.cli_cert = certs.default_load(conf.make_master_url()) authentication.cli_auth = authentication.Authentication( conf.make_master_url(), try_reauth=True) pool = mp.pool.ThreadPool(processes=7) experiment_id = exp.create_experiment( conf.fixtures_path("mnist_pytorch/adaptive_short.yaml"), conf.tutorials_path("mnist_pytorch"), ) # To fully test the streaming APIs, the requests need to start running immediately after the # experiment, and then stay open until the experiment is complete. To accomplish this with all # of the API calls on a single experiment, we spawn them all in threads. # The HP importance portion of this test is commented out until the feature is enabled by # default metric_names_thread = pool.apply_async(request_metric_names, (experiment_id, )) train_metric_batches_thread = pool.apply_async( request_train_metric_batches, (experiment_id, )) valid_metric_batches_thread = pool.apply_async( request_valid_metric_batches, (experiment_id, )) train_trials_snapshot_thread = pool.apply_async( request_train_trials_snapshot, (experiment_id, )) valid_trials_snapshot_thread = pool.apply_async( request_valid_trials_snapshot, (experiment_id, )) train_trials_sample_thread = pool.apply_async(request_train_trials_sample, (experiment_id, )) valid_trials_sample_thread = pool.apply_async(request_valid_trials_sample, (experiment_id, )) metric_names_results = metric_names_thread.get() train_metric_batches_results = train_metric_batches_thread.get() valid_metric_batches_results = valid_metric_batches_thread.get() train_trials_snapshot_results = train_trials_snapshot_thread.get() valid_trials_snapshot_results = valid_trials_snapshot_thread.get() train_trials_sample_results = train_trials_sample_thread.get() valid_trials_sample_results = valid_trials_sample_thread.get() if metric_names_results is not None: pytest.fail("metric-names: %s. Results: %s" % metric_names_results) if train_metric_batches_results is not None: pytest.fail("metric-batches (training): %s. Results: %s" % train_metric_batches_results) if valid_metric_batches_results is not None: pytest.fail("metric-batches (validation): %s. Results: %s" % valid_metric_batches_results) if train_trials_snapshot_results is not None: pytest.fail("trials-snapshot (training): %s. Results: %s" % train_trials_snapshot_results) if valid_trials_snapshot_results is not None: pytest.fail("trials-snapshot (validation): %s. Results: %s" % valid_trials_snapshot_results) if train_trials_sample_results is not None: pytest.fail("trials-sample (training): %s. Results: %s" % train_trials_sample_results) if valid_trials_sample_results is not None: pytest.fail("trials-sample (validation): %s. Results: %s" % valid_trials_sample_results)
def test_workspace_org() -> None: master_url = conf.make_master_url() authentication.cli_auth = authentication.Authentication(master_url, try_reauth=True) sess = session.Session(master_url, None, None, None) test_experiments: List[bindings.v1Experiment] = [] test_projects: List[bindings.v1Project] = [] test_workspaces: List[bindings.v1Workspace] = [] try: # Uncategorized workspace / project should exist already. r = bindings.get_GetWorkspaces(sess, name="Uncategorized") assert len(r.workspaces) == 1 default_workspace = r.workspaces[0] assert default_workspace.immutable r2 = bindings.get_GetWorkspaceProjects(sess, id=default_workspace.id) assert len(r2.projects) == 1 default_project = r2.projects[0] assert default_project.name == "Uncategorized" assert default_project.immutable # Add a test workspace. r3 = bindings.post_PostWorkspace( sess, body=bindings.v1PostWorkspaceRequest(name="_TestOnly")) made_workspace = r3.workspace test_workspaces.append(made_workspace) get_workspace = bindings.get_GetWorkspace( sess, id=made_workspace.id).workspace assert get_workspace.name == made_workspace.name assert not made_workspace.immutable and not get_workspace.immutable # Patch the workspace w_patch = bindings.v1PatchWorkspace.from_json(made_workspace.to_json()) w_patch.name = "_TestPatched" bindings.patch_PatchWorkspace(sess, body=w_patch, id=made_workspace.id) get_workspace = bindings.get_GetWorkspace( sess, id=made_workspace.id).workspace assert get_workspace.name == "_TestPatched" # Archive the workspace assert not made_workspace.archived bindings.post_ArchiveWorkspace(sess, id=made_workspace.id) get_workspace_2 = bindings.get_GetWorkspace( sess, id=made_workspace.id).workspace assert get_workspace_2.archived with pytest.raises(errors.APIException): # Cannot patch archived workspace bindings.patch_PatchWorkspace(sess, body=w_patch, id=made_workspace.id) with pytest.raises(errors.APIException): # Cannot create project inside archived workspace bindings.post_PostProject( sess, body=bindings.v1PostProjectRequest( name="Nope2", workspaceId=made_workspace.id), workspaceId=made_workspace.id, ) bindings.post_UnarchiveWorkspace(sess, id=made_workspace.id) get_workspace_3 = bindings.get_GetWorkspace( sess, id=made_workspace.id).workspace assert not get_workspace_3.archived # Refuse to patch, archive, unarchive, or delete the default workspace with pytest.raises(errors.APIException): bindings.patch_PatchWorkspace(sess, body=w_patch, id=default_workspace.id) with pytest.raises(errors.APIException): bindings.post_ArchiveWorkspace(sess, id=default_workspace.id) with pytest.raises(errors.APIException): bindings.post_UnarchiveWorkspace(sess, id=default_workspace.id) with pytest.raises(errors.APIException): bindings.delete_DeleteWorkspace(sess, id=default_workspace.id) # Sort test and default workspaces. workspace2 = bindings.post_PostWorkspace( sess, body=bindings.v1PostWorkspaceRequest(name="_TestWS")).workspace test_workspaces.append(workspace2) list_test_1 = bindings.get_GetWorkspaces(sess).workspaces assert ["Uncategorized", "_TestPatched", "_TestWS"] == [w.name for w in list_test_1] list_test_2 = bindings.get_GetWorkspaces( sess, orderBy=bindings.v1OrderBy.ORDER_BY_DESC).workspaces assert ["_TestWS", "_TestPatched", "Uncategorized"] == [w.name for w in list_test_2] list_test_3 = bindings.get_GetWorkspaces( sess, sortBy=bindings.v1GetWorkspacesRequestSortBy.SORT_BY_NAME ).workspaces assert ["_TestPatched", "_TestWS", "Uncategorized"] == [w.name for w in list_test_3] # Test pinned workspaces. pinned = bindings.get_GetWorkspaces( sess, pinned=True, ).workspaces assert len(pinned) == 2 bindings.post_UnpinWorkspace(sess, id=made_workspace.id) pinned = bindings.get_GetWorkspaces( sess, pinned=True, ).workspaces assert len(pinned) == 1 bindings.post_PinWorkspace(sess, id=made_workspace.id) pinned = bindings.get_GetWorkspaces( sess, pinned=True, ).workspaces assert len(pinned) == 2 # Add a test project to a workspace. r4 = bindings.post_PostProject( sess, body=bindings.v1PostProjectRequest(name="_TestOnly", workspaceId=made_workspace.id), workspaceId=made_workspace.id, ) made_project = r4.project test_projects.append(made_project) get_project = bindings.get_GetProject(sess, id=made_project.id).project assert get_project.name == made_project.name assert not made_project.immutable and not get_project.immutable # Project cannot be created in the default workspace. with pytest.raises(errors.APIException): bindings.post_PostProject( sess, body=bindings.v1PostProjectRequest( name="Nope", workspaceId=default_workspace.id), workspaceId=default_workspace.id, ) # Patch the project p_patch = bindings.v1PatchProject.from_json(made_project.to_json()) p_patch.name = "_TestPatchedProject" bindings.patch_PatchProject(sess, body=p_patch, id=made_project.id) get_project = bindings.get_GetProject(sess, id=made_project.id).project assert get_project.name == "_TestPatchedProject" # Archive the project assert not made_project.archived bindings.post_ArchiveProject(sess, id=made_project.id) get_project_2 = bindings.get_GetProject(sess, id=made_project.id).project assert get_project_2.archived # Cannot patch or move an archived project with pytest.raises(errors.APIException): bindings.patch_PatchProject(sess, body=p_patch, id=made_project.id) with pytest.raises(errors.APIException): bindings.post_MoveProject( sess, projectId=made_project.id, body=bindings.v1MoveProjectRequest( destinationWorkspaceId=workspace2.id, projectId=made_project.id, ), ) # Unarchive the project bindings.post_UnarchiveProject(sess, id=made_project.id) get_project_3 = bindings.get_GetProject(sess, id=made_project.id).project assert not get_project_3.archived # Can't archive, un-archive, or move while parent workspace is archived bindings.post_ArchiveWorkspace(sess, id=made_workspace.id) get_project_4 = bindings.get_GetProject(sess, id=made_project.id).project assert get_project_4.archived with pytest.raises(errors.APIException): bindings.post_ArchiveProject(sess, id=made_project.id) with pytest.raises(errors.APIException): bindings.post_UnarchiveProject(sess, id=made_project.id) with pytest.raises(errors.APIException): bindings.post_MoveProject( sess, projectId=made_project.id, body=bindings.v1MoveProjectRequest( destinationWorkspaceId=workspace2.id, projectId=made_project.id, ), ) bindings.post_UnarchiveWorkspace(sess, id=made_workspace.id) # Refuse to patch, archive, unarchive, or delete the default project with pytest.raises(errors.APIException): bindings.patch_PatchProject(sess, body=p_patch, id=default_project.id) with pytest.raises(errors.APIException): bindings.post_ArchiveProject(sess, id=default_project.id) with pytest.raises(errors.APIException): bindings.post_UnarchiveProject(sess, id=default_project.id) with pytest.raises(errors.APIException): bindings.delete_DeleteProject(sess, id=default_project.id) # Sort workspaces' projects. p1 = bindings.post_PostProject( sess, body=bindings.v1PostProjectRequest(name="_TestPRJ", workspaceId=made_workspace.id), workspaceId=made_workspace.id, ).project p2 = bindings.post_PostProject( sess, body=bindings.v1PostProjectRequest(name="_TestEarly", workspaceId=made_workspace.id), workspaceId=made_workspace.id, ).project test_projects += [p1, p2] list_test_4 = bindings.get_GetWorkspaceProjects( sess, id=made_workspace.id).projects assert ["_TestPatchedProject", "_TestPRJ", "_TestEarly"] == [p.name for p in list_test_4] list_test_5 = bindings.get_GetWorkspaceProjects( sess, id=made_workspace.id, orderBy=bindings.v1OrderBy.ORDER_BY_DESC).projects assert ["_TestEarly", "_TestPRJ", "_TestPatchedProject"] == [p.name for p in list_test_5] list_test_6 = bindings.get_GetWorkspaceProjects( sess, id=made_workspace.id, sortBy=bindings.v1GetWorkspaceProjectsRequestSortBy.SORT_BY_NAME, ).projects assert ["_TestEarly", "_TestPatchedProject", "_TestPRJ"] == [p.name for p in list_test_6] # Move a project to another workspace bindings.post_MoveProject( sess, projectId=made_project.id, body=bindings.v1MoveProjectRequest( destinationWorkspaceId=workspace2.id, projectId=made_project.id, ), ) get_project = bindings.get_GetProject(sess, id=made_project.id).project assert get_project.workspaceId == workspace2.id # Default project cannot be moved. with pytest.raises(errors.APIException): bindings.post_MoveProject( sess, projectId=default_project.id, body=bindings.v1MoveProjectRequest( destinationWorkspaceId=workspace2.id, projectId=default_project.id, ), ) # Project cannot be moved into the default workspace. with pytest.raises(errors.APIException): bindings.post_MoveProject( sess, projectId=made_project.id, body=bindings.v1MoveProjectRequest( destinationWorkspaceId=default_workspace.id, projectId=made_project.id, ), ) # Project cannot be moved into an archived workspace. bindings.post_ArchiveWorkspace(sess, id=made_workspace.id) with pytest.raises(errors.APIException): bindings.post_MoveProject( sess, projectId=made_project.id, body=bindings.v1MoveProjectRequest( destinationWorkspaceId=made_workspace.id, projectId=made_project.id, ), ) bindings.post_UnarchiveWorkspace(sess, id=made_workspace.id) # Add a test note to a project. note = bindings.v1Note(name="Hello", contents="Hello World") note2 = bindings.v1Note(name="Hello 2", contents="Hello World") bindings.post_AddProjectNote( sess, body=note, projectId=made_project.id, ) r5 = bindings.post_AddProjectNote( sess, body=note2, projectId=made_project.id, ) returned_notes = r5.notes assert len(returned_notes) == 2 # Put notes r6 = bindings.put_PutProjectNotes( sess, body=bindings.v1PutProjectNotesRequest(notes=[note], projectId=made_project.id), projectId=made_project.id, ) returned_notes = r6.notes assert len(returned_notes) == 1 # Create an experiment in the default project. test_exp_id = run_basic_test(conf.fixtures_path("no_op/single.yaml"), conf.fixtures_path("no_op"), 1) test_exp = bindings.get_GetExperiment( sess, experimentId=test_exp_id).experiment test_experiments.append(test_exp) wait_for_experiment_state( test_exp_id, bindings.determinedexperimentv1State.STATE_COMPLETED) assert test_exp.projectId == default_project.id # Move the test experiment into a user-made project dproj_exp = bindings.get_GetProjectExperiments( sess, id=default_project.id).experiments exp_count = len( bindings.get_GetProjectExperiments(sess, id=made_project.id).experiments) assert exp_count == 0 mbody = bindings.v1MoveExperimentRequest( destinationProjectId=made_project.id, experimentId=test_exp_id) bindings.post_MoveExperiment(sess, experimentId=test_exp_id, body=mbody) modified_exp = bindings.get_GetExperiment( sess, experimentId=test_exp_id).experiment assert modified_exp.projectId == made_project.id # Confirm the test experiment is in the new project, no longer in old project. exp_count = len( bindings.get_GetProjectExperiments(sess, id=made_project.id).experiments) assert exp_count == 1 dproj_exp2 = bindings.get_GetProjectExperiments( sess, id=default_project.id).experiments assert len(dproj_exp2) == len(dproj_exp) - 1 # Cannot move an experiment out of an archived project bindings.post_ArchiveProject(sess, id=made_project.id) mbody2 = bindings.v1MoveExperimentRequest( destinationProjectId=default_project.id, experimentId=test_exp_id) with pytest.raises(errors.APIException): bindings.post_MoveExperiment(sess, experimentId=test_exp_id, body=mbody2) bindings.post_UnarchiveProject(sess, id=made_project.id) # Moving an experiment into default project bindings.post_MoveExperiment(sess, experimentId=test_exp_id, body=mbody2) # Cannot move an experiment into an archived project bindings.post_ArchiveProject(sess, id=made_project.id) with pytest.raises(errors.APIException): bindings.post_MoveExperiment(sess, experimentId=test_exp_id, body=mbody) finally: # Clean out experiments, projects, workspaces. # In dependency order: for e in test_experiments: bindings.delete_DeleteExperiment(sess, experimentId=e.id) for p in test_projects: bindings.delete_DeleteProject(sess, id=p.id) for w in test_workspaces: bindings.delete_DeleteWorkspace(sess, id=w.id)