Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OCPVE-663: feat: introduce k8s events to lvmcluster and vgmanager #403

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
8 changes: 8 additions & 0 deletions bundle/manifests/lvms-operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,14 @@ spec:
- get
- patch
- update
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- update
serviceAccountName: vg-manager
strategy: deployment
installModes:
Expand Down
15 changes: 10 additions & 5 deletions cmd/vgmanager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ import (
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/klog/v2"
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
_ "k8s.io/client-go/plugin/pkg/client/auth"

ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
Expand All @@ -55,7 +57,9 @@ func main() {
opts := zap.Options{}
opts.BindFlags(flag.CommandLine)
opts.Development = developmentMode
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
logr := zap.New(zap.UseFlagOptions(&opts))
ctrl.SetLogger(logr)
klog.SetLogger(logr)

setupLog := ctrl.Log.WithName("setup")

Expand All @@ -73,10 +77,11 @@ func main() {
}

if err = (&vgmanager.VGReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
NodeName: os.Getenv("NODE_NAME"),
Namespace: os.Getenv("POD_NAMESPACE"),
Client: mgr.GetClient(),
EventRecorder: mgr.GetEventRecorderFor(vgmanager.ControllerName),
Scheme: mgr.GetScheme(),
NodeName: os.Getenv("NODE_NAME"),
Namespace: os.Getenv("POD_NAMESPACE"),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "VGManager")
os.Exit(1)
Expand Down
8 changes: 8 additions & 0 deletions config/rbac/vg_manager_role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,11 @@ rules:
- get
- patch
- update
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- update
51 changes: 38 additions & 13 deletions controllers/lvmcluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
k8serrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
corev1helper "k8s.io/component-helpers/scheduling/corev1"

ctrl "sigs.k8s.io/controller-runtime"
Expand All @@ -42,6 +43,13 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

type EventReasonInfo string
type EventReasonError string

const EventReasonErrorDeletionPending EventReasonError = "DeletionPending"
const EventReasonErrorResourceReconciliationFailed EventReasonError = "ResourceReconciliationFailed"
const EventReasonResourceReconciliationSuccess EventReasonInfo = "ResourceReconciliationSuccess"

var lvmClusterFinalizer = "lvmcluster.topolvm.io"

const (
Expand All @@ -64,6 +72,7 @@ type resourceManager interface {
// LVMClusterReconciler reconciles a LVMCluster object
type LVMClusterReconciler struct {
client.Client
record.EventRecorder
Scheme *runtime.Scheme
ClusterType cluster.Type
Namespace string
Expand Down Expand Up @@ -146,15 +155,17 @@ func (r *LVMClusterReconciler) reconcile(ctx context.Context, instance *lvmv1alp
return ctrl.Result{}, fmt.Errorf("failed to check if LogicalVolumes exist: %w", err)
}
if lvsExist {
waitForLVRemoval := time.Second * 10
err := fmt.Errorf("found PVCs provisioned by topolvm, waiting %s for their deletion: %w", waitForLVRemoval, err)
r.WarningEvent(ctx, instance, EventReasonErrorDeletionPending, err)
// check every 10 seconds if there are still PVCs present
return ctrl.Result{RequeueAfter: time.Second * 10},
fmt.Errorf("found PVCs provisioned by topolvm, waiting for their deletion: %w", err)
return ctrl.Result{RequeueAfter: waitForLVRemoval}, err
}

logger.Info("processing LVMCluster deletion")
if err := r.processDelete(ctx, instance); err != nil {
// check every 10 seconds if there are still PVCs present or the LogicalVolumes are removed
return ctrl.Result{Requeue: true}, fmt.Errorf("failed to process LVMCluster deletion")
return ctrl.Result{Requeue: true}, fmt.Errorf("failed to process LVMCluster deletion: %w", err)
}
return reconcile.Result{}, nil
}
Expand Down Expand Up @@ -199,11 +210,14 @@ func (r *LVMClusterReconciler) reconcile(ctx context.Context, instance *lvmv1alp

resourceSyncElapsedTime := time.Since(resourceSyncStart)
if len(errs) > 0 {
return ctrl.Result{}, fmt.Errorf("failed to reconcile resources managed by LVMCluster within %v: %w",
resourceSyncElapsedTime, errors.Join(errs...))
err := fmt.Errorf("failed to reconcile resources managed by LVMCluster: %w", errors.Join(errs...))
r.WarningEvent(ctx, instance, EventReasonErrorResourceReconciliationFailed, err)
return ctrl.Result{}, err
}

logger.Info("successfully reconciled LVMCluster", "resourceSyncElapsedTime", resourceSyncElapsedTime)
msg := "successfully reconciled LVMCluster"
logger.Info(msg, "resourceSyncElapsedTime", resourceSyncElapsedTime)
r.NormalEvent(ctx, instance, EventReasonResourceReconciliationSuccess, msg)

return ctrl.Result{}, nil
}
Expand Down Expand Up @@ -358,15 +372,13 @@ func (r *LVMClusterReconciler) logicalVolumesExist(ctx context.Context) (bool, e
return false, fmt.Errorf("failed to get TopoLVM LogicalVolume list: %w", err)
}
if len(logicalVolumeList.Items) > 0 {

return true, nil
}
return false, nil
}

func (r *LVMClusterReconciler) processDelete(ctx context.Context, instance *lvmv1alpha1.LVMCluster) error {
if controllerutil.ContainsFinalizer(instance, lvmClusterFinalizer) {

resourceDeletionList := []resourceManager{
&topolvmVolumeSnapshotClass{},
&topolvmStorageClass{},
Expand All @@ -383,16 +395,29 @@ func (r *LVMClusterReconciler) processDelete(ctx context.Context, instance *lvmv

for _, unit := range resourceDeletionList {
if err := unit.ensureDeleted(r, ctx, instance); err != nil {
return fmt.Errorf("failed cleaning up: %s %w", unit.getName(), err)
err := fmt.Errorf("failed cleaning up %s: %w", unit.getName(), err)
r.WarningEvent(ctx, instance, EventReasonErrorDeletionPending, err)
return err
}
}
}

if update := controllerutil.RemoveFinalizer(instance, lvmClusterFinalizer); update {
if err := r.Client.Update(ctx, instance); err != nil {
return fmt.Errorf("failed to remove finalizer from LVMCluster %s: %w", instance.GetName(), err)
}
if update := controllerutil.RemoveFinalizer(instance, lvmClusterFinalizer); update {
if err := r.Client.Update(ctx, instance); err != nil {
return fmt.Errorf("failed to remove finalizer from LVMCluster %s: %w", instance.GetName(), err)
}
}

return nil
}

func (r *LVMClusterReconciler) WarningEvent(_ context.Context, obj client.Object, reason EventReasonError, err error) {
r.Event(obj, corev1.EventTypeWarning, string(reason), err.Error())
}

func (r *LVMClusterReconciler) NormalEvent(ctx context.Context, obj client.Object, reason EventReasonInfo, message string) {
if !log.FromContext(ctx).V(1).Enabled() {
return
}
r.Event(obj, corev1.EventTypeNormal, string(reason), message)
}
11 changes: 6 additions & 5 deletions controllers/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,12 @@ var _ = BeforeSuite(func() {
Expect(err).ToNot(HaveOccurred())

err = (&LVMClusterReconciler{
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
ClusterType: clusterType,
Namespace: testLvmClusterNamespace,
ImageName: testImageName,
Client: k8sManager.GetClient(),
EventRecorder: k8sManager.GetEventRecorderFor(ControllerName),
Scheme: k8sManager.GetScheme(),
ClusterType: clusterType,
Namespace: testLvmClusterNamespace,
ImageName: testImageName,
}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())

Expand Down
8 changes: 4 additions & 4 deletions controllers/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,17 +95,17 @@ func verifyDaemonSetReadiness(ds *appsv1.DaemonSet) error {

func verifyDeploymentReadiness(dep *appsv1.Deployment) error {
if len(dep.Status.Conditions) == 0 {
return fmt.Errorf("the Deployment is not ready: %s/%s. deployment cannot be ready as no condition was found", dep.Namespace, dep.Name)
return fmt.Errorf("the Deployment %s/%s is not ready as no condition was found", dep.Namespace, dep.Name)
}
for _, condition := range dep.Status.Conditions {
if condition.Type == appsv1.DeploymentAvailable && condition.Status == corev1.ConditionFalse {
return fmt.Errorf("the Deployment is not ready: %s/%s. deployment has not reached minimum availability and thus not ready: %v",
return fmt.Errorf("the Deployment %s/%s has not reached minimum availability and is not ready: %v",
dep.Namespace, dep.Name, condition)
} else if condition.Type == appsv1.DeploymentProgressing && condition.Status == corev1.ConditionFalse {
return fmt.Errorf("the Deployment is not ready: %s/%s. deployment has not progressed and is thus not ready: %v",
return fmt.Errorf("the Deployment %s/%s has not progressed and is not ready: %v",
dep.Namespace, dep.Name, condition)
} else if condition.Type == appsv1.DeploymentReplicaFailure && condition.Status == corev1.ConditionTrue {
return fmt.Errorf("the Deployment is not ready: %s/%s. deployment has a replica failure and is thus not ready: %v",
return fmt.Errorf("the Deployment %s/%s has a replica failure and is not ready: %v",
dep.Namespace, dep.Name, condition)
}
}
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ func main() {
if err = (&controllers.LVMClusterReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
EventRecorder: mgr.GetEventRecorderFor(controllers.ControllerName),
ClusterType: clusterType,
Namespace: operatorNamespace,
TopoLVMLeaderElectionPassthrough: leaderElectionConfig,
Expand Down
16 changes: 10 additions & 6 deletions pkg/vgmanager/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,7 @@ func (r *VGReconciler) setVolumeGroupStatus(ctx context.Context, status *lvmv1al
logger := log.FromContext(ctx)

// Get LVMVolumeGroupNodeStatus and set the relevant VGStatus
nodeStatus := &lvmv1alpha1.LVMVolumeGroupNodeStatus{
ObjectMeta: metav1.ObjectMeta{
Name: r.NodeName,
Namespace: r.Namespace,
},
}
nodeStatus := r.getLVMVolumeGroupNodeStatus()

result, err := ctrl.CreateOrUpdate(ctx, r.Client, nodeStatus, func() error {
exists := false
Expand Down Expand Up @@ -171,3 +166,12 @@ func (r *VGReconciler) setDevices(status *lvmv1alpha1.VGStatus) (bool, error) {

return devicesExist, nil
}

func (r *VGReconciler) getLVMVolumeGroupNodeStatus() *lvmv1alpha1.LVMVolumeGroupNodeStatus {
return &lvmv1alpha1.LVMVolumeGroupNodeStatus{
ObjectMeta: metav1.ObjectMeta{
Name: r.NodeName,
Namespace: r.Namespace,
},
}
}
Loading