WIP: rebase for 2026-05-18 #7

Closed
codinget wants to merge 234 commits from rebase/2026-05-18 into webnet
4 changed files with 101 additions and 6 deletions
Showing only changes of commit 7735b15de3 - Show all commits
+1 -1
View File
@@ -1081,7 +1081,7 @@ func certResourceLabels(pgName, domain string) map[string]string {
return map[string]string{
kubetypes.LabelManaged: "true",
labelProxyGroup: pgName,
labelDomain: domain,
labelDomain: tsoperator.TruncateLabelValue(domain),
}
}
+6 -5
View File
@@ -19,6 +19,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
kube "tailscale.com/k8s-operator"
tsapi "tailscale.com/k8s-operator/apis/v1alpha1"
"tailscale.com/kube/kubetypes"
)
@@ -227,13 +228,13 @@ func metricsResourceLabels(opts *metricsOpts) map[string]string {
kubetypes.LabelManaged: "true",
labelMetricsTarget: opts.proxyStsName,
labelPromProxyType: opts.proxyType,
labelPromProxyParentName: opts.proxyLabels[LabelParentName],
labelPromProxyParentName: kube.TruncateLabelValue(opts.proxyLabels[LabelParentName]),
}
// Include namespace label for proxies created for a namespaced type.
if isNamespacedProxyType(opts.proxyType) {
lbls[labelPromProxyParentNamespace] = opts.proxyLabels[LabelParentNamespace]
lbls[labelPromProxyParentNamespace] = kube.TruncateLabelValue(opts.proxyLabels[LabelParentNamespace])
}
lbls[labelPromJob] = promJobName(opts)
lbls[labelPromJob] = kube.TruncateLabelValue(promJobName(opts))
return lbls
}
@@ -250,11 +251,11 @@ func promJobName(opts *metricsOpts) string {
func metricsSvcSelector(proxyLabels map[string]string, proxyType string) map[string]string {
sel := map[string]string{
labelPromProxyType: proxyType,
labelPromProxyParentName: proxyLabels[LabelParentName],
labelPromProxyParentName: kube.TruncateLabelValue(proxyLabels[LabelParentName]),
}
// Include namespace label for proxies created for a namespaced type.
if isNamespacedProxyType(proxyType) {
sel[labelPromProxyParentNamespace] = proxyLabels[LabelParentNamespace]
sel[labelPromProxyParentNamespace] = kube.TruncateLabelValue(proxyLabels[LabelParentNamespace])
}
return sel
}
+16
View File
@@ -7,6 +7,8 @@
package kube
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"tailscale.com/tailcfg"
@@ -50,3 +52,17 @@ func CapVerFromFileName(name string) (tailcfg.CapabilityVersion, error) {
_, err := fmt.Sscanf(name, "cap-%d.hujson", &cap)
return cap, err
}
// TruncateLabelValue truncates a Kubernetes label value to fit within the
// 63-character limit. If the value exceeds the limit, it is truncated and a
// short hash suffix is appended to preserve uniqueness.
func TruncateLabelValue(val string) string {
const maxLen = 63
if len(val) <= maxLen {
return val
}
hash := sha256.Sum256([]byte(val))
suffix := hex.EncodeToString(hash[:4]) // 8 hex chars
truncated := val[:maxLen-len(suffix)-1]
return truncated + "-" + suffix
}
+78
View File
@@ -0,0 +1,78 @@
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
//go:build !plan9
package kube
import (
"strings"
"testing"
)
func TestTruncateLabelValue(t *testing.T) {
tests := []struct {
name string
input string
want string // empty means expect input unchanged
}{
{
name: "short-value-unchanged",
input: "my-service",
},
{
name: "exactly-63-chars-unchanged",
input: strings.Repeat("a", 63),
},
{
name: "64-chars-gets-truncated",
input: strings.Repeat("a", 64),
},
{
name: "very-long-value-gets-truncated",
input: "tailscale-nginx-clickhouse-o11y-server-https-with-extra-long-suffix-that-exceeds-limit",
},
{
name: "253-chars-max-k8s-resource-name",
input: strings.Repeat("x", 253),
},
{
name: "empty-string-unchanged",
input: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := TruncateLabelValue(tt.input)
if len(got) > 63 {
t.Errorf("TruncateLabelValue(%q) = %q (len %d), exceeds 63 chars", tt.input, got, len(got))
}
if len(tt.input) <= 63 && got != tt.input {
t.Errorf("TruncateLabelValue(%q) = %q, want unchanged input", tt.input, got)
}
if len(tt.input) > 63 && got == tt.input {
t.Errorf("TruncateLabelValue(%q) was not truncated", tt.input)
}
})
}
}
func TestTruncateLabelValueDeterministic(t *testing.T) {
input := strings.Repeat("a", 100)
first := TruncateLabelValue(input)
for i := 0; i < 10; i++ {
got := TruncateLabelValue(input)
if got != first {
t.Fatalf("non-deterministic: got %q, want %q", got, first)
}
}
}
func TestTruncateLabelValueUniqueness(t *testing.T) {
// Two inputs sharing a long prefix but differing at the end should produce different outputs.
a := strings.Repeat("a", 100) + "-one"
b := strings.Repeat("a", 100) + "-two"
if TruncateLabelValue(a) == TruncateLabelValue(b) {
t.Errorf("collision: %q and %q produce the same truncated label", a, b)
}
}