diff --git a/bundle/manifests/cryostat-operator.clusterserviceversion.yaml b/bundle/manifests/cryostat-operator.clusterserviceversion.yaml index abbe21a2..0d794ced 100644 --- a/bundle/manifests/cryostat-operator.clusterserviceversion.yaml +++ b/bundle/manifests/cryostat-operator.clusterserviceversion.yaml @@ -53,7 +53,7 @@ metadata: capabilities: Seamless Upgrades categories: Monitoring, Developer Tools containerImage: quay.io/cryostat/cryostat-operator:3.0.0-dev - createdAt: "2024-05-10T19:40:13Z" + createdAt: "2024-05-11T06:47:54Z" 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 30459721..e88495e5 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:3.0.0-20240416210645 + image: quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240511064726 labels: suite: cryostat test: operator-install @@ -80,7 +80,7 @@ stages: - entrypoint: - cryostat-scorecard-tests - cryostat-cr - image: quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240416210645 + image: quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240511064726 labels: suite: cryostat test: cryostat-cr @@ -90,7 +90,7 @@ stages: - entrypoint: - cryostat-scorecard-tests - cryostat-multi-namespace - image: quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240416210645 + image: quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240511064726 labels: suite: cryostat test: cryostat-multi-namespace @@ -100,7 +100,7 @@ stages: - entrypoint: - cryostat-scorecard-tests - cryostat-recording - image: quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240416210645 + image: quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240511064726 labels: suite: cryostat test: cryostat-recording @@ -110,7 +110,7 @@ stages: - entrypoint: - cryostat-scorecard-tests - cryostat-config-change - image: quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240416210645 + image: quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240511064726 labels: suite: cryostat test: cryostat-config-change @@ -120,7 +120,7 @@ stages: - entrypoint: - cryostat-scorecard-tests - cryostat-report - image: quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240416210645 + image: quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240511064726 labels: suite: cryostat test: cryostat-report diff --git a/config/scorecard/patches/custom.config.yaml b/config/scorecard/patches/custom.config.yaml index 30b92707..bada4b26 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:3.0.0-20240510194331" + image: "quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240511064726" labels: suite: cryostat test: operator-install @@ -18,7 +18,7 @@ entrypoint: - cryostat-scorecard-tests - cryostat-cr - image: "quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240510194331" + image: "quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240511064726" labels: suite: cryostat test: cryostat-cr @@ -28,7 +28,7 @@ entrypoint: - cryostat-scorecard-tests - cryostat-multi-namespace - image: "quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240510194331" + image: "quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240511064726" labels: suite: cryostat test: cryostat-multi-namespace @@ -38,7 +38,7 @@ entrypoint: - cryostat-scorecard-tests - cryostat-recording - image: "quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240510194331" + image: "quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240511064726" labels: suite: cryostat test: cryostat-recording @@ -48,7 +48,7 @@ entrypoint: - cryostat-scorecard-tests - cryostat-config-change - image: "quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240510194331" + image: "quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240511064726" labels: suite: cryostat test: cryostat-config-change @@ -58,7 +58,7 @@ entrypoint: - cryostat-scorecard-tests - cryostat-report - image: "quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240510194331" + image: "quay.io/cryostat/cryostat-operator-scorecard:3.0.0-20240511064726" labels: suite: cryostat test: cryostat-report diff --git a/internal/test/scorecard/clients.go b/internal/test/scorecard/clients.go index 05965499..b655b2c7 100644 --- a/internal/test/scorecard/clients.go +++ b/internal/test/scorecard/clients.go @@ -16,7 +16,6 @@ package scorecard import ( "context" - "encoding/base64" "encoding/json" "errors" "fmt" @@ -276,8 +275,8 @@ type RecordingClient struct { *commonCryostatRESTClient } -func (client *RecordingClient) List(ctx context.Context, connectUrl string) ([]Recording, error) { - url := client.Base.JoinPath(fmt.Sprintf("/api/v1/targets/%s/recordings", url.PathEscape(connectUrl))) +func (client *RecordingClient) List(ctx context.Context, target *Target) ([]Recording, error) { + url := client.Base.JoinPath(fmt.Sprintf("/api/v3/targets/%d/recordings", target.Id)) header := make(http.Header) header.Add("Accept", "*/*") @@ -300,8 +299,8 @@ func (client *RecordingClient) List(ctx context.Context, connectUrl string) ([]R return recordings, nil } -func (client *RecordingClient) Get(ctx context.Context, connectUrl string, recordingName string) (*Recording, error) { - recordings, err := client.List(ctx, connectUrl) +func (client *RecordingClient) Get(ctx context.Context, target *Target, recordingName string) (*Recording, error) { + recordings, err := client.List(ctx, target) if err != nil { return nil, err } @@ -312,11 +311,11 @@ func (client *RecordingClient) Get(ctx context.Context, connectUrl string, recor } } - return nil, fmt.Errorf("recording %s does not exist for target %s", recordingName, connectUrl) + return nil, fmt.Errorf("recording %s does not exist for target %s", recordingName, target.ConnectUrl) } -func (client *RecordingClient) Create(ctx context.Context, connectUrl string, options *RecordingCreateOptions) (*Recording, error) { - url := client.Base.JoinPath(fmt.Sprintf("/api/v1/targets/%s/recordings", url.PathEscape(connectUrl))) +func (client *RecordingClient) Create(ctx context.Context, target *Target, options *RecordingCreateOptions) (*Recording, error) { + url := client.Base.JoinPath(fmt.Sprintf("/api/v3/targets/%d/recordings", target.Id)) body := options.ToFormData() header := make(http.Header) header.Add("Content-Type", "application/x-www-form-urlencoded") @@ -341,8 +340,8 @@ func (client *RecordingClient) Create(ctx context.Context, connectUrl string, op return recording, err } -func (client *RecordingClient) Archive(ctx context.Context, connectUrl string, recordingName string) (string, error) { - url := client.Base.JoinPath(fmt.Sprintf("/api/v1/targets/%s/recordings/%s", url.PathEscape(connectUrl), url.PathEscape(recordingName))) +func (client *RecordingClient) Archive(ctx context.Context, target *Target, recordingId uint32) (string, error) { + url := client.Base.JoinPath(fmt.Sprintf("/api/v3/targets/%d/recordings/%d", target.Id, recordingId)) body := "SAVE" header := make(http.Header) header.Add("Content-Type", "text/plain") @@ -366,8 +365,8 @@ func (client *RecordingClient) Archive(ctx context.Context, connectUrl string, r return bodyAsString, nil } -func (client *RecordingClient) Stop(ctx context.Context, connectUrl string, recordingName string) error { - url := client.Base.JoinPath(fmt.Sprintf("/api/v1/targets/%s/recordings/%s", url.PathEscape(connectUrl), url.PathEscape(recordingName))) +func (client *RecordingClient) Stop(ctx context.Context, target *Target, recordingId uint32) error { + url := client.Base.JoinPath(fmt.Sprintf("/api/v3/targets/%d/recordings/%d", target.Id, recordingId)) body := "STOP" header := make(http.Header) header.Add("Content-Type", "text/plain") @@ -386,8 +385,8 @@ func (client *RecordingClient) Stop(ctx context.Context, connectUrl string, reco return nil } -func (client *RecordingClient) Delete(ctx context.Context, connectUrl string, recordingName string) error { - url := client.Base.JoinPath(fmt.Sprintf("/api/v1/targets/%s/recordings/%s", url.PathEscape(connectUrl), url.PathEscape(recordingName))) +func (client *RecordingClient) Delete(ctx context.Context, target *Target, recordingId uint32) error { + url := client.Base.JoinPath(fmt.Sprintf("/api/v3/targets/%d/recordings/%d", target.Id, recordingId)) header := make(http.Header) resp, err := SendRequest(ctx, client.Client, http.MethodDelete, url.String(), nil, header) @@ -403,17 +402,17 @@ func (client *RecordingClient) Delete(ctx context.Context, connectUrl string, re return nil } -func (client *RecordingClient) GenerateReport(ctx context.Context, connectUrl string, recordingName *Recording) (map[string]interface{}, error) { - reportURL := recordingName.ReportURL - - if len(reportURL) < 1 { +func (client *RecordingClient) GenerateReport(ctx context.Context, target *Target, recording *Recording) (map[string]interface{}, error) { + if len(recording.ReportURL) < 1 { return nil, fmt.Errorf("report URL is not available") } + reportURL := client.Base.JoinPath(recording.ReportURL) + header := make(http.Header) header.Add("Accept", "application/json") - resp, err := SendRequest(ctx, client.Client, http.MethodGet, reportURL, nil, header) + resp, err := SendRequest(ctx, client.Client, http.MethodGet, reportURL.String(), nil, header) if err != nil { return nil, err } @@ -432,27 +431,34 @@ func (client *RecordingClient) GenerateReport(ctx context.Context, connectUrl st return report, nil } -func (client *RecordingClient) ListArchives(ctx context.Context, connectUrl string) ([]Archive, error) { +func (client *RecordingClient) ListArchives(ctx context.Context, target *Target) ([]Archive, error) { url := client.Base.JoinPath("/api/v2.2/graphql") query := &GraphQLQuery{ Query: ` - query ArchivedRecordingsForTarget($connectUrl: String) { - archivedRecordings(filter: { sourceTarget: $connectUrl }) { - data { - name - downloadUrl - reportUrl - metadata { - labels + query ArchivedRecordingsForTarget($id: BigInteger!) { + targetNodes(filter: { targetIds: [$id] }) { + target { + archivedRecordings { + data { + name + downloadUrl + reportUrl + metadata { + labels { + key + value + } + } + size + } } - size } } } `, - Variables: map[string]string{ - connectUrl: connectUrl, + Variables: map[string]any{ + "id": target.Id, }, } queryJSON, err := query.ToJSON() @@ -481,7 +487,7 @@ func (client *RecordingClient) ListArchives(ctx context.Context, connectUrl stri return nil, fmt.Errorf("failed to read response body: %s", err.Error()) } - return graphQLResponse.Data.ArchivedRecordings.Data, nil + return graphQLResponse.Data.TargetNodes[0].Target.ArchivedRecordings.Data, nil } type CredentialClient struct { @@ -515,7 +521,7 @@ func ReadJSON(resp *http.Response, result interface{}) error { err = json.Unmarshal(body, result) if err != nil { - return err + return fmt.Errorf("failed to unmarshal JSON: %s, response body: %s ", err.Error(), body) } return nil } @@ -565,15 +571,19 @@ func NewHttpRequest(ctx context.Context, method string, url string, body *string if err != nil { return nil, err } - if header != nil { - req.Header = header + + req.Header = header + if req.Header == nil { + req.Header = make(http.Header) } - // Authentication is only enabled on OCP. Ignored on k8s. + + // Authentication for OpenShift SSO config, err := rest.InClusterConfig() if err != nil { return nil, fmt.Errorf("failed to get in-cluster configurations: %s", err.Error()) } - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", base64.StdEncoding.EncodeToString([]byte(config.BearerToken)))) + + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", config.BearerToken)) return req, nil } @@ -583,7 +593,7 @@ func StatusOK(statusCode int) bool { func SendRequest(ctx context.Context, httpClient *http.Client, method string, url string, body *string, header http.Header) (*http.Response, error) { var response *http.Response - err := wait.PollImmediateUntilWithContext(ctx, time.Second, func(ctx context.Context) (done bool, err error) { + err := wait.PollUntilContextCancel(ctx, time.Second, true, func(ctx context.Context) (done bool, err error) { // Create a new request req, err := NewHttpRequest(ctx, method, url, body, header) if err != nil { diff --git a/internal/test/scorecard/common_utils.go b/internal/test/scorecard/common_utils.go index f8af81f1..ae9c9254 100644 --- a/internal/test/scorecard/common_utils.go +++ b/internal/test/scorecard/common_utils.go @@ -56,7 +56,7 @@ type TestResources struct { } func (r *TestResources) waitForDeploymentAvailability(ctx context.Context, name string, namespace string) error { - err := wait.PollImmediateUntilWithContext(ctx, time.Second, func(ctx context.Context) (done bool, err error) { + err := wait.PollUntilContextCancel(ctx, time.Second, true, func(ctx context.Context) (done bool, err error) { deploy, err := r.Client.AppsV1().Deployments(namespace).Get(ctx, name, metav1.GetOptions{}) if err != nil { if kerrors.IsNotFound(err) { @@ -344,7 +344,7 @@ func (r *TestResources) createAndWaitTillCryostatAvailable(cr *operatorv1beta2.C return nil, err } - err = wait.PollImmediateUntilWithContext(ctx, time.Second, func(ctx context.Context) (done bool, err error) { + err = wait.PollUntilContextCancel(ctx, time.Second, true, func(ctx context.Context) (done bool, err error) { cr, err = r.Client.OperatorCRDs().Cryostats(cr.Namespace).Get(ctx, cr.Name) if err != nil { return false, fmt.Errorf("failed to get Cryostat CR: %s", err.Error()) @@ -421,7 +421,7 @@ func (r *TestResources) sendHealthRequest(base *url.URL, healthCheck func(resp * ctx, cancel := context.WithTimeout(context.Background(), testTimeout) defer cancel() - err := wait.PollImmediateUntilWithContext(ctx, time.Second, func(ctx context.Context) (done bool, err error) { + err := wait.PollUntilContextCancel(ctx, time.Second, true, func(ctx context.Context) (done bool, err error) { url := base.JoinPath("/health") req, err := NewHttpRequest(ctx, http.MethodGet, url.String(), nil, make(http.Header)) if err != nil { @@ -483,7 +483,7 @@ func (r *TestResources) updateAndWaitTillCryostatAvailable(cr *operatorv1beta2.C // Poll the deployment until it becomes available or we timeout ctx, cancel := context.WithTimeout(context.Background(), testTimeout) defer cancel() - err = wait.PollImmediateUntilWithContext(ctx, time.Second, func(ctx context.Context) (done bool, err error) { + err = wait.PollUntilContextCancel(ctx, time.Second, true, func(ctx context.Context) (done bool, err error) { deploy, err := r.Client.AppsV1().Deployments(cr.Namespace).Get(ctx, cr.Name, metav1.GetOptions{}) if err != nil { if kerrors.IsNotFound(err) { diff --git a/internal/test/scorecard/openshift.go b/internal/test/scorecard/openshift.go index 7880221c..341f9ed6 100644 --- a/internal/test/scorecard/openshift.go +++ b/internal/test/scorecard/openshift.go @@ -175,7 +175,7 @@ func (r *TestResources) installOpenShiftCertManager() error { // Check CSV status until we know cert-manager installed successfully ctx, cancel := context.WithTimeout(ctx, testTimeout) defer cancel() - return wait.PollImmediateUntilWithContext(ctx, time.Second, func(ctx context.Context) (bool, error) { + return wait.PollUntilContextCancel(ctx, time.Second, true, func(ctx context.Context) (bool, error) { err := olmClient.Get().Resource("subscriptions").Namespace(sub.Namespace).Name(sub.Name).Do(ctx).Into(sub) if err != nil { return false, fmt.Errorf("failed to get Subscription: %s", err.Error()) diff --git a/internal/test/scorecard/tests.go b/internal/test/scorecard/tests.go index 3ad6257c..a06b6e7a 100644 --- a/internal/test/scorecard/tests.go +++ b/internal/test/scorecard/tests.go @@ -185,7 +185,6 @@ func CryostatRecordingTest(bundle *apimanifests.Bundle, namespace string, openSh return r.fail(fmt.Sprintf("failed to create a target: %s", err.Error())) } r.Log += fmt.Sprintf("created a custom target: %+v\n", target) - connectUrl := target.ConnectUrl jmxSecretName := CryostatRecordingTestName + "-jmx-auth" secret, err := r.Client.CoreV1().Secrets(namespace).Get(context.Background(), jmxSecretName, metav1.GetOptions{}) @@ -217,48 +216,48 @@ func CryostatRecordingTest(bundle *apimanifests.Bundle, namespace string, openSh MaxSize: 0, MaxAge: 0, } - rec, err := apiClient.Recordings().Create(context.Background(), connectUrl, options) + rec, err := apiClient.Recordings().Create(context.Background(), target, options) if err != nil { return r.fail(fmt.Sprintf("failed to create a recording: %s", err.Error())) } r.Log += fmt.Sprintf("created a recording: %+v\n", rec) // View the current recording list after creating one - recs, err := apiClient.Recordings().List(context.Background(), connectUrl) + recs, err := apiClient.Recordings().List(context.Background(), target) if err != nil { return r.fail(fmt.Sprintf("failed to list recordings: %s", err.Error())) } r.Log += fmt.Sprintf("current list of recordings: %+v\n", recs) - // Allow the recording to run for 10s + // Allow the recording to run for 30s time.Sleep(30 * time.Second) // Archive the recording - archiveName, err := apiClient.Recordings().Archive(context.Background(), connectUrl, rec.Name) + archiveName, err := apiClient.Recordings().Archive(context.Background(), target, rec.Id) if err != nil { return r.fail(fmt.Sprintf("failed to archive the recording: %s", err.Error())) } r.Log += fmt.Sprintf("archived the recording %s at: %s\n", rec.Name, archiveName) - archives, err := apiClient.Recordings().ListArchives(context.Background(), connectUrl) + archives, err := apiClient.Recordings().ListArchives(context.Background(), target) if err != nil { return r.fail(fmt.Sprintf("failed to list archives: %s", err.Error())) } r.Log += fmt.Sprintf("current list of archives: %+v\n", archives) - report, err := apiClient.Recordings().GenerateReport(context.Background(), connectUrl, rec) + report, err := apiClient.Recordings().GenerateReport(context.Background(), target, rec) if err != nil { return r.fail(fmt.Sprintf("failed to generate report for the recording: %s", err.Error())) } r.Log += fmt.Sprintf("generated report for the recording %s: %+v\n", rec.Name, report) // Stop the recording - err = apiClient.Recordings().Stop(context.Background(), connectUrl, rec.Name) + err = apiClient.Recordings().Stop(context.Background(), target, rec.Id) if err != nil { return r.fail(fmt.Sprintf("failed to stop the recording %s: %s", rec.Name, err.Error())) } // Get the recording to verify its state - rec, err = apiClient.Recordings().Get(context.Background(), connectUrl, rec.Name) + rec, err = apiClient.Recordings().Get(context.Background(), target, rec.Name) if err != nil { return r.fail(fmt.Sprintf("failed to get the recordings: %s", err.Error())) } @@ -268,14 +267,14 @@ func CryostatRecordingTest(bundle *apimanifests.Bundle, namespace string, openSh r.Log += fmt.Sprintf("stopped the recording: %s\n", rec.Name) // Delete the recording - err = apiClient.Recordings().Delete(context.Background(), connectUrl, rec.Name) + err = apiClient.Recordings().Delete(context.Background(), target, rec.Id) if err != nil { return r.fail(fmt.Sprintf("failed to delete the recording %s: %s", rec.Name, err.Error())) } r.Log += fmt.Sprintf("deleted the recording: %s\n", rec.Name) // View the current recording list after deleting one - recs, err = apiClient.Recordings().List(context.Background(), connectUrl) + recs, err = apiClient.Recordings().List(context.Background(), target) if err != nil { return r.fail(fmt.Sprintf("failed to list recordings: %s", err.Error())) } diff --git a/internal/test/scorecard/types.go b/internal/test/scorecard/types.go index 7e34ff4a..8c26603d 100644 --- a/internal/test/scorecard/types.go +++ b/internal/test/scorecard/types.go @@ -99,13 +99,16 @@ type Recording struct { } type Archive struct { - Name string - DownloadUrl string - ReportUrl string + Name string `json:"name"` + DownloadUrl string `json:"downloadUrl"` + ReportUrl string `json:"reportUrl"` Metadata struct { - Labels map[string]interface{} - } - Size int32 + Labels []struct { + Key string `json:"key"` + Value string `json:"value"` + } + } `json:"metadata"` + Size uint32 `json:"size"` } type CustomTargetResponse struct { @@ -115,6 +118,7 @@ type CustomTargetResponse struct { } type Target struct { + Id uint32 `json:"id,omitempty"` ConnectUrl string `json:"connectUrl"` Alias string `json:"alias,omitempty"` } @@ -129,8 +133,8 @@ func (target *Target) ToFormData() string { } type GraphQLQuery struct { - Query string `json:"query"` - Variables map[string]string `json:"variables,omitempty"` + Query string `json:"query"` + Variables map[string]any `json:"variables,omitempty"` } func (query *GraphQLQuery) ToJSON() ([]byte, error) { @@ -139,8 +143,12 @@ func (query *GraphQLQuery) ToJSON() ([]byte, error) { type ArchiveGraphQLResponse struct { Data struct { - ArchivedRecordings struct { - Data []Archive `json:"data"` - } `json:"archivedRecordings"` + TargetNodes []struct { + Target struct { + ArchivedRecordings struct { + Data []Archive `json:"data"` + } `json:"archivedRecordings"` + } `json:"target"` + } `json:"targetNodes"` } `json:"data"` }