diff --git a/bundle/manifests/cryostat-operator.clusterserviceversion.yaml b/bundle/manifests/cryostat-operator.clusterserviceversion.yaml index 960e558f9..1f978f085 100644 --- a/bundle/manifests/cryostat-operator.clusterserviceversion.yaml +++ b/bundle/manifests/cryostat-operator.clusterserviceversion.yaml @@ -54,7 +54,7 @@ metadata: capabilities: Seamless Upgrades categories: Monitoring, Developer Tools containerImage: quay.io/cryostat/cryostat-operator:2.5.0-dev - createdAt: "2024-03-27T17:54:03Z" + createdAt: "2024-04-04T17:17:25Z" description: JVM monitoring and profiling tool operatorframework.io/initialization-resource: |- { diff --git a/bundle/tests/scorecard/config.yaml b/bundle/tests/scorecard/config.yaml index ae53496a9..f536a45d3 100644 --- a/bundle/tests/scorecard/config.yaml +++ b/bundle/tests/scorecard/config.yaml @@ -70,7 +70,7 @@ stages: - entrypoint: - cryostat-scorecard-tests - operator-install - image: quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240326210241 + image: quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240404171629 labels: suite: cryostat test: operator-install @@ -80,17 +80,27 @@ stages: - entrypoint: - cryostat-scorecard-tests - cryostat-cr - image: quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240326210241 + image: quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240404171629 labels: suite: cryostat test: cryostat-cr storage: spec: mountPath: {} + - entrypoint: + - cryostat-scorecard-tests + - cryostat-multi-namespace + image: quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240404171629 + labels: + suite: cryostat + test: cryostat-multi-namespace + storage: + spec: + mountPath: {} - entrypoint: - cryostat-scorecard-tests - cryostat-recording - image: quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240326210241 + image: quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240404171629 labels: suite: cryostat test: cryostat-recording @@ -100,7 +110,7 @@ stages: - entrypoint: - cryostat-scorecard-tests - cryostat-config-change - image: quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240326210241 + image: quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240404171629 labels: suite: cryostat test: cryostat-config-change @@ -110,7 +120,7 @@ stages: - entrypoint: - cryostat-scorecard-tests - cryostat-report - image: quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240326210241 + image: quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240404171629 labels: suite: cryostat test: cryostat-report diff --git a/config/scorecard/patches/custom.config.yaml b/config/scorecard/patches/custom.config.yaml index 4bc52ec07..333688c77 100644 --- a/config/scorecard/patches/custom.config.yaml +++ b/config/scorecard/patches/custom.config.yaml @@ -8,7 +8,7 @@ entrypoint: - cryostat-scorecard-tests - operator-install - image: "quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240327175405" + image: "quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240404171725" labels: suite: cryostat test: operator-install @@ -18,17 +18,27 @@ entrypoint: - cryostat-scorecard-tests - cryostat-cr - image: "quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240327175405" + image: "quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240404171725" labels: suite: cryostat test: cryostat-cr +- op: add + path: /stages/1/tests/- + value: + entrypoint: + - cryostat-scorecard-tests + - cryostat-multi-namespace + image: "quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240404171725" + labels: + suite: cryostat + test: cryostat-multi-namespace - op: add path: /stages/1/tests/- value: entrypoint: - cryostat-scorecard-tests - cryostat-recording - image: "quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240327175405" + image: "quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240404171725" labels: suite: cryostat test: cryostat-recording @@ -38,7 +48,7 @@ entrypoint: - cryostat-scorecard-tests - cryostat-config-change - image: "quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240327175405" + image: "quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240404171725" labels: suite: cryostat test: cryostat-config-change @@ -48,7 +58,7 @@ entrypoint: - cryostat-scorecard-tests - cryostat-report - image: "quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240327175405" + image: "quay.io/cryostat/cryostat-operator-scorecard:2.5.0-20240404171725" labels: suite: cryostat test: cryostat-report diff --git a/hack/custom.config.yaml.in b/hack/custom.config.yaml.in index 4336abbe4..75306a910 100644 --- a/hack/custom.config.yaml.in +++ b/hack/custom.config.yaml.in @@ -21,6 +21,16 @@ labels: suite: cryostat test: cryostat-cr +- op: add + path: /stages/1/tests/- + value: + entrypoint: + - cryostat-scorecard-tests + - cryostat-multi-namespace + image: "${CUSTOM_SCORECARD_IMG}" + labels: + suite: cryostat + test: cryostat-multi-namespace - op: add path: /stages/1/tests/- value: diff --git a/internal/images/custom-scorecard-tests/main.go b/internal/images/custom-scorecard-tests/main.go index cce16f5a6..5592e766f 100644 --- a/internal/images/custom-scorecard-tests/main.go +++ b/internal/images/custom-scorecard-tests/main.go @@ -79,6 +79,7 @@ func printValidTests() []scapiv1alpha3.TestResult { str := fmt.Sprintf("valid tests for this image include: %s", strings.Join([]string{ tests.OperatorInstallTestName, tests.CryostatCRTestName, + tests.CryostatMultiNamespaceTestName, tests.CryostatRecordingTestName, tests.CryostatConfigChangeTestName, tests.CryostatReportTestName, @@ -93,6 +94,7 @@ func validateTests(testNames []string) bool { switch testName { case tests.OperatorInstallTestName: case tests.CryostatCRTestName: + case tests.CryostatMultiNamespaceTestName: case tests.CryostatRecordingTestName: case tests.CryostatConfigChangeTestName: case tests.CryostatReportTestName: @@ -114,6 +116,8 @@ func runTests(testNames []string, bundle *apimanifests.Bundle, namespace string, results = append(results, *tests.OperatorInstallTest(bundle, namespace, openShiftCertManager)) case tests.CryostatCRTestName: results = append(results, *tests.CryostatCRTest(bundle, namespace, openShiftCertManager)) + case tests.CryostatMultiNamespaceTestName: + results = append(results, *tests.CryostatMultiNamespaceTest(bundle, namespace, openShiftCertManager)) case tests.CryostatRecordingTestName: results = append(results, *tests.CryostatRecordingTest(bundle, namespace, openShiftCertManager)) case tests.CryostatConfigChangeTestName: diff --git a/internal/images/custom-scorecard-tests/rbac/scorecard_role.yaml b/internal/images/custom-scorecard-tests/rbac/scorecard_role.yaml index 7eaedd854..61b87d7de 100644 --- a/internal/images/custom-scorecard-tests/rbac/scorecard_role.yaml +++ b/internal/images/custom-scorecard-tests/rbac/scorecard_role.yaml @@ -150,3 +150,18 @@ rules: - namespaces verbs: - create + - delete +- apiGroups: + - operator.cryostat.io + resources: + - clustercryostats + verbs: + - create + - get + - delete +- apiGroups: + - operator.cryostat.io + resources: + - clustercryostats/status + verbs: + - get diff --git a/internal/test/scorecard/clients.go b/internal/test/scorecard/clients.go index 2c831e72f..54ef1fc40 100644 --- a/internal/test/scorecard/clients.go +++ b/internal/test/scorecard/clients.go @@ -89,6 +89,14 @@ func (c *OperatorCRDClient) Cryostats(namespace string) *CryostatClient { } } +// ClusterCryostats returns a ClusterCryostatClient +func (c *OperatorCRDClient) ClusterCryostats() *CryostatClient { + return &CryostatClient{ + restClient: c.client, + resource: "clustercryostats", + } +} + func newOperatorCRDClient(config *rest.Config) (*OperatorCRDClient, error) { client, err := newCRDClient(config) if err != nil { @@ -124,6 +132,21 @@ type CryostatClient struct { resource string } +// Get returns a Cryostat CR for the given name +func (c *CryostatClient) GetNonNamespaced(ctx context.Context, name string) (*operatorv1beta1.ClusterCryostat, error) { + return get(ctx, c.restClient, c.resource, c.namespace, name, &operatorv1beta1.ClusterCryostat{}) +} + +// Create creates the provided ClusterCryostat CR +func (c *CryostatClient) CreateNonNamespaced(ctx context.Context, obj *operatorv1beta1.ClusterCryostat) (*operatorv1beta1.ClusterCryostat, error) { + return create(ctx, c.restClient, c.resource, c.namespace, obj, &operatorv1beta1.ClusterCryostat{}) +} + +// Delete deletes the Cryostat CR with the given name +func (c *CryostatClient) DeleteNonNamespaced(ctx context.Context, name string, options *metav1.DeleteOptions) error { + return delete(ctx, c.restClient, c.resource, c.namespace, name, options) +} + // Get returns a Cryostat CR for the given name func (c *CryostatClient) Get(ctx context.Context, name string) (*operatorv1beta1.Cryostat, error) { return get(ctx, c.restClient, c.resource, c.namespace, name, &operatorv1beta1.Cryostat{}) @@ -145,31 +168,38 @@ func (c *CryostatClient) Delete(ctx context.Context, name string, options *metav } func get[r runtime.Object](ctx context.Context, c rest.Interface, res string, ns string, name string, result r) (r, error) { - err := c.Get(). - Namespace(ns).Resource(res). - Name(name).Do(ctx).Into(result) + rq := c.Get().Resource(res).Name(name) + if len(ns) > 0 { + rq = rq.Namespace(ns) + } + err := rq.Do(ctx).Into(result) return result, err } func create[r runtime.Object](ctx context.Context, c rest.Interface, res string, ns string, obj r, result r) (r, error) { - err := c.Post(). - Namespace(ns).Resource(res). - Body(obj).Do(ctx).Into(result) + rq := c.Post().Resource(res).Body(obj) + if len(ns) > 0 { + rq = rq.Namespace(ns) + } + err := rq.Do(ctx).Into(result) return result, err } func update[r runtime.Object](ctx context.Context, c rest.Interface, res string, ns string, obj r, result r, name string) (r, error) { - err := c.Put(). - Namespace(ns).Resource(res).Name(name). - Body(obj).Do(ctx).Into(result) + rq := c.Put().Resource(res).Name(name).Body(obj) + if len(ns) > 0 { + rq = rq.Namespace(ns) + } + err := rq.Do(ctx).Into(result) return result, err } func delete(ctx context.Context, c rest.Interface, res string, ns string, name string, opts *metav1.DeleteOptions) error { - return c.Delete(). - Namespace(ns).Resource(res). - Name(name).Body(opts).Do(ctx). - Error() + rq := c.Delete().Resource(res).Name(name).Body(opts) + if len(ns) > 0 { + rq = rq.Namespace(ns) + } + return rq.Do(ctx).Error() } // CryostatRESTClientset contains methods to interact with diff --git a/internal/test/scorecard/common_utils.go b/internal/test/scorecard/common_utils.go index 99c639ee0..6bc7e6afa 100644 --- a/internal/test/scorecard/common_utils.go +++ b/internal/test/scorecard/common_utils.go @@ -231,42 +231,85 @@ func (r *TestResources) setupCRTestResources(openShiftCertManager bool) error { return nil } +func (r *TestResources) setupTargetNamespace(name string) error { + ctx := context.Background() + + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } + ns, err := r.Client.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{}) + if err != nil { + return fmt.Errorf("failed to create namespace %s: %s", name, err.Error()) + } + r.Log += fmt.Sprintf("created namespace: %s\n", ns.Name) + return nil +} + func newCryostatCR(name string, namespace string, withIngress bool) *operatorv1beta1.Cryostat { cr := &operatorv1beta1.Cryostat{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, - Spec: operatorv1beta1.CryostatSpec{ - Minimal: false, - EnableCertManager: &[]bool{true}[0], + Spec: newCryostatSpec(), + } + + if withIngress { + configureIngress(name, &cr.Spec) + } + return cr +} + +func newClusterCryostatCR(name string, namespace string, namespaces []string, withIngress bool) *operatorv1beta1.ClusterCryostat { + cr := &operatorv1beta1.ClusterCryostat{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: operatorv1beta1.ClusterCryostatSpec{ + InstallNamespace: namespace, + TargetNamespaces: namespaces, + CryostatSpec: newCryostatSpec(), }, } if withIngress { - pathType := netv1.PathTypePrefix - cr.Spec.NetworkOptions = &operatorv1beta1.NetworkConfigurationList{ - CoreConfig: &operatorv1beta1.NetworkConfiguration{ - Annotations: map[string]string{ - "nginx.ingress.kubernetes.io/backend-protocol": "HTTPS", - }, - IngressSpec: &netv1.IngressSpec{ - TLS: []netv1.IngressTLS{{}}, - Rules: []netv1.IngressRule{ - { - Host: "testing.cryostat", - IngressRuleValue: netv1.IngressRuleValue{ - HTTP: &netv1.HTTPIngressRuleValue{ - Paths: []netv1.HTTPIngressPath{ - { - Path: "/", - PathType: &pathType, - Backend: netv1.IngressBackend{ - Service: &netv1.IngressServiceBackend{ - Name: name, - Port: netv1.ServiceBackendPort{ - Number: 8181, - }, + configureIngress(name, &cr.Spec.CryostatSpec) + } + return cr +} + +func newCryostatSpec() operatorv1beta1.CryostatSpec { + return operatorv1beta1.CryostatSpec{ + Minimal: false, + EnableCertManager: &[]bool{true}[0], + } +} + +func configureIngress(name string, cryostatSpec *operatorv1beta1.CryostatSpec) { + pathType := netv1.PathTypePrefix + cryostatSpec.NetworkOptions = &operatorv1beta1.NetworkConfigurationList{ + CoreConfig: &operatorv1beta1.NetworkConfiguration{ + Annotations: map[string]string{ + "nginx.ingress.kubernetes.io/backend-protocol": "HTTPS", + }, + IngressSpec: &netv1.IngressSpec{ + TLS: []netv1.IngressTLS{{}}, + Rules: []netv1.IngressRule{ + { + Host: "testing.cryostat", + IngressRuleValue: netv1.IngressRuleValue{ + HTTP: &netv1.HTTPIngressRuleValue{ + Paths: []netv1.HTTPIngressPath{ + { + Path: "/", + PathType: &pathType, + Backend: netv1.IngressBackend{ + Service: &netv1.IngressServiceBackend{ + Name: name, + Port: netv1.ServiceBackendPort{ + Number: 8181, }, }, }, @@ -277,27 +320,27 @@ func newCryostatCR(name string, namespace string, withIngress bool) *operatorv1b }, }, }, - GrafanaConfig: &operatorv1beta1.NetworkConfiguration{ - Annotations: map[string]string{ - "nginx.ingress.kubernetes.io/backend-protocol": "HTTPS", - }, - IngressSpec: &netv1.IngressSpec{ - TLS: []netv1.IngressTLS{{}}, - Rules: []netv1.IngressRule{ - { - Host: "testing.cryostat-grafana", - IngressRuleValue: netv1.IngressRuleValue{ - HTTP: &netv1.HTTPIngressRuleValue{ - Paths: []netv1.HTTPIngressPath{ - { - Path: "/", - PathType: &pathType, - Backend: netv1.IngressBackend{ - Service: &netv1.IngressServiceBackend{ - Name: fmt.Sprintf("%s-grafana", name), - Port: netv1.ServiceBackendPort{ - Number: 3000, - }, + }, + GrafanaConfig: &operatorv1beta1.NetworkConfiguration{ + Annotations: map[string]string{ + "nginx.ingress.kubernetes.io/backend-protocol": "HTTPS", + }, + IngressSpec: &netv1.IngressSpec{ + TLS: []netv1.IngressTLS{{}}, + Rules: []netv1.IngressRule{ + { + Host: "testing.cryostat-grafana", + IngressRuleValue: netv1.IngressRuleValue{ + HTTP: &netv1.HTTPIngressRuleValue{ + Paths: []netv1.HTTPIngressPath{ + { + Path: "/", + PathType: &pathType, + Backend: netv1.IngressBackend{ + Service: &netv1.IngressServiceBackend{ + Name: fmt.Sprintf("%s-grafana", name), + Port: netv1.ServiceBackendPort{ + Number: 3000, }, }, }, @@ -308,9 +351,8 @@ func newCryostatCR(name string, namespace string, withIngress bool) *operatorv1b }, }, }, - } + }, } - return cr } func (r *TestResources) createAndWaitTillCryostatAvailable(cr *operatorv1beta1.Cryostat) (*operatorv1beta1.Cryostat, error) { @@ -349,6 +391,52 @@ func (r *TestResources) createAndWaitTillCryostatAvailable(cr *operatorv1beta1.C return cr, nil } +func (r *TestResources) createAndWaitTillClusterCryostatAvailable(cr *operatorv1beta1.ClusterCryostat) (*operatorv1beta1.ClusterCryostat, error) { + cr, err := r.Client.OperatorCRDs().ClusterCryostats().CreateNonNamespaced(context.Background(), cr) + if err != nil { + r.logError(fmt.Sprintf("failed to create ClusterCryostat CR: %s", err.Error())) + return nil, err + } + + // Poll the deployment until it becomes available or we timeout + ctx, cancel := context.WithTimeout(context.Background(), testTimeout) + defer cancel() + err = r.waitForDeploymentAvailability(ctx, cr.Spec.InstallNamespace, cr.Name) + if err != nil { + r.logError(fmt.Sprintf("ClusterCryostat main deployment did not become available: %s", err.Error())) + return nil, err + } + + err = wait.PollImmediateUntilWithContext(ctx, time.Second, func(ctx context.Context) (done bool, err error) { + cr, err = r.Client.OperatorCRDs().ClusterCryostats().GetNonNamespaced(ctx, cr.Name) + if err != nil { + return false, fmt.Errorf("failed to get ClusterCryostat CR: %s", err.Error()) + } + if len(cr.Status.TargetNamespaces) != len(cr.Spec.TargetNamespaces) { + r.Log += "application's target namespaces are not yet available" + return false, nil // Retry + } + for i := range cr.Status.TargetNamespaces { + if cr.Status.TargetNamespaces[i] != cr.Spec.TargetNamespaces[i] { + return false, fmt.Errorf("application's target namespaces do not correctly match CR's") + } + } + if len(cr.Status.CryostatStatus.ApplicationURL) == 0 { + r.Log += "application URL is not yet available\n" + return false, nil // Retry + } + return true, nil + }) + if err != nil { + r.logError(fmt.Sprintf("application URL not found in CR: %s", err.Error())) + return nil, err + } + r.Log += fmt.Sprintf("application has access to the following namespaces: %s\n", cr.Status.TargetNamespaces) + r.Log += fmt.Sprintf("application is available at %s\n", cr.Status.CryostatStatus.ApplicationURL) + + return cr, nil +} + func (r *TestResources) waitTillCryostatReady(base *url.URL) error { return r.sendHealthRequest(base, func(resp *http.Response, r *scapiv1alpha3.TestResult) (done bool, err error) { health := &HealthResponse{} @@ -533,3 +621,40 @@ func (r *TestResources) getCryostatPodNameForCR(cr *operatorv1beta1.Cryostat) (s return pods.Items[0].ObjectMeta.Name, nil } + +func (r *TestResources) cleanupClusterCryostat(name string, namespace string, targetNamespaces []string) { + r.LogWorkloadEventsOnError(namespace, name) + + cr := &operatorv1beta1.ClusterCryostat{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: operatorv1beta1.ClusterCryostatSpec{ + InstallNamespace: namespace, + }, + } + + ctx := context.Background() + err := r.Client.OperatorCRDs().ClusterCryostats().DeleteNonNamespaced(ctx, + cr.Name, &metav1.DeleteOptions{}) + if err != nil { + if !kerrors.IsNotFound(err) { + r.Log += fmt.Sprintf("failed to delete ClusterCryostat: %s\n", err.Error()) + } + } + + for _, ns := range targetNamespaces { + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: ns, + }, + } + + err := r.Client.CoreV1().Namespaces().Delete(ctx, ns.Name, metav1.DeleteOptions{}) + if err != nil { + if !kerrors.IsNotFound(err) { + r.Log += fmt.Sprintf("failed to delete namespace %s: %s\n", ns, err.Error()) + } + } + } +} diff --git a/internal/test/scorecard/tests.go b/internal/test/scorecard/tests.go index 9d3d251a6..c5d7e5b86 100644 --- a/internal/test/scorecard/tests.go +++ b/internal/test/scorecard/tests.go @@ -29,11 +29,12 @@ import ( ) const ( - OperatorInstallTestName string = "operator-install" - CryostatCRTestName string = "cryostat-cr" - CryostatRecordingTestName string = "cryostat-recording" - CryostatConfigChangeTestName string = "cryostat-config-change" - CryostatReportTestName string = "cryostat-report" + OperatorInstallTestName string = "operator-install" + CryostatCRTestName string = "cryostat-cr" + CryostatMultiNamespaceTestName string = "cryostat-multi-namespace" + CryostatConfigChangeTestName string = "cryostat-config-change" + CryostatRecordingTestName string = "cryostat-recording" + CryostatReportTestName string = "cryostat-report" ) // OperatorInstallTest checks that the operator installed correctly @@ -76,6 +77,34 @@ func CryostatCRTest(bundle *apimanifests.Bundle, namespace string, openShiftCert return r.TestResult } +// CryostatMultiNamespaceTest checks that the operator installs ClusterCryostat in response to a ClusterCryostat CR +func CryostatMultiNamespaceTest(bundle *apimanifests.Bundle, namespace string, openShiftCertManager bool) *scapiv1alpha3.TestResult { + r := newTestResources(CryostatMultiNamespaceTestName) + namespaces := []string{namespace + "-other"} + + err := r.setupCRTestResources(openShiftCertManager) + if err != nil { + return r.fail(fmt.Sprintf("failed to set up %s test: %s", CryostatMultiNamespaceTestName, err.Error())) + } + defer r.cleanupClusterCryostat(CryostatMultiNamespaceTestName, namespace, namespaces) + + for _, ns := range namespaces { + err = r.setupTargetNamespace(ns) + if err != nil { + return r.fail(fmt.Sprintf("failed to create an additional namespace %s for %s test: %s", ns, CryostatMultiNamespaceTestName, err.Error())) + } + } + + // Create a default ClusterCryostat CR + _, err = r.createAndWaitTillClusterCryostatAvailable(newClusterCryostatCR(CryostatMultiNamespaceTestName, namespace, namespaces, !r.OpenShift)) + if err != nil { + return r.fail(fmt.Sprintf("%s test failed: %s", CryostatMultiNamespaceTestName, err.Error())) + } + + return r.TestResult +} + +// CryostatConfigChangeTest checks that the operator redeploys Cryostat in response to a change to Cryostat CR func CryostatConfigChangeTest(bundle *apimanifests.Bundle, namespace string, openShiftCertManager bool) *scapiv1alpha3.TestResult { r := newTestResources(CryostatConfigChangeTestName) @@ -280,6 +309,7 @@ func CryostatRecordingTest(bundle *apimanifests.Bundle, namespace string, openSh return r.TestResult } +// CryostatReportTest checks that the operator deploys a report sidecar in response to a Cryostat CR func CryostatReportTest(bundle *apimanifests.Bundle, namespace string, openShiftCertManager bool) *scapiv1alpha3.TestResult { r := newTestResources(CryostatReportTestName)