Skip to content

Commit 94a6c7a

Browse files
authored
Merge pull request #42 from nirmata/leader-election
feat: add leader election
2 parents 7330150 + 92a3fc3 commit 94a6c7a

File tree

5 files changed

+151
-25
lines changed

5 files changed

+151
-25
lines changed

configs/install.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,17 @@ rules:
9898
- create
9999
- update
100100
- delete
101+
- apiGroups:
102+
- coordination.k8s.io
103+
resources:
104+
- leases
105+
verbs:
106+
- get
107+
- list
108+
- watch
109+
- create
110+
- update
111+
- delete
101112
---
102113
apiVersion: rbac.authorization.k8s.io/v1
103114
kind: RoleBinding
@@ -177,6 +188,14 @@ spec:
177188
valueFrom:
178189
fieldRef:
179190
fieldPath: metadata.namespace
191+
- name: SERVICE_NAME
192+
value: svc
193+
- name: DEPLOYMENT_NAME
194+
value: kyverno-notation-aws
195+
- name: POD_NAME
196+
valueFrom:
197+
fieldRef:
198+
fieldPath: metadata.name
180199
# USE IF IRSA IS NOT CONFIGURED
181200
# - name: AWS_ACCESS_KEY_ID
182201
# value: ${AWS_ACCESS_KEY_ID}

configs/samples/kyverno-policy.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ spec:
2222
context:
2323
- name: tlscerts
2424
apiCall:
25-
urlPath: "/api/v1/namespaces/kyverno-notation-aws/secrets/kyverno-notation-aws.kyverno-notation-aws.svc.tls-pair"
25+
urlPath: "/api/v1/namespaces/kyverno-notation-aws/secrets/svc.kyverno-notation-aws.svc.tls-pair"
2626
jmesPath: "base64_decode( data.\"tls.crt\" )"
2727
- name: response
2828
apiCall:

controller.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"sync"
6+
7+
"github.com/go-logr/logr"
8+
"github.com/kyverno/kyverno/pkg/controllers"
9+
)
10+
11+
type Controller interface {
12+
Run(context.Context, logr.Logger, *sync.WaitGroup)
13+
}
14+
15+
type controller struct {
16+
name string
17+
controller controllers.Controller
18+
workers int
19+
}
20+
21+
func NewController(name string, c controllers.Controller, w int) Controller {
22+
return controller{
23+
name: name,
24+
controller: c,
25+
workers: w,
26+
}
27+
}
28+
29+
func (c controller) Run(ctx context.Context, logger logr.Logger, wg *sync.WaitGroup) {
30+
wg.Add(1)
31+
go func(logger logr.Logger) {
32+
logger.Info("starting controller", "workers", c.workers)
33+
defer logger.Info("controller stopped")
34+
defer wg.Done()
35+
c.controller.Run(ctx, c.workers)
36+
}(logger.WithValues("name", c.name))
37+
}

main.go

Lines changed: 86 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,40 @@ import (
77
"log"
88
"net/http"
99
"os"
10+
"os/signal"
11+
"sync"
12+
"syscall"
1013
"time"
1114

1215
"github.com/go-logr/zapr"
16+
"github.com/kyverno/kyverno/pkg/leaderelection"
1317
"github.com/kyverno/pkg/certmanager"
1418
tlsMgr "github.com/kyverno/pkg/tls"
1519
"github.com/nirmata/kyverno-notation-verifier/kubenotation"
1620
knvSetup "github.com/nirmata/kyverno-notation-verifier/setup"
1721
knvVerifier "github.com/nirmata/kyverno-notation-verifier/verifier"
1822
_ "github.com/notaryproject/notation-core-go/signature/cose"
1923
_ "github.com/notaryproject/notation-core-go/signature/jws"
24+
"github.com/pkg/errors"
2025
"go.uber.org/zap"
2126
"go.uber.org/zap/zapcore"
27+
corev1 "k8s.io/api/core/v1"
2228
"k8s.io/client-go/kubernetes"
2329
"k8s.io/client-go/rest"
2430
ctrl "sigs.k8s.io/controller-runtime"
2531
)
2632

2733
var (
28-
namespace = "kyverno-notation-aws"
34+
Namespace = os.Getenv("POD_NAMESPACE")
35+
PodName = os.Getenv("POD_NAME")
36+
ServiceName = getEnvWithFallback("SERVICE_NAME", "svc")
37+
DeploymentName = getEnvWithFallback("DEPLOYMENT_NAME", "kyverno-notation-aws")
38+
2939
CertRenewalInterval = 12 * time.Hour
3040
CAValidityDuration = 365 * 24 * time.Hour
3141
TLSValidityDuration = 150 * 24 * time.Hour
32-
resyncPeriod = 15 * time.Minute
42+
43+
resyncPeriod = 15 * time.Minute
3344
)
3445

3546
func main() {
@@ -96,39 +107,74 @@ func main() {
96107
log.Fatalf("failed to initialize kube client: %v", err)
97108
}
98109

110+
signalCtx, sdown := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
111+
defer sdown()
112+
99113
tlsMgrConfig := &tlsMgr.Config{
100-
ServiceName: "kyverno-notation-aws",
101-
Namespace: namespace,
114+
ServiceName: ServiceName,
115+
Namespace: Namespace,
102116
}
103117

104-
certRenewer := tlsMgr.NewCertRenewer(
105-
zapr.NewLogger(logger),
106-
kubeClient.CoreV1().Secrets(namespace),
107-
CertRenewalInterval,
108-
CAValidityDuration,
109-
TLSValidityDuration,
110-
"",
111-
tlsMgrConfig,
112-
)
113-
114118
caStopCh := make(chan struct{}, 1)
115-
caInformer := NewSecretInformer(kubeClient, namespace, tlsMgr.GenerateRootCASecretName(tlsMgrConfig), resyncPeriod)
119+
caInformer := NewSecretInformer(kubeClient, Namespace, tlsMgr.GenerateRootCASecretName(tlsMgrConfig), resyncPeriod)
116120
go caInformer.Informer().Run(caStopCh)
117121

118122
tlsStopCh := make(chan struct{}, 1)
119-
tlsInformer := NewSecretInformer(kubeClient, namespace, tlsMgr.GenerateTLSPairSecretName(tlsMgrConfig), resyncPeriod)
123+
tlsInformer := NewSecretInformer(kubeClient, Namespace, tlsMgr.GenerateTLSPairSecretName(tlsMgrConfig), resyncPeriod)
120124
go tlsInformer.Informer().Run(tlsStopCh)
121125

122-
certManager := certmanager.NewController(
123-
zapr.NewLogger(logger),
124-
caInformer,
125-
tlsInformer,
126-
certRenewer,
127-
tlsMgrConfig,
126+
le, err := leaderelection.New(
127+
zapr.NewLogger(logger).WithName("leader-election"),
128+
DeploymentName,
129+
Namespace,
130+
kubeClient,
131+
PodName,
132+
2*time.Second,
133+
func(ctx context.Context) {
134+
135+
certRenewer := tlsMgr.NewCertRenewer(
136+
zapr.NewLogger(logger).WithName("tls").WithValues("pod", PodName),
137+
kubeClient.CoreV1().Secrets(Namespace),
138+
CertRenewalInterval,
139+
CAValidityDuration,
140+
TLSValidityDuration,
141+
"",
142+
tlsMgrConfig,
143+
)
144+
145+
certManager := certmanager.NewController(
146+
zapr.NewLogger(logger).WithName("certmanager").WithValues("pod", PodName),
147+
caInformer,
148+
tlsInformer,
149+
certRenewer,
150+
tlsMgrConfig,
151+
)
152+
153+
leaderControllers := []Controller{NewController("cert-manager", certManager, 1)}
154+
155+
// start leader controllers
156+
var wg sync.WaitGroup
157+
for _, controller := range leaderControllers {
158+
controller.Run(signalCtx, zapr.NewLogger(logger).WithName("controllers"), &wg)
159+
}
160+
// wait all controllers shut down
161+
wg.Wait()
162+
},
163+
nil,
128164
)
165+
if err != nil {
166+
log.Fatalf("failed to initialize leader election: %v", err)
167+
os.Exit(1)
168+
}
129169

170+
// start leader election
130171
go func() {
131-
certManager.Run(context.TODO(), 1)
172+
select {
173+
case <-signalCtx.Done():
174+
return
175+
default:
176+
le.Run(signalCtx)
177+
}
132178
}()
133179

134180
crdSetup, err := kubenotation.Setup(zapr.NewLogger(logger), metricsAddr, probeAddr, enableLeaderElection)
@@ -172,7 +218,23 @@ func main() {
172218
errsTLS := make(chan error, 1)
173219
if !flagNoTLS {
174220
tlsConf := &tls.Config{
175-
GetCertificate: certManager.GetCertificate,
221+
GetCertificate: func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
222+
secret, err := tlsInformer.Lister().Secrets(tlsMgrConfig.Namespace).Get(tlsMgr.GenerateTLSPairSecretName(tlsMgrConfig))
223+
if err != nil {
224+
return nil, err
225+
} else if secret == nil {
226+
return nil, errors.New("tls secret not found")
227+
} else if secret.Type != corev1.SecretTypeTLS {
228+
return nil, errors.New("secret is not a TLS secret")
229+
}
230+
231+
cert, err := tls.X509KeyPair(secret.Data[corev1.TLSCertKey], secret.Data[corev1.TLSPrivateKeyKey])
232+
if err != nil {
233+
return nil, err
234+
}
235+
236+
return &cert, nil
237+
},
176238
}
177239
srv := &http.Server{
178240
Addr: ":9443",

utils.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"os"
45
"time"
56

67
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -44,3 +45,10 @@ func (i *secretInformer) Informer() cache.SharedIndexInformer {
4445
func (i *secretInformer) Lister() corev1listers.SecretLister {
4546
return i.lister
4647
}
48+
49+
func getEnvWithFallback(name, fallback string) string {
50+
if value := os.Getenv(name); value != "" {
51+
return value
52+
}
53+
return fallback
54+
}

0 commit comments

Comments
 (0)