@@ -3305,6 +3305,21 @@ def test_dx_run_extra_args_instanceTypeSelector_rejected(self):
33053305 run('dx run ' + applet_id + ' --extra-args ' +
33063306 '\'{"regionalOptions": {"aws:us-east-1": {"systemRequirements": {"*": {"instanceTypeSelector": {"allowedInstanceTypes": ["mem2_ssd1_v2_x2"]}}}}}}\'')
33073307
3308+ # Test with systemRequirementsByExecutable
3309+ with self.assertSubprocessFailure(stderr_regexp='instanceTypeSelector cannot be specified in runtime', exit_code=3):
3310+ run('dx run ' + applet_id + ' --extra-args ' +
3311+ '\'{"systemRequirementsByExecutable": {"' + applet_id + '": {"main": {"instanceTypeSelector": {"allowedInstanceTypes": ["mem2_ssd1_v2_x2"]}}}}}\'')
3312+
3313+ # Test with systemRequirementsByExecutable with wildcard entry point
3314+ with self.assertSubprocessFailure(stderr_regexp='instanceTypeSelector cannot be specified in runtime', exit_code=3):
3315+ run('dx run ' + applet_id + ' --extra-args ' +
3316+ '\'{"systemRequirementsByExecutable": {"' + applet_id + '": {"*": {"instanceTypeSelector": {"allowedInstanceTypes": ["mem2_ssd1_v2_x2"]}}}}}\'')
3317+
3318+ # Test with regionalOptions.systemRequirementsByExecutable
3319+ with self.assertSubprocessFailure(stderr_regexp='instanceTypeSelector cannot be specified in runtime', exit_code=3):
3320+ run('dx run ' + applet_id + ' --extra-args ' +
3321+ '\'{"regionalOptions": {"aws:us-east-1": {"systemRequirementsByExecutable": {"' + applet_id + '": {"main": {"instanceTypeSelector": {"allowedInstanceTypes": ["mem2_ssd1_v2_x2"]}}}}}}}\'')
3322+
33083323 def test_dx_run_sys_reqs(self):
33093324 app_spec = {"project": self.project,
33103325 "dxapi": "1.0.0",
@@ -3403,6 +3418,64 @@ def test_dx_run_clone_nvidia_driver(self):
34033418 cloned_job_desc = dxpy.api.job_describe(cloned_job_id_nvidia_override)
34043419 assert cloned_job_desc["systemRequirements"]["*"]["nvidiaDriver"] == build_nvidia_version
34053420
3421+ def test_dx_run_clone_with_instance_type_selector(self):
3422+ """
3423+ Test that when cloning a job from an applet that uses instanceTypeSelector:
3424+ 1. Cloning without runtime args works - instanceTypeSelector is preserved
3425+ 2. Cloning with --instance-type works - runtime instanceType overrides instanceTypeSelector
3426+ 3. Cloning with --instance-count fails - clusterSpec and instanceTypeSelector are mutually exclusive
3427+ 4. Job description contains instanceTypeSelector in systemRequirements
3428+ """
3429+ # Create an applet with instanceTypeSelector
3430+ applet_id = dxpy.api.applet_new({"project": self.project,
3431+ "dxapi": "1.0.0",
3432+ "runSpec": {"interpreter": "bash",
3433+ "distribution": "Ubuntu",
3434+ "release": "20.04",
3435+ "version": "0",
3436+ "code": "echo 'hello'",
3437+ "systemRequirements": {
3438+ "*": {
3439+ "instanceTypeSelector": {
3440+ "allowedInstanceTypes": ["mem1_ssd1_x2", "mem2_ssd1_x2"]
3441+ }
3442+ }
3443+ }}
3444+ })['id']
3445+
3446+ # Run the applet to create the origin job
3447+ # The API will resolve instanceTypeSelector to an actual instanceType
3448+ origin_job_id = run(f"dx run {applet_id} --brief -y").strip().split('\n')[-1]
3449+
3450+ # Verify the origin job description contains instanceTypeSelector
3451+ origin_job_desc = dxpy.api.job_describe(origin_job_id)
3452+ assert "systemRequirements" in origin_job_desc
3453+ assert "*" in origin_job_desc["systemRequirements"]
3454+ assert "instanceTypeSelector" in origin_job_desc["systemRequirements"]["*"]
3455+ assert "allowedInstanceTypes" in origin_job_desc["systemRequirements"]["*"]["instanceTypeSelector"]
3456+
3457+ # Clone without runtime args - should work, instanceTypeSelector is preserved
3458+ cloned_job_id_1 = run(f"dx run --clone {origin_job_id} --brief -y").strip()
3459+ assert cloned_job_id_1.startswith("job-")
3460+ cloned_job_desc_1 = dxpy.api.job_describe(cloned_job_id_1)
3461+ # Verify instanceTypeSelector is in the cloned job's systemRequirements
3462+ assert "instanceTypeSelector" in cloned_job_desc_1["systemRequirements"]["*"]
3463+
3464+ # Clone with --instance-type - should work, runtime instanceType overrides instanceTypeSelector
3465+ cloned_job_id_2 = run(f"dx run --clone {origin_job_id} --instance-type mem2_hdd2_x4 --brief -y").strip()
3466+ assert cloned_job_id_2.startswith("job-")
3467+ cloned_job_desc_2 = dxpy.api.job_describe(cloned_job_id_2)
3468+ # Verify instanceType is used instead of instanceTypeSelector
3469+ assert "instanceType" in cloned_job_desc_2["systemRequirements"]["*"]
3470+ assert cloned_job_desc_2["systemRequirements"]["*"]["instanceType"] == "mem2_hdd2_x4"
3471+ # instanceTypeSelector should not be in the runtime systemRequirements when overridden
3472+ # TODO uncomment later - for now this is not included in the backend behaviour
3473+ # assert "instanceTypeSelector" not in cloned_job_desc_2["systemRequirements"]["*"]
3474+
3475+ # Clone with --instance-count - should fail (clusterSpec and instanceTypeSelector are mutually exclusive)
3476+ with self.assertSubprocessFailure(stderr_regexp='Cannot specify --instance-count.*instanceTypeSelector', exit_code=3):
3477+ run(f"dx run --clone {origin_job_id} --instance-count 3 --brief -y")
3478+
34063479 def test_dx_run_clone(self):
34073480 applet_id = dxpy.api.applet_new({"project": self.project,
34083481 "dxapi": "1.0.0",
0 commit comments