Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions cmd/hyperconverged-cluster-operator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import (
"github.com/kubevirt/hyperconverged-cluster-operator/api"
hcov1beta1 "github.com/kubevirt/hyperconverged-cluster-operator/api/v1beta1"
"github.com/kubevirt/hyperconverged-cluster-operator/cmd/cmdcommon"
"github.com/kubevirt/hyperconverged-cluster-operator/controllers/admissionpolicy"
"github.com/kubevirt/hyperconverged-cluster-operator/controllers/crd"
"github.com/kubevirt/hyperconverged-cluster-operator/controllers/descheduler"
"github.com/kubevirt/hyperconverged-cluster-operator/controllers/handlers"
Expand Down Expand Up @@ -254,6 +255,12 @@ func main() {
}
}

if err = admissionpolicy.RegisterReconciler(mgr); err != nil {
logger.Error(err, "failed to register the admission policy controller")
eventEmitter.EmitEvent(nil, corev1.EventTypeWarning, "InitError", "Unable to register admission policy controller; "+err.Error())
os.Exit(1)
}

err = createPriorityClass(ctx, mgr)
cmdHelper.ExitOnError(err, "Failed creating PriorityClass")

Expand Down Expand Up @@ -350,6 +357,12 @@ func getCacheOption(operatorNamespace string, ci hcoutil.ClusterInfo) cache.Opti
Field: namespaceSelector,
},
&apiextensionsv1.CustomResourceDefinition{}: {},
&admissionregistrationv1.ValidatingAdmissionPolicy{}: {
Label: labels.SelectorFromSet(labels.Set{hcoutil.AppLabel: hcoutil.HyperConvergedName}),
},
&admissionregistrationv1.ValidatingAdmissionPolicyBinding{}: {
Label: labels.SelectorFromSet(labels.Set{hcoutil.AppLabel: hcoutil.HyperConvergedName}),
},
},
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/hyperconverged-cluster-webhook/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ func main() {
err = bearertokencontroller.RegisterReconciler(mgr, ci, eventEmitter)
cmdHelper.ExitOnError(err, "Cannot register the Bearer Token reconciler")

if err = webhooks.SetupWebhookWithManager(ctx, mgr, ci.IsOpenshift(), hcoTLSSecurityProfile); err != nil {
if err = webhooks.SetupWebhookWithManager(mgr, ci.IsOpenshift(), hcoTLSSecurityProfile); err != nil {
logger.Error(err, "unable to create webhook", "webhook", "HyperConverged")
eventEmitter.EmitEvent(nil, corev1.EventTypeWarning, "InitError", "Unable to create webhook")
os.Exit(1)
Expand Down
205 changes: 205 additions & 0 deletions controllers/admissionpolicy/admission_policy_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package admissionpolicy

import (
"context"
"errors"
"fmt"
"reflect"

"github.com/go-logr/logr"
"github.com/google/uuid"
operatorhandler "github.com/operator-framework/operator-lib/handler"
admissionv1 "k8s.io/api/admissionregistration/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
k8stypes "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"

hcoutil "github.com/kubevirt/hyperconverged-cluster-operator/pkg/util"
)

const controllerName = "admission-policy-controller"

var (
initLogger = logf.Log.WithName(controllerName)

randomConstSuffix = uuid.New().String()

startupReq = reconcile.Request{
NamespacedName: k8stypes.NamespacedName{
Name: "startup-req-" + randomConstSuffix,
},
}
)

// RegisterReconciler creates a new Nodes Reconciler and registers it into manager.
func RegisterReconciler(mgr manager.Manager) error {
startupEvent := make(chan event.GenericEvent, 1)
defer close(startupEvent)

r := newReconciler(mgr, startupEvent)

startupEvent <- event.GenericEvent{}

return add(mgr, r)
}

// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager, startupEvent <-chan event.GenericEvent) *ReconcileAdmissionPolicy {
initLogger.Info("Initializing the admission policy controller")

r := &ReconcileAdmissionPolicy{
Client: mgr.GetClient(),
startupEvent: startupEvent,
}

return r
}

// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r *ReconcileAdmissionPolicy) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func add(mgr manager.Manager, r *ReconcileAdmissionPolicy) error {
func addController(mgr manager.Manager, r *ReconcileAdmissionPolicy) error {

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use this name in all existing controller. Let's keep the name for consistency.

// Create a new controller
c, err := controller.New(controllerName, mgr, controller.Options{Reconciler: r})
if err != nil {
return err
}

// Watch for changes to the ValidatingAdmissionPolicy
if err = c.Watch(
source.Kind[*admissionv1.ValidatingAdmissionPolicy](
mgr.GetCache(), &admissionv1.ValidatingAdmissionPolicy{},
&operatorhandler.InstrumentedEnqueueRequestForObject[*admissionv1.ValidatingAdmissionPolicy]{},
policyPredicate,
),
); err != nil {
return err
}

// Watch for changes to the ValidatingAdmissionPolicyBinding
if err = c.Watch(
source.Kind[*admissionv1.ValidatingAdmissionPolicyBinding](
mgr.GetCache(), &admissionv1.ValidatingAdmissionPolicyBinding{},
&handler.TypedEnqueueRequestForObject[*admissionv1.ValidatingAdmissionPolicyBinding]{},
bindingPredicate,
),
); err != nil {
return err
}

return c.Watch(
source.Channel(
r.startupEvent,
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, a client.Object) []reconcile.Request {
logr.FromContextOrDiscard(ctx).Info("first reconciliation of ValidatingAdmissionPolicy")
return []reconcile.Request{startupReq}
}),
),
)
}

// ReconcileAdmissionPolicy reconciles the ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding
type ReconcileAdmissionPolicy struct {
client.Client
startupEvent <-chan event.GenericEvent
}

// Reconcile updates the ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding
func (r *ReconcileAdmissionPolicy) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) {
logger, err := logr.FromContext(ctx)
if err != nil {
logger = initLogger.WithValues("Request.Name", req.Name)
}

logger.Info(fmt.Sprintf("Reconciling admission policy %s", req.Name))

startup := startupReq == req
var policyErr, bindingErr error

if req.Name == policyName || startup {
policyErr = r.reconcilePolicy(ctx, logger)
}

if req.Name == policyBindingName || startup {
bindingErr = r.reconcileBinding(ctx, logger)
}

err = errors.Join(policyErr, bindingErr)
if err != nil {
logger.Error(err, "Reconciliation failed")
}

return reconcile.Result{}, err
}

func (r *ReconcileAdmissionPolicy) reconcilePolicy(ctx context.Context, logger logr.Logger) error {
policy := getRequiredPolicy()
key := client.ObjectKeyFromObject(policy)
foundPolicy := &admissionv1.ValidatingAdmissionPolicy{}

if err := r.Get(ctx, key, foundPolicy); err != nil {
if k8serrors.IsNotFound(err) {
logger.Info("ValidatingAdmissionPolicy does not exist; creating it", "name", policy.Name)
return r.Create(ctx, policy.DeepCopy())
}

return err
}

changed := false
if !reflect.DeepEqual(foundPolicy.Spec, policy.Spec) {
policy.Spec.DeepCopyInto(&foundPolicy.Spec)
changed = true
}

if !hcoutil.CompareLabels(policy, foundPolicy) {
hcoutil.MergeLabels(&policy.ObjectMeta, &foundPolicy.ObjectMeta)
changed = true
}

if changed {
logger.Info("ValidatingAdmissionPolicy was modified; updating it", "name", policy.Name)
return r.Update(ctx, foundPolicy)
}

return nil
}

func (r *ReconcileAdmissionPolicy) reconcileBinding(ctx context.Context, logger logr.Logger) error {
binding := getRequiredBinding()

key := client.ObjectKeyFromObject(binding)
foundBinding := &admissionv1.ValidatingAdmissionPolicyBinding{}

if err := r.Get(ctx, key, foundBinding); err != nil {
if k8serrors.IsNotFound(err) {
logger.Info("ValidatingAdmissionPolicyBinding does not exist; creating it", "name", binding.Name)
return r.Create(ctx, binding.DeepCopy())
}

return err
}

changed := false
if !reflect.DeepEqual(foundBinding.Spec, binding.Spec) {
binding.Spec.DeepCopyInto(&foundBinding.Spec)
changed = true
}

if !hcoutil.CompareLabels(binding, foundBinding) {
hcoutil.MergeLabels(&binding.ObjectMeta, &foundBinding.ObjectMeta)
changed = true
}

if changed {
logger.Info("ValidatingAdmissionPolicyBinding was modified; updating it", "name", binding.Name)
return r.Update(ctx, foundBinding)
}

return nil
}
Loading