Skip to content

Commit 50de070

Browse files
Implement OpenShift Tests Extension (OTE) framework for INFW
Integrate the OpenShift Test Extension framework to enable external test contribution to openshift-tests suites with proper OTE library usage. Implementation details: - Use OTE library API instead of custom implementation: * extension.NewExtension() to create extension registry * ginkgo.BuildExtensionTestSpecsFromOpenShiftGinkgoSuite() for test specs * cmd.DefaultExtensionCommands() for standard CLI commands (info, list, run-test) - Define test suites with CEL qualifiers: * openshift/ingress-node-firewall/conformance/parallel (excludes Serial/Slow) * openshift/ingress-node-firewall/conformance/serial (Serial tests only) * openshift/ingress-node-firewall/optional/slow (Slow tests only) * openshift/ingress-node-firewall/all (all INFW tests) - Build statically linked binary with CGO_ENABLED=0 and GO_COMPLIANCE_POLICY="exempt_all" Test implementation: - Add LEVEL0 test case: OCP-61481 - Ingress Node Firewall Operator Installation - Add E2E test utilities: OCClient for oc command execution - Add Kubernetes helper functions for namespace, pod, deployment operations - Implement test using Ginkgo v2 and Gomega assertion framework Build and packaging: - test/Makefile: Build target for static test binary with ART compliance - Dockerfile.openshift: Include compressed test binary in container image - Makefile: Top-level target to build test binary Validation: - Binary: 40MB, statically linked (no dynamic dependencies) - Commands functional: info, list tests, list suites, run-test - Conforms to OTE framework requirements and OpenShift operator patterns Signed-off-by: Anurag Saxena <anusaxen@redhat.com> Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 111fb7a commit 50de070

8 files changed

Lines changed: 407 additions & 0 deletions

File tree

Dockerfile.openshift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,19 @@ COPY controllers/ controllers/
1414
COPY pkg/ pkg/
1515
COPY vendor/ vendor/
1616
COPY bindata/manifests/ bindata/manifests/
17+
COPY test/ test/
1718

1819
# Build
1920
RUN CGO_ENABLED=0 GO111MODULE=on go build -a -mod=vendor -o manager main.go
2021

22+
# Build extended tests
23+
RUN make -C test build-e2e-tests && \
24+
gzip test/bin/ingress-node-firewall-tests
25+
2126
FROM registry.ci.openshift.org/ocp/4.22:base-rhel9
2227
WORKDIR /
2328
COPY --from=builder /workspace/manager .
2429
COPY --from=builder /workspace/bindata/manifests /bindata/manifests
30+
COPY --from=builder /workspace/test/bin/ingress-node-firewall-tests.gz /usr/bin/
2531

2632
ENTRYPOINT ["/manager"]

Makefile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,3 +480,12 @@ podman-build-daemon: ## Build the daemon image with podman. To change location,
480480
.PHONY: podman-push-daemon
481481
podman-push-daemon: ## Push the daemon image with docker. To change location, specify DAEMON_IMG=<image>.
482482
podman push ${DAEMON_IMG}
483+
484+
##@ Extended Tests (OTE)
485+
.PHONY: build-e2e-tests
486+
build-e2e-tests: ## Build the extended e2e test binary for OpenShift
487+
$(MAKE) -C test build-e2e-tests
488+
489+
.PHONY: clean-e2e-tests
490+
clean-e2e-tests: ## Clean the extended e2e test artifacts
491+
$(MAKE) -C test clean

test/Makefile

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# test/Makefile - Build targets for ingress-node-firewall extended tests
2+
3+
SHELL := /bin/bash
4+
5+
# Binary name
6+
BINARY_NAME := ingress-node-firewall-tests
7+
8+
# Build directory
9+
BUILD_DIR := bin
10+
BINARY_PATH := $(BUILD_DIR)/$(BINARY_NAME)
11+
12+
# Go build flags
13+
GO := go
14+
GOFLAGS ?=
15+
LDFLAGS := -w -s
16+
17+
# Version information
18+
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "unknown")
19+
GIT_COMMIT ?= $(shell git rev-parse HEAD 2>/dev/null || echo "unknown")
20+
BUILD_DATE ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
21+
22+
# LDFLAGS with version info
23+
LDFLAGS += -X github.com/openshift/ingress-node-firewall/test/version.Version=$(VERSION)
24+
LDFLAGS += -X github.com/openshift/ingress-node-firewall/test/version.GitCommit=$(GIT_COMMIT)
25+
LDFLAGS += -X github.com/openshift/ingress-node-firewall/test/version.BuildDate=$(BUILD_DATE)
26+
27+
.PHONY: all
28+
all: build-e2e-tests
29+
30+
.PHONY: build-e2e-tests
31+
build-e2e-tests: ## Build the extended e2e test binary (static, ART compliant)
32+
@echo "Building $(BINARY_NAME)..."
33+
@mkdir -p $(BUILD_DIR)
34+
CGO_ENABLED=0 GO_COMPLIANCE_POLICY="exempt_all" $(GO) build \
35+
-a -mod=vendor \
36+
-ldflags "$(LDFLAGS)" \
37+
-o $(BINARY_PATH) ./cmd/main.go
38+
@echo "Built $(BINARY_PATH)"
39+
40+
.PHONY: clean
41+
clean: ## Clean build artifacts
42+
@echo "Cleaning test build artifacts..."
43+
@rm -rf $(BUILD_DIR)
44+
45+
.PHONY: help
46+
help: ## Display this help
47+
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
38.4 MB
Binary file not shown.

test/cmd/main.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
"github.com/spf13/cobra"
8+
9+
// OTE library imports
10+
"github.com/openshift-eng/openshift-tests-extension/pkg/cmd"
11+
e "github.com/openshift-eng/openshift-tests-extension/pkg/extension"
12+
g "github.com/openshift-eng/openshift-tests-extension/pkg/ginkgo"
13+
14+
// Import test suites (blank import to register Ginkgo tests)
15+
_ "github.com/openshift/ingress-node-firewall/test/e2e/operator"
16+
)
17+
18+
func main() {
19+
// 1. Create registry and extension
20+
registry := e.NewRegistry()
21+
ext := e.NewExtension("openshift", "payload", "ingress-node-firewall")
22+
23+
// 2. Add test suites with qualifiers
24+
ext.AddSuite(e.Suite{
25+
Name: "openshift/ingress-node-firewall/conformance/parallel",
26+
Parents: []string{"openshift/conformance/parallel"},
27+
Qualifiers: []string{`[sig-network] INFW && !labels.exists(l, l=="Serial") && !labels.exists(l, l=="Slow")`},
28+
})
29+
30+
ext.AddSuite(e.Suite{
31+
Name: "openshift/ingress-node-firewall/conformance/serial",
32+
Parents: []string{"openshift/conformance/serial"},
33+
Qualifiers: []string{`[sig-network] INFW && labels.exists(l, l=="Serial")`},
34+
})
35+
36+
ext.AddSuite(e.Suite{
37+
Name: "openshift/ingress-node-firewall/optional/slow",
38+
Parents: []string{},
39+
Qualifiers: []string{`[sig-network] INFW && labels.exists(l, l=="Slow")`},
40+
})
41+
42+
ext.AddSuite(e.Suite{
43+
Name: "openshift/ingress-node-firewall/all",
44+
Parents: []string{},
45+
Qualifiers: []string{`[sig-network] INFW`},
46+
})
47+
48+
// 3. Build test specs from Ginkgo suite
49+
specs, err := g.BuildExtensionTestSpecsFromOpenShiftGinkgoSuite()
50+
if err != nil {
51+
fmt.Fprintf(os.Stderr, "error: couldn't build extension test specs from ginkgo: %v\n", err)
52+
os.Exit(1)
53+
}
54+
55+
// 4. Add specs to extension and register
56+
ext.AddSpecs(specs)
57+
registry.Register(ext)
58+
59+
// 5. Create root command with OTE commands
60+
rootCmd := &cobra.Command{
61+
Use: "ingress-node-firewall-tests",
62+
Short: "OpenShift extended tests for ingress-node-firewall",
63+
Long: "This binary contains extended e2e tests for ingress-node-firewall operator",
64+
}
65+
66+
rootCmd.AddCommand(cmd.DefaultExtensionCommands(registry)...)
67+
68+
if err := rootCmd.Execute(); err != nil {
69+
os.Exit(1)
70+
}
71+
}

test/e2e/cli.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package e2e
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"fmt"
7+
"os/exec"
8+
"strings"
9+
)
10+
11+
// OCClient provides helper methods for executing oc commands
12+
type OCClient struct {
13+
kubeconfig string
14+
}
15+
16+
// NewOCClient creates a new OCClient
17+
func NewOCClient(kubeconfig string) *OCClient {
18+
if kubeconfig == "" {
19+
kubeconfig = GetKubeconfig()
20+
}
21+
return &OCClient{
22+
kubeconfig: kubeconfig,
23+
}
24+
}
25+
26+
// Run executes an oc command and returns the output
27+
func (c *OCClient) Run(ctx context.Context, args ...string) (string, error) {
28+
cmdArgs := append([]string{"--kubeconfig", c.kubeconfig}, args...)
29+
cmd := exec.CommandContext(ctx, "oc", cmdArgs...)
30+
31+
var stdout, stderr bytes.Buffer
32+
cmd.Stdout = &stdout
33+
cmd.Stderr = &stderr
34+
35+
err := cmd.Run()
36+
if err != nil {
37+
return "", fmt.Errorf("command failed: %v, stderr: %s", err, stderr.String())
38+
}
39+
40+
return strings.TrimSpace(stdout.String()), nil
41+
}
42+
43+
// Apply applies a resource from a file
44+
func (c *OCClient) Apply(ctx context.Context, file string) error {
45+
_, err := c.Run(ctx, "apply", "-f", file)
46+
return err
47+
}
48+
49+
// Delete deletes a resource
50+
func (c *OCClient) Delete(ctx context.Context, resourceType, name, namespace string) error {
51+
args := []string{"delete", resourceType, name}
52+
if namespace != "" {
53+
args = append(args, "-n", namespace)
54+
}
55+
_, err := c.Run(ctx, args...)
56+
return err
57+
}
58+
59+
// Get gets a resource
60+
func (c *OCClient) Get(ctx context.Context, resourceType, name, namespace string) (string, error) {
61+
args := []string{"get", resourceType}
62+
if name != "" {
63+
args = append(args, name)
64+
}
65+
if namespace != "" {
66+
args = append(args, "-n", namespace)
67+
}
68+
return c.Run(ctx, args...)
69+
}

test/e2e/operator/operator.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package operator
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
"time"
8+
9+
g "github.com/onsi/ginkgo/v2"
10+
o "github.com/onsi/gomega"
11+
12+
e2e "github.com/openshift/ingress-node-firewall/test/e2e"
13+
)
14+
15+
var _ = g.Describe("[sig-network] INFW", func() {
16+
defer g.GinkgoRecover()
17+
18+
var (
19+
oc *e2e.OCClient
20+
ctx context.Context
21+
cancel context.CancelFunc
22+
opNamespace = "openshift-ingress-node-firewall"
23+
)
24+
25+
g.BeforeEach(func() {
26+
ctx, cancel = context.WithTimeout(context.Background(), 10*time.Minute)
27+
oc = e2e.NewOCClient("")
28+
})
29+
30+
g.AfterEach(func() {
31+
if cancel != nil {
32+
cancel()
33+
}
34+
})
35+
36+
// OCP-61481 - Ingress Node Firewall Operator Installation
37+
g.It("Author:anusaxen-High-61481-[LEVEL0][OTP]-StagerunBoth-Ingress Node Firewall Operator Installation [apigroup:ingressnodefirewall.openshift.io]", func() {
38+
g.By("Checking Ingress Node Firewall operator installation")
39+
40+
// Check that the operator namespace exists
41+
output, err := oc.Get(ctx, "namespace", opNamespace, "")
42+
o.Expect(err).NotTo(o.HaveOccurred(), "Operator namespace should exist")
43+
o.Expect(output).To(o.ContainSubstring(opNamespace), "Namespace output should contain the operator namespace")
44+
45+
g.By("Verifying CRDs are installed")
46+
crdOutput, err := oc.Run(ctx, "get", "crd")
47+
o.Expect(err).NotTo(o.HaveOccurred(), "Should be able to list CRDs")
48+
49+
expectedCRDs := []string{
50+
"ingressnodefirewallconfigs.ingressnodefirewall.openshift.io",
51+
"ingressnodefirewallnodestates.ingressnodefirewall.openshift.io",
52+
"ingressnodefirewalls.ingressnodefirewall.openshift.io",
53+
}
54+
55+
for _, crd := range expectedCRDs {
56+
o.Expect(strings.Contains(crdOutput, crd)).To(o.BeTrue(),
57+
"CRD %s should be installed", crd)
58+
}
59+
60+
g.By("Verifying operator deployment is running")
61+
// Check that the operator deployment exists and is ready
62+
deploymentOutput, err := oc.Run(ctx, "get", "deployment", "-n", opNamespace, "-o=jsonpath={.items[*].metadata.name}")
63+
o.Expect(err).NotTo(o.HaveOccurred(), "Should be able to list deployments in operator namespace")
64+
o.Expect(deploymentOutput).NotTo(o.BeEmpty(), "There should be at least one deployment in the operator namespace")
65+
66+
// Wait for the operator deployment to be ready
67+
deploymentName := "ingress-node-firewall-controller-manager"
68+
_, err = oc.Run(ctx, "wait", "deployment/"+deploymentName, "-n", opNamespace, "--for=condition=Available", "--timeout=5m")
69+
o.Expect(err).NotTo(o.HaveOccurred(), "Operator deployment should be available")
70+
71+
g.By("SUCCESS - Ingress Node Firewall operator and CRDs installed")
72+
fmt.Println("Operator install and CRDs check successful!")
73+
})
74+
})

0 commit comments

Comments
 (0)