Skip to content

Commit 06d5fb5

Browse files
chad119Chad Chiang
andauthored
Add crd format check for inference (#369)
* integration test for jumpstart with mig profile * template fix for mig with jumpstart * skipped mig tests until instances setup finished * enable the mig integration tests * add crd format check for inference --------- Co-authored-by: Chad Chiang <chadchc@amazon.com>
1 parent dd41610 commit 06d5fb5

1 file changed

Lines changed: 163 additions & 0 deletions

File tree

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
"""
2+
Simplified unit tests for CRD format validation.
3+
4+
This module contains essential tests to validate the basic format and structure
5+
of the CRD YAML files used by the inference operator, focusing on core
6+
Kubernetes CRD requirements.
7+
"""
8+
9+
import unittest
10+
import yaml
11+
from pathlib import Path
12+
13+
14+
class TestCRDFormat(unittest.TestCase):
15+
"""Test class for validating essential CRD format requirements."""
16+
17+
def setUp(self):
18+
"""Set up test class with file paths."""
19+
self.base_path = Path(__file__).parent.parent.parent.parent
20+
self.crd_path = self.base_path / "helm_chart" / "HyperPodHelmChart" / "charts" / "inference-operator" / "config" / "crd"
21+
22+
self.crd_files = [
23+
self.crd_path / "inference.sagemaker.aws.amazon.com_inferenceendpointconfigs.yaml",
24+
self.crd_path / "inference.sagemaker.aws.amazon.com_jumpstartmodels.yaml",
25+
self.crd_path / "inference.sagemaker.aws.amazon.com_sagemakerendpointregistrations.yaml"
26+
]
27+
28+
def test_crd_files_exist_and_valid_yaml(self):
29+
"""Test that all CRD files exist and have valid YAML syntax."""
30+
for file_path in self.crd_files:
31+
with self.subTest(file=file_path.name):
32+
# Check file exists
33+
self.assertTrue(file_path.exists(), f"CRD file does not exist: {file_path}")
34+
35+
# Check for tab characters (not allowed in YAML)
36+
with open(file_path, 'r', encoding='utf-8') as f:
37+
content_text = f.read()
38+
if '\t' in content_text:
39+
self.fail(f"File {file_path.name} contains tab characters. YAML should use spaces for indentation.")
40+
41+
# Check valid YAML
42+
with open(file_path, 'r', encoding='utf-8') as f:
43+
try:
44+
content = yaml.safe_load(f)
45+
self.assertIsNotNone(content, f"YAML content is empty in {file_path.name}")
46+
except yaml.YAMLError as e:
47+
self.fail(f"Invalid YAML syntax in {file_path.name}: {e}")
48+
49+
def test_required_crd_structure(self):
50+
"""Test that all CRD files have the required Kubernetes CRD structure."""
51+
for file_path in self.crd_files:
52+
with self.subTest(file=file_path.name):
53+
with open(file_path, 'r', encoding='utf-8') as f:
54+
content = yaml.safe_load(f)
55+
56+
# Check required top-level fields
57+
required_fields = ['apiVersion', 'kind', 'metadata', 'spec']
58+
for field in required_fields:
59+
self.assertIn(field, content, f"Missing required field '{field}' in {file_path.name}")
60+
61+
# Verify this is a CustomResourceDefinition
62+
self.assertEqual(content['apiVersion'], "apiextensions.k8s.io/v1",
63+
f"Expected apiVersion 'apiextensions.k8s.io/v1' in {file_path.name}")
64+
self.assertEqual(content['kind'], "CustomResourceDefinition",
65+
f"Expected kind 'CustomResourceDefinition' in {file_path.name}")
66+
67+
def test_crd_spec_structure(self):
68+
"""Test that CRD spec has required fields and basic structure."""
69+
for file_path in self.crd_files:
70+
with self.subTest(file=file_path.name):
71+
with open(file_path, 'r', encoding='utf-8') as f:
72+
content = yaml.safe_load(f)
73+
74+
spec = content.get('spec', {})
75+
76+
# Check required spec fields
77+
required_spec_fields = ['group', 'names', 'scope', 'versions']
78+
for field in required_spec_fields:
79+
self.assertIn(field, spec, f"Missing required spec field '{field}' in {file_path.name}")
80+
81+
# Validate spec.group
82+
self.assertEqual(spec['group'], "inference.sagemaker.aws.amazon.com",
83+
f"Expected group 'inference.sagemaker.aws.amazon.com' in {file_path.name}")
84+
85+
# Validate spec.scope
86+
self.assertEqual(spec['scope'], "Namespaced",
87+
f"Expected scope 'Namespaced' in {file_path.name}")
88+
89+
def test_crd_names_structure(self):
90+
"""Test that CRD names section has required fields."""
91+
for file_path in self.crd_files:
92+
with self.subTest(file=file_path.name):
93+
with open(file_path, 'r', encoding='utf-8') as f:
94+
content = yaml.safe_load(f)
95+
96+
names = content.get('spec', {}).get('names', {})
97+
98+
# Check required names fields
99+
required_names_fields = ['kind', 'listKind', 'plural', 'singular']
100+
for field in required_names_fields:
101+
self.assertIn(field, names, f"Missing required names field '{field}' in {file_path.name}")
102+
self.assertTrue(names[field], f"Empty value for names.{field} in {file_path.name}")
103+
104+
def test_crd_versions_structure(self):
105+
"""Test that CRD versions are properly structured with required fields."""
106+
for file_path in self.crd_files:
107+
with self.subTest(file=file_path.name):
108+
with open(file_path, 'r', encoding='utf-8') as f:
109+
content = yaml.safe_load(f)
110+
111+
versions = content.get('spec', {}).get('versions', [])
112+
113+
# Validate versions is a non-empty list
114+
self.assertIsInstance(versions, list, f"spec.versions should be a list in {file_path.name}")
115+
self.assertGreater(len(versions), 0, f"spec.versions should not be empty in {file_path.name}")
116+
117+
# Check each version has required fields
118+
for i, version in enumerate(versions):
119+
required_version_fields = ['name', 'served', 'storage', 'schema']
120+
for field in required_version_fields:
121+
self.assertIn(field, version,
122+
f"Missing required field '{field}' in version {i} of {file_path.name}")
123+
124+
# Validate schema has openAPIV3Schema
125+
schema = version.get('schema', {})
126+
self.assertIn('openAPIV3Schema', schema,
127+
f"Missing 'openAPIV3Schema' in version {i} schema of {file_path.name}")
128+
129+
openapi_schema = schema.get('openAPIV3Schema', {})
130+
self.assertIn('type', openapi_schema,
131+
f"Missing 'type' in openAPIV3Schema for version {i} of {file_path.name}")
132+
self.assertEqual(openapi_schema['type'], 'object',
133+
f"Expected 'type: object' in openAPIV3Schema for version {i} of {file_path.name}")
134+
135+
def test_metadata_name_format(self):
136+
"""Test that metadata.name follows the expected CRD naming convention."""
137+
expected_names = {
138+
'inferenceendpointconfigs': 'inferenceendpointconfigs.inference.sagemaker.aws.amazon.com',
139+
'jumpstartmodels': 'jumpstartmodels.inference.sagemaker.aws.amazon.com',
140+
'sagemakerendpointregistrations': 'sagemakerendpointregistrations.inference.sagemaker.aws.amazon.com'
141+
}
142+
143+
for file_path in self.crd_files:
144+
with self.subTest(file=file_path.name):
145+
with open(file_path, 'r', encoding='utf-8') as f:
146+
content = yaml.safe_load(f)
147+
148+
name = content.get('metadata', {}).get('name', '')
149+
150+
# Find expected name based on filename
151+
expected_name = None
152+
for key, value in expected_names.items():
153+
if key in file_path.name:
154+
expected_name = value
155+
break
156+
157+
self.assertIsNotNone(expected_name, f"Could not determine expected name for {file_path.name}")
158+
self.assertEqual(name, expected_name,
159+
f"Expected metadata.name '{expected_name}' in {file_path.name}, got '{name}'")
160+
161+
162+
if __name__ == '__main__':
163+
unittest.main()

0 commit comments

Comments
 (0)