Skip to content

Commit f3388b1

Browse files
committed
fix: truncate proposal name in label values to 63 chars
The operator uses proposal.Name directly as a Kubernetes label value (agentic.openshift.io/proposal) when creating result CRs, RBAC resources, sandbox pods, and SandboxClaim CRs. Kubernetes label values are limited to 63 characters, so names exceeding this limit cause resource creation failures. Apply truncateK8sName() to proposalName in all four label-setting locations as defense in depth. Signed-off-by: Tomáš Remeš <tremes@redhat.com> Assisted-by: Claude Code:claude-opus-4-6
1 parent 2bfc16f commit f3388b1

8 files changed

Lines changed: 71 additions & 4 deletions

File tree

controller/proposal/bare_pod_manager.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func (m *BarePodManager) Claim(ctx context.Context, proposalName, step, _ string
7272
Name: podName,
7373
Namespace: m.Namespace,
7474
Labels: map[string]string{
75-
LabelProposal: proposalName,
75+
LabelProposal: truncateK8sName(proposalName),
7676
LabelStep: step,
7777
},
7878
},

controller/proposal/bare_pod_manager_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package proposal
22

33
import (
44
"context"
5+
"strings"
56
"testing"
67
"time"
78

@@ -79,6 +80,32 @@ func TestBarePodManager_Claim_UsesPerProposalSA(t *testing.T) {
7980
}
8081
}
8182

83+
func TestBarePodManager_Claim_TruncatesLongProposalNameInLabel(t *testing.T) {
84+
fc := newBarePodClient().Build()
85+
builder := &PodSpecBuilder{Image: "quay.io/test/sandbox:latest"}
86+
m := NewBarePodManager(fc, builder, "test-ns")
87+
m.SetStep(
88+
&agenticv1alpha1.Agent{Spec: agenticv1alpha1.AgentSpec{Model: "claude-opus-4-6"}},
89+
testLLMProvider(agenticv1alpha1.LLMProviderAnthropic),
90+
nil,
91+
defaultSandboxSA,
92+
)
93+
94+
longName := strings.Repeat("a", 80)
95+
name, err := m.Claim(context.Background(), longName, "analysis", "")
96+
if err != nil {
97+
t.Fatalf("Claim: %v", err)
98+
}
99+
100+
var pod corev1.Pod
101+
if err := fc.Get(context.Background(), types.NamespacedName{Name: name, Namespace: "test-ns"}, &pod); err != nil {
102+
t.Fatalf("pod not created: %v", err)
103+
}
104+
if len(pod.Labels[LabelProposal]) > 63 {
105+
t.Fatalf("proposal label length %d exceeds 63", len(pod.Labels[LabelProposal]))
106+
}
107+
}
108+
82109
func TestBarePodManager_Claim_AlreadyExists(t *testing.T) {
83110
existing := &corev1.Pod{}
84111
existing.Name = "ls-analysis-my-proposal"

controller/proposal/rbac.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ func clusterRoleName(proposalName string) string {
228228

229229
func rbacLabels(proposalName, component string) map[string]string {
230230
return map[string]string{
231-
LabelProposal: proposalName,
231+
LabelProposal: truncateK8sName(proposalName),
232232
LabelComponent: component,
233233
}
234234
}

controller/proposal/rbac_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,3 +778,14 @@ func TestRBACLabels(t *testing.T) {
778778
t.Fatalf("expected 2 labels, got %d", len(labels))
779779
}
780780
}
781+
782+
func TestRBACLabels_TruncatesLongProposalName(t *testing.T) {
783+
longName := strings.Repeat("a", 80)
784+
labels := rbacLabels(longName, "execution-rbac")
785+
if len(labels[LabelProposal]) > 63 {
786+
t.Fatalf("proposal label length %d exceeds 63", len(labels[LabelProposal]))
787+
}
788+
if labels[LabelProposal] != strings.Repeat("a", 63) {
789+
t.Errorf("proposal label = %q, want %q", labels[LabelProposal], strings.Repeat("a", 63))
790+
}
791+
}

controller/proposal/results.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func proposalOwnerRef(proposal *agenticv1alpha1.Proposal) metav1.OwnerReference
3636

3737
func resultLabels(proposalName, step string) map[string]string {
3838
return map[string]string{
39-
LabelProposal: proposalName,
39+
LabelProposal: truncateK8sName(proposalName),
4040
LabelStep: step,
4141
}
4242
}

controller/proposal/results_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package proposal
22

33
import (
44
"context"
5+
"strings"
56
"testing"
67

78
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -11,6 +12,20 @@ import (
1112
agenticv1alpha1 "github.com/openshift/lightspeed-agentic-operator/api/v1alpha1"
1213
)
1314

15+
func TestResultLabels_TruncatesLongProposalName(t *testing.T) {
16+
longName := strings.Repeat("a", 80)
17+
labels := resultLabels(longName, "analysis")
18+
if len(labels[LabelProposal]) > 63 {
19+
t.Fatalf("proposal label length %d exceeds 63", len(labels[LabelProposal]))
20+
}
21+
if labels[LabelProposal] != strings.Repeat("a", 63) {
22+
t.Errorf("proposal label = %q, want %q", labels[LabelProposal], strings.Repeat("a", 63))
23+
}
24+
if labels[LabelStep] != "analysis" {
25+
t.Errorf("step label = %q, want analysis", labels[LabelStep])
26+
}
27+
}
28+
1429
func TestCreateIdempotent_StatusFieldsWritten(t *testing.T) {
1530
scheme := testScheme()
1631
fc := fake.NewClientBuilder().WithScheme(scheme).

controller/proposal/sandbox.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func (m *SandboxManager) buildClaim(claimName, proposalName, step, templateName
8080
"name": claimName,
8181
"namespace": m.Namespace,
8282
"labels": map[string]any{
83-
LabelProposal: proposalName,
83+
LabelProposal: truncateK8sName(proposalName),
8484
LabelStep: step,
8585
},
8686
},

controller/proposal/sandbox_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,20 @@ func TestBuildClaim_Labels(t *testing.T) {
107107
}
108108
}
109109

110+
func TestBuildClaim_TruncatesLongProposalName(t *testing.T) {
111+
longName := strings.Repeat("a", 80)
112+
m := NewSandboxManager(nil, "ns", "")
113+
claim := m.buildClaim("c", longName, "execution", "tpl")
114+
115+
labels := claim.GetLabels()
116+
if len(labels[LabelProposal]) > 63 {
117+
t.Fatalf("proposal label length %d exceeds 63", len(labels[LabelProposal]))
118+
}
119+
if labels[LabelProposal] != strings.Repeat("a", 63) {
120+
t.Errorf("proposal label = %q, want %q", labels[LabelProposal], strings.Repeat("a", 63))
121+
}
122+
}
123+
110124
func TestBuildClaim_TemplateRef(t *testing.T) {
111125
m := NewSandboxManager(nil, "ns", "")
112126
claim := m.buildClaim("c", "p", "analysis", "my-template")

0 commit comments

Comments
 (0)