Skip to content

Commit

Permalink
Merge pull request #42 from jetstack/stable-leader-election-id
Browse files Browse the repository at this point in the history
Stable leader election
  • Loading branch information
jakexks authored Jun 14, 2021
2 parents 37a94e3 + 864e78f commit a2db40c
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 30 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Visit the [GitHub releases](https://github.com/jetstack/google-cas-issuer/releas
and copy the command, e.g.

```shell
kubectl apply -f https://github.com/jetstack/google-cas-issuer/releases/download/v0.5.0/google-cas-issuer-v0.5.0.yaml
kubectl apply -f https://github.com/jetstack/google-cas-issuer/releases/download/v0.5.2/google-cas-issuer-v0.5.2.yaml
```

You can then skip to the [Setting up Google Cloud IAM](#setting-up-google-cloud-iam) section.
Expand Down
19 changes: 2 additions & 17 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ limitations under the License.
package cmd

import (
"crypto/rand"
"math/big"
"os"

cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand Down Expand Up @@ -49,6 +45,7 @@ func init() {
// Issuer flags
rootCmd.PersistentFlags().String("metrics-addr", ":8080", "The address the metric endpoint binds to.")
rootCmd.PersistentFlags().Bool("enable-leader-election", false, "Enable leader election for controller manager.")
rootCmd.PersistentFlags().String("leader-election-id", "cm-google-cas-issuer", "The ID of the leader election lock that the controller should attempt to acquire.")
rootCmd.PersistentFlags().String("cluster-resource-namespace", "cert-manager", "The namespace for secrets in which cluster-scoped resources are found.")
rootCmd.PersistentFlags().Bool("disable-approval-check", false, "Don't check whether a CertificateRequest is approved before signing. For compatibility with cert-manager <v1.3.0.")

Expand Down Expand Up @@ -78,25 +75,13 @@ func root() error {
return err
}

// Get the hostname for leader election
hostname, err := os.Hostname()
if err != nil {
// fall back to a random number
random, err := rand.Int(rand.Reader, big.NewInt(999999999999))
if err != nil {
setupLog.Error(err, "unable to get a suitable leader election id")
os.Exit(1)
}
hostname = random.String()
}

// Create controller-manager
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: viper.GetString("metrics-addr"),
Port: 9443,
LeaderElection: viper.GetBool("enable-leader-election"),
LeaderElectionID: hostname,
LeaderElectionID: viper.GetString("leader-election-id"),
})
if err != nil {
setupLog.Error(err, "unable to start manager")
Expand Down
2 changes: 1 addition & 1 deletion pkg/cas/cas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ z5B9C4cjanJ67w==
PemCertificate: `-----BEGIN CERTIFICATE-----
leaf
-----END CERTIFICATE-----`,
PemCertificateChain: []string{`-----BEGIN CERTIFICATE-----
PemCertificateChain: []string{`-----BEGIN CERTIFICATE-----
intermediate2
-----END CERTIFICATE-----`, `-----BEGIN CERTIFICATE-----
intermediate1
Expand Down
7 changes: 4 additions & 3 deletions test/e2e/suite/issuers/issuers.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@ import (
"filippo.io/age"
certmanagerv1 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
cmmetav1 "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
"github.com/jetstack/google-cas-issuer/test/e2e/framework"
"github.com/jetstack/google-cas-issuer/test/e2e/framework/config"
"github.com/jetstack/google-cas-issuer/test/e2e/util"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/serializer/yaml"

"github.com/jetstack/google-cas-issuer/test/e2e/framework"
"github.com/jetstack/google-cas-issuer/test/e2e/framework/config"
"github.com/jetstack/google-cas-issuer/test/e2e/util"
)

const (
Expand Down
110 changes: 110 additions & 0 deletions test/e2e/suite/leaderelection/leaderelection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package leaderelection

import (
"context"
"fmt"
"strings"
"time"

"github.com/jetstack/google-cas-issuer/test/e2e/framework"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/client-go/util/retry"
)

type leaderElectionAnnotation struct {
HolderIdentity string `json:"holderIdentity"`
LeaseDurationSeconds int `json:"leaseDurationSeconds"`
AcquireTime time.Time `json:"acquireTime"`
RenewTime time.Time `json:"renewTime"`
LeaderTransitions int `json:"leaderTransitions"`
}

var _ = framework.CasesDescribe("leader election", func() {
f := framework.NewDefaultFramework("leader election")
It("Tests leader election", func() {
By("Waiting for all pods to be ready")
err := f.Helper().WaitForPodsReady(f.Config().Namespace, 10*time.Second)
Expect(err).NotTo(HaveOccurred())
By("Finding all cas issuer leader election config maps")
findAllCASIssuerConfigMaps := func() ([]*corev1.ConfigMap, error) {
configMapList, err := f.KubeClientSet.CoreV1().ConfigMaps("cert-manager").List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, fmt.Errorf("couldn't retrieve a list of config maps: %w", err)
}
var configMaps []*corev1.ConfigMap
for _, cm := range configMapList.Items {
if leaderAnnotationJSON, found := cm.Annotations["control-plane.alpha.kubernetes.io/leader"]; found {
leaderInfo := new(leaderElectionAnnotation)
if err := json.Unmarshal([]byte(leaderAnnotationJSON), leaderInfo); err != nil {
return nil, err
}
if strings.HasPrefix(leaderInfo.HolderIdentity, "google-cas-issuer") {
configMaps = append(configMaps, &cm)
}
}
}
return configMaps, nil
}

configMaps, err := findAllCASIssuerConfigMaps()
Expect(err).NotTo(HaveOccurred())
By("Expecting only one config map pointing to a google CAS issuer")
Expect(configMaps).Should(HaveLen(1))

By("Scaling google cas issuer to 3 replicas")
err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
deployment, err := f.KubeClientSet.AppsV1().Deployments("cert-manager").Get(context.TODO(), "google-cas-issuer", metav1.GetOptions{})
if err != nil {
return err
}
want3Replicas := int32(3)
newDeployment := deployment.DeepCopy()
newDeployment.Spec.Replicas = &want3Replicas
_, err = f.KubeClientSet.AppsV1().Deployments("cert-manager").Update(context.TODO(), newDeployment, metav1.UpdateOptions{})
if err != nil {
return err
}
return nil
})
Expect(err).NotTo(HaveOccurred())

By("Waiting for all pods to be ready")
err = f.Helper().WaitForPodsReady(f.Config().Namespace, 10*time.Second)
Expect(err).NotTo(HaveOccurred())

By("Ensuring there is still a single config map")
configMaps, err = findAllCASIssuerConfigMaps()
Expect(err).NotTo(HaveOccurred())
Expect(configMaps).Should(HaveLen(1))

By("Scaling google cas issuer to 1 replicas")
err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
deployment, err := f.KubeClientSet.AppsV1().Deployments("cert-manager").Get(context.TODO(), "google-cas-issuer", metav1.GetOptions{})
if err != nil {
return err
}
newDeployment := deployment.DeepCopy()
want1Replica := int32(1)
newDeployment.Spec.Replicas = &want1Replica
_, err = f.KubeClientSet.AppsV1().Deployments("cert-manager").Update(context.TODO(), newDeployment, metav1.UpdateOptions{})
if err != nil {
return err
}
return nil
})
Expect(err).NotTo(HaveOccurred())

By("Waiting for all pods to be ready")
err = f.Helper().WaitForPodsReady(f.Config().Namespace, 10*time.Second)
Expect(err).NotTo(HaveOccurred())

By("Ensuring there is still a single config map")
configMaps, err = findAllCASIssuerConfigMaps()
Expect(err).NotTo(HaveOccurred())
Expect(configMaps).Should(HaveLen(1))
})
})
1 change: 1 addition & 0 deletions test/e2e/suite/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ package suite

import (
_ "github.com/jetstack/google-cas-issuer/test/e2e/suite/issuers"
_ "github.com/jetstack/google-cas-issuer/test/e2e/suite/leaderelection"
_ "github.com/jetstack/google-cas-issuer/test/e2e/suite/validation"
)
11 changes: 3 additions & 8 deletions test/e2e/suite/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package validation

import (
"context"
"github.com/jetstack/google-cas-issuer/test/e2e/framework"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
"time"

"github.com/jetstack/google-cas-issuer/test/e2e/framework"
)

var _ = framework.CasesDescribe("validation", func() {
Expand All @@ -25,12 +26,6 @@ var _ = framework.CasesDescribe("validation", func() {
Expect(err).NotTo(HaveOccurred())
})

It("All pods in ns cert-manager are ready", func() {
By("waiting until all pods have ready condition")
err := f.Helper().WaitForPodsReady("cert-manager", 2*time.Minute)
Expect(err).NotTo(HaveOccurred())
})

It("Has the google-cas-issuer CRDs installed", func() {
By("using the dynamic client to create a google-cas-issuer")
casYAML := `apiVersion: cas-issuer.jetstack.io/v1beta1
Expand Down

0 comments on commit a2db40c

Please sign in to comment.