diff --git a/controllers/logging/logging_controller.go b/controllers/logging/logging_controller.go index 34a8e73f75..16e6a22b52 100644 --- a/controllers/logging/logging_controller.go +++ b/controllers/logging/logging_controller.go @@ -20,6 +20,7 @@ import ( "fmt" "os" "regexp" + "slices" "strings" "emperror.dev/errors" @@ -30,6 +31,7 @@ import ( v1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" "github.com/prometheus/client_golang/prometheus" corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -128,6 +130,18 @@ func (r *LoggingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct return reconcile.Result{}, errors.WrapIfWithDetails(err, "failed to get logging resources", "logging", logging) } + if ExternalFluentdRef := slices.Index(logging.Finalizers, logging.Status.FluentdConfigName); ExternalFluentdRef != -1 { + finalizer := logging.Finalizers[ExternalFluentdRef] + fluentdConfigName := types.NamespacedName{Namespace: logging.Spec.ControlNamespace, Name: finalizer} + + fluentdConfigRefCleaned := r.cleanupFluentdConfigReference(ctx, log, &logging, fluentdConfigName) + + if !fluentdConfigRefCleaned && logging.DeletionTimestamp != nil { + // FluentdConfig still exists + return reconcile.Result{}, errors.NewWithDetails("failed to delete logging resources, delete fluentdConfig first", "fluentdConfig", logging.Status.FluentdConfigName) + } + } + r.dynamicDefaults(ctx, log, loggingResources.GetSyslogNGSpec()) // metrics @@ -177,7 +191,7 @@ func (r *LoggingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct var loggingDataProvider loggingdataprovider.LoggingDataProvider - fluentdSpec := loggingResources.GetFluentdSpec() + fluentdExternal, fluentdSpec := loggingResources.GetFluentd() if fluentdSpec != nil { fluentdConfig, secretList, err := r.clusterConfigurationFluentd(loggingResources) if err != nil { @@ -188,9 +202,9 @@ func (r *LoggingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct } else { log.V(1).Info("flow configuration", "config", fluentdConfig) - reconcilers = append(reconcilers, fluentd.New(r.Client, r.Log, &logging, fluentdSpec, &fluentdConfig, secretList, reconcilerOpts).Reconcile) + reconcilers = append(reconcilers, fluentd.New(r.Client, r.Log, &logging, fluentdSpec, fluentdExternal, &fluentdConfig, secretList, reconcilerOpts).Reconcile) } - loggingDataProvider = fluentd.NewDataProvider(r.Client, &logging, fluentdSpec) + loggingDataProvider = fluentd.NewDataProvider(r.Client, &logging, fluentdSpec, fluentdExternal) } syslogNGSpec := loggingResources.GetSyslogNGSpec() @@ -265,7 +279,7 @@ func (r *LoggingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct log.Error(errors.New("nodeagent definition conflict"), problem) } } - reconcilers = append(reconcilers, nodeagent.New(r.Client, r.Log, &logging, fluentdSpec, agents, reconcilerOpts, fluentd.NewDataProvider(r.Client, &logging, fluentdSpec)).Reconcile) + reconcilers = append(reconcilers, nodeagent.New(r.Client, r.Log, &logging, fluentdSpec, agents, reconcilerOpts, fluentd.NewDataProvider(r.Client, &logging, fluentdSpec, fluentdExternal)).Reconcile) } for _, rec := range reconcilers { @@ -282,6 +296,21 @@ func (r *LoggingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct return ctrl.Result{}, nil } +func (r *LoggingReconciler) cleanupFluentdConfigReference(ctx context.Context, log logr.Logger, logging *loggingv1beta1.Logging, fluentdConfigRef types.NamespacedName) bool { + var fluentdConfig loggingv1beta1.FluentdConfig + // Check if it's safe to remove + if err := r.Client.Get(ctx, fluentdConfigRef, &fluentdConfig); err != nil && apierrors.IsNotFound(err) { + // Safe to delete, remove finalizer + logging.Finalizers = slices.DeleteFunc(logging.Finalizers, func(finalizer string) bool { + return finalizer == fluentdConfigRef.Name + }) + logging.Status.FluentdConfigName = "" + log.Info("cleaned up fluentdConfigRef", "name", fluentdConfigRef.Name) + return true + } + return false +} + func (r *LoggingReconciler) dynamicDefaults(ctx context.Context, log logr.Logger, syslogNGSpec *v1beta1.SyslogNGSpec) { nodes := corev1.NodeList{} if err := r.Client.List(ctx, &nodes); err != nil { diff --git a/pkg/resources/fluentd/dataprovider.go b/pkg/resources/fluentd/dataprovider.go index ccba81e864..d816178dfc 100644 --- a/pkg/resources/fluentd/dataprovider.go +++ b/pkg/resources/fluentd/dataprovider.go @@ -25,23 +25,25 @@ import ( ) type DataProvider struct { - client client.Client - logging *v1beta1.Logging - fluentdSpec *v1beta1.FluentdSpec + client client.Client + logging *v1beta1.Logging + fluentdSpec *v1beta1.FluentdSpec + fluentdConfig *v1beta1.FluentdConfig } -func NewDataProvider(client client.Client, logging *v1beta1.Logging, fluentdSpec *v1beta1.FluentdSpec) *DataProvider { +func NewDataProvider(client client.Client, logging *v1beta1.Logging, fluentdSpec *v1beta1.FluentdSpec, fluentdConfig *v1beta1.FluentdConfig) *DataProvider { return &DataProvider{ - client: client, - logging: logging, - fluentdSpec: fluentdSpec, + client: client, + logging: logging, + fluentdSpec: fluentdSpec, + fluentdConfig: fluentdConfig, } } func (p *DataProvider) GetReplicaCount(ctx context.Context) (*int32, error) { if p.fluentdSpec != nil { sts := &v1.StatefulSet{} - om := p.logging.FluentdObjectMeta(StatefulSetName, ComponentFluentd, *p.fluentdSpec) + om := p.logging.FluentdObjectMeta(StatefulSetName, ComponentFluentd, *p.fluentdSpec, p.fluentdConfig) err := p.client.Get(ctx, types.NamespacedName{Namespace: om.Namespace, Name: om.Name}, sts) if err != nil { return nil, errors.WrapIf(client.IgnoreNotFound(err), "getting fluentd statefulset") diff --git a/pkg/resources/fluentd/fluentd.go b/pkg/resources/fluentd/fluentd.go index a7e0995377..18b8e89c6a 100644 --- a/pkg/resources/fluentd/fluentd.go +++ b/pkg/resources/fluentd/fluentd.go @@ -68,8 +68,9 @@ const ( // Reconciler holds info what resource to reconcile type Reconciler struct { - Logging *v1beta1.Logging - fluentdSpec *v1beta1.FluentdSpec + Logging *v1beta1.Logging + fluentdSpec *v1beta1.FluentdSpec + fluentdConfig *v1beta1.FluentdConfig *reconciler.GenericResourceReconciler config *string secrets *secret.MountSecrets @@ -112,10 +113,11 @@ func (r *Reconciler) getServiceAccount() string { } func New(client client.Client, log logr.Logger, - logging *v1beta1.Logging, fluentdSpec *v1beta1.FluentdSpec, config *string, secrets *secret.MountSecrets, opts reconciler.ReconcilerOpts) *Reconciler { + logging *v1beta1.Logging, fluentdSpec *v1beta1.FluentdSpec, fluentdConfig *v1beta1.FluentdConfig, config *string, secrets *secret.MountSecrets, opts reconciler.ReconcilerOpts) *Reconciler { return &Reconciler{ Logging: logging, fluentdSpec: fluentdSpec, + fluentdConfig: fluentdConfig, GenericResourceReconciler: reconciler.NewGenericReconciler(client, log, opts), config: config, secrets: secrets, @@ -315,7 +317,7 @@ func (r *Reconciler) reconcileDrain(ctx context.Context) (*reconcile.Result, err } } - replicaCount, err := NewDataProvider(r.Client, r.Logging, r.fluentdSpec).GetReplicaCount(ctx) + replicaCount, err := NewDataProvider(r.Client, r.Logging, r.fluentdSpec, r.fluentdConfig).GetReplicaCount(ctx) if err != nil { return nil, errors.WrapIf(err, "get replica count for fluentd") } diff --git a/pkg/resources/fluentd/meta.go b/pkg/resources/fluentd/meta.go index 9833736ce6..b722b4288a 100644 --- a/pkg/resources/fluentd/meta.go +++ b/pkg/resources/fluentd/meta.go @@ -21,19 +21,29 @@ import ( // FluentdObjectMeta creates an objectMeta for resource fluentd func (r *Reconciler) FluentdObjectMeta(name, component string) metav1.ObjectMeta { + ownerReference := metav1.OwnerReference{ + APIVersion: r.Logging.APIVersion, + Kind: r.Logging.Kind, + Name: r.Logging.Name, + UID: r.Logging.UID, + Controller: util.BoolPointer(true), + } + + if r.fluentdConfig != nil { + ownerReference = metav1.OwnerReference{ + APIVersion: r.fluentdConfig.APIVersion, + Kind: r.fluentdConfig.Kind, + Name: r.fluentdConfig.Name, + UID: r.fluentdConfig.UID, + Controller: util.BoolPointer(true), + } + } + o := metav1.ObjectMeta{ - Name: r.Logging.QualifiedName(name), - Namespace: r.Logging.Spec.ControlNamespace, - Labels: r.Logging.GetFluentdLabels(component, *r.fluentdSpec), - OwnerReferences: []metav1.OwnerReference{ - { - APIVersion: r.Logging.APIVersion, - Kind: r.Logging.Kind, - Name: r.Logging.Name, - UID: r.Logging.UID, - Controller: util.BoolPointer(true), - }, - }, + Name: r.Logging.QualifiedName(name), + Namespace: r.Logging.Spec.ControlNamespace, + Labels: r.Logging.GetFluentdLabels(component, *r.fluentdSpec), + OwnerReferences: []metav1.OwnerReference{ownerReference}, } return *o.DeepCopy() } diff --git a/pkg/resources/model/reconciler.go b/pkg/resources/model/reconciler.go index 4e37d9b748..0c0dfe967f 100644 --- a/pkg/resources/model/reconciler.go +++ b/pkg/resources/model/reconciler.go @@ -235,6 +235,7 @@ func NewValidationReconciler( } logger.Info("found detached fluentd aggregator, making association", "name", resources.Fluentd.Configuration.Name) resources.Logging.Status.FluentdConfigName = resources.Fluentd.Configuration.Name + resources.Logging.Finalizers = append(resources.Logging.Finalizers, resources.Fluentd.Configuration.Name) resources.Fluentd.Configuration.Status.Active = utils.BoolPointer(true) resources.Fluentd.Configuration.Status.Logging = resources.Logging.Name diff --git a/pkg/resources/model/resources.go b/pkg/resources/model/resources.go index d42ed65dc5..d816ea8387 100644 --- a/pkg/resources/model/resources.go +++ b/pkg/resources/model/resources.go @@ -29,23 +29,19 @@ type LoggingResources struct { WatchNamespaces []string } -func (l LoggingResources) GetFluentd() *v1beta1.FluentdConfig { +func (l LoggingResources) getFluentdConfig() *v1beta1.FluentdConfig { if l.Fluentd.Configuration != nil { return l.Fluentd.Configuration } return nil } -func (l LoggingResources) GetFluentdSpec() *v1beta1.FluentdSpec { - - if detachedFluentd := l.GetFluentd(); detachedFluentd != nil { - return &detachedFluentd.Spec - } - if l.Logging.Spec.FluentdSpec != nil { - return l.Logging.Spec.FluentdSpec +func (l LoggingResources) GetFluentd() (*v1beta1.FluentdConfig, *v1beta1.FluentdSpec) { + if detachedFluentd := l.getFluentdConfig(); detachedFluentd != nil { + return detachedFluentd, &detachedFluentd.Spec } - return nil + return nil, l.Logging.Spec.FluentdSpec } type FluentdLoggingResources struct { diff --git a/pkg/resources/model/system.go b/pkg/resources/model/system.go index 5ac6228794..761dd1a7c2 100644 --- a/pkg/resources/model/system.go +++ b/pkg/resources/model/system.go @@ -32,7 +32,7 @@ import ( func CreateSystem(resources LoggingResources, secrets SecretLoaderFactory, logger logr.Logger) (*types.System, error) { logging := resources.Logging - fluentdSpec := resources.GetFluentdSpec() + _, fluentdSpec := resources.GetFluentd() var forwardInput *input.ForwardInputConfig if fluentdSpec != nil && fluentdSpec.ForwardInputConfig != nil { diff --git a/pkg/sdk/logging/api/v1beta1/logging_types.go b/pkg/sdk/logging/api/v1beta1/logging_types.go index 08eed053de..16a941a299 100644 --- a/pkg/sdk/logging/api/v1beta1/logging_types.go +++ b/pkg/sdk/logging/api/v1beta1/logging_types.go @@ -461,20 +461,29 @@ func persistentVolumeModePointer(mode v1.PersistentVolumeMode) *v1.PersistentVol } // FluentdObjectMeta creates an objectMeta for resource fluentd -func (l *Logging) FluentdObjectMeta(name, component string, f FluentdSpec) metav1.ObjectMeta { +func (l *Logging) FluentdObjectMeta(name, component string, f FluentdSpec, fc *FluentdConfig) metav1.ObjectMeta { + ownerReference := metav1.OwnerReference{ + APIVersion: l.APIVersion, + Kind: l.Kind, + Name: l.Name, + UID: l.UID, + Controller: util.BoolPointer(true), + } + + if fc != nil { + ownerReference = metav1.OwnerReference{ + APIVersion: fc.APIVersion, + Kind: fc.Kind, + Name: fc.Name, + UID: fc.UID, + Controller: util.BoolPointer(true), + } + } o := metav1.ObjectMeta{ - Name: l.QualifiedName(name), - Namespace: l.Spec.ControlNamespace, - Labels: l.GetFluentdLabels(component, f), - OwnerReferences: []metav1.OwnerReference{ - { - APIVersion: l.APIVersion, - Kind: l.Kind, - Name: l.Name, - UID: l.UID, - Controller: util.BoolPointer(true), - }, - }, + Name: l.QualifiedName(name), + Namespace: l.Spec.ControlNamespace, + Labels: l.GetFluentdLabels(component, f), + OwnerReferences: []metav1.OwnerReference{ownerReference}, } return o }