m/n/k/reconciler: implement leader election

Before this change, the reconciler runs on all Kubernetes controllers. 
When we are in a rolling upgrade of the cluster where a reconciled 
object changes, this will cause the old and new versions of the 
reconciler to fight each other, constantly updating the object back and 
forth.

Now, the reconciler is elected among nodes of the latest release. The 
status of the reconciliation is communicated to all Kubernetes 
controllers through a new key-value in etcd.

Additionally, compatibility constraints can be expressed by changing the 
constants minReconcilerRelease and minApiserverRelease, allowing 
reconciliation to happen in a controlled way that ensures compatibility 
even during rolling upgrades.

Change-Id: Iaf7c27702bd9809a13d47bcf041b71438353bef2
Reviewed-on: https://review.monogon.dev/c/monogon/+/3062
Tested-by: Jenkins CI
Reviewed-by: Serge Bazanski <serge@monogon.tech>
diff --git a/version/version.go b/version/version.go
index b495528..668e0fa 100644
--- a/version/version.go
+++ b/version/version.go
@@ -10,18 +10,17 @@
 
 // Release converts a spec.Version's Release field into a SemVer 2.0.0 compatible
 // string in the X.Y.Z form.
-func Release(v *spec.Version) string {
-	if v == nil || v.Release == nil {
+func Release(rel *spec.Version_Release) string {
+	if rel == nil {
 		return "0.0.0"
 	}
-	rel := v.Release
 	return fmt.Sprintf("%d.%d.%d", rel.Major, rel.Minor, rel.Patch)
 }
 
 // Semver converts a spec.Version proto message into a SemVer 2.0.0 compatible
 // string.
 func Semver(v *spec.Version) string {
-	ver := "v" + Release(v)
+	ver := "v" + Release(v.Release)
 	var prerelease []string
 	if git := v.GitInformation; git != nil {
 		if n := git.CommitsSinceRelease; n != 0 {
@@ -38,3 +37,17 @@
 	}
 	return ver
 }
+
+// ReleaseLessThan returns true if Release a is lexicographically smaller than b.
+func ReleaseLessThan(a, b *spec.Version_Release) bool {
+	if a.Major != b.Major {
+		return a.Major < b.Major
+	}
+	if a.Minor != b.Minor {
+		return a.Minor < b.Minor
+	}
+	if a.Patch != b.Patch {
+		return a.Patch < b.Patch
+	}
+	return false
+}