Skip to content
This repository was archived by the owner on Apr 17, 2025. It is now read-only.

Commit 55c0c2a

Browse files
authored
Merge pull request #163 from adrianludwin/webhook-only
EXPERIMENTAL HA support
2 parents 2925068 + 4e63495 commit 55c0c2a

File tree

7 files changed

+75
-22
lines changed

7 files changed

+75
-22
lines changed

cmd/manager/main.go

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
3636
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
3737
ctrl "sigs.k8s.io/controller-runtime"
38+
"sigs.k8s.io/controller-runtime/pkg/healthz"
3839
"sigs.k8s.io/controller-runtime/pkg/log/zap"
3940
"sigs.k8s.io/controller-runtime/pkg/metrics"
4041

@@ -59,7 +60,7 @@ var (
5960
maxReconciles int
6061
enableLeaderElection bool
6162
leaderElectionId string
62-
novalidation bool
63+
noWebhooks bool
6364
debugLogs bool
6465
testLog bool
6566
internalCert bool
@@ -71,6 +72,7 @@ var (
7172
managedNamespaceLabels arrayArg
7273
managedNamespaceAnnots arrayArg
7374
includedNamespacesRegex string
75+
webhooksOnly bool
7476
)
7577

7678
// init preloads some global vars before main() starts. Since this is the top-level module, I'm not
@@ -93,12 +95,19 @@ func main() {
9395
defer metricsCleanupFn()
9496
mgr := createManager()
9597

96-
// Make sure certs are generated and valid if webhooks are enabled and internal certs are used.
97-
setupLog.Info("Starting certificate generation")
98-
certsReady, err := setup.CreateCertsIfNeeded(mgr, novalidation, internalCert, restartOnSecretRefresh)
99-
if err != nil {
100-
setupLog.Error(err, "unable to set up cert rotation")
101-
os.Exit(1)
98+
// Make sure certs are managed if requested. In webhooks-only mode, we don't run the manager, and
99+
// rely on either a controller running in a different HNC deployment, or an external tool such as
100+
// cert-manager.
101+
certsReady := make(chan struct{})
102+
if internalCert && !webhooksOnly {
103+
setupLog.Info("Starting certificate generation")
104+
err := setup.ManageCerts(mgr, certsReady, restartOnSecretRefresh)
105+
if err != nil {
106+
setupLog.Error(err, "unable to set up cert rotation")
107+
os.Exit(1)
108+
}
109+
} else {
110+
close(certsReady)
102111
}
103112

104113
setupProbeEndpoints(mgr, certsReady)
@@ -125,7 +134,7 @@ func parseFlags() {
125134
"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
126135
flag.StringVar(&leaderElectionId, "leader-election-id", "controller-leader-election-helper",
127136
"Leader election id determines the name of the configmap that leader election will use for holding the leader lock.")
128-
flag.BoolVar(&novalidation, "novalidation", false, "Disables validating webhook")
137+
flag.BoolVar(&noWebhooks, "no-webhooks", false, "Disables webhooks")
129138
flag.BoolVar(&debugLogs, "debug-logs", false, "Shows verbose logs.")
130139
flag.BoolVar(&testLog, "enable-test-log", false, "Enables test log.")
131140
flag.BoolVar(&internalCert, "enable-internal-cert-management", false, "Enables internal cert management. See the user guide for more information.")
@@ -139,6 +148,7 @@ func parseFlags() {
139148
flag.BoolVar(&restartOnSecretRefresh, "cert-restart-on-secret-refresh", false, "Kills the process when secrets are refreshed so that the pod can be restarted (secrets take up to 60s to be updated by running pods)")
140149
flag.Var(&managedNamespaceLabels, "managed-namespace-label", "A regex indicating the labels on namespaces that are managed by HNC. These labels may only be set via the HierarchyConfiguration object. All regexes are implictly wrapped by \"^...$\". This argument can be specified multiple times. See the user guide for more information.")
141150
flag.Var(&managedNamespaceAnnots, "managed-namespace-annotation", "A regex indicating the annotations on namespaces that are managed by HNC. These annotations may only be set via the HierarchyConfiguration object. All regexes are implictly wrapped by \"^...$\". This argument can be specified multiple times. See the user guide for more information.")
151+
flag.BoolVar(&webhooksOnly, "webhooks-only", false, "Disables the controllers so HNC can be run in HA webhook mode")
142152
flag.Parse()
143153

144154
// Assign the array args to the configuration variables after the args are parsed.
@@ -148,6 +158,12 @@ func parseFlags() {
148158
setupLog.Error(err, "Illegal flag values")
149159
os.Exit(1)
150160
}
161+
162+
// Basic legality checks
163+
if webhooksOnly && noWebhooks {
164+
setupLog.Info("Cannot set both --webhooks-only and --no-webhooks")
165+
os.Exit(1)
166+
}
151167
}
152168

153169
// enableMetrics returns a function to call from main() to export any remaining metrics when main()
@@ -252,6 +268,10 @@ func setupProbeEndpoints(mgr ctrl.Manager, certsReady chan struct{}) {
252268
return errors.New("HNC internal certs are not yet ready")
253269
}
254270
}
271+
// If we're not running the webhooks, no point checking to see if they're up.
272+
if noWebhooks {
273+
checker = healthz.Ping
274+
}
255275
if err := mgr.AddHealthzCheck("healthz", checker); err != nil {
256276
setupLog.Error(err, "unable to set up health check")
257277
os.Exit(1)
@@ -278,14 +298,14 @@ func startControllers(mgr ctrl.Manager, certsReady chan struct{}) {
278298
f := forest.NewForest()
279299

280300
// Create all validating and mutating admission controllers.
281-
if !novalidation {
282-
setupLog.Info("Registering validating webhook (won't work when running locally; use --novalidation)")
301+
if !noWebhooks {
302+
setupLog.Info("Registering validating webhook (won't work when running locally; use --no-webhooks)")
283303
setup.CreateWebhooks(mgr, f)
284304
}
285305

286306
// Create all reconciling controllers
287307
setupLog.Info("Creating controllers", "maxReconciles", maxReconciles)
288-
if err := setup.CreateReconcilers(mgr, f, maxReconciles, false); err != nil {
308+
if err := setup.CreateReconcilers(mgr, f, maxReconciles, webhooksOnly, false); err != nil {
289309
setupLog.Error(err, "cannot create controllers")
290310
os.Exit(1)
291311
}

internal/anchor/reconciler.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ type Reconciler struct {
5151
// https://book-v1.book.kubebuilder.io/beyond_basics/controller_watches.html) that is used to
5252
// enqueue additional objects that need updating.
5353
Affected chan event.GenericEvent
54+
55+
// ReadOnly disables writebacks
56+
ReadOnly bool
5457
}
5558

5659
// Reconcile sets up some basic variables and then calls the business logic. It currently
@@ -339,6 +342,10 @@ func (r *Reconciler) getInstance(ctx context.Context, pnm, nm string) (*api.Subn
339342
}
340343

341344
func (r *Reconciler) writeInstance(ctx context.Context, log logr.Logger, inst *api.SubnamespaceAnchor) error {
345+
if r.ReadOnly {
346+
return nil
347+
}
348+
342349
if inst.CreationTimestamp.IsZero() {
343350
if err := r.Create(ctx, inst); err != nil {
344351
log.Error(err, "while creating on apiserver")
@@ -356,6 +363,10 @@ func (r *Reconciler) writeInstance(ctx context.Context, log logr.Logger, inst *a
356363
// deleteInstance deletes the anchor instance. Note: Make sure there's no
357364
// finalizers on the instance before calling this function.
358365
func (r *Reconciler) deleteInstance(ctx context.Context, inst *api.SubnamespaceAnchor) error {
366+
if r.ReadOnly {
367+
return nil
368+
}
369+
359370
if err := r.Delete(ctx, inst); err != nil {
360371
return fmt.Errorf("while deleting on apiserver: %w", err)
361372
}
@@ -378,6 +389,10 @@ func (r *Reconciler) getNamespace(ctx context.Context, nm string) (*corev1.Names
378389
}
379390

380391
func (r *Reconciler) writeNamespace(ctx context.Context, log logr.Logger, nm, pnm string) error {
392+
if r.ReadOnly {
393+
return nil
394+
}
395+
381396
inst := &corev1.Namespace{}
382397
inst.ObjectMeta.Name = nm
383398
metadata.SetAnnotation(inst, api.SubnamespaceOf, pnm)
@@ -395,6 +410,10 @@ func (r *Reconciler) writeNamespace(ctx context.Context, log logr.Logger, nm, pn
395410
}
396411

397412
func (r *Reconciler) deleteNamespace(ctx context.Context, log logr.Logger, inst *corev1.Namespace) error {
413+
if r.ReadOnly {
414+
return nil
415+
}
416+
398417
if err := r.Delete(ctx, inst); err != nil {
399418
log.Error(err, "While deleting subnamespace")
400419
return err

internal/hierarchyconfig/reconciler.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ type Reconciler struct {
7878
// These are interfaces to the other reconcilers
7979
AnchorReconciler AnchorReconcilerType
8080
HNCConfigReconciler HNCConfigReconcilerType
81+
82+
ReadOnly bool
8183
}
8284

8385
type AnchorReconcilerType interface {
@@ -704,6 +706,10 @@ func (r *Reconciler) writeHierarchy(ctx context.Context, log logr.Logger, orig,
704706
if reflect.DeepEqual(orig, inst) {
705707
return false, nil
706708
}
709+
if r.ReadOnly {
710+
// We *wanted* to change something, so assume that we need to re-reconcile other objects as well
711+
return true, nil
712+
}
707713
exists := !inst.CreationTimestamp.IsZero()
708714
if !exists && isDeletingNS {
709715
log.Info("Will not create hierarchyconfiguration since namespace is being deleted")
@@ -732,6 +738,10 @@ func (r *Reconciler) writeNamespace(ctx context.Context, log logr.Logger, orig,
732738
if reflect.DeepEqual(orig, inst) {
733739
return false, nil
734740
}
741+
if r.ReadOnly {
742+
// We *wanted* to change something, so assume that we need to re-reconcile other objects as well
743+
return true, nil
744+
}
735745

736746
// NB: HCR can't create namespaces, that's only in anchor reconciler
737747
stats.WriteNamespace()

internal/hncconfig/reconciler.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ type Reconciler struct {
5353
// not use it.
5454
HierarchyConfigUpdates chan event.GenericEvent
5555

56+
// ReadOnly disables writebacks
57+
ReadOnly bool
58+
5659
// activeGVKMode contains GRs that are configured in the Spec and their mapping
5760
// GVKs and configured modes.
5861
activeGVKMode gr2gvkMode
@@ -233,6 +236,10 @@ func (r *Reconciler) validateSingleton(inst *api.HNCConfiguration) {
233236
// We will write the singleton to apiserver even it is not changed because we assume this
234237
// reconciler is called very infrequently and is not performance critical.
235238
func (r *Reconciler) writeSingleton(ctx context.Context, inst *api.HNCConfiguration) error {
239+
if r.ReadOnly {
240+
return nil
241+
}
242+
236243
if inst.CreationTimestamp.IsZero() {
237244
// No point creating it if the CRD's being deleted
238245
if isDeleted, err := crd.IsDeletingCRD(ctx, api.HNCConfigSingletons); isDeleted || err != nil {

internal/integtest/setup.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func HNCBeforeSuite() {
102102
Expect(err).ToNot(HaveOccurred())
103103

104104
By("creating reconcilers")
105-
err = setup.CreateReconcilers(k8sManager, forest.NewForest(), 100, true)
105+
err = setup.CreateReconcilers(k8sManager, forest.NewForest(), 100, false, true)
106106
Expect(err).ToNot(HaveOccurred())
107107

108108
By("Creating clients")

internal/setup/reconcilers.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
// CreateReconcilers creates all reconcilers.
1818
//
1919
// This function is called both from main.go as well as from the integ tests.
20-
func CreateReconcilers(mgr ctrl.Manager, f *forest.Forest, maxReconciles int, useFakeClient bool) error {
20+
func CreateReconcilers(mgr ctrl.Manager, f *forest.Forest, maxReconciles int, readOnly, useFakeClient bool) error {
2121
if err := crd.Setup(mgr, useFakeClient); err != nil {
2222
return err
2323
}
@@ -31,6 +31,7 @@ func CreateReconcilers(mgr ctrl.Manager, f *forest.Forest, maxReconciles int, us
3131
Log: ctrl.Log.WithName("anchor").WithName("reconcile"),
3232
Forest: f,
3333
Affected: anchorChan,
34+
ReadOnly: readOnly,
3435
}
3536
if err := ar.SetupWithManager(mgr); err != nil {
3637
return fmt.Errorf("cannot create anchor reconciler: %s", err.Error())
@@ -50,6 +51,7 @@ func CreateReconcilers(mgr ctrl.Manager, f *forest.Forest, maxReconciles int, us
5051
Forest: f,
5152
Trigger: make(chan event.GenericEvent),
5253
HierarchyConfigUpdates: hcChan,
54+
ReadOnly: readOnly,
5355
}
5456
if err := hnccfgr.SetupWithManager(mgr); err != nil {
5557
return fmt.Errorf("cannot create Config reconciler: %s", err.Error())
@@ -63,6 +65,7 @@ func CreateReconcilers(mgr ctrl.Manager, f *forest.Forest, maxReconciles int, us
6365
AnchorReconciler: ar,
6466
HNCConfigReconciler: hnccfgr,
6567
Affected: hcChan,
68+
ReadOnly: readOnly,
6669
}
6770
if err := hcr.SetupWithManager(mgr, maxReconciles); err != nil {
6871
return fmt.Errorf("cannot create Hierarchy reconciler: %s", err.Error())

internal/setup/webhooks.go

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,9 @@ const (
3030
// DNSName is <service name>.<namespace>.svc
3131
var dnsName = fmt.Sprintf("%s.%s.svc", serviceName, secretNamespace)
3232

33-
// CreateCertsIfNeeded creates all certs for webhooks. This function is called from main.go.
34-
func CreateCertsIfNeeded(mgr ctrl.Manager, novalidation, internalCert, restartOnSecretRefresh bool) (chan struct{}, error) {
35-
setupFinished := make(chan struct{})
36-
if novalidation || !internalCert {
37-
close(setupFinished)
38-
return setupFinished, nil
39-
}
40-
41-
return setupFinished, cert.AddRotator(mgr, &cert.CertRotator{
33+
// ManageCerts creates all certs for webhooks. This function is called from main.go.
34+
func ManageCerts(mgr ctrl.Manager, setupFinished chan struct{}, restartOnSecretRefresh bool) error {
35+
return cert.AddRotator(mgr, &cert.CertRotator{
4236
SecretKey: types.NamespacedName{
4337
Namespace: secretNamespace,
4438
Name: secretName,

0 commit comments

Comments
 (0)