Skip to content
Closed
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
54 changes: 48 additions & 6 deletions test/e2e/framework/framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@ type Framework struct {
RootCA *x509.CertPool
MetricsClientCert *tls.Certificate
OperatorNamespace string
ClusterVersion *configv1.ClusterVersion
}

// Setup finalizes the initilization of the Framework object by setting
// parameters which are specific to OpenShift.
func (f *Framework) Setup() error {
clusterVersion := &configv1.ClusterVersion{}

if err := f.K8sClient.Get(context.Background(), client.ObjectKey{Name: "version"}, clusterVersion); err != nil {
if meta.IsNoMatchError(err) {
return nil
Expand All @@ -53,6 +55,7 @@ func (f *Framework) Setup() error {
return fmt.Errorf("failed to get clusterversion %w", err)
}

f.ClusterVersion = clusterVersion
f.IsOpenshiftCluster = true

// Load the service CA operator's certificate authority.
Expand Down Expand Up @@ -260,19 +263,58 @@ func (f *Framework) CleanUp(t *testing.T, cleanupFunc func()) {
})
}

// TODO: remove ForceFailure — temporary helper to exercise DumpOnFailure logging.
func (f *Framework) ForceFailure(t *testing.T) {
t.Helper()
t.Error("forced failure to test debug dump output")
}

// DebugFunc is a diagnostic function invoked when a test fails.
// Implementations should use t.Logf to emit relevant state.
type DebugFunc func(t *testing.T)

// DumpOnFailure registers a t.Cleanup that runs the given debug functions
// when the test fails. It can be called multiple times to add more debug
// functions as resources become available during the test.
//
// Cleanups run in LIFO order, so call DumpOnFailure before registering
// resource deletions via CleanUp to ensure debug info is captured while
// the resources still exist.
func (f *Framework) DumpOnFailure(t *testing.T, fns ...DebugFunc) {
t.Helper()
t.Cleanup(func() {
if !t.Failed() {
return
}
for _, fn := range fns {
fn(t)
}
})
}

// DebugNamespace returns a DebugFunc that dumps deployments, pods, and events
// for the given namespaces.
func (f *Framework) DebugNamespace(namespaces ...string) DebugFunc {
return func(t *testing.T) {
t.Helper()
for _, ns := range namespaces {
t.Logf("--- Dumping debug info for namespace %s ---", ns)
f.DumpNamespaceDebug(t, ns)
}
}
}

// SkipIfClusterVersionBelow skips the test if the cluster version is below
// minVersion. The minVersion string should be a semver-compatible version
// (e.g. "4.19" or "v4.19").
func (f *Framework) SkipIfClusterVersionBelow(t *testing.T, minVersion string) {
t.Helper()
cv := &configv1.ClusterVersion{}
err := f.K8sClient.Get(t.Context(), client.ObjectKey{Name: "version"}, cv)
if err != nil {
t.Fatalf("failed to determine cluster version: %v", err)
if f.ClusterVersion == nil {
t.Fatal("cluster version not available (non-OpenShift cluster?)")
return
}

actual := cv.Status.Desired.Version
actual := f.ClusterVersion.Status.Desired.Version
if actual == "" {
t.Fatal("cluster version is empty")
return
Expand All @@ -295,7 +337,7 @@ func (f *Framework) SkipIfClusterVersionBelow(t *testing.T, minVersion string) {
}

if semver.Compare(canonicalActual, canonicalMin) < 0 {
t.Skipf("Skipping: cluster version %s is below minimum required %s", cv.Status.Desired.Version, minVersion)
t.Skipf("Skipping: cluster version %s is below minimum required %s", f.ClusterVersion.Status.Desired.Version, minVersion)
}
}

Expand Down
2 changes: 2 additions & 0 deletions test/e2e/monitoring_stack_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ func assertCRDExists(t *testing.T, crds ...string) {
}

func TestMonitoringStackController(t *testing.T) {
f.DumpOnFailure(t, f.DebugNamespace(e2eTestNamespace))
f.ForceFailure(t) // TODO: remove — temporary, exercises debug dump
err := stack.AddToScheme(scheme.Scheme)
assert.NilError(t, err, "adding stack to scheme failed")
assertCRDExists(t,
Expand Down
2 changes: 2 additions & 0 deletions test/e2e/observability_installer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ func TestObservabilityInstallerController(t *testing.T) {
}

func testObservabilityInstallerTracing(t *testing.T) {
f.DumpOnFailure(t, f.DebugNamespace(f.OperatorNamespace, "tracing-observability"))
f.ForceFailure(t) // TODO: remove — temporary, exercises debug dump
ctx := context.Background()

// The ObservabilityInstaller installs operators via subscriptions,
Expand Down
2 changes: 2 additions & 0 deletions test/e2e/operator_metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
)

func TestOperatorMetrics(t *testing.T) {
f.DumpOnFailure(t, f.DebugNamespace(f.OperatorNamespace))
f.ForceFailure(t) // TODO: remove — temporary, exercises debug dump
t.Run("operator exposes metrics", func(t *testing.T) {
pod := f.GetOperatorPod(t)

Expand Down
2 changes: 2 additions & 0 deletions test/e2e/po_admission_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
)

func TestPrometheusRuleWebhook(t *testing.T) {
f.DumpOnFailure(t, f.DebugNamespace(e2eTestNamespace))
f.ForceFailure(t) // TODO: remove — temporary, exercises debug dump
assertCRDExists(t,
"prometheusrules.monitoring.rhobs",
)
Expand Down
4 changes: 4 additions & 0 deletions test/e2e/prometheus_operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ type testCase struct {
}

func TestPrometheusOperatorForNonOwnedResources(t *testing.T) {
f.DumpOnFailure(t, f.DebugNamespace(e2eTestNamespace))
f.ForceFailure(t) // TODO: remove — temporary, exercises debug dump
resources := []client.Object{
newPrometheus(nil),
newAlertmanager(nil),
Expand Down Expand Up @@ -74,6 +76,8 @@ func TestPrometheusOperatorForNonOwnedResources(t *testing.T) {
}

func TestPrometheusOperatorForOwnedResources(t *testing.T) {
f.DumpOnFailure(t, f.DebugNamespace(e2eTestNamespace))
f.ForceFailure(t) // TODO: remove — temporary, exercises debug dump
resources := []client.Object{
newPrometheus(ownedResourceLabels),
newAlertmanager(ownedResourceLabels),
Expand Down
2 changes: 2 additions & 0 deletions test/e2e/thanos_querier_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import (
)

func TestThanosQuerierController(t *testing.T) {
f.DumpOnFailure(t, f.DebugNamespace(e2eTestNamespace))
f.ForceFailure(t) // TODO: remove — temporary, exercises debug dump
assertCRDExists(t, "thanosqueriers.monitoring.rhobs")

ts := []testCase{
Expand Down
46 changes: 22 additions & 24 deletions test/e2e/uiplugin_cluster_health_analyzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,21 @@ func clusterHealthAnalyzer(t *testing.T) {
err := monv1.AddToScheme(f.K8sClient.Scheme())
assert.NilError(t, err, "failed to add monv1 to scheme")

f.DumpOnFailure(t, f.DebugNamespace(uiPluginInstallNS))

plugin := resetMonitoringUIPlugin(t)
err = f.K8sClient.Create(t.Context(), plugin)
assert.NilError(t, err, "failed to create monitoring UIPlugin")

t.Cleanup(func() {
if t.Failed() {
dumpClusterHealthAnalyzerDebug(t, plugin.Name)
}
})
f.DumpOnFailure(t, dumpUIPluginDebug(plugin.Name))
f.ForceFailure(t) // TODO: remove — temporary, exercises debug dump

t.Log("Waiting for health-analyzer deployment to become ready...")
haDeployment := appsv1.Deployment{}
f.GetResourceWithRetry(t, healthAnalyzerDeploymentName, uiPluginInstallNS, &haDeployment)
f.AssertDeploymentReady(healthAnalyzerDeploymentName, uiPluginInstallNS, framework.WithTimeout(5*time.Minute))(t)

// Use a unique suffix so re-runs don't conflict with leftover rules from prior executions.
suffix := strconv.FormatInt(time.Now().UnixNano()%100000, 10)
ruleName := "e2e-health-analyzer-" + suffix
alertName := "E2EHealthAnalyzer" + suffix
Expand Down Expand Up @@ -179,15 +179,16 @@ func newAlwaysFiringRule(t *testing.T, ruleName, alertName string) *monv1.Promet
return rule
}

func dumpClusterHealthAnalyzerDebug(t *testing.T, pluginName string) {
t.Helper()
ctx := context.WithoutCancel(t.Context())
func dumpUIPluginDebug(pluginName string) framework.DebugFunc {
return func(t *testing.T) {
t.Helper()
ctx := context.WithoutCancel(t.Context())

// UIPlugin-specific diagnostics
var plugin uiv1.UIPlugin
if err := f.K8sClient.Get(ctx, client.ObjectKey{Name: pluginName}, &plugin); err != nil {
t.Logf("Failed to get UIPlugin %q: %v", pluginName, err)
} else {
var plugin uiv1.UIPlugin
if err := f.K8sClient.Get(ctx, client.ObjectKey{Name: pluginName}, &plugin); err != nil {
t.Logf("Failed to get UIPlugin %q: %v", pluginName, err)
return
}
t.Logf("UIPlugin %q generation=%d, resourceVersion=%s", pluginName, plugin.Generation, plugin.ResourceVersion)
t.Logf("UIPlugin spec.type=%s", plugin.Spec.Type)
if plugin.Spec.Monitoring != nil {
Expand All @@ -204,18 +205,15 @@ func dumpClusterHealthAnalyzerDebug(t *testing.T, pluginName string) {
for _, c := range plugin.Status.Conditions {
t.Logf("UIPlugin condition: type=%s status=%s reason=%s message=%s", c.Type, c.Status, c.Reason, c.Message)
}
}

var plugins uiv1.UIPluginList
if err := f.K8sClient.List(ctx, &plugins); err != nil {
t.Logf("Failed to list UIPlugins: %v", err)
} else {
t.Logf("Total UIPlugins in cluster: %d", len(plugins.Items))
for _, p := range plugins.Items {
t.Logf(" UIPlugin: name=%s type=%s conditions=%d", p.Name, p.Spec.Type, len(p.Status.Conditions))
var plugins uiv1.UIPluginList
if err := f.K8sClient.List(ctx, &plugins); err != nil {
t.Logf("Failed to list UIPlugins: %v", err)
} else {
t.Logf("Total UIPlugins in cluster: %d", len(plugins.Items))
for _, p := range plugins.Items {
t.Logf(" UIPlugin: name=%s type=%s conditions=%d", p.Name, p.Spec.Type, len(p.Status.Conditions))
}
}
}

// Generic namespace diagnostics (deployments, pods, events)
f.DumpNamespaceDebug(t, uiPluginInstallNS)
}
2 changes: 2 additions & 0 deletions test/e2e/uiplugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ func TestUIPlugin(t *testing.T) {
}

func dashboardsUIPlugin(t *testing.T) {
f.DumpOnFailure(t, f.DebugNamespace(uiPluginInstallNS))
f.ForceFailure(t) // TODO: remove — temporary, exercises debug dump
db := newDashboardsUIPlugin(t)
err := f.K8sClient.Create(context.Background(), db)
assert.NilError(t, err, "failed to create a dashboards UIPlugin")
Expand Down
Loading