This is an automated email from the ASF dual-hosted git repository.

pjfanning pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko-management.git


The following commit(s) were added to refs/heads/main by this push:
     new 876507e8 Use precompiled regex patterns for name normalisation (#1002)
876507e8 is described below

commit 876507e8713d1c15a9d4ee196c182e9437d5fea0
Author: akashchamp <[email protected]>
AuthorDate: Fri Oct 9 00:32:55 2026 +0530

    Use precompiled regex patterns for name normalisation (#1002)
    
    Motivation:
    String.replaceAll compiles its regex on every call. The Kubernetes
    lease/rolling-update name normalisation and the bootstrap host matching
    call it repeatedly with constant patterns.
    
    Modification:
    Hoist the remaining regexes into precompiled scala.util.matching.Regex
    vals. Replace the fixed-character replaceAll calls (" ", "_", ".",
    and square brackets) with plain String.replace.
    
    Result:
    Same output, without recompiling the patterns on every call.
    
    Tests:
    Not run locally (sbt is not available on the authoring machine); relying on 
CI.
    The new specs pin the normalised output of the lease path,
    DNS-1039 and resource name helpers.
    
    References:
    Fixes #852
---
 .../lease/kubernetes/AbstractKubernetesLease.scala |  9 ++--
 .../internal/AbstractKubernetesApiImpl.scala       |  8 ++++
 .../kubernetes/internal/KubernetesApiImpl.scala    |  4 +-
 .../internal/NativeKubernetesApiImpl.scala         |  4 +-
 .../lease/kubernetes/KubernetesApiSpec.scala       |  5 +++
 .../lease/kubernetes/NativeKubernetesApiSpec.scala |  5 +++
 .../bootstrap/ClusterBootstrapSettings.scala       |  2 +-
 .../cluster/bootstrap/SelfAwareJoinDecider.scala   |  2 +-
 .../rollingupdate/kubernetes/KubernetesApi.scala   | 11 ++---
 .../kubernetes/KubernetesApiImpl.scala             |  4 +-
 .../kubernetes/MakeDNS1039CompatibleSpec.scala     | 51 ++++++++++++++++++++++
 11 files changed, 84 insertions(+), 21 deletions(-)

diff --git 
a/lease-kubernetes/src/main/scala/org/apache/pekko/coordination/lease/kubernetes/AbstractKubernetesLease.scala
 
b/lease-kubernetes/src/main/scala/org/apache/pekko/coordination/lease/kubernetes/AbstractKubernetesLease.scala
index 2486b5c1..df2bf9f8 100644
--- 
a/lease-kubernetes/src/main/scala/org/apache/pekko/coordination/lease/kubernetes/AbstractKubernetesLease.scala
+++ 
b/lease-kubernetes/src/main/scala/org/apache/pekko/coordination/lease/kubernetes/AbstractKubernetesLease.scala
@@ -38,6 +38,7 @@ import org.slf4j.LoggerFactory
 object AbstractKubernetesLease {
   val configPath = "pekko.coordination.lease.kubernetes"
   private val leaseCounter = new AtomicInteger(1)
+  private val InvalidDNS1039Chars = "[^-a-z0-9]".r
 
   // Base32 alphabet (RFC 4648 ยง6), **lowercased** so every character is a 
valid DNS 1039 label
   // character.  '=' padding is intentionally omitted: we stream full 5-bit 
groups and emit one
@@ -97,11 +98,9 @@ object AbstractKubernetesLease {
    * If hashLength >= maxLength the result consists entirely of the first 
maxLength hash characters.
    */
   private[kubernetes] def makeDNS1039Compatible(name: String, maxLength: Int = 
63, hashLength: Int = 0): String = {
-    val normalized =
-      Normalizer.normalize(name, Normalizer.Form.NFKD)
-        .toLowerCase(Locale.ROOT)
-        .replaceAll("[_.]", "-")
-        .replaceAll("[^-a-z0-9]", "")
+    val lower = Normalizer.normalize(name, 
Normalizer.Form.NFKD).toLowerCase(Locale.ROOT)
+    val withHyphens = lower.replace('_', '-').replace('.', '-')
+    val normalized = InvalidDNS1039Chars.replaceAllIn(withHyphens, "")
     if (normalized.length <= maxLength || hashLength <= 0) {
       trim(truncateToLength(normalized, maxLength), List('-'))
     } else {
diff --git 
a/lease-kubernetes/src/main/scala/org/apache/pekko/coordination/lease/kubernetes/internal/AbstractKubernetesApiImpl.scala
 
b/lease-kubernetes/src/main/scala/org/apache/pekko/coordination/lease/kubernetes/internal/AbstractKubernetesApiImpl.scala
index 37213eb2..2e778f5e 100644
--- 
a/lease-kubernetes/src/main/scala/org/apache/pekko/coordination/lease/kubernetes/internal/AbstractKubernetesApiImpl.scala
+++ 
b/lease-kubernetes/src/main/scala/org/apache/pekko/coordination/lease/kubernetes/internal/AbstractKubernetesApiImpl.scala
@@ -33,6 +33,14 @@ import java.nio.file.{ Files, Paths }
 import javax.net.ssl.SSLContext
 import scala.concurrent.{ ExecutionContext, Future, Promise }
 import scala.util.control.NonFatal
+import scala.util.matching.Regex
+
+/**
+ * INTERNAL API
+ */
+@InternalApi private[kubernetes] object AbstractKubernetesApiImpl {
+  val InvalidLeaseNameChars: Regex = "[^\\d\\w\\-\\.]".r
+}
 
 /**
  * Could be shared between leases: 
https://github.com/akka/akka-management/issues/680
diff --git 
a/lease-kubernetes/src/main/scala/org/apache/pekko/coordination/lease/kubernetes/internal/KubernetesApiImpl.scala
 
b/lease-kubernetes/src/main/scala/org/apache/pekko/coordination/lease/kubernetes/internal/KubernetesApiImpl.scala
index 6ded7b8e..d9a127b5 100644
--- 
a/lease-kubernetes/src/main/scala/org/apache/pekko/coordination/lease/kubernetes/internal/KubernetesApiImpl.scala
+++ 
b/lease-kubernetes/src/main/scala/org/apache/pekko/coordination/lease/kubernetes/internal/KubernetesApiImpl.scala
@@ -134,9 +134,7 @@ PUTs must contain resourceVersions. Response:
   override def pathForLease(name: String): Future[Uri.Path] = {
     namespace.map { ns =>
       Uri.Path.Empty / "apis" / "pekko.apache.org" / "v1" / "namespaces" / ns 
/ "leases" /
-      name
-        .replaceAll("[^\\d\\w\\-\\.]", "")
-        .toLowerCase(Locale.ROOT)
+      AbstractKubernetesApiImpl.InvalidLeaseNameChars.replaceAllIn(name, 
"").toLowerCase(Locale.ROOT)
     }(ExecutionContext.parasitic)
   }
 
diff --git 
a/lease-kubernetes/src/main/scala/org/apache/pekko/coordination/lease/kubernetes/internal/NativeKubernetesApiImpl.scala
 
b/lease-kubernetes/src/main/scala/org/apache/pekko/coordination/lease/kubernetes/internal/NativeKubernetesApiImpl.scala
index d6e2a2f7..20a99529 100644
--- 
a/lease-kubernetes/src/main/scala/org/apache/pekko/coordination/lease/kubernetes/internal/NativeKubernetesApiImpl.scala
+++ 
b/lease-kubernetes/src/main/scala/org/apache/pekko/coordination/lease/kubernetes/internal/NativeKubernetesApiImpl.scala
@@ -144,9 +144,7 @@ object NativeKubernetesApiImpl {
   override def pathForLease(name: String): Future[Uri.Path] = {
     namespace.map { ns =>
       Uri.Path.Empty / "apis" / "coordination.k8s.io" / "v1" / "namespaces" / 
ns / "leases" /
-      name
-        .replaceAll("[^\\d\\w\\-\\.]", "")
-        .toLowerCase(Locale.ROOT)
+      AbstractKubernetesApiImpl.InvalidLeaseNameChars.replaceAllIn(name, 
"").toLowerCase(Locale.ROOT)
     }(ExecutionContext.parasitic)
   }
 
diff --git 
a/lease-kubernetes/src/test/scala/org/apache/pekko/coordination/lease/kubernetes/KubernetesApiSpec.scala
 
b/lease-kubernetes/src/test/scala/org/apache/pekko/coordination/lease/kubernetes/KubernetesApiSpec.scala
index 92e85d10..c7bbe383 100644
--- 
a/lease-kubernetes/src/test/scala/org/apache/pekko/coordination/lease/kubernetes/KubernetesApiSpec.scala
+++ 
b/lease-kubernetes/src/test/scala/org/apache/pekko/coordination/lease/kubernetes/KubernetesApiSpec.scala
@@ -208,6 +208,11 @@ class KubernetesApiSpec
       response shouldEqual Left(LeaseResource(Some(conflictedOwner), 
updatedVersion, timestamp))
     }
 
+    "strip characters that are not allowed in a resource name from the lease 
path" in {
+      underTest.pathForLease("My_Lease.1@host:7355/x").futureValue.toString 
shouldEqual
+      "/apis/pekko.apache.org/v1/namespaces/lease/leases/my_lease.1host7355x"
+    }
+
     "remove lease via DELETE" in {
       val lease = "lease-1"
       stubFor(
diff --git 
a/lease-kubernetes/src/test/scala/org/apache/pekko/coordination/lease/kubernetes/NativeKubernetesApiSpec.scala
 
b/lease-kubernetes/src/test/scala/org/apache/pekko/coordination/lease/kubernetes/NativeKubernetesApiSpec.scala
index 9fd58677..7b952a29 100644
--- 
a/lease-kubernetes/src/test/scala/org/apache/pekko/coordination/lease/kubernetes/NativeKubernetesApiSpec.scala
+++ 
b/lease-kubernetes/src/test/scala/org/apache/pekko/coordination/lease/kubernetes/NativeKubernetesApiSpec.scala
@@ -189,6 +189,11 @@ class NativeKubernetesApiSpec
         fromRFC3339MicroString(timestamp)))
     }
 
+    "strip characters that are not allowed in a resource name from the lease 
path" in {
+      underTest.pathForLease("My_Lease.1@host:7355/x").futureValue.toString 
shouldEqual
+      
"/apis/coordination.k8s.io/v1/namespaces/lease/leases/my_lease.1host7355x"
+    }
+
     "remove lease via DELETE" in {
       val lease = "lease-1"
       stubFor(
diff --git 
a/management-cluster-bootstrap/src/main/scala/org/apache/pekko/management/cluster/bootstrap/ClusterBootstrapSettings.scala
 
b/management-cluster-bootstrap/src/main/scala/org/apache/pekko/management/cluster/bootstrap/ClusterBootstrapSettings.scala
index 1ffc49c8..9e8f2299 100644
--- 
a/management-cluster-bootstrap/src/main/scala/org/apache/pekko/management/cluster/bootstrap/ClusterBootstrapSettings.scala
+++ 
b/management-cluster-bootstrap/src/main/scala/org/apache/pekko/management/cluster/bootstrap/ClusterBootstrapSettings.scala
@@ -61,7 +61,7 @@ final class ClusterBootstrapSettings(config: Config, log: 
LoggingAdapter) {
     def effectiveName(system: ActorSystem): String =
       discoveryConfig.optDefinedValue("effective-name").getOrElse {
         val service =
-          
serviceName.getOrElse(system.name.toLowerCase(Locale.ROOT).replaceAll(" ", 
"-").replace("_", "-"))
+          serviceName.getOrElse(system.name.toLowerCase(Locale.ROOT).replace(" 
", "-").replace("_", "-"))
 
         val namespace = serviceNamespace match {
           case Some(ns) => s".$ns"
diff --git 
a/management-cluster-bootstrap/src/main/scala/org/apache/pekko/management/cluster/bootstrap/SelfAwareJoinDecider.scala
 
b/management-cluster-bootstrap/src/main/scala/org/apache/pekko/management/cluster/bootstrap/SelfAwareJoinDecider.scala
index 4ae8d4de..7d301127 100644
--- 
a/management-cluster-bootstrap/src/main/scala/org/apache/pekko/management/cluster/bootstrap/SelfAwareJoinDecider.scala
+++ 
b/management-cluster-bootstrap/src/main/scala/org/apache/pekko/management/cluster/bootstrap/SelfAwareJoinDecider.scala
@@ -85,7 +85,7 @@ import scala.concurrent.duration._
    * Checks for both host name and IP address for discovery mechanisms that 
return both.
    */
   protected def hostMatches(host: String, target: ResolvedTarget): Boolean = {
-    val hostWithoutBracket = host.replaceAll("[\\[\\]]", "")
+    val hostWithoutBracket = host.replace("[", "").replace("]", "")
     host == target.host || hostWithoutBracket == target.host ||
     target.address
       .map(_.getHostAddress)
diff --git 
a/rolling-update-kubernetes/src/main/scala/org/apache/pekko/rollingupdate/kubernetes/KubernetesApi.scala
 
b/rolling-update-kubernetes/src/main/scala/org/apache/pekko/rollingupdate/kubernetes/KubernetesApi.scala
index 3a3ae97c..5eddc8d0 100644
--- 
a/rolling-update-kubernetes/src/main/scala/org/apache/pekko/rollingupdate/kubernetes/KubernetesApi.scala
+++ 
b/rolling-update-kubernetes/src/main/scala/org/apache/pekko/rollingupdate/kubernetes/KubernetesApi.scala
@@ -79,6 +79,9 @@ private[pekko] final case class PodCost(podName: String, 
cost: Int, address: Str
  */
 @InternalApi private[pekko] object KubernetesApi {
 
+  private val InvalidDNS1039Chars = "[^-a-z0-9]".r
+  private[kubernetes] val InvalidResourceNameChars = "[^\\d\\w\\-\\.]".r
+
   /**
    * Removes from the leading and trailing positions the specified characters.
    */
@@ -91,11 +94,9 @@ private[pekko] final case class PodCost(podName: String, 
cost: Int, address: Str
    * Validates the resulting name to be at most 63 characters, otherwise 
throws `IllegalArgumentException`.
    */
   def makeDNS1039Compatible(name: String): String = {
-    val normalized =
-      Normalizer.normalize(name, Normalizer.Form.NFKD)
-        .toLowerCase(Locale.ROOT)
-        .replaceAll("[_.]", "-")
-        .replaceAll("[^-a-z0-9]", "")
+    val lower = Normalizer.normalize(name, 
Normalizer.Form.NFKD).toLowerCase(Locale.ROOT)
+    val withHyphens = lower.replace('_', '-').replace('.', '-')
+    val normalized = InvalidDNS1039Chars.replaceAllIn(withHyphens, "")
     if (normalized.length > 63)
       throw new IllegalArgumentException(
         s"Resource name [$normalized] is too long. At most 63 characters are 
accepted. " +
diff --git 
a/rolling-update-kubernetes/src/main/scala/org/apache/pekko/rollingupdate/kubernetes/KubernetesApiImpl.scala
 
b/rolling-update-kubernetes/src/main/scala/org/apache/pekko/rollingupdate/kubernetes/KubernetesApiImpl.scala
index 53d408f1..5017497a 100644
--- 
a/rolling-update-kubernetes/src/main/scala/org/apache/pekko/rollingupdate/kubernetes/KubernetesApiImpl.scala
+++ 
b/rolling-update-kubernetes/src/main/scala/org/apache/pekko/rollingupdate/kubernetes/KubernetesApiImpl.scala
@@ -254,9 +254,7 @@ PUTs must contain resourceVersions. Response:
 
   private def pathForPodCostResource(crName: String): Uri.Path =
     Uri.Path.Empty / "apis" / "pekko.apache.org" / "v1" / "namespaces" / 
namespace / "podcosts" /
-    crName
-      .replaceAll("[^\\d\\w\\-\\.]", "")
-      .toLowerCase(Locale.ROOT)
+    KubernetesApi.InvalidResourceNameChars.replaceAllIn(crName, 
"").toLowerCase(Locale.ROOT)
 
   private def requestForPath(
       path: Uri.Path,
diff --git 
a/rolling-update-kubernetes/src/test/scala/org/apache/pekko/rollingupdate/kubernetes/MakeDNS1039CompatibleSpec.scala
 
b/rolling-update-kubernetes/src/test/scala/org/apache/pekko/rollingupdate/kubernetes/MakeDNS1039CompatibleSpec.scala
new file mode 100644
index 00000000..426dddc5
--- /dev/null
+++ 
b/rolling-update-kubernetes/src/test/scala/org/apache/pekko/rollingupdate/kubernetes/MakeDNS1039CompatibleSpec.scala
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pekko.rollingupdate.kubernetes
+
+import org.scalatest.matchers.should.Matchers
+import org.scalatest.wordspec.AnyWordSpec
+
+class MakeDNS1039CompatibleSpec extends AnyWordSpec with Matchers {
+
+  "KubernetesApi.makeDNS1039Compatible" should {
+
+    "leave a valid pod name unchanged" in {
+      KubernetesApi.makeDNS1039Compatible("my-pod-1") shouldEqual "my-pod-1"
+    }
+
+    "convert underscores and dots to hyphens" in {
+      KubernetesApi.makeDNS1039Compatible("my.pod_name") shouldEqual 
"my-pod-name"
+    }
+
+    "lowercase and drop characters that are not allowed" in {
+      KubernetesApi.makeDNS1039Compatible("My@Pod!Name") shouldEqual 
"mypodname"
+    }
+
+    "strip accents after unicode normalization" in {
+      KubernetesApi.makeDNS1039Compatible("p\u00f6d-\u00e9") shouldEqual 
"pod-e"
+    }
+
+    "trim leading and trailing hyphens" in {
+      KubernetesApi.makeDNS1039Compatible("_my-pod.") shouldEqual "my-pod"
+    }
+
+    "reject names longer than 63 characters" in {
+      an[IllegalArgumentException] should be thrownBy 
KubernetesApi.makeDNS1039Compatible("a" * 64)
+    }
+  }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to