Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,22 @@
"exclude": "topology==\"External\""
}
},
{
"name": "[sig-olmv1][Jira:OLM] clustercatalog PolarionID:85889-[OTP][Skipped:Disconnected]Validate catalogd content via port-forward [Serial]",
"labels": {
"ClusterCatalog": {},
"Extended": {},
"NonHyperShiftHOST": {}
},
"resources": {
"isolation": {}
},
"source": "openshift:payload:olmv1",
"lifecycle": "blocking",
"environmentSelector": {
"exclude": "topology==\"External\""
}
},
{
"name": "[sig-olmv1][Jira:OLM] clustercatalog PolarionID:73219-[OTP][Skipped:Disconnected]Fetch deprecation data from the catalogd http server",
"labels": {
Expand Down
60 changes: 51 additions & 9 deletions openshift/tests-extension/pkg/bindata/qe/bindata.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

268 changes: 268 additions & 0 deletions openshift/tests-extension/test/qe/specs/olmv1_cc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@ package specs

import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -324,6 +331,267 @@ var _ = g.Describe("[sig-olmv1][Jira:OLM] clustercatalog", g.Label("NonHyperShif
exutil.AssertWaitPollNoErr(errWait, "Cannot get the result")
})

g.It("PolarionID:85889-[OTP][Skipped:Disconnected]Validate catalogd content via port-forward [Serial]", func() {
caseID := "85889"
catalogName := "catalog-" + caseID
catalogImage := "quay.io/openshifttest/nginxolm-operator-index:nginxolm74924"
baseDir := exutil.FixturePath("testdata", "olm")
catalogTemplate := filepath.Join(baseDir, "clustercatalog-with-pollinterval.yaml")
outputDir := e2e.TestContext.OutputDir
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bandrade

outputDir := e2e.TestContext.OutputDir
		if outputDir == "" {
			outputDir = os.TempDir()
		}
		err := os.MkdirAll(outputDir, 0755)
		o.Expect(err).NotTo(o.HaveOccurred())

to

		outputDir := "/tmp/" + caseID + "-"+exutil.GetRandomString()
		err := os.MkdirAll(outputDir, 0755)
		o.Expect(err).NotTo(o.HaveOccurred())
		defer os.RemoveAll(outputDir)

if outputDir == "" {
outputDir = os.TempDir()
}
err := os.MkdirAll(outputDir, 0755)
o.Expect(err).NotTo(o.HaveOccurred())

redhatOperatorsFile := filepath.Join(outputDir, "redhat-operators-all.json")
customCatalogFile := filepath.Join(outputDir, "all.json")

type catalogEntry struct {
Schema string `json:"schema"`
Name string `json:"name,omitempty"`
Package string `json:"package,omitempty"`
}

parseCatalogEntries := func(data []byte) ([]catalogEntry, error) {
var entries []catalogEntry
for _, line := range strings.Split(strings.TrimSpace(string(data)), "\n") {
line = strings.TrimSpace(line)
if line == "" || !strings.HasPrefix(line, "{") {
continue
}
var entry catalogEntry
if err := json.Unmarshal([]byte(line), &entry); err != nil {
return nil, err
}
if entry.Schema != "" {
entries = append(entries, entry)
}
}
if len(entries) == 0 {
return nil, fmt.Errorf("no catalog entries parsed")
}
return entries, nil
}

fetchToFile := func(url, path string) ([]byte, error) {
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec // uses local port-forward with self-signed cert
},
Timeout: 2 * time.Minute,
}
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, url, nil)
if err != nil {
return nil, err
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status %s for %s", resp.Status, url)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if err = os.WriteFile(path, body, 0600); err != nil {
return nil, err
}
return body, nil
}

getConsoleLogs := func() (string, error) {
return oc.AsAdmin().WithoutNamespace().Run("logs").Args("-n", "openshift-console", "deploy/console", "--since=10m").Output()
}

getReconcileTotal := func() (float64, error) {
metrics, err := oc.AsAdmin().WithoutNamespace().Run("rsh").Args("-n", "openshift-console", "deploy/console", "curl", "-s", "localhost:9440/metrics").Output()
if err != nil {
return 0, err
}
re := regexp.MustCompile(`(?im)^.*reconcile_total.*clustercatalog.*\\s+([0-9]+(?:\\.[0-9]+)?)$`)
matches := re.FindAllStringSubmatch(metrics, -1)
if len(matches) == 0 {
return 0, fmt.Errorf("clustercatalog reconcile_total not found in metrics output")
}
var max float64
for _, match := range matches {
value, err := strconv.ParseFloat(match[1], 64)
if err != nil {
continue
}
if value > max {
max = value
}
}
return max, nil
}

clustercatalog := olmv1util.ClusterCatalogDescription{Name: catalogName}
deleted := false
defer func() {
if !deleted {
clustercatalog.Delete(oc)
}
}()

g.By("Check OLM namespaces in ClusterOperator")
nsOutput, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("co", "olm", `-o=jsonpath={.status.relatedObjects[?(@.resource=="namespaces")].name}`).Output()
o.Expect(err).NotTo(o.HaveOccurred())
o.Expect(nsOutput).To(o.ContainSubstring("openshift-catalogd"))
o.Expect(nsOutput).To(o.ContainSubstring("openshift-operator-controller"))

g.By("Verify OLM CRDs referenced")
crdOutput, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("co", "olm", `-o=jsonpath={.status.relatedObjects[?(@.resource=="customresourcedefinitions")].name}`).Output()
o.Expect(err).NotTo(o.HaveOccurred())
o.Expect(crdOutput).To(o.ContainSubstring("clustercatalogs.catalogd.operatorframework.io"))
o.Expect(crdOutput).To(o.ContainSubstring("clusterextensions.olm.operatorframework.io"))

g.By("Create test ClusterCatalog")
clustercatalog.Template = catalogTemplate
clustercatalog.Imageref = catalogImage
clustercatalog.PollIntervalMinutes = "300s"
clustercatalog.Create(oc)

g.By("Verify ClusterCatalog list and phases")
listOutput, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("clustercatalog", `-o=jsonpath={range .items[*]}{.metadata.name}{"="}{.status.phase}{"\n"}{end}`).Output()
o.Expect(err).NotTo(o.HaveOccurred())
o.Expect(listOutput).To(o.ContainSubstring("openshift-certified-operators="))
o.Expect(listOutput).To(o.ContainSubstring("openshift-community-operators="))
o.Expect(listOutput).To(o.ContainSubstring("openshift-redhat-operators="))
o.Expect(listOutput).To(o.ContainSubstring("openshift-redhat-marketplace="))
var catalogPhase string
for _, line := range strings.Split(strings.TrimSpace(listOutput), "\n") {
if strings.HasPrefix(line, catalogName+"=") {
catalogPhase = strings.TrimPrefix(line, catalogName+"=")
break
}
}
o.Expect(catalogPhase).NotTo(o.BeEmpty())

g.By("Find catalogd service")
svcPorts, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("svc", "-n", "openshift-catalogd", "catalogd-catalogserver", "-o=jsonpath={.spec.ports[*].port}").Output()
o.Expect(err).NotTo(o.HaveOccurred())
o.Expect(svcPorts).To(o.ContainSubstring("443"))

g.By("Port-forward catalogd service")
pfCmd, _, _, err := oc.AsAdmin().WithoutNamespace().Run("port-forward").Args("-n", "openshift-catalogd", "svc/catalogd-catalogserver", "8443:443").Background()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bandrade suggest to use the way like GetContentURL to replace svc, and do not use port-forward.

Just FYI.

o.Expect(err).NotTo(o.HaveOccurred())
defer func() {
_ = pfCmd.Process.Kill()
_ = pfCmd.Wait()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bandrade do not need 'pfCmd.Wait()'

}()
errWait := wait.PollUntilContextTimeout(context.TODO(), 2*time.Second, 30*time.Second, false, func(ctx context.Context) (bool, error) {
conn, err := net.DialTimeout("tcp", "127.0.0.1:8443", 2*time.Second)
if err != nil {
return false, nil
}
_ = conn.Close()
return true, nil
})
exutil.AssertWaitPollNoErr(errWait, "catalogd port-forward did not become ready")

g.By("Create redhat-operators-all.json")
redhatData, err := fetchToFile("https://localhost:8443/catalogs/redhat-operators/all.json", redhatOperatorsFile)
o.Expect(err).NotTo(o.HaveOccurred())
_, err = os.Stat(redhatOperatorsFile)
o.Expect(err).NotTo(o.HaveOccurred())

g.By("Create all.json for catalog-85889")
customData, err := fetchToFile(fmt.Sprintf("https://localhost:8443/catalogs/%s/all.json", catalogName), customCatalogFile)
o.Expect(err).NotTo(o.HaveOccurred())
_, err = os.Stat(customCatalogFile)
o.Expect(err).NotTo(o.HaveOccurred())

g.By("List OLM packages from redhat-operators")
redhatEntries, err := parseCatalogEntries(redhatData)
o.Expect(err).NotTo(o.HaveOccurred())
var packageNames []string
for _, entry := range redhatEntries {
if entry.Schema == "olm.package" && entry.Name != "" {
packageNames = append(packageNames, entry.Name)
}
}
o.Expect(packageNames).NotTo(o.BeEmpty())

g.By("Get channel info for nginx68821")
customEntries, err := parseCatalogEntries(customData)
o.Expect(err).NotTo(o.HaveOccurred())
channelFound := false
for _, entry := range customEntries {
if entry.Schema == "olm.channel" && entry.Package == "nginx68821" {
channelFound = true
break
}
}
o.Expect(channelFound).To(o.BeTrue())

g.By("Get bundle info for nginx68821.v1.1.0")
bundleFound := false
for _, entry := range customEntries {
if entry.Schema == "olm.bundle" && entry.Package == "nginx68821" && entry.Name == "nginx68821.v1.1.0" {
bundleFound = true
break
}
}
o.Expect(bundleFound).To(o.BeTrue())

g.By("Check ClusterCatalog reconciliation logs")
consoleLogs, err := getConsoleLogs()
o.Expect(err).NotTo(o.HaveOccurred())
consoleLogsLower := strings.ToLower(consoleLogs)
o.Expect(consoleLogsLower).To(o.ContainSubstring("reconcil"))
o.Expect(consoleLogsLower).To(o.ContainSubstring("clustercatalog"))
o.Expect(consoleLogs).To(o.ContainSubstring(catalogName))
o.Expect(consoleLogsLower).NotTo(o.ContainSubstring("panic"))
o.Expect(consoleLogsLower).NotTo(o.ContainSubstring("stack trace"))

g.By("Check reconcile_total metrics before patch")
reconcileBefore, err := getReconcileTotal()
o.Expect(err).NotTo(o.HaveOccurred())

g.By("Patch catalog-85889 image")
patch := `{"spec":{"source":{"type":"Image","image":{"ref":"quay.io/operatorhubio/catalog:latest"}}}}`
clustercatalog.Patch(oc, patch)

g.By("Confirm reconcile_total increases after patch")
errWait = wait.PollUntilContextTimeout(context.TODO(), 10*time.Second, 2*time.Minute, false, func(ctx context.Context) (bool, error) {
reconcileAfter, err := getReconcileTotal()
if err != nil {
return false, nil
}
return reconcileAfter > reconcileBefore, nil
})
exutil.AssertWaitPollNoErr(errWait, "reconcile_total did not increase after patch")

g.By("Delete test ClusterCatalog")
clustercatalog.Delete(oc)
deleted = true

g.By("Verify clean finalization logs")
errWait = wait.PollUntilContextTimeout(context.TODO(), 10*time.Second, 2*time.Minute, false, func(ctx context.Context) (bool, error) {
consoleLogs, err = getConsoleLogs()
if err != nil {
return false, nil
}
consoleLogsLower = strings.ToLower(consoleLogs)
if !strings.Contains(consoleLogsLower, "finaliz") {
return false, nil
}
if !strings.Contains(consoleLogs, catalogName) {
return false, nil
}
if strings.Contains(consoleLogsLower, "panic") || strings.Contains(consoleLogsLower, "stack trace") {
return false, fmt.Errorf("panic or stack trace detected in logs")
}
return true, nil
})
exutil.AssertWaitPollNoErr(errWait, "catalog-85889 finalization logs not found or contained errors")
})

g.It("PolarionID:73219-[OTP][Skipped:Disconnected]Fetch deprecation data from the catalogd http server", func() {
var (
baseDir = exutil.FixturePath("testdata", "olm")
Expand Down