diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 15a5a5df0..9a7915bc2 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -16,7 +16,7 @@ runs: - name: Set up Go 1.21 uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.21.4 - name: Disable default go problem matcher run: echo "::remove-matcher owner=go::" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index a1b09044a..83a70f93c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -45,7 +45,7 @@ jobs: - name: Set up Go 1.21 uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.21.4 - name: Install ginkgo run: make install-tools diff --git a/.github/workflows/merge.yaml b/.github/workflows/merge.yaml index 5649900a0..64855646f 100644 --- a/.github/workflows/merge.yaml +++ b/.github/workflows/merge.yaml @@ -24,7 +24,7 @@ jobs: - name: Set up Go 1.21 uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.21.4 - name: Check out code into the Go module directory uses: actions/checkout@v4 diff --git a/.github/workflows/pre-main.yaml b/.github/workflows/pre-main.yaml index 0cf1831a0..0cd044974 100644 --- a/.github/workflows/pre-main.yaml +++ b/.github/workflows/pre-main.yaml @@ -41,7 +41,7 @@ jobs: - name: Set up Go 1.21 uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.21.4 - name: Disable default go problem matcher run: echo "::remove-matcher owner=go::" @@ -113,7 +113,7 @@ jobs: - name: Set up Go 1.21 uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.21.4 - name: Disable default go problem matcher run: echo "::remove-matcher owner=go::" @@ -179,7 +179,7 @@ jobs: - name: Set up Go 1.21 uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.21.4 - name: Disable default go problem matcher run: echo "::remove-matcher owner=go::" @@ -334,7 +334,7 @@ jobs: - name: Set up Go 1.21 uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.21.4 @@ -386,12 +386,12 @@ jobs: run: TNF_LOG_LEVEL=${TNF_SMOKE_TESTS_LOG_LEVEL} ./run-tnf-container.sh ${{ env.TESTING_CMD_PARAMS }} - name: 'Test: Run Smoke Tests in a TNF container' - run: TNF_LOG_LEVEL=${TNF_SMOKE_TESTS_LOG_LEVEL} TNF_ENABLE_DATA_COLLECTION=true ./run-tnf-container.sh ${{ env.TESTING_CMD_PARAMS }} -l "${SMOKE_TESTS_GINKGO_LABELS_FILTER}" + run: TNF_LOG_LEVEL=${TNF_SMOKE_TESTS_LOG_LEVEL} TNF_ENABLE_DATA_COLLECTION=false ./run-tnf-container.sh ${{ env.TESTING_CMD_PARAMS }} -l "${SMOKE_TESTS_GINKGO_LABELS_FILTER}" - - name: Run sanity check on collector - uses: ./collector/.github/actions/run-sanity-check - with: - working_directory: collector + # - name: Run sanity check on collector + # uses: ./collector/.github/actions/run-sanity-check + # with: + # working_directory: collector - name: Upload container test results as an artifact uses: actions/upload-artifact@v3 diff --git a/.github/workflows/preflight.yml b/.github/workflows/preflight.yml index 2af36f708..8b55e41c2 100644 --- a/.github/workflows/preflight.yml +++ b/.github/workflows/preflight.yml @@ -20,7 +20,7 @@ jobs: - name: Set up Go 1.21 uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.21.4 - name: Disable default go problem matcher run: echo "::remove-matcher owner=go::" diff --git a/.github/workflows/qe-hosted.yml b/.github/workflows/qe-hosted.yml index 88d72d115..833d8e910 100644 --- a/.github/workflows/qe-hosted.yml +++ b/.github/workflows/qe-hosted.yml @@ -49,7 +49,7 @@ jobs: - name: Set up Go 1.21 uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.21.4 - name: Disable default go problem matcher run: echo "::remove-matcher owner=go::" diff --git a/.github/workflows/qe-ocp-intrusive.yaml b/.github/workflows/qe-ocp-413-intrusive.yaml similarity index 89% rename from .github/workflows/qe-ocp-intrusive.yaml rename to .github/workflows/qe-ocp-413-intrusive.yaml index 7709dcf34..bee1a95c6 100644 --- a/.github/workflows/qe-ocp-intrusive.yaml +++ b/.github/workflows/qe-ocp-413-intrusive.yaml @@ -1,4 +1,4 @@ -name: QE OCP Intrusive Testing +name: QE OCP 4.13 Intrusive Testing on: # pull_request: @@ -11,7 +11,7 @@ env: QE_REPO: test-network-function/cnfcert-tests-verification jobs: - qe-ocp-intrusive-testing: + qe-ocp-413-intrusive-testing: runs-on: qe-ocp strategy: fail-fast: false @@ -25,6 +25,8 @@ jobs: TEST_TNF_IMAGE_NAME: quay.io/testnetworkfunction/cnf-certification-test TEST_TNF_IMAGE_TAG: localtest DOCKER_CONFIG_DIR: '/home/labuser/.docker' + TNF_CONFIG_DIR: '/home/labuser/tnf_config' + TNF_REPORT_DIR: '/home/labuser/tnf_report' steps: - name: Check out code @@ -74,5 +76,5 @@ jobs: GITHUB_REPO: https://github.com/test-network-function/cnf-certification-test run: | curl -X POST --data "{ - \"text\": \"🚨⚠️ Failed to run intrusive OCP QE tests from commit \<$GITHUB_REPO/commit/$COMMIT_SHA|$COMMIT_SHA\>, job ID \<$GITHUB_REPO/actions/runs/$JOB_RUN_ID/attempts/$JOB_RUN_ATTEMPT|$JOB_RUN_ID\> \" + \"text\": \"🚨⚠️ Failed to run intrusive OCP 4.13 QE tests from commit \<$GITHUB_REPO/commit/$COMMIT_SHA|$COMMIT_SHA\>, job ID \<$GITHUB_REPO/actions/runs/$JOB_RUN_ID/attempts/$JOB_RUN_ATTEMPT|$JOB_RUN_ID\> \" }" -H 'Content-type: application/json; charset=UTF-8' ${{ secrets.QE_NIGHTLY_WEBHOOK }} diff --git a/.github/workflows/qe-ocp.yaml b/.github/workflows/qe-ocp-413.yaml similarity index 89% rename from .github/workflows/qe-ocp.yaml rename to .github/workflows/qe-ocp-413.yaml index 702224833..152b186b9 100644 --- a/.github/workflows/qe-ocp.yaml +++ b/.github/workflows/qe-ocp-413.yaml @@ -1,4 +1,4 @@ -name: QE OCP Testing +name: QE OCP 4.13 Testing on: # pull_request: @@ -11,7 +11,7 @@ env: QE_REPO: test-network-function/cnfcert-tests-verification jobs: - qe-ocp-testing: + qe-ocp-413-testing: runs-on: qe-ocp strategy: fail-fast: false @@ -24,6 +24,8 @@ jobs: TEST_TNF_IMAGE_NAME: quay.io/testnetworkfunction/cnf-certification-test TEST_TNF_IMAGE_TAG: localtest DOCKER_CONFIG_DIR: '/home/labuser/.docker' + TNF_CONFIG_DIR: '/home/labuser/tnf_config' + TNF_REPORT_DIR: '/home/labuser/tnf_report' steps: - name: Check out code @@ -73,5 +75,5 @@ jobs: GITHUB_REPO: https://github.com/test-network-function/cnf-certification-test run: | curl -X POST --data "{ - \"text\": \"🚨⚠️ Failed to run non-intrusive OCP QE tests from commit \<$GITHUB_REPO/commit/$COMMIT_SHA|$COMMIT_SHA\>, job ID \<$GITHUB_REPO/actions/runs/$JOB_RUN_ID/attempts/$JOB_RUN_ATTEMPT|$JOB_RUN_ID\> \" + \"text\": \"🚨⚠️ Failed to run non-intrusive OCP 4.13 QE tests from commit \<$GITHUB_REPO/commit/$COMMIT_SHA|$COMMIT_SHA\>, job ID \<$GITHUB_REPO/actions/runs/$JOB_RUN_ID/attempts/$JOB_RUN_ATTEMPT|$JOB_RUN_ID\> \" }" -H 'Content-type: application/json; charset=UTF-8' ${{ secrets.QE_NIGHTLY_WEBHOOK }} diff --git a/.github/workflows/qe-ocp-414-intrusive.yaml b/.github/workflows/qe-ocp-414-intrusive.yaml new file mode 100644 index 000000000..22e340a9a --- /dev/null +++ b/.github/workflows/qe-ocp-414-intrusive.yaml @@ -0,0 +1,80 @@ +name: QE OCP 4.14 Intrusive Testing + +on: + # pull_request: + # branches: [ main ] + workflow_dispatch: + # Schedule a daily cron at midnight UTC + schedule: + - cron: '0 0 * * *' +env: + QE_REPO: test-network-function/cnfcert-tests-verification + +jobs: + qe-ocp-414-intrusive-testing: + runs-on: qe-ocp-414 + strategy: + fail-fast: false + matrix: + # Add more suites if more intrusive tests are added to the QE repo + suite: [lifecycle] + env: + SHELL: /bin/bash + KUBECONFIG: '/home/labuser2/.kube/config' + PFLT_DOCKERCONFIG: '/home/labuser2/.docker/config' + TEST_TNF_IMAGE_NAME: quay.io/testnetworkfunction/cnf-certification-test + TEST_TNF_IMAGE_TAG: localtest + DOCKER_CONFIG_DIR: '/home/labuser2/.docker' + TNF_CONFIG_DIR: '/home/labuser2/tnf_config' + TNF_REPORT_DIR: '/home/labuser2/tnf_report' + + steps: + - name: Check out code + uses: actions/checkout@v4 + with: + ref: ${{ github.sha }} + + - name: Run initial setup + uses: ./.github/actions/setup + + - name: Preemptively prune docker resources + run: docker system prune -f --volumes + + - name: Build the test image + run: make build-image-local # quay.io/testnetworkfunction/cnf-certification-test:localtest + + - name: Show pods + run: oc get pods -A + + - name: Clone the QE repository + uses: actions/checkout@v4 + with: + repository: ${{ env.QE_REPO }} + path: cnfcert-tests-verification + + - name: Preemptively potential QE namespaces + run: ./scripts/delete-namespaces.sh + working-directory: cnfcert-tests-verification + + - name: Preemptively delete report and config folders + shell: bash + run: | + sudo rm -rf /tmp/tnf_config/ + sudo rm -rf /tmp/tnf_report/ + + # Setup is complete. Time to run the QE tests. + - name: Run the tests + run: FEATURES=${{matrix.suite}} TNF_REPO_PATH=${GITHUB_WORKSPACE} TNF_IMAGE=${{env.TEST_TNF_IMAGE_NAME}} TNF_IMAGE_TAG=${{env.TEST_TNF_IMAGE_TAG}} DISABLE_INTRUSIVE_TESTS=false ENABLE_PARALLEL=false ENABLE_FLAKY_RETRY=true make test-features + working-directory: cnfcert-tests-verification + + - name: (if on main and upstream) Send chat msg to dev team if failed to run QE tests + if: ${{ failure() && github.ref == 'refs/heads/main' && github.repository_owner == 'test-network-function' }} + env: + COMMIT_SHA: ${{ github.sha }} + JOB_RUN_ID: ${{ github.run_id }} + JOB_RUN_ATTEMPT: ${{ github.run_attempt }} + GITHUB_REPO: https://github.com/test-network-function/cnf-certification-test + run: | + curl -X POST --data "{ + \"text\": \"🚨⚠️ Failed to run intrusive OCP 4.14 QE tests from commit \<$GITHUB_REPO/commit/$COMMIT_SHA|$COMMIT_SHA\>, job ID \<$GITHUB_REPO/actions/runs/$JOB_RUN_ID/attempts/$JOB_RUN_ATTEMPT|$JOB_RUN_ID\> \" + }" -H 'Content-type: application/json; charset=UTF-8' ${{ secrets.QE_NIGHTLY_WEBHOOK }} diff --git a/.github/workflows/qe-ocp-414.yaml b/.github/workflows/qe-ocp-414.yaml new file mode 100644 index 000000000..eed2acbba --- /dev/null +++ b/.github/workflows/qe-ocp-414.yaml @@ -0,0 +1,79 @@ +name: QE OCP 4.14 Testing + +on: + # pull_request: + # branches: [ main ] + workflow_dispatch: + # Schedule a daily cron at midnight UTC + schedule: + - cron: '0 0 * * *' +env: + QE_REPO: test-network-function/cnfcert-tests-verification + +jobs: + qe-ocp-414-testing: + runs-on: qe-ocp-414 + strategy: + fail-fast: false + matrix: + suite: [accesscontrol, affiliatedcertification, manageability, networking, lifecycle, performance, platformalteration, observability, operator] + env: + SHELL: /bin/bash + KUBECONFIG: '/home/labuser2/.kube/config' + PFLT_DOCKERCONFIG: '/home/labuser2/.docker/config' + TEST_TNF_IMAGE_NAME: quay.io/testnetworkfunction/cnf-certification-test + TEST_TNF_IMAGE_TAG: localtest + DOCKER_CONFIG_DIR: '/home/labuser2/.docker' + TNF_CONFIG_DIR: '/home/labuser2/tnf_config' + TNF_REPORT_DIR: '/home/labuser2/tnf_report' + + steps: + - name: Check out code + uses: actions/checkout@v4 + with: + ref: ${{ github.sha }} + + - name: Run initial setup + uses: ./.github/actions/setup + + - name: Preemptively prune docker resources + run: docker system prune -f --volumes + + - name: Build the test image + run: make build-image-local # quay.io/testnetworkfunction/cnf-certification-test:localtest + + - name: Show pods + run: oc get pods -A + + - name: Clone the QE repository + uses: actions/checkout@v4 + with: + repository: ${{ env.QE_REPO }} + path: cnfcert-tests-verification + + - name: Preemptively potential QE namespaces + run: ./scripts/delete-namespaces.sh + working-directory: cnfcert-tests-verification + + - name: Preemptively delete report and config folders + shell: bash + run: | + sudo rm -rf /tmp/tnf_config/ + sudo rm -rf /tmp/tnf_report/ + + # Setup is complete. Time to run the QE tests. + - name: Run the tests + run: FEATURES=${{matrix.suite}} TNF_REPO_PATH=${GITHUB_WORKSPACE} TNF_IMAGE=${{env.TEST_TNF_IMAGE_NAME}} TNF_IMAGE_TAG=${{env.TEST_TNF_IMAGE_TAG}} DISABLE_INTRUSIVE_TESTS=true ENABLE_PARALLEL=true ENABLE_FLAKY_RETRY=true make test-features + working-directory: cnfcert-tests-verification + + - name: (if on main and upstream) Send chat msg to dev team if failed to run QE tests + if: ${{ failure() && github.ref == 'refs/heads/main' && github.repository_owner == 'test-network-function' }} + env: + COMMIT_SHA: ${{ github.sha }} + JOB_RUN_ID: ${{ github.run_id }} + JOB_RUN_ATTEMPT: ${{ github.run_attempt }} + GITHUB_REPO: https://github.com/test-network-function/cnf-certification-test + run: | + curl -X POST --data "{ + \"text\": \"🚨⚠️ Failed to run non-intrusive OCP 4.14 QE tests from commit \<$GITHUB_REPO/commit/$COMMIT_SHA|$COMMIT_SHA\>, job ID \<$GITHUB_REPO/actions/runs/$JOB_RUN_ID/attempts/$JOB_RUN_ATTEMPT|$JOB_RUN_ID\> \" + }" -H 'Content-type: application/json; charset=UTF-8' ${{ secrets.QE_NIGHTLY_WEBHOOK }} diff --git a/.github/workflows/update-rhcos-mapping.yml b/.github/workflows/update-rhcos-mapping.yml index eff535f19..08e62c1d6 100644 --- a/.github/workflows/update-rhcos-mapping.yml +++ b/.github/workflows/update-rhcos-mapping.yml @@ -23,7 +23,7 @@ jobs: - name: Set up Go 1.21 uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.21.4 # This prevents any failures due to the updated rhcos_versions_map file from # making it into the PR phase. diff --git a/Dockerfile b/Dockerfile index 620e3fa0e..63c9ef3a2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,7 +22,7 @@ RUN \ # Install Go binary and set the PATH ENV \ GO_DL_URL=https://golang.org/dl \ - GO_BIN_TAR=go1.21.3.linux-amd64.tar.gz \ + GO_BIN_TAR=go1.21.4.linux-amd64.tar.gz \ GOPATH=/root/go ENV GO_BIN_URL_x86_64=${GO_DL_URL}/${GO_BIN_TAR} RUN \ diff --git a/cnf-certification-test/performance/suite.go b/cnf-certification-test/performance/suite.go index fad47e3d1..ce5bf652f 100644 --- a/cnf-certification-test/performance/suite.go +++ b/cnf-certification-test/performance/suite.go @@ -18,6 +18,7 @@ package performance import ( "fmt" + "slices" "strconv" "strings" @@ -325,13 +326,11 @@ func filterProbeProcesses(allProcesses []*crclient.Process, cut *provider.Contai } // remove all exec probes and their children from the process list for _, p := range allProcesses { - for _, parentProbePid := range execProbeProcesses { - if p.Pid == parentProbePid || p.PPid == parentProbePid { - // skip exec probe processes (child or parent) - continue - } - notExecProbeProcesses = append(notExecProbeProcesses, p) + if slices.Contains(execProbeProcesses, p.Pid) || slices.Contains(execProbeProcesses, p.PPid) { + // this process is part of an exec probe (child or parent), continue + continue } + notExecProbeProcesses = append(notExecProbeProcesses, p) } return notExecProbeProcesses, compliantObjects } diff --git a/cnf-certification-test/platform/operatingsystem/files/rhcos_version_map b/cnf-certification-test/platform/operatingsystem/files/rhcos_version_map index 386a8c385..d1ca1d661 100644 --- a/cnf-certification-test/platform/operatingsystem/files/rhcos_version_map +++ b/cnf-certification-test/platform/operatingsystem/files/rhcos_version_map @@ -188,6 +188,7 @@ 4.12.40 / 412.86.202310170023-0 4.12.41 / 412.86.202310210217-0 4.12.42 / 412.86.202310302215-0 +4.12.43 / 412.86.202311051457-0 4.12.5 / 412.86.202302170236-0 4.12.6 / 412.86.202302282003-0 4.12.7 / 412.86.202303011010-0 diff --git a/cnf-certification-test/suite_test.go b/cnf-certification-test/suite_test.go index 21ed4ade1..2946edeac 100644 --- a/cnf-certification-test/suite_test.go +++ b/cnf-certification-test/suite_test.go @@ -17,16 +17,24 @@ package suite import ( + "bytes" _ "embed" + "encoding/json" "flag" + "fmt" + "io" + rlog "log" + "net/http" "os" "path/filepath" + "strings" "testing" "time" "github.com/onsi/ginkgo/v2" log "github.com/sirupsen/logrus" "github.com/test-network-function/cnf-certification-test/cnf-certification-test/results" + "github.com/test-network-function/cnf-certification-test/pkg/claim" "github.com/test-network-function/cnf-certification-test/pkg/claimhelper" "github.com/test-network-function/cnf-certification-test/pkg/collector" "github.com/test-network-function/cnf-certification-test/pkg/loghelper" @@ -44,6 +52,7 @@ import ( _ "github.com/test-network-function/cnf-certification-test/cnf-certification-test/performance" _ "github.com/test-network-function/cnf-certification-test/cnf-certification-test/platform" _ "github.com/test-network-function/cnf-certification-test/cnf-certification-test/preflight" + "github.com/test-network-function/cnf-certification-test/cnf-certification-test/webserver" "github.com/test-network-function/cnf-certification-test/internal/clientsholder" "github.com/test-network-function/cnf-certification-test/internal/version" "github.com/test-network-function/cnf-certification-test/pkg/configuration" @@ -55,13 +64,16 @@ const ( defaultClaimPath = ".." defaultCliArgValue = "" junitFlagKey = "junit" + serverModeFlag = "serverMode" TNFReportKey = "cnf-certification-test" extraInfoKey = "testsExtraInfo" + defaultServerMode = false ) var ( - claimPath *string - junitPath *string + claimPath *string + junitPath *string + serverMode *bool ) func init() { @@ -69,6 +81,7 @@ func init() { "the path where the claimfile will be output") junitPath = flag.String(junitFlagKey, defaultCliArgValue, "the path for the junit format report") + serverMode = flag.Bool("serverMode", defaultServerMode, "run test with webserver") } // setLogLevel sets the log level for logrus based on the "TNF_LOG_LEVEL" environment variable @@ -99,8 +112,7 @@ func getK8sClientsConfigFileNames() []string { return fileNames } -// TestTest invokes the CNF Certification Test Suite. -func TestTest(t *testing.T) { +func PreRun(t *testing.T) (claimData *claim.Claim, claimRoot *claim.Root) { // When running unit tests, skip the suite if os.Getenv("UNIT_TEST") != "" { t.Skip("Skipping test suite when running unit tests") @@ -119,20 +131,13 @@ func TestTest(t *testing.T) { log.Infof("TNF Version : %v", version.GetGitVersion()) log.Infof("Ginkgo Version : %v", ginkgo.GINKGO_VERSION) log.Infof("Labels filter : %v", ginkgoConfig.LabelFilter) - - // Diagnostic functions will run when no labels are provided. - var diagnosticMode bool - if ginkgoConfig.LabelFilter == "" { - log.Infof("TNF will run in diagnostic mode so no test case will be launched.") - diagnosticMode = true - } - + log.Infof("run test with webserver : %v", *serverMode) // Set clientsholder singleton with the filenames from the env vars. _ = clientsholder.GetClientsHolder(getK8sClientsConfigFileNames()...) // Initialize the claim with the start time, tnf version, etc. - claimRoot := claimhelper.CreateClaimRoot() - claimData := claimRoot.Claim + claimRoot = claimhelper.CreateClaimRoot() + claimData = claimRoot.Claim claimData.Configurations = make(map[string]interface{}) claimData.Nodes = make(map[string]interface{}) claimhelper.IncorporateVersions(claimData) @@ -142,21 +147,42 @@ func TestTest(t *testing.T) { log.Errorf("Configuration node missing because of: %s", err) t.FailNow() } - claimData.Nodes = claimhelper.GenerateNodes() claimhelper.UnmarshalConfigurations(configurations, claimData.Configurations) + return claimData, claimRoot +} + +// TestTest invokes the CNF Certification Test Suite. +func TestTest(t *testing.T) { + ginkgoConfig, _ := ginkgo.GinkgoConfiguration() + if !*serverMode { + claimData, claimRoot := PreRun(t) + var diagnosticMode bool + // Diagnostic functions will run when no labels are provided. + + if ginkgoConfig.LabelFilter == "" { + log.Infof("TNF will run in diagnostic mode so no test case will be launched.") + diagnosticMode = true + } - // initialize abort flag - testhelper.AbortTrigger = "" + // initialize abort flag + testhelper.AbortTrigger = "" - // Run tests specs only if not in diagnostic mode, otherwise all TSs would run. - var env provider.TestEnvironment - if !diagnosticMode { - env.SetNeedsRefresh() - env = provider.GetTestEnvironment() - ginkgo.RunSpecs(t, CnfCertificationTestSuiteName) + // Run tests specs only if not in diagnostic mode, otherwise all TSs would run. + var env provider.TestEnvironment + if !diagnosticMode { + env.SetNeedsRefresh() + env = provider.GetTestEnvironment() + ginkgo.RunSpecs(t, CnfCertificationTestSuiteName) + } + ContinueRun(diagnosticMode, &env, claimData, claimRoot) + } else { + go StartServer() + select {} } +} +func ContinueRun(diagnosticMode bool, env *provider.TestEnvironment, claimData *claim.Claim, claimRoot *claim.Root) { endTime := time.Now() claimData.Metadata.EndTime = endTime.UTC().Format(claimhelper.DateTimeFormatDirective) @@ -183,7 +209,7 @@ func TestTest(t *testing.T) { // Send claim file to the collector if specified by env var if configuration.GetTestParameters().EnableDataCollection { - err = collector.SendClaimFileToCollector(env.CollectorAppEndPoint, claimOutputFile, env.ExecutedBy, env.PartnerName, env.CollectorAppPassword) + err := collector.SendClaimFileToCollector(env.CollectorAppEndPoint, claimOutputFile, env.ExecutedBy, env.PartnerName, env.CollectorAppPassword) if err != nil { log.Errorf("Failed to send post request to the collector: %v", err) } @@ -224,3 +250,103 @@ func TestTest(t *testing.T) { } } } + +func StartServer() { + server := &http.Server{ + Addr: ":8084", // Server address + ReadTimeout: 10 * time.Second, // Maximum duration for reading the entire request + WriteTimeout: 10 * time.Second, // Maximum duration for writing the entire response + IdleTimeout: 120 * time.Second, // Maximum idle duration before closing the connection + } + webserver.HandlereqFunc() + + http.HandleFunc("/runFunction", RunHandler) + + fmt.Println("Server is running on :8084...") + if err := server.ListenAndServe(); err != nil { + panic(err) + } +} + +// Define an HTTP handler that triggers Ginkgo tests +func RunHandler(w http.ResponseWriter, r *http.Request) { + webserver.Buf = bytes.NewBufferString("") + log.SetOutput(webserver.Buf) + rlog.SetOutput(webserver.Buf) + + jsonData := r.FormValue("jsonData") // "jsonData" is the name of the JSON input field + log.Info(jsonData) + var data webserver.RequestedData + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + fmt.Println("Error:", err) + } + var flattenedOptions []string + flattenedOptions = webserver.FlattenData(data.SelectedOptions, flattenedOptions) + + // Get the file from the request + file, handler, err := r.FormFile("kubeConfigPath") // "fileInput" is the name of the file input field + if err != nil { + http.Error(w, "Unable to retrieve file from form", http.StatusBadRequest) + return + } + defer file.Close() + + // Create a new file on the server to store the uploaded content + uploadedFile, err := os.Create(handler.Filename) + if err != nil { + http.Error(w, "Unable to create file for writing", http.StatusInternalServerError) + return + } + defer uploadedFile.Close() + + // Copy the uploaded file's content to the new file + _, err = io.Copy(uploadedFile, file) + if err != nil { + http.Error(w, "Unable to copy file", http.StatusInternalServerError) + return + } + + // Copy the uploaded file to the server file + + os.Setenv("KUBECONFIG", handler.Filename) + log.Infof("KUBECONFIG : %v", handler.Filename) + + log.Infof("Labels filter : %v", flattenedOptions) + t := testing.T{} + claimData, claimRoot := PreRun(&t) + var env provider.TestEnvironment + env.SetNeedsRefresh() + env = provider.GetTestEnvironment() + + // fetch the current config + suiteConfig, reporterConfig := ginkgo.GinkgoConfiguration() + // adjust it + suiteConfig.SkipStrings = []string{"NEVER-RUN"} + reporterConfig.FullTrace = true + reporterConfig.JUnitReport = "cnf-certification-tests_junit.xml" + // pass it in to RunSpecs + suiteConfig.LabelFilter = strings.Join(flattenedOptions, "") + ginkgo.RunSpecs(&t, CnfCertificationTestSuiteName, suiteConfig, reporterConfig) + + ContinueRun(false, &env, claimData, claimRoot) + // Return the result as JSON + response := struct { + Message string `json:"Message"` + }{ + Message: fmt.Sprintf("Sucsses to run %s", strings.Join(flattenedOptions, "")), + } + // Serialize the response data to JSON + jsonResponse, err := json.Marshal(response) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + // Set the Content-Type header to specify that the response is JSON + w.Header().Set("Content-Type", "application/json") + // Write the JSON response to the client + _, err = w.Write(jsonResponse) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} diff --git a/cnf-certification-test/webserver/index.html b/cnf-certification-test/webserver/index.html new file mode 100644 index 000000000..07611612a --- /dev/null +++ b/cnf-certification-test/webserver/index.html @@ -0,0 +1,238 @@ + + + + + + CNF Certification Test + + + + + + + + + + + + +
+ Red Hat +
+ +
+

CNF Certification Test

+ +
+ + +
+ Environment Configuration + + +
+ +
+ TNF Configuration + + + + + + +
+ +
+ Select a Test + + + +
+ + Run Certification Test +
+ Show Log +
+ + +

Logs

+ No Logs Found + Close +
+ + + + + + © 2022 Red Hat, Inc. + + + + + ({ ...acc, + [key]: key in acc ? [acc[key], val] : val + }), {}); + + delete fields.submit; + console.log(fields); + formdata.append("jsonData", JSON.stringify(fields)); + + // Send an HTTP request to the server to run the function + let heading; + let message; + let state = 'success'; + + try { + const data = await fetch('/runFunction', { + method: 'POST', + body: formdata, + }).then(response => { + if (response.ok) { + return response.json(); + } else { + throw new Error(response.statusText); + } + }); + + heading = 'Success'; + message = data.Message; + + console.log(data); + } catch (error) { + console.error(error); + heading = 'Error' + message = error.message; + state = 'danger'; + } finally { + form.elements.submit.disabled = false; + for (const el of form.elements) if (el instanceof HTMLFieldSetElement) el.disabled = false + } + + return { heading, message, state }; + } \ No newline at end of file diff --git a/cnf-certification-test/webserver/toast.js b/cnf-certification-test/webserver/toast.js new file mode 100644 index 000000000..1516826de --- /dev/null +++ b/cnf-certification-test/webserver/toast.js @@ -0,0 +1,53 @@ +import '@rhds/elements/rh-alert/rh-alert.js'; + +export async function toast({ + heading, + message, + state = 'info', + timeout = 8_000, +}) { + await import('@rhds/elements/rh-alert/rh-alert.js'); + const h2 = document.createElement('h2'); + h2.textContent = heading; + h2.slot = 'header'; + const alert = document.createElement('rh-alert'); + alert.setAttribute('aria-live', 'polite'); + alert.dismissable = true; + alert.state = state; + alert.classList.add('toast'); + alert.style.position = 'fixed'; + alert.style.margin = '0'; + alert.style.setProperty('z-index', '1000'); + alert.style.setProperty('inset-inline-end', 'var(--rh-space-xl, 24px)'); + alert.style.setProperty('inset-block-start', 'var(--rh-space-xl, 24px)'); + alert.append(h2); + if (message) { + const p = document.createElement('p'); + p.textContent = message; + alert.append(message); + } + + alert.animate({ translate: ['100% 0', '0 0'] }, { duration: 200 }); + + await Promise.all(Array.from(document.querySelectorAll('rh-alert.toast'), toast => + // TODO: handle more than 2 toasts + toast.animate({ + translate: [ + '0 auto', + '0 calc(100% + 20px)', + ], + }, { + duration: 200, + composite: 'accumulate', + rangeEnd: '100%', + fill: 'forwards', + }).finished)); + + setTimeout(() => { + if (alert.isConnected) { + alert.remove(); + } + }, timeout); + + document.body.append(alert); +} \ No newline at end of file diff --git a/cnf-certification-test/webserver/webserver_function.go b/cnf-certification-test/webserver/webserver_function.go new file mode 100644 index 000000000..1069432f6 --- /dev/null +++ b/cnf-certification-test/webserver/webserver_function.go @@ -0,0 +1,132 @@ +package webserver + +import ( + "bufio" + "bytes" + _ "embed" + "fmt" + "net/http" + + "github.com/gorilla/websocket" + "github.com/robert-nix/ansihtml" + "github.com/sirupsen/logrus" +) + +//go:embed index.html +var indexHTML []byte + +//go:embed submit.js +var submit []byte + +//go:embed logs.js +var logs []byte + +//go:embed toast.js +var toast []byte +var Buf *bytes.Buffer + +var upgrader = websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + return true + }, +} + +func logStreamHandler(w http.ResponseWriter, r *http.Request) { + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + logrus.Printf("WebSocket upgrade error: %v", err) + return + } + defer conn.Close() + // Create a scanner to read the log file line by line + for { + scanner := bufio.NewScanner(Buf) + for scanner.Scan() { + line := scanner.Bytes() + line = append(ansihtml.ConvertToHTML(line), []byte("
")...) + + // Send each log line to the client + if err := conn.WriteMessage(websocket.TextMessage, line); err != nil { + fmt.Println(err) + return + } + } + if err := scanner.Err(); err != nil { + logrus.Printf("Error reading log file: %v", err) + return + } + } +} + +type RequestedData struct { + SelectedOptions interface{} `json:"selectedOptions"` +} +type ResponseData struct { + Message string `json:"message"` +} + +func FlattenData(data interface{}, result []string) []string { + switch v := data.(type) { + case string: + result = append(result, v) + case []interface{}: + for _, item := range v { + result = FlattenData(item, result) + } + case map[string]interface{}: + for key, item := range v { + if key == "selectedOptions" { + result = FlattenData(item, result) + } + result = FlattenData(item, result) + } + } + return result +} +func HandlereqFunc() { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + // Set the content type to "text/html". + w.Header().Set("Content-Type", "text/html") + // Write the embedded HTML content to the response. + _, err := w.Write(indexHTML) + if err != nil { + http.Error(w, "Failed to write response", http.StatusInternalServerError) + return + } + }) + + http.HandleFunc("/submit.js", func(w http.ResponseWriter, r *http.Request) { + // Set the content type to "application/javascript". + w.Header().Set("Content-Type", "application/javascript") + // Write the embedded JavaScript content to the response. + _, err := w.Write(submit) + if err != nil { + http.Error(w, "Failed to write response", http.StatusInternalServerError) + return + } + }) + + http.HandleFunc("/logs.js", func(w http.ResponseWriter, r *http.Request) { + // Set the content type to "application/javascript". + w.Header().Set("Content-Type", "application/javascript") + // Write the embedded JavaScript content to the response. + _, err := w.Write(logs) + if err != nil { + http.Error(w, "Failed to write response", http.StatusInternalServerError) + return + } + }) + + http.HandleFunc("/toast.js", func(w http.ResponseWriter, r *http.Request) { + // Set the content type to "application/javascript". + w.Header().Set("Content-Type", "application/javascript") + // Write the embedded JavaScript content to the response. + _, err := w.Write(toast) + if err != nil { + http.Error(w, "Failed to write response", http.StatusInternalServerError) + return + } + }) + // Serve the static HTML file + http.HandleFunc("/logstream", logStreamHandler) +} diff --git a/cnf-certification-test/webserver/webserver_function_test.go b/cnf-certification-test/webserver/webserver_function_test.go new file mode 100644 index 000000000..c5303c20a --- /dev/null +++ b/cnf-certification-test/webserver/webserver_function_test.go @@ -0,0 +1 @@ +package webserver diff --git a/docs/runtime-env.md b/docs/runtime-env.md index 32c4aa4c0..1d2ecfca6 100644 --- a/docs/runtime-env.md +++ b/docs/runtime-env.md @@ -68,4 +68,4 @@ export TNF_PARTNER_REPO=registry.dfwt5g.lab:5000/testnetworkfunction ``` Note that you can also specify the debug pod image to use with `SUPPORT_IMAGE` -environment variable, default to `debug-partner:4.5.4`. +environment variable, default to `debug-partner:4.5.5`. diff --git a/docs/test-container.md b/docs/test-container.md index 86bc36de4..a9fb044a4 100644 --- a/docs/test-container.md +++ b/docs/test-container.md @@ -112,8 +112,8 @@ Two env vars allow to control the web artifacts and the the new tar.gz file gene ### Build locally ```shell -podman build -t cnf-certification-test:v4.5.4 \ - --build-arg TNF_VERSION=v4.5.4 \ +podman build -t cnf-certification-test:v4.5.5 \ + --build-arg TNF_VERSION=v4.5.5 \ ``` * `TNF_VERSION` value is set to a branch, a tag, or a hash of a commit that will be installed into the image @@ -125,8 +125,8 @@ The unofficial source could be a fork of the TNF repository. Use the `TNF_SRC_URL` build argument to override the URL to a source repository. ```shell -podman build -t cnf-certification-test:v4.5.4 \ - --build-arg TNF_VERSION=v4.5.4 \ +podman build -t cnf-certification-test:v4.5.5 \ + --build-arg TNF_VERSION=v4.5.5 \ --build-arg TNF_SRC_URL=https://github.com/test-network-function/cnf-certification-test . ``` @@ -135,7 +135,7 @@ podman build -t cnf-certification-test:v4.5.4 \ Specify the custom TNF image using the `-i` parameter. ```shell -./run-tnf-container.sh -i cnf-certification-test:v4.5.4 +./run-tnf-container.sh -i cnf-certification-test:v4.5.5 -t ~/tnf/config -o ~/tnf/output -l "networking,access-control" ``` diff --git a/go.mod b/go.mod index 32d9c88f8..a5fe1ad6f 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/test-network-function/cnf-certification-test -go 1.21.3 +go 1.21.4 require ( github.com/Masterminds/semver/v3 v3.2.1 @@ -24,7 +24,7 @@ require ( github.com/operator-framework/api v0.19.0 github.com/operator-framework/operator-lifecycle-manager v0.20.0 github.com/pkg/errors v0.9.1 // indirect - helm.sh/helm/v3 v3.13.1 + helm.sh/helm/v3 v3.13.2 k8s.io/api v0.28.3 k8s.io/apimachinery v0.28.3 k8s.io/klog/v2 v2.100.1 // indirect @@ -211,10 +211,12 @@ require ( github.com/fatih/color v1.16.0 github.com/go-logr/logr v1.3.0 github.com/go-logr/stdr v1.2.2 + github.com/gorilla/websocket v1.5.1 github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0 github.com/manifoldco/promptui v0.9.0 github.com/openshift/machine-config-operator v0.0.1-0.20230515070935-49f32d46538e github.com/redhat-openshift-ecosystem/openshift-preflight v0.0.0-20231018165107-f04b78186455 + github.com/robert-nix/ansihtml v1.0.1 github.com/test-network-function/oct v0.0.3 github.com/test-network-function/privileged-daemonset v1.0.14 gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index f4b2c7deb..fdb06e209 100644 --- a/go.sum +++ b/go.sum @@ -315,6 +315,8 @@ github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= @@ -517,6 +519,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/robert-nix/ansihtml v1.0.1 h1:VTiyQ6/+AxSJoSSLsMecnkh8i0ZqOEdiRl/odOc64fc= +github.com/robert-nix/ansihtml v1.0.1/go.mod h1:CJwclxYaTPc2RfcxtanEACsYuTksh4yDXcNeHHKZINE= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= @@ -1001,8 +1005,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -helm.sh/helm/v3 v3.13.1 h1:DG+XLGzBJeZvMLlMbm6bPDLV1dGaVW9eZsDoUd1/LM0= -helm.sh/helm/v3 v3.13.1/go.mod h1:TdQRMiq46CSWcc68Hb0uVhvAWusaN90YwAV54cz6JzU= +helm.sh/helm/v3 v3.13.2 h1:IcO9NgmmpetJODLZhR3f3q+6zzyXVKlRizKFwbi7K8w= +helm.sh/helm/v3 v3.13.2/go.mod h1:GIHDwZggaTGbedevTlrQ6DB++LBN6yuQdeGj0HNaDx0= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/configuration/configuration.go b/pkg/configuration/configuration.go index d70396020..aaec22a47 100644 --- a/pkg/configuration/configuration.go +++ b/pkg/configuration/configuration.go @@ -103,5 +103,5 @@ type TestParameters struct { PfltDockerconfig string `split_words:"true" envconfig:"PFLT_DOCKERCONFIG"` IncludeWebFilesInOutputFolder bool `split_words:"true" default:"false"` OmitArtifactsZipFile bool `split_words:"true" default:"false"` - EnableDataCollection bool `split_words:"true" default:"false"` + EnableDataCollection bool `split_words:"true" envconfig:"ENABLE_DATA_COLLECTION" default:"false"` } diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index b63695d0e..fb1a8e492 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -60,7 +60,7 @@ const ( cscosName = "CentOS Stream CoreOS" rhelName = "Red Hat Enterprise Linux" tnfPartnerRepoDef = "quay.io/testnetworkfunction" - supportImageDef = "debug-partner:4.5.4" + supportImageDef = "debug-partner:4.5.5" ) // Node's roles labels. Node is role R if it has **any** of the labels of each list. diff --git a/pkg/provider/provider_test.go b/pkg/provider/provider_test.go index 1662ba58f..392369246 100644 --- a/pkg/provider/provider_test.go +++ b/pkg/provider/provider_test.go @@ -788,7 +788,7 @@ func TestBuildImageWithVersion(t *testing.T) { { repoVar: "", supportImageVar: "", - expectedOutput: "quay.io/testnetworkfunction/debug-partner:4.5.4", + expectedOutput: "quay.io/testnetworkfunction/debug-partner:4.5.5", }, } diff --git a/run-cnf-suites.sh b/run-cnf-suites.sh index 7e7a0465d..625be8052 100755 --- a/run-cnf-suites.sh +++ b/run-cnf-suites.sh @@ -8,16 +8,17 @@ set -x export OUTPUT_LOC="$PWD/cnf-certification-test" usage() { - echo "$0 [-o OUTPUT_LOC] [-l LABEL...]" + echo "$0 [-o OUTPUT_LOC] [-l LABEL...] [-s run from webpage]" echo "Call the script and list the test suites to run" echo " e.g." echo " $0 [ARGS] -l \"access-control,lifecycle\"" echo " will run the access-control and lifecycle suites" echo " $0 [ARGS] -l all will run all the tests" + echo " $0 [ARGS] -s true will run the test from server" echo "" echo "Allowed suites are listed in the README." echo "" - echo "The specs can be listed with $0 -L|--list [-l LABEL...]" + echo "The specs can be listed with $0 -L|--list [-l LABEL...] [-s run from webpage]" } usage_error() { @@ -28,6 +29,7 @@ usage_error() { TIMEOUT=24h0m0s LABEL='' LIST=false +SERVER_RUN=false BASEDIR=$(dirname "$(realpath "$0")") # Parse args beginning with "-". @@ -49,6 +51,9 @@ while [[ $1 == -* ]]; do exit 1 fi ;; + -s) + SERVER_RUN=true + ;; -l | --label) while (("$#" >= 2)) && ! [[ $2 = --* ]] && ! [[ $2 = -* ]]; do LABEL="$LABEL $2" @@ -91,6 +96,10 @@ GINKGO_ARGS="\ -test.v\ " +if [ "$SERVER_RUN" = "true" ]; then + GINKGO_ARGS="$GINKGO_ARGS -serverMode" +fi + if [[ $LABEL == "all" ]]; then LABEL='common,extended,faredge,telco' fi @@ -100,7 +109,7 @@ echo "Report will be output to '$OUTPUT_LOC'" echo "ginkgo arguments '${GINKGO_ARGS}'" LABEL_STRING='' -if [ -z "$LABEL" ]; then +if [ -z "$LABEL" ] && { [ -z "$SERVER_RUN" ] || [ "$SERVER_RUN" == "false" ]; }; then echo "No test label (-l) was set, so only diagnostic functions will run." else LABEL_STRING="-ginkgo.label-filter=${LABEL}" diff --git a/version.json b/version.json index 28ae5575f..3e259f195 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "partner_tag": "v4.5.4", + "partner_tag": "v4.5.5", "claimFormat": "v0.1.0", - "parserTag": "v0.1.3" + "parserTag": "v0.1.4" }