Skip to content
Merged
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
76 changes: 76 additions & 0 deletions .github/workflows/e2e-tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: E2E Tests

on:
push:
branches:
- main
- master
pull_request:
branches:
- main
- master
workflow_dispatch: # Allow manual trigger

jobs:
e2e-tests:
name: Run E2E Tests
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
cache: true

- name: Install dependencies
run: |
# Install kubebuilder for CRD generation
curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH)
chmod +x kubebuilder && sudo mv kubebuilder /usr/local/bin/

- name: Create kind cluster
uses: helm/kind-action@v1
with:
cluster_name: kind
config: scripts/kind-config-ci.yaml
wait: 300s

- name: Verify cluster
run: |
kubectl cluster-info
kubectl get nodes
kubectl get pods -A

- name: Build operator image
run: |
docker build -t example.com/vector-operator:v0.0.1 .

- name: Load image into kind
run: |
kind load docker-image example.com/vector-operator:v0.0.1 --name kind

- name: Run E2E tests
run: make test-e2e
env:
KUBECONFIG: /home/runner/.kube/config

- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-results-${{ github.run_number }}
path: test/e2e/results/
retention-days: 7

- name: Publish test results
if: always()
uses: EnricoMi/publish-unit-test-result-action@v2
with:
files: test/e2e/results/run-*/reports/junit-report.xml
check_name: E2E Test Results
comment_mode: off
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ testbin/*
__debug_bin

vendor

# E2E test results and artifacts
test/e2e/results/
84 changes: 80 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,79 @@ vet: ## Run go vet against code.
test: manifests generate fmt vet envtest ## Run tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out

# Utilize Kind or modify the e2e tests to load the image locally, enabling compatibility with other vendors.
.PHONY: test-e2e # Run the e2e tests against a Kind k8s instance that is spun up.
test-e2e:
go test ./test/e2e/ -v -ginkgo.v
# E2E test configuration
E2E_FAIL_FAST ?= false
E2E_RUN_DESCRIPTION ?=
E2E_LABEL_FILTER ?=
NAMESPACE ?= vector

.PHONY: test-e2e # Run e2e tests with comprehensive reporting (JUnit XML + JSON + logs + artifacts)
test-e2e: ginkgo
@TIMESTAMP=$$(date +%Y-%m-%d-%H%M%S); \
RUN_DIR="test/e2e/results/run-$$TIMESTAMP"; \
echo "==> Running e2e tests..."; \
echo "==> Results will be saved to: $$RUN_DIR"; \
mkdir -p "$$RUN_DIR/reports"; \
export E2E_ARTIFACTS_DIR="$$RUN_DIR/artifacts"; \
export E2E_ARTIFACTS_ENABLED=true; \
export E2E_GIT_COMMIT=$$(git rev-parse HEAD 2>/dev/null || echo "unknown"); \
export E2E_GIT_BRANCH=$$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown"); \
export E2E_GIT_DIRTY=$$(git diff --quiet 2>/dev/null || echo "dirty"; git diff --cached --quiet 2>/dev/null || echo "staged"); \
export E2E_RUN_DESCRIPTION="$(E2E_RUN_DESCRIPTION)"; \
echo "==> Git info: commit=$$E2E_GIT_COMMIT branch=$$E2E_GIT_BRANCH dirty=$$E2E_GIT_DIRTY"; \
if [ -n "$$E2E_RUN_DESCRIPTION" ]; then \
echo "==> Run description: $$E2E_RUN_DESCRIPTION"; \
fi; \
GINKGO_FLAGS="-v -timeout=30m"; \
if [ "$(E2E_FAIL_FAST)" = "true" ]; then \
echo "==> Fail-fast mode enabled (stop on first failure)"; \
GINKGO_FLAGS="$$GINKGO_FLAGS --fail-fast"; \
fi; \
if [ -n "$(E2E_LABEL_FILTER)" ]; then \
echo "==> Label filter: $(E2E_LABEL_FILTER)"; \
GINKGO_FLAGS="$$GINKGO_FLAGS --label-filter=\"$(E2E_LABEL_FILTER)\""; \
fi; \
cd test/e2e && $(GINKGO) $$GINKGO_FLAGS \
--junit-report="../../$$RUN_DIR/reports/junit-report.xml" \
--json-report="../../$$RUN_DIR/reports/report.json" \
| tee "../../$$RUN_DIR/reports/test-output.log"; \
EXIT_CODE=$$?; \
echo ""; \
echo "==> Test run complete!"; \
echo "==> All results in one place: $$RUN_DIR"; \
echo " Reports:"; \
echo " - JUnit XML: $$RUN_DIR/reports/junit-report.xml"; \
echo " - JSON: $$RUN_DIR/reports/report.json"; \
echo " - Logs: $$RUN_DIR/reports/test-output.log"; \
if [ -d "$$RUN_DIR/artifacts" ] && [ "$$(find $$RUN_DIR/artifacts -mindepth 1 -maxdepth 1 2>/dev/null | wc -l)" -gt 1 ]; then \
echo " Artifacts: $$RUN_DIR/artifacts/ (collected for failed tests)"; \
else \
echo " Artifacts: None (all tests passed)"; \
fi; \
echo ""; \
echo "Quick commands:"; \
echo " View summary: cat $$RUN_DIR/artifacts/metadata.json 2>/dev/null || echo 'All tests passed'"; \
echo " View failures: grep -A 5 'FAILED' $$RUN_DIR/reports/test-output.log 2>/dev/null || echo 'No failures'"; \
exit $$EXIT_CODE

.PHONY: test-report
test-report: ## Generate interactive HTML report from e2e test results
@echo "==> Generating test report..."
@cd test/e2e/results && python3 ../scripts/generate_report.py
@echo "==> Report generated: test/e2e/results/test_results_report.html"

.PHONY: deploy-helm-e2e
deploy-helm-e2e: manifests ## Deploy operator using Helm for e2e tests (use IMG and NAMESPACE variables)
@echo "==> Installing CRDs..."
$(KUBECTL) apply -f config/crd/bases
@echo "==> Creating namespace $(NAMESPACE)..."
$(KUBECTL) create namespace $(NAMESPACE) || true
@echo "==> Deploying operator via Helm to namespace $(NAMESPACE)..."
helm upgrade --install vector-operator ./helm/charts/vector-operator \
--namespace $(NAMESPACE) \
--set image.repository=$$(echo $(IMG) | cut -d: -f1) \
--set image.tag=$$(echo $(IMG) | cut -d: -f2) \
--wait --timeout 5m

.PHONY: lint
lint: golangci-lint ## Run golangci-lint linter
Expand Down Expand Up @@ -160,12 +229,14 @@ KUSTOMIZE ?= $(LOCALBIN)/kustomize
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
ENVTEST ?= $(LOCALBIN)/setup-envtest
GOLANGCI_LINT = $(LOCALBIN)/golangci-lint
GINKGO ?= $(LOCALBIN)/ginkgo

## Tool Versions
KUSTOMIZE_VERSION ?= v5.4.3
CONTROLLER_TOOLS_VERSION ?= v0.16.1
ENVTEST_VERSION ?= release-0.19
GOLANGCI_LINT_VERSION ?= v1.64.8
GINKGO_VERSION ?= v2.20.2

.PHONY: kustomize
kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary.
Expand All @@ -187,6 +258,11 @@ golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
$(GOLANGCI_LINT): $(LOCALBIN)
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))

.PHONY: ginkgo
ginkgo: $(GINKGO) ## Download ginkgo locally if necessary.
$(GINKGO): $(LOCALBIN)
$(call go-install-tool,$(GINKGO),github.com/onsi/ginkgo/v2/ginkgo,$(GINKGO_VERSION))

# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
# $1 - target path with name of binary
# $2 - package url which can be installed
Expand Down
174 changes: 174 additions & 0 deletions docs/ci-cd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# CI/CD Documentation

## GitHub Actions Workflows

### E2E Tests Workflow

The E2E tests workflow automatically runs end-to-end tests for every pull request and push to the main branch.

**Workflow File:** `.github/workflows/e2e-tests.yaml`

#### Triggers

- **Push to main/master**: Runs on every push to the main or master branch
- **Pull Requests**: Runs on PRs targeting main/master
- **Manual**: Can be triggered manually via GitHub Actions UI (workflow_dispatch)

#### Workflow Steps

1. **Checkout code**: Clones the repository
2. **Set up Go**: Installs Go using version from `go.mod`
3. **Install dependencies**: Installs kubebuilder for CRD generation
4. **Create kind cluster**: Creates a single-node Kubernetes cluster using `scripts/kind-config-ci.yaml`
5. **Verify cluster**: Checks cluster health and connectivity
6. **Build image**: Builds operator Docker image
7. **Load image**: Loads image into the kind cluster
8. **Run E2E tests**: Executes `make test-e2e` with JUnit reporting
9. **Upload test results**: Saves test results as artifacts (retained for 7 days)
10. **Publish test results**: Publishes JUnit results as GitHub check

#### Configuration

**Kind Cluster (CI):** `scripts/kind-config-ci.yaml`
- Single control-plane node
- Control-plane allows scheduling workloads for faster execution
- Port mappings for ingress (80, 443)

#### Test Reports

Test results are available in multiple formats:

1. **JUnit XML**: `test/e2e/results/run-*/reports/junit-report.xml`
- Machine-readable format
- Used by GitHub Actions to display test results

2. **JSON Report**: `test/e2e/results/run-*/reports/report.json`
- Detailed test execution data
- Suitable for programmatic analysis

3. **Plain text log**: `test/e2e/results/run-*/reports/test-output.log`
- Human-readable test output
- Contains full test execution logs

4. **HTML Report**: Generated via `make test-report`
- Interactive visualization
- Requires Python 3

#### Artifacts

**Test Results** (7 days retention):
- JUnit XML report
- JSON report
- Plain text test output
- Failure artifacts (pod logs, events, resource states)
- Available for all workflow runs

#### Viewing Results

1. **GitHub UI**:
- Go to Actions tab → E2E Tests workflow
- Click on a specific run to view results

2. **PR Checks**:
- Test results appear as a check on PRs
- Click "Details" to view full report

#### Running E2E Tests Locally

```bash
# Run e2e tests with full reporting
make test-e2e

# Run with fail-fast (stop on first failure)
make test-e2e E2E_FAIL_FAST=true

# Run with label filter
make test-e2e E2E_LABEL_FILTER="smoke"

# Run with description
make test-e2e E2E_RUN_DESCRIPTION="Testing new feature"

# Generate HTML report from results
make test-report
```

#### Troubleshooting

**Tests fail in CI but pass locally:**
- Check timing issues (CI may be slower)
- Verify kind-config-ci.yaml configuration
- Check resource limits in CI environment

**Cluster creation timeout:**
- Increase `wait` timeout in workflow
- Check Docker daemon health in CI
- Verify kind version compatibility

**Image loading fails:**
- Ensure Docker build succeeds
- Check image names match between build and load steps
- Verify kind cluster name is correct

**Tests timeout:**
- Default timeout is 30 minutes
- Adjust `timeout-minutes` in workflow if needed
- Check for hanging pods or resources

#### Manual Trigger

To manually trigger the E2E tests workflow:

1. Go to Actions tab in GitHub
2. Select "E2E Tests" workflow
3. Click "Run workflow" button
4. Select branch and click "Run workflow"

#### Performance

**Typical execution time:**
- Cluster creation: ~1-2 minutes
- Image build: ~2-3 minutes
- Image load: ~30 seconds
- E2E tests: ~10-15 minutes
- **Total: ~15-20 minutes**

### Lint Workflow

**Workflow File:** `.github/workflows/lint.yaml`

#### Jobs

1. **golangci-lint**: Runs golangci-lint with project configuration
2. **go fmt**: Checks code formatting
3. **go vet**: Runs Go static analysis

#### Configuration

Linter configuration is defined in `.golangci.yml`:

```yaml
linters:
enable:
- gosimple
- govet
- ineffassign
- staticcheck
- unused
- errcheck
- gofmt
- goimports

linters-settings:
goimports:
local-prefixes: github.com/kaasops/vector-operator
```

#### Running Locally

```bash
# Run linter
make lint

# Run linter with auto-fix
make lint-fix
```
Loading