Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions sentry_streams_k8s/sentry_streams_k8s/consumer_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def load_base_template(file_name: str) -> dict[str, Any]:
return cast(dict[str, Any], yaml.safe_load(template_content.read_text()))


def make_k8s_name(name: str) -> str:
def make_k8s_name(name: str, limit: int | None = None) -> str:
"""
Generate a valid Kubernetes name from a string.

Expand All @@ -74,6 +74,10 @@ def make_k8s_name(name: str) -> str:
name = name.replace(".", "-").replace("_", "-").lower()
name = re.sub(r"[^a-z0-9-]", "", name)
name = name.strip("-")
if limit is not None:
# Truncation can land on a hyphen (e.g. a dotted module path). Kubernetes
# label values must start and end with an alphanumeric character.
return name[:limit].rstrip("-")
return name


Expand Down Expand Up @@ -292,7 +296,7 @@ def _build_merged_pipeline_deployment(

def _pipeline_labels(spec: ConsumerSpec) -> dict[str, str]:
return {
"pipeline-app": make_k8s_name(spec.pipeline_module),
"pipeline-app": make_k8s_name(spec.pipeline_module, limit=63),
"pipeline": make_k8s_name(spec.pipeline_name),
"service": make_k8s_name(spec.service_name),
}
Expand Down
56 changes: 56 additions & 0 deletions sentry_streams_k8s/tests/test_pipeline_step.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,62 @@ def test_make_k8s_name() -> None:
# Test with special characters (should be removed)
assert make_k8s_name("my@module.sub#module") == "mymodule-submodule"

long_name = "a" * 80
assert make_k8s_name(long_name, limit=63) == "a" * 63
assert make_k8s_name(long_name) == long_name

# Truncation must not leave a trailing hyphen (invalid K8s label value).
assert make_k8s_name("abc.def", limit=4) == "abc"
assert not make_k8s_name("a.b.c.d.e.f.g.h", limit=8).endswith("-")


def test_pipeline_app_label_truncated_to_63_chars() -> None:
"""Kubernetes label values are limited to 63 characters."""
# Repeating "ab." sanitizes to "ab-ab-..." so a 63-char slice would end in "-".
long_module = "ab." * 30
sanitized = make_k8s_name(long_module)
assert len(sanitized) > 63
assert sanitized[:63].endswith("-")

context: dict[str, Any] = {
"service_name": "my-service",
"pipeline_name": "profiles",
"deployment_template": {},
"container_template": {},
"pipeline_config": {
"env": {},
"pipeline": {
"segments": [
{
"steps_config": {
"myinput": {
"starts_segment": True,
"bootstrap_servers": ["127.0.0.1:9092"],
}
}
}
]
},
},
"pipeline_module": long_module,
"image_name": "my-image:latest",
"cpu_per_process": 1000,
"memory_per_process": 512,
"segment_id": 0,
"replicas": 1,
}

result = PipelineStep().run(context)
expected = make_k8s_name(long_module, limit=63)
assert not expected.endswith("-")
assert len(expected) <= 63
assert result["deployment"]["metadata"]["labels"]["pipeline-app"] == expected
assert result["deployment"]["spec"]["selector"]["matchLabels"]["pipeline-app"] == expected
assert (
result["deployment"]["spec"]["template"]["metadata"]["labels"]["pipeline-app"] == expected
)
assert result["configmap"]["metadata"]["labels"]["pipeline-app"] == expected


def test_parse_context() -> None:

Expand Down
Loading