def test_environment_definition_equality(): env_def_1 = EnvironmentDefinition('{"packages": {"foo": "1.2.3"}}') env_def_2 = EnvironmentDefinition('{"packages": {"foo": "1.2.4"}}') env_def_3 = EnvironmentDefinition('{"packages": {"foo": "1.2.3"}}') assert env_def_1 == env_def_3 assert env_def_2 != env_def_3 assert env_def_2 == env_def_2
def test_env_service_multiple_env_defs(env_service: EnvironmentService): for n in [4, 5, 6]: _ = EnvironmentDefinition('{"packages": {"foo": "1.2.%s"}}' % n) env_service.save_env_def(_) env_id = EnvironmentDefinition('{"packages": {"foo": "1.2.5"}}').id env_def = env_service.get_env_def(env_id) assert env_def.id == env_id assert env_def.packages == {"foo": "1.2.5"}
def test_create_invalid_environment_definition_blank_packages(): with pytest.raises( ValueError, match="Your packages cannot be blank/empty in your " "environment definition.", ): EnvironmentDefinition('{"packages": {}}')
def test_create_invalid_environment_definition_missing_packages(): with pytest.raises( ValueError, match='You must specify "packages" in your ' "environment definition.", ): EnvironmentDefinition("{}")
def test_create_simple_environment_definition(): env_def = EnvironmentDefinition(TEST_INPUT) assert env_def.id == "92f9752" assert (env_def.long_id == "92f97528f268a88eb83866586f9281634d4b93b2c98503326ba99363f55c0e0c") assert env_def.packages == {"foo": "1.2.3"} assert env_def.channels == []
def test_get_env_def_for_app_env(mock_responses, client): mock_responses.add( responses.GET, "http://baseurl/appenvs?app=myapp&env=myenv", json={ "app": "myapp", "env": "myenv", "env_def": { "env_id": "abcd1234", "packages": { "pkg-a": "1.0", "pkg-b": ">2.0,<3", }, "channels": [], }, }, status=200, ) app_env = client.get_env_def_for_app_env("myapp", "myenv") expected_app_env = ApplicationEnvironment( app=Application("myapp"), env="myenv", env_def=EnvironmentDefinition.from_dict( {"packages": { "pkg-a": "1.0", "pkg-b": ">2.0,<3" }}), ) assert expected_app_env == app_env
def _env_def_from_orm_to_business_model( cls, env_def_orm_obj: EnvDef) -> EnvironmentDefinition: env_def = EnvironmentDefinition.from_dict(env_def_orm_obj.env_def) if env_def.id != env_def_orm_obj.id: # sanity check raise EnvironmentPersistenceError( f"IDs do not match: {env_def.id = } != {env_def_orm_obj.id = }" ) return env_def
def test_associate_application_environment(env_service: EnvironmentService): env_def = EnvironmentDefinition('{"packages": {"foo": "9.8.7"}}') env_service.save_env_def(env_def) app = Application(name="my-app") app_env = ApplicationEnvironment(app=app, env="prod", env_def=env_def) env_service.save_app_env(app_env)
def test_env_service_roundtrip_persistence_for_env_def( env_service: EnvironmentService): raw = '{"packages": {"foo": "1.2.3"}}' env_def = EnvironmentDefinition(raw) env_service.save_env_def(env_def) env_def_returned = env_service.get_env_def(env_def.id) assert env_def_returned == env_def
def test_deploy_env_with_command_first_time(): deployer = MockDeployer() deployer.env_exists = Mock(return_value=False) deployer.create_env = Mock() deployer.execute = Mock() env_def = EnvironmentDefinition(TEST_INPUT) deployer.deploy(env_def, 'command.exe') deployer.env_exists.assert_called_once_with(env_def.long_id) deployer.create_env.assert_called_once_with(env_def) deployer.execute.assert_called_once_with(env_def.long_id, 'command.exe')
def test_create_env_second_time(): deployer = MockDeployer() deployer.env_exists = Mock(return_value=True) deployer.create_env = Mock() deployer.execute = Mock() env_def = EnvironmentDefinition(TEST_INPUT) deployer.deploy(env_def) deployer.env_exists.assert_called_once_with(env_def.long_id) deployer.create_env.assert_not_called() deployer.execute.assert_not_called()
async def create_env_def(env_def: EnvDef): # TODO: do we need here 2 models, one pure Python and one Pydantic? I don't think # so... ? env_def_dict = {"packages": env_def.packages} if env_def.channels: env_def_dict["channels"] = env_def.channels env_def = EnvironmentDefinition.from_dict(env_def_dict) store.save_env_def(env_def) return {"env_id": env_def.id}
def test_create_env_first_time_id(): deployer = MockDeployer(use_long_id=False) deployer.env_exists = Mock(return_value=False) deployer.create_env = Mock() deployer.execute = Mock() env_def = EnvironmentDefinition(TEST_INPUT) deployer.deploy(env_def) deployer.env_exists.assert_called_once_with(env_def.id) deployer.create_env.assert_called_once_with(env_def) deployer.execute.assert_not_called()
def test_application_environment_roundtrip(env_service: EnvironmentService): env_def = EnvironmentDefinition('{"packages": {"foo": "9.8.7"}}') env_service.save_env_def(env_def) app = Application(name="my-app") app_env = ApplicationEnvironment(app=app, env="prod", env_def=env_def) env_service.save_app_env(app_env) app_env_returned = env_service.get_app_env("my-app", "prod") assert app_env_returned.env_def.packages == {"foo": "9.8.7"}
def test_deployer_can_run_something(): deployer = InMemoryDeploymentBackend() assert [] == deployer.envs assert [] == deployer.executed_commands env_def = EnvironmentDefinition('{"packages": {"foo": "1.2.3"}}') cmd_args = ["command-main", "arg1", "--option=foo"] deployer.run(env_def, cmd_args) assert [("92f9752", env_def)] == deployer.envs assert [("92f9752", cmd_args)] == deployer.executed_commands # If you run something on the environment for the second time # it doesn't create a new environment cmd_args_2 = ["other-command"] deployer.run(env_def, cmd_args_2) assert [("92f9752", env_def)] == deployer.envs assert [ ("92f9752", cmd_args), ("92f9752", cmd_args_2), ] == deployer.executed_commands # Now we want to use a brand new environment env_def_2 = EnvironmentDefinition('{"packages": {"bar": "4.5.6"}}') cmd_args_3 = ["foobar", "123"] deployer.run(env_def_2, cmd_args_3) # We will check that environment was created - new deployment! assert [("92f9752", env_def), ("95e885b", env_def_2)] == deployer.envs expected_runs = [ ("92f9752", cmd_args), ("92f9752", cmd_args_2), ("95e885b", cmd_args_3), ] assert expected_runs == deployer.executed_commands
def test_run(env_service: EnvironmentService): # First we create an environment definition env_def = EnvironmentDefinition('{"packages": {"foo": "9.8.7"}}') env_service.save_env_def(env_def) # Then we associate it to an (application, environment) app = Application(name="some_app") app_env = ApplicationEnvironment(app=app, env="uat", env_def=env_def) env_service.save_app_env(app_env) # Finally we ask the service to run something on that (app, env) env_service.run("some_app", "uat", ["hello", "world"]) # We can then run the asserts against the deployment backend inside our service assert [("b262deb", env_def)] == env_service.deployment_backend.envs assert [("b262deb", ["hello", "world"]) ] == env_service.deployment_backend.executed_commands
def test_conda_deployment_backend_run_command(mocker, in_memory_store): backend = CondaDeploymentBackend() service = EnvironmentService(store=in_memory_store, deployment_backend=backend) env_def = EnvironmentDefinition( '{"packages": {"foo": "1.2.3", "bar": ">=4.5.1,<5.0"}}') service.save_env_def(env_def) app = Application(name="my-app") app_env = ApplicationEnvironment(app=app, env="prod", env_def=env_def) service.save_app_env(app_env) mock_subprocess_run = mocker.patch( "ee.backends.conda_deployment_backend.subprocess.run", autospec=True) mock_subprocess_Popen = mocker.patch( "ee.backends.conda_deployment_backend.subprocess.Popen", autospec=True) service.run("my-app", "prod", ["foo", "bar"]) # This should trigger 3 calls to subprocess.run # 1) for checking if the environment already exists # 2) for creating the environment the first time # 3) for running the command assert mock_subprocess_run.call_args_list == [ mocker.call(["conda", "list", "-n", "412b992"], shell=SHELL, capture_output=True), mocker.call( 'conda create -n 412b992 -y "foo=1.2.3" "bar>=4.5.1,<5.0"'.split(), shell=SHELL, capture_output=True, ), ] mock_subprocess_Popen.assert_has_calls([ mocker.call( "conda run --no-capture-output -n base " "conda run --no-capture-output -n 412b992 foo bar".split(), shell=SHELL, ), ])
def get_env_def_for_app_env(self, app: str, env: str) -> ApplicationEnvironment: """Send a request to fetch the env def for the pair (app, env).""" # TODO: should return only the EnvironmentDefinition instead? url = f"{self.base_url}/appenvs?app={app}&env={env}" resp = self.client.get(url, timeout=EE_REQUEST_TIMEOUT) resp.raise_for_status() data = resp.json() env_def_dict = {"packages": data["env_def"]["packages"]} if data["env_def"].get("channels"): env_def_dict["channels"] = data["env_def"]["channels"] app_env = ApplicationEnvironment( app=Application(data["app"]), env=data["env"], env_def=EnvironmentDefinition.from_dict(env_def_dict), ) return app_env
def new(filename: Path): """Add a new Env Spec based on the given filename. It must be a JSON file defining "packages". Optionally it may define "channels". E.g.: # my_env.json { "packages": { "pandas": ">=1.2.1,<1.3", "python": "3.8" } } $ ee new my_env.json """ temp = EnvironmentDefinition(filename.open().read()) env_def = EnvDef(packages=temp.packages, channels=temp.channels) typer.echo(f"Read EnvironmentDefinition from file: {filename}") env_id = client.new_env_def(env_def) typer.secho(f"New Environment defined. ID: {env_id}", fg="yellow")
def test_create_environment_with_channels_specified(): env_def = EnvironmentDefinition(INPUT_WITH_CHANNELS) assert env_def.channels == ["chan1", "chan2"] assert env_def.id == "22594ba"
def simple_env_def(): env_def_dict = {"packages": {"pandas": ">=1.1,<1.2"}} raw = json.dumps(env_def_dict) env_def = EnvironmentDefinition(raw) return env_def