diff --git a/cmd/main.go b/cmd/main.go index 0166d79..88af138 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -26,6 +26,7 @@ import ( managedpostgresoperatorhoppscalecomv1alpha1 "github.com/hoppscale/managed-postgres-operator/api/v1alpha1" "github.com/hoppscale/managed-postgres-operator/internal/controller" "github.com/hoppscale/managed-postgres-operator/internal/postgresql" + "github.com/hoppscale/managed-postgres-operator/internal/utils" // +kubebuilder:scaffold:imports ) @@ -163,7 +164,7 @@ func main() { Metrics: metricsServerOptions, HealthProbeBindAddress: probeAddr, LeaderElection: enableLeaderElection, - LeaderElectionID: "b707bc79.managed-postgres-operator.hoppscale.com", + LeaderElectionID: utils.GetLeaderElectionID(operatorInstanceName), // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily // when the Manager ends. This requires the binary to immediately end when the // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 049b98f..8e98e87 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -1,5 +1,10 @@ package utils +import ( + "crypto/sha256" + "fmt" +) + const OperatorInstanceAnnotationName string = "managed-postgres-operator.hoppscale.com/instance" func IsManagedByOperatorInstance(annotations map[string]string, instanceName string) bool { @@ -13,3 +18,18 @@ func IsManagedByOperatorInstance(annotations map[string]string, instanceName str return false } + +func GetLeaderElectionID(instanceName string) string { + leaderName := "default" + + if instanceName != "" { + leaderName = instanceName + } + + h := sha256.New() + h.Write([]byte(leaderName)) + leaderNameHash := fmt.Sprintf("%x", h.Sum(nil)) + leaderElectionID := fmt.Sprintf("%.14s-%.8s.managed-postgres-operator.hoppscale.com", leaderName, leaderNameHash) + + return leaderElectionID +} diff --git a/internal/utils/utils_test.go b/internal/utils/utils_test.go index f94e1e7..97a64a7 100644 --- a/internal/utils/utils_test.go +++ b/internal/utils/utils_test.go @@ -55,4 +55,44 @@ var _ = Describe("Utils functions", func() { }) }) }) + + Context("Calling GetLeaderElectionID", func() { + When("operator's instance is not defined", func() { + It("should return the default id", func() { + result := GetLeaderElectionID("") + Expect(result).To(Equal("default-37a8eec1.managed-postgres-operator.hoppscale.com")) + Expect(len(result)).To(BeNumerically("<=", 63)) + }) + }) + + When("operator's instance is defined and the name has a standard length", func() { + It("should return the instance name with a unique id", func() { + result := GetLeaderElectionID("foobar") + Expect(result).To(Equal("foobar-c3ab8ff1.managed-postgres-operator.hoppscale.com")) + Expect(len(result)).To(BeNumerically("<=", 63)) + }) + }) + + When("operator's instance is defined and the name is too long", func() { + It("should return the first few characters of the instance name and a unique ID", func() { + result := GetLeaderElectionID("myverylonginstancename") + Expect(result).To(Equal("myverylonginst-3b8d6ea3.managed-postgres-operator.hoppscale.com")) + Expect(len(result)).To(BeNumerically("<=", 63)) + }) + }) + + When("multiple instances begin with the same characters", func() { + It("shouldn't cause a conflict", func() { + result1 := GetLeaderElectionID("postgresinstance-dev") + result2 := GetLeaderElectionID("postgresinstance-int") + result3 := GetLeaderElectionID("postgresinstance-test") + result4 := GetLeaderElectionID("postgresinstance-prod") + + Expect(result1).NotTo(BeElementOf([]string{result2, result3, result4})) + Expect(result2).NotTo(BeElementOf([]string{result1, result3, result4})) + Expect(result3).NotTo(BeElementOf([]string{result1, result2, result4})) + Expect(result4).NotTo(BeElementOf([]string{result1, result2, result3})) + }) + }) + }) })