diff --git a/api/observability/v1/clusterlogforwarder_types.go b/api/observability/v1/clusterlogforwarder_types.go index e5799a15f..ce05cf5ce 100644 --- a/api/observability/v1/clusterlogforwarder_types.go +++ b/api/observability/v1/clusterlogforwarder_types.go @@ -143,6 +143,15 @@ type CollectorSpec struct { // +kubebuilder:validation:Pattern="^(?:[0-9]{1,2}|100)%?$" // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Max Unavailable",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:text"} MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"` + + // TerminationGracePeriodSeconds defines the termination grace period for collector pods + // in seconds. If not specified, the default is 10 seconds. + // + // +nullable + // +kubebuilder:validation:Optional + // +kubebuilder:validation:Minimum:=1 + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Termination Grace Period Seconds",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:number"} + TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"` } type NetworkPolicy struct { diff --git a/api/observability/v1/zz_generated.deepcopy.go b/api/observability/v1/zz_generated.deepcopy.go index 218ee7395..0321bc574 100644 --- a/api/observability/v1/zz_generated.deepcopy.go +++ b/api/observability/v1/zz_generated.deepcopy.go @@ -627,6 +627,11 @@ func (in *CollectorSpec) DeepCopyInto(out *CollectorSpec) { *out = new(intstr.IntOrString) **out = **in } + if in.TerminationGracePeriodSeconds != nil { + in, out := &in.TerminationGracePeriodSeconds, &out.TerminationGracePeriodSeconds + *out = new(int64) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CollectorSpec. diff --git a/bundle/manifests/cluster-logging.clusterserviceversion.yaml b/bundle/manifests/cluster-logging.clusterserviceversion.yaml index f22394e97..9a952ef73 100644 --- a/bundle/manifests/cluster-logging.clusterserviceversion.yaml +++ b/bundle/manifests/cluster-logging.clusterserviceversion.yaml @@ -82,7 +82,7 @@ metadata: categories: OpenShift Optional, Logging & Tracing, Observability certified: "false" containerImage: quay.io/openshift-logging/cluster-logging-operator:latest - createdAt: "2026-06-17T14:13:34Z" + createdAt: "2026-07-07T19:26:30Z" description: The Red Hat OpenShift Logging Operator for OCP provides a means for configuring and managing log collection and forwarding. features.operators.openshift.io/cnf: "false" @@ -172,6 +172,13 @@ spec: path: collector.resources x-descriptors: - urn:alm:descriptor:com.tectonic.ui:resourceRequirements + - description: |- + TerminationGracePeriodSeconds defines the termination grace period for collector pods + in seconds. If not specified, the default is 10 seconds. + displayName: Termination Grace Period Seconds + path: collector.terminationGracePeriodSeconds + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:number - description: Define the tolerations the collector pods will accept displayName: Tolerations path: collector.tolerations diff --git a/bundle/manifests/observability.openshift.io_clusterlogforwarders.yaml b/bundle/manifests/observability.openshift.io_clusterlogforwarders.yaml index 7f4bb8448..3cd225f12 100644 --- a/bundle/manifests/observability.openshift.io_clusterlogforwarders.yaml +++ b/bundle/manifests/observability.openshift.io_clusterlogforwarders.yaml @@ -1033,6 +1033,14 @@ spec: More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object + terminationGracePeriodSeconds: + description: |- + TerminationGracePeriodSeconds defines the termination grace period for collector pods + in seconds. If not specified, the default is 10 seconds. + format: int64 + minimum: 1 + nullable: true + type: integer tolerations: description: Define the tolerations the collector pods will accept items: diff --git a/config/crd/bases/observability.openshift.io_clusterlogforwarders.yaml b/config/crd/bases/observability.openshift.io_clusterlogforwarders.yaml index c6b194e2c..fcdf4f6c9 100644 --- a/config/crd/bases/observability.openshift.io_clusterlogforwarders.yaml +++ b/config/crd/bases/observability.openshift.io_clusterlogforwarders.yaml @@ -1033,6 +1033,14 @@ spec: More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object + terminationGracePeriodSeconds: + description: |- + TerminationGracePeriodSeconds defines the termination grace period for collector pods + in seconds. If not specified, the default is 10 seconds. + format: int64 + minimum: 1 + nullable: true + type: integer tolerations: description: Define the tolerations the collector pods will accept items: diff --git a/config/manifests/bases/cluster-logging.clusterserviceversion.yaml b/config/manifests/bases/cluster-logging.clusterserviceversion.yaml index 3ba461347..c105acde5 100644 --- a/config/manifests/bases/cluster-logging.clusterserviceversion.yaml +++ b/config/manifests/bases/cluster-logging.clusterserviceversion.yaml @@ -95,6 +95,13 @@ spec: path: collector.resources x-descriptors: - urn:alm:descriptor:com.tectonic.ui:resourceRequirements + - description: |- + TerminationGracePeriodSeconds defines the termination grace period for collector pods + in seconds. If not specified, the default is 10 seconds. + displayName: Termination Grace Period Seconds + path: collector.terminationGracePeriodSeconds + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:number - description: Define the tolerations the collector pods will accept displayName: Tolerations path: collector.tolerations diff --git a/internal/collector/collector.go b/internal/collector/collector.go index 47ac59a06..2af85d55f 100644 --- a/internal/collector/collector.go +++ b/internal/collector/collector.go @@ -26,30 +26,31 @@ const ( //DefaultMaxUnavailable is the maxUnavailable collector setting when not defined by spec.collector.maxUnavailable DefaultMaxUnavailable = "100%" - defaultAudience = "openshift" - clusterLoggingPriorityClassName = "system-node-critical" - metricsVolumeName = "metrics" - metricsVolumePath = "/etc/collector/metrics" - saTokenVolumeName = "sa-token" - saTokenExpirationSecs = 3600 //1 hour - sourcePodsName = "varlogpods" - sourcePodsPath = "/var/log/pods" - sourceJournalName = "varlogjournal" - sourceJournalPath = "/var/log/journal" - sourceAuditdName = "varlogaudit" - sourceAuditdPath = "/var/log/audit" - sourceAuditOVNName = "varlogovn" - sourceOVNPath = "/var/log/ovn" - sourceOAuthServerName = "varlogoauthserver" - sourceOAuthServerPath = "/var/log/oauth-server" - sourceOAuthAPIServerName = "varlogoauthapiserver" - sourceOAuthAPIServerPath = "/var/log/oauth-apiserver" - sourceOpenshiftAPIServerName = "varlogopenshiftapiserver" - sourceOpenshiftAPIServerPath = "/var/log/openshift-apiserver" - sourceKubeAPIServerName = "varlogkubeapiserver" - sourceKubeAPIServerPath = "/var/log/kube-apiserver" - tmpVolumeName = "tmp" - tmpPath = "/tmp" + defaultAudience = "openshift" + clusterLoggingPriorityClassName = "system-node-critical" + metricsVolumeName = "metrics" + metricsVolumePath = "/etc/collector/metrics" + saTokenVolumeName = "sa-token" + saTokenExpirationSecs = 3600 //1 hour + defaultTerminationGracePeriodSeconds int64 = 10 + sourcePodsName = "varlogpods" + sourcePodsPath = "/var/log/pods" + sourceJournalName = "varlogjournal" + sourceJournalPath = "/var/log/journal" + sourceAuditdName = "varlogaudit" + sourceAuditdPath = "/var/log/audit" + sourceAuditOVNName = "varlogovn" + sourceOVNPath = "/var/log/ovn" + sourceOAuthServerName = "varlogoauthserver" + sourceOAuthServerPath = "/var/log/oauth-server" + sourceOAuthAPIServerName = "varlogoauthapiserver" + sourceOAuthAPIServerPath = "/var/log/oauth-apiserver" + sourceOpenshiftAPIServerName = "varlogopenshiftapiserver" + sourceOpenshiftAPIServerPath = "/var/log/openshift-apiserver" + sourceKubeAPIServerName = "varlogkubeapiserver" + sourceKubeAPIServerPath = "/var/log/kube-apiserver" + tmpVolumeName = "tmp" + tmpPath = "/tmp" ) type Visitor func(collector *v1.Container, podSpec *v1.PodSpec, resNames *factory.ForwarderResourceNames, namespace, logLevel string) @@ -144,11 +145,18 @@ func (f *Factory) NewDeployment(namespace, name string, trustedCABundle *v1.Conf func (f *Factory) NewPodSpec(trustedCABundle *v1.ConfigMap, spec obs.ClusterLogForwarderSpec, clusterID string, tlsProfileSpec configv1.TLSProfileSpec, namespace string) *v1.PodSpec { + var gracePeriod *int64 + if f.CollectorSpec.TerminationGracePeriodSeconds != nil { + gracePeriod = f.CollectorSpec.TerminationGracePeriodSeconds + } else { + gracePeriod = utils.GetPtr(defaultTerminationGracePeriodSeconds) + } + podSpec := &v1.PodSpec{ NodeSelector: utils.EnsureLinuxNodeSelector(f.NodeSelector()), PriorityClassName: clusterLoggingPriorityClassName, ServiceAccountName: f.ResourceNames.ServiceAccount, - TerminationGracePeriodSeconds: utils.GetPtr[int64](10), + TerminationGracePeriodSeconds: gracePeriod, Tolerations: append(constants.DefaultTolerations(), f.Tolerations()...), Affinity: f.Affinity(), diff --git a/internal/collector/collector_test.go b/internal/collector/collector_test.go index 9aad4faf3..303da4f43 100644 --- a/internal/collector/collector_test.go +++ b/internal/collector/collector_test.go @@ -169,6 +169,20 @@ var _ = Describe("Factory#Daemonset", func() { Describe("when creating the podSpec", func() { + Context("and evaluating terminationGracePeriodSeconds", func() { + It("should default to 10 when not specified", func() { + Expect(*podSpec.TerminationGracePeriodSeconds).To(Equal(int64(10))) + }) + + It("should use spec value when specified", func() { + factory.CollectorSpec = obs.CollectorSpec{ + TerminationGracePeriodSeconds: utils.GetPtr[int64](60), + } + podSpec = *factory.NewPodSpec(nil, obs.ClusterLogForwarderSpec{}, "1234", tls.GetClusterTLSProfileSpec(nil), constants.OpenshiftNS) + Expect(*podSpec.TerminationGracePeriodSeconds).To(Equal(int64(60))) + }) + }) + Context("and evaluating tolerations", func() { It("should add only defaults when none are defined", func() { Expect(podSpec.Tolerations).To(Equal(constants.DefaultTolerations())) @@ -529,6 +543,20 @@ var _ = Describe("Factory#Deployment", func() { Describe("when creating the podSpec", func() { + Context("and evaluating terminationGracePeriodSeconds", func() { + It("should default to 10 when not specified", func() { + Expect(*podSpec.TerminationGracePeriodSeconds).To(Equal(int64(10))) + }) + + It("should use spec value when specified", func() { + factory.CollectorSpec = obs.CollectorSpec{ + TerminationGracePeriodSeconds: utils.GetPtr[int64](60), + } + podSpec = *factory.NewPodSpec(nil, obs.ClusterLogForwarderSpec{}, "1234", tls.GetClusterTLSProfileSpec(nil), constants.OpenshiftNS) + Expect(*podSpec.TerminationGracePeriodSeconds).To(Equal(int64(60))) + }) + }) + Context("and mounting volumes", func() { It("should not mount host path or sa token volumes", func() { Expect(podSpec.Volumes).NotTo(ContainElement(v1.Volume{Name: saTokenVolumeName, VolumeSource: v1.VolumeSource{Projected: &v1.ProjectedVolumeSource{Sources: []v1.VolumeProjection{saToken}}}}))