@@ -4,12 +4,14 @@ import (
44 "testing"
55
66 operatorv1 "github.com/openshift/api/operator/v1"
7+ corev1 "k8s.io/api/core/v1"
78 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
89 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
910 "k8s.io/apimachinery/pkg/types"
1011
1112 "github.com/opendatahub-io/opendatahub-operator/v2/internal/controller/status"
1213 "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster/gvk"
14+ "github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/labels"
1315 "github.com/opendatahub-io/opendatahub-operator/v2/pkg/utils/test/matchers/jq"
1416 "github.com/opendatahub-io/opendatahub-operator/v2/pkg/utils/test/testf"
1517
@@ -25,8 +27,6 @@ const (
2527// createMonitorsEnvironment sets up the namespace and monitors with specific labels.
2628// Pre-test cleanup: Ensures resources from previous test runs are deleted before creation.
2729// Post-test: Respects --deletion-policy flag (cleanup handled by framework, not this function).
28- //
29- //nolint:unused // Helper function for future webhook tests - will be used when webhook validation tests are added
3030func (tc * MonitoringTestCtx ) createMonitorsEnvironment (t * testing.T , namespaceLabels map [string ]string , monitorLabels map [string ]string ) {
3131 t .Helper ()
3232
@@ -50,16 +50,16 @@ func (tc *MonitoringTestCtx) createMonitorsEnvironment(t *testing.T, namespaceLa
5050 t .Logf ("Pre-test cleanup completed" )
5151
5252 // Helper to apply metadata labels if provided
53- applyLabels := func (labels map [string ]string ) testf.TransformFn {
53+ applyLabels := func (lbls map [string ]string ) testf.TransformFn {
5454 return func (obj * unstructured.Unstructured ) error {
55- if len (labels ) == 0 {
55+ if len (lbls ) == 0 {
5656 return nil
5757 }
5858 currentLabels := obj .GetLabels ()
5959 if currentLabels == nil {
6060 currentLabels = make (map [string ]string )
6161 }
62- for k , v := range labels {
62+ for k , v := range lbls {
6363 currentLabels [k ] = v
6464 }
6565 obj .SetLabels (currentLabels )
@@ -126,3 +126,165 @@ func (tc *MonitoringTestCtx) ValidateMonitoringWebhookTestsSetup(t *testing.T) {
126126
127127 t .Logf ("Webhook tests setup complete: monitoring is enabled and ready" )
128128}
129+
130+ // ValidateMonitoringLabelValueEnforcementOnNamespace tests that the validation policy blocks invalid monitoring label values.
131+ func (tc * MonitoringTestCtx ) ValidateMonitoringLabelValueEnforcementOnNamespace (t * testing.T ) {
132+ t .Helper ()
133+
134+ // Pre-test cleanup: ensure namespace doesn't exist from prior runs
135+ tc .DeleteResource (
136+ WithMinimalObject (gvk .Namespace , types.NamespacedName {Name : TestNamespaceName }),
137+ WithIgnoreNotFound (true ),
138+ WithWaitForDeletion (true ),
139+ )
140+
141+ // Attempt to create namespace with INVALID monitoring label value (not "true" or "false")
142+ invalidNamespace := & corev1.Namespace {
143+ ObjectMeta : metav1.ObjectMeta {
144+ Name : TestNamespaceName ,
145+ Labels : map [string ]string {
146+ labels .ODHLabelMonitoring : "invalid-value" , // Invalid!
147+ },
148+ },
149+ }
150+
151+ // Expect this to be BLOCKED by validation policy
152+ err := tc .Client ().Create (tc .Context (), invalidNamespace )
153+ tc .g .Expect (err ).To (HaveOccurred (), "Validation policy should block namespace with invalid monitoring label value" )
154+ tc .g .Expect (err ).To (MatchError (ContainSubstring ("must be set to 'true' or 'false'" )), "Error message should indicate valid values" )
155+
156+ // Explicit cleanup based on deletion policy (runs only if test reaches this point)
157+ switch testOpts .deletionPolicy {
158+ case DeletionPolicyAlways :
159+ t .Logf ("Deletion Policy: Always. Cleaning up test namespace %s" , TestNamespaceName )
160+ tc .DeleteResource (
161+ WithMinimalObject (gvk .Namespace , types.NamespacedName {Name : TestNamespaceName }),
162+ WithIgnoreNotFound (true ),
163+ WithWaitForDeletion (true ),
164+ )
165+ case DeletionPolicyOnFailure :
166+ if t .Failed () {
167+ t .Logf ("Test failed. Deletion Policy: On Failure. Cleaning up test namespace %s" , TestNamespaceName )
168+ tc .DeleteResource (
169+ WithMinimalObject (gvk .Namespace , types.NamespacedName {Name : TestNamespaceName }),
170+ WithIgnoreNotFound (true ),
171+ WithWaitForDeletion (true ),
172+ )
173+ }
174+ case DeletionPolicyNever :
175+ t .Logf ("Deletion Policy: Never. Skipping cleanup of test namespace %s" , TestNamespaceName )
176+ }
177+ }
178+
179+ // ValidateMonitoringLabelValueEnforcementOnMonitors tests that the validation policy blocks invalid monitoring label values.
180+ func (tc * MonitoringTestCtx ) ValidateMonitoringLabelValueEnforcementOnMonitors (t * testing.T ) {
181+ t .Helper ()
182+
183+ // Pre-test cleanup: ensure invalid monitors don't exist from prior runs
184+ tc .DeleteResource (
185+ WithMinimalObject (gvk .CoreosPodMonitor , types.NamespacedName {Name : "test-invalid-podmonitor" , Namespace : TestNamespaceName }),
186+ WithIgnoreNotFound (true ),
187+ WithWaitForDeletion (true ),
188+ )
189+ tc .DeleteResource (
190+ WithMinimalObject (gvk .CoreosServiceMonitor , types.NamespacedName {Name : "test-invalid-servicemonitor" , Namespace : TestNamespaceName }),
191+ WithIgnoreNotFound (true ),
192+ WithWaitForDeletion (true ),
193+ )
194+
195+ // 1. Create a valid namespace first (validation usually requires the namespace to exist)
196+ tc .createMonitorsEnvironment (t , nil , nil ) // Creates namespace & clean monitors. We will ignore the clean monitors.
197+
198+ // Define the invalid labels we want to test
199+ invalidLabels := map [string ]string {
200+ labels .ODHLabelMonitoring : "invalid-value" ,
201+ }
202+
203+ // --- Test PodMonitor Validation ---
204+
205+ // Define an invalid PodMonitor object locally
206+ invalidPodMonitor := & unstructured.Unstructured {}
207+ invalidPodMonitor .SetGroupVersionKind (gvk .CoreosPodMonitor )
208+ invalidPodMonitor .SetName ("test-invalid-podmonitor" )
209+ invalidPodMonitor .SetNamespace (TestNamespaceName )
210+ invalidPodMonitor .SetLabels (invalidLabels )
211+ // Minimal valid spec so K8s doesn't reject it for schema reasons
212+ invalidPodMonitor .Object ["spec" ] = map [string ]interface {}{
213+ "selector" : map [string ]interface {}{
214+ "matchLabels" : map [string ]interface {}{"app" : "test" },
215+ },
216+ "podMetricsEndpoints" : []interface {}{
217+ map [string ]interface {}{"port" : "metrics" },
218+ },
219+ }
220+
221+ // Attempt to create it - Expect Error
222+ err := tc .Client ().Create (tc .Context (), invalidPodMonitor )
223+ tc .g .Expect (err ).To (HaveOccurred (), "Validation policy should block PodMonitor with invalid monitoring label value" )
224+ tc .g .Expect (err ).To (MatchError (ContainSubstring ("must be set to 'true' or 'false'" )), "Error message should indicate valid values for PodMonitor" )
225+
226+ // --- Test ServiceMonitor Validation ---
227+
228+ // Define an invalid ServiceMonitor object locally
229+ invalidServiceMonitor := & unstructured.Unstructured {}
230+ invalidServiceMonitor .SetGroupVersionKind (gvk .CoreosServiceMonitor )
231+ invalidServiceMonitor .SetName ("test-invalid-servicemonitor" )
232+ invalidServiceMonitor .SetNamespace (TestNamespaceName )
233+ invalidServiceMonitor .SetLabels (invalidLabels )
234+ // Minimal valid spec
235+ invalidServiceMonitor .Object ["spec" ] = map [string ]interface {}{
236+ "selector" : map [string ]interface {}{
237+ "matchLabels" : map [string ]interface {}{"app" : "test" },
238+ },
239+ "endpoints" : []interface {}{
240+ map [string ]interface {}{"port" : "metrics" },
241+ },
242+ }
243+
244+ // Attempt to create it - Expect Error
245+ err = tc .Client ().Create (tc .Context (), invalidServiceMonitor )
246+ tc .g .Expect (err ).To (HaveOccurred (), "Validation policy should block ServiceMonitor with invalid monitoring label value" )
247+ tc .g .Expect (err ).To (MatchError (ContainSubstring ("must be set to 'true' or 'false'" )), "Error message should indicate valid values for ServiceMonitor" )
248+
249+ // Explicit cleanup based on deletion policy (runs only if test reaches this point)
250+ switch testOpts .deletionPolicy {
251+ case DeletionPolicyAlways :
252+ t .Logf ("Deletion Policy: Always. Cleaning up test resources in namespace %s" , TestNamespaceName )
253+ tc .DeleteResource (
254+ WithMinimalObject (gvk .CoreosPodMonitor , types.NamespacedName {Name : "test-invalid-podmonitor" , Namespace : TestNamespaceName }),
255+ WithIgnoreNotFound (true ),
256+ WithWaitForDeletion (true ),
257+ )
258+ tc .DeleteResource (
259+ WithMinimalObject (gvk .CoreosServiceMonitor , types.NamespacedName {Name : "test-invalid-servicemonitor" , Namespace : TestNamespaceName }),
260+ WithIgnoreNotFound (true ),
261+ WithWaitForDeletion (true ),
262+ )
263+ tc .DeleteResource (
264+ WithMinimalObject (gvk .Namespace , types.NamespacedName {Name : TestNamespaceName }),
265+ WithIgnoreNotFound (true ),
266+ WithWaitForDeletion (true ),
267+ )
268+ case DeletionPolicyOnFailure :
269+ if t .Failed () {
270+ t .Logf ("Test failed. Deletion Policy: On Failure. Cleaning up test resources in namespace %s" , TestNamespaceName )
271+ tc .DeleteResource (
272+ WithMinimalObject (gvk .CoreosPodMonitor , types.NamespacedName {Name : "test-invalid-podmonitor" , Namespace : TestNamespaceName }),
273+ WithIgnoreNotFound (true ),
274+ WithWaitForDeletion (true ),
275+ )
276+ tc .DeleteResource (
277+ WithMinimalObject (gvk .CoreosServiceMonitor , types.NamespacedName {Name : "test-invalid-servicemonitor" , Namespace : TestNamespaceName }),
278+ WithIgnoreNotFound (true ),
279+ WithWaitForDeletion (true ),
280+ )
281+ tc .DeleteResource (
282+ WithMinimalObject (gvk .Namespace , types.NamespacedName {Name : TestNamespaceName }),
283+ WithIgnoreNotFound (true ),
284+ WithWaitForDeletion (true ),
285+ )
286+ }
287+ case DeletionPolicyNever :
288+ t .Logf ("Deletion Policy: Never. Skipping cleanup of test resources in namespace %s" , TestNamespaceName )
289+ }
290+ }
0 commit comments