Skip to content

Commit a18c770

Browse files
COMPUTE-1703_instance_type_selector_validation
(feat) app_builder.py, test_dxpy.py - added regional options
1 parent 084823d commit a18c770

File tree

2 files changed

+235
-2
lines changed

2 files changed

+235
-2
lines changed

src/python/dxpy/app_builder.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,28 @@ def _validate_applet_spec(applet_spec):
101101
if 'runSpec' not in applet_spec:
102102
raise AppBuilderException("Required field 'runSpec' not found in dxapp.json")
103103

104-
# Validate systemRequirements for instanceTypeSelector constraints
104+
# Validate systemRequirements for instanceTypeSelector constraints in runSpec
105105
if 'systemRequirements' in applet_spec.get('runSpec', {}):
106106
_validate_system_requirements(applet_spec['runSpec']['systemRequirements'])
107107

108+
# Validate systemRequirements in regionalOptions for each region
109+
regional_options = applet_spec.get('regionalOptions', {})
110+
if isinstance(regional_options, dict):
111+
for region, options in regional_options.items():
112+
if isinstance(options, dict) and 'systemRequirements' in options:
113+
_validate_system_requirements(options['systemRequirements'])
114+
108115
def _validate_app_spec(app_spec):
109-
pass
116+
# Validate systemRequirements for instanceTypeSelector constraints in runSpec
117+
if 'runSpec' in app_spec and 'systemRequirements' in app_spec['runSpec']:
118+
_validate_system_requirements(app_spec['runSpec']['systemRequirements'])
119+
120+
# Validate systemRequirements in regionalOptions for each region
121+
regional_options = app_spec.get('regionalOptions', {})
122+
if isinstance(regional_options, dict):
123+
for region, options in regional_options.items():
124+
if isinstance(options, dict) and 'systemRequirements' in options:
125+
_validate_system_requirements(options['systemRequirements'])
110126

111127
def _get_applet_spec(src_dir):
112128
applet_spec_file = os.path.join(src_dir, "dxapp.json")

src/python/test/test_dxpy.py

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3303,6 +3303,223 @@ def test_validate_system_requirements_instance_type_selector(self):
33033303
self.assertIn("mutually exclusive", str(cm.exception))
33043304
self.assertIn("process", str(cm.exception))
33053305

3306+
def test_validate_system_requirements_in_regional_options(self):
3307+
"""Test that instanceTypeSelector validation works for regionalOptions"""
3308+
validate_applet = app_builder._validate_applet_spec
3309+
validate_app = app_builder._validate_app_spec
3310+
3311+
# Valid cases - should not raise exceptions
3312+
3313+
# Valid: instanceTypeSelector in regionalOptions
3314+
validate_applet({
3315+
"name": "test_applet",
3316+
"runSpec": {
3317+
"interpreter": "python3",
3318+
"file": "code.py"
3319+
},
3320+
"regionalOptions": {
3321+
"aws:us-east-1": {
3322+
"systemRequirements": {
3323+
"main": {
3324+
"instanceTypeSelector": {
3325+
"allowedInstanceTypes": ["mem1_ssd1_x4", "mem1_ssd1_x8"]
3326+
}
3327+
}
3328+
}
3329+
}
3330+
}
3331+
})
3332+
3333+
# Valid: Different system requirements per region
3334+
validate_applet({
3335+
"name": "test_applet",
3336+
"runSpec": {
3337+
"interpreter": "python3",
3338+
"file": "code.py"
3339+
},
3340+
"regionalOptions": {
3341+
"aws:us-east-1": {
3342+
"systemRequirements": {
3343+
"main": {
3344+
"instanceType": "mem1_ssd1_x4"
3345+
}
3346+
}
3347+
},
3348+
"azure:westus": {
3349+
"systemRequirements": {
3350+
"main": {
3351+
"instanceTypeSelector": {
3352+
"allowedInstanceTypes": ["mem1_ssd1_x8"]
3353+
}
3354+
}
3355+
}
3356+
}
3357+
}
3358+
})
3359+
3360+
# Valid: systemRequirements in both runSpec and regionalOptions (different entry points)
3361+
validate_applet({
3362+
"name": "test_applet",
3363+
"runSpec": {
3364+
"interpreter": "python3",
3365+
"file": "code.py",
3366+
"systemRequirements": {
3367+
"main": {
3368+
"instanceType": "mem1_ssd1_x2"
3369+
}
3370+
}
3371+
},
3372+
"regionalOptions": {
3373+
"aws:us-east-1": {
3374+
"systemRequirements": {
3375+
"process": {
3376+
"instanceTypeSelector": {
3377+
"allowedInstanceTypes": ["mem1_ssd1_x4"]
3378+
}
3379+
}
3380+
}
3381+
}
3382+
}
3383+
})
3384+
3385+
# Invalid cases - should raise AppBuilderException
3386+
3387+
# Invalid: instanceType + instanceTypeSelector in regionalOptions
3388+
with self.assertRaises(app_builder.AppBuilderException) as cm:
3389+
validate_applet({
3390+
"name": "test_applet",
3391+
"runSpec": {
3392+
"interpreter": "python3",
3393+
"file": "code.py"
3394+
},
3395+
"regionalOptions": {
3396+
"aws:us-east-1": {
3397+
"systemRequirements": {
3398+
"main": {
3399+
"instanceType": "mem1_ssd1_x4",
3400+
"instanceTypeSelector": {
3401+
"allowedInstanceTypes": ["mem1_ssd1_x4", "mem1_ssd1_x8"]
3402+
}
3403+
}
3404+
}
3405+
}
3406+
}
3407+
})
3408+
self.assertIn("mutually exclusive", str(cm.exception))
3409+
self.assertIn("instanceType", str(cm.exception))
3410+
self.assertIn("instanceTypeSelector", str(cm.exception))
3411+
3412+
# Invalid: instanceTypeSelector + clusterSpec in regionalOptions
3413+
with self.assertRaises(app_builder.AppBuilderException) as cm:
3414+
validate_applet({
3415+
"name": "test_applet",
3416+
"runSpec": {
3417+
"interpreter": "python3",
3418+
"file": "code.py"
3419+
},
3420+
"regionalOptions": {
3421+
"azure:westus": {
3422+
"systemRequirements": {
3423+
"main": {
3424+
"instanceTypeSelector": {
3425+
"allowedInstanceTypes": ["mem1_ssd1_x4"]
3426+
},
3427+
"clusterSpec": {
3428+
"type": "generic",
3429+
"numInstances": 3
3430+
}
3431+
}
3432+
}
3433+
}
3434+
}
3435+
})
3436+
self.assertIn("mutually exclusive", str(cm.exception))
3437+
self.assertIn("instanceTypeSelector", str(cm.exception))
3438+
self.assertIn("clusterSpec", str(cm.exception))
3439+
3440+
# Invalid: Error in one region out of multiple
3441+
with self.assertRaises(app_builder.AppBuilderException) as cm:
3442+
validate_applet({
3443+
"name": "test_applet",
3444+
"runSpec": {
3445+
"interpreter": "python3",
3446+
"file": "code.py"
3447+
},
3448+
"regionalOptions": {
3449+
"aws:us-east-1": {
3450+
"systemRequirements": {
3451+
"main": {
3452+
"instanceType": "mem1_ssd1_x4"
3453+
}
3454+
}
3455+
},
3456+
"azure:westus": {
3457+
"systemRequirements": {
3458+
"main": {
3459+
"instanceType": "mem1_ssd1_x4",
3460+
"instanceTypeSelector": {
3461+
"allowedInstanceTypes": ["mem1_ssd1_x8"]
3462+
}
3463+
}
3464+
}
3465+
}
3466+
}
3467+
})
3468+
self.assertIn("mutually exclusive", str(cm.exception))
3469+
3470+
# Test with app spec as well
3471+
with self.assertRaises(app_builder.AppBuilderException) as cm:
3472+
validate_app({
3473+
"name": "test_app",
3474+
"version": "1.0.0",
3475+
"runSpec": {
3476+
"interpreter": "python3",
3477+
"file": "code.py"
3478+
},
3479+
"regionalOptions": {
3480+
"aws:us-east-1": {
3481+
"systemRequirements": {
3482+
"main": {
3483+
"instanceType": "mem1_ssd1_x4",
3484+
"instanceTypeSelector": {
3485+
"allowedInstanceTypes": ["mem1_ssd1_x4"]
3486+
}
3487+
}
3488+
}
3489+
}
3490+
}
3491+
})
3492+
self.assertIn("mutually exclusive", str(cm.exception))
3493+
3494+
# Invalid: Multiple entry points with error in regionalOptions
3495+
with self.assertRaises(app_builder.AppBuilderException) as cm:
3496+
validate_applet({
3497+
"name": "test_applet",
3498+
"runSpec": {
3499+
"interpreter": "python3",
3500+
"file": "code.py"
3501+
},
3502+
"regionalOptions": {
3503+
"aws:us-east-1": {
3504+
"systemRequirements": {
3505+
"main": {
3506+
"instanceTypeSelector": {
3507+
"allowedInstanceTypes": ["mem1_ssd1_x4"]
3508+
}
3509+
},
3510+
"process": {
3511+
"instanceType": "mem1_ssd1_x4",
3512+
"instanceTypeSelector": {
3513+
"allowedInstanceTypes": ["mem1_ssd1_x8"]
3514+
}
3515+
}
3516+
}
3517+
}
3518+
}
3519+
})
3520+
self.assertIn("mutually exclusive", str(cm.exception))
3521+
self.assertIn("process", str(cm.exception))
3522+
33063523
class TestWorkflowBuilderUtils(testutil.DXTestCaseBuildWorkflows):
33073524
def setUp(self):
33083525
super(TestWorkflowBuilderUtils, self).setUp()

0 commit comments

Comments
 (0)