diff --git a/.claude/settings.json b/.claude/settings.json deleted file mode 100644 index 34ba63bb..00000000 --- a/.claude/settings.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(make go-build)", - "Bash(make go-test)", - "Bash(make go-check)", - "Bash(make lint)", - "Bash(make generate)", - "Bash(make coverage)", - "Bash(make validate)", - "Bash(go build ./...)", - "Bash(go test ./...)", - "Bash(go test *)", - "Bash(go fmt ./...)", - "Bash(go mod tidy)", - "Bash(prek run)", - "Bash(prek run *)", - "Bash(prek install)", - "Bash(prek --version)", - "Bash(boilerplate/_lib/container-make generate)", - "Bash(git status *)", - "Bash(git diff *)", - "Bash(git log *)", - "Bash(git branch *)" - ], - "ask": [ - "Bash(make run)", - "Bash(boilerplate/_lib/container-make)", - "Bash(boilerplate/_lib/container-make *)", - "Bash(grep *)", - "Bash(find *)", - "Bash(ls *)", - "Bash(cat *)", - "Bash(git commit *)", - "Bash(git push *)", - "Bash(git reset *)", - "Bash(git rebase *)", - "Bash(git push --force-with-lease *)", - "Bash(git push * --force-with-lease)", - "Bash(git push * --force-with-lease *)", - "Bash(make docker-build)", - "Bash(kubectl *)", - "Bash(oc *)" - ], - "deny": [ - "Bash(git commit --no-verify)", - "Bash(git commit --no-verify *)", - "Bash(git commit -n)", - "Bash(git commit -n *)", - "Bash(git push --force)", - "Bash(git push --force *)", - "Bash(git push -f)", - "Bash(git push -f *)", - "Bash(git push * --force)", - "Bash(git push * --force *)", - "Bash(git push * -f)", - "Bash(git push * -f *)", - "Bash(rm -rf /)", - "Bash(chmod 777 *)" - ] - }, - "hooks": { - "SessionStart": [ - { - "hooks": [ - { - "type": "command", - "command": "bash \"$(git rev-parse --show-toplevel)/.claude/hooks/session-start-prek-setup.sh\"", - "async": true - } - ] - } - ], - "Stop": [ - { - "hooks": [ - { - "type": "command", - "command": "bash \"$(git rev-parse --show-toplevel)/.claude/hooks/stop-prek-validation.sh\"" - } - ] - } - ] - } -} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 6573fe0f..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,152 +0,0 @@ -# Contributing to deadmanssnitch-operator - -Thank you for your interest in contributing to this project. - -## Quick Start - -1. **Setup**: Install Go 1.25.4+ -2. **Install prek**: `uv tool install prek && prek install` -3. **Build**: `make go-build` -4. **Test**: `make go-test` -5. **Lint**: `make go-check` - -See [DEVELOPMENT.md](./DEVELOPMENT.md) for detailed setup instructions. - -## Before Submitting a PR - -All contributions must pass: - -1. **Formatting & linting**: `prek run --all-files` -2. **Unit tests**: `make go-test` -3. **Build verification**: `make go-build` -4. **Security scan**: Automatic via prek (gitleaks) - -## Development Workflow - -### Human Contributors - -```bash -# Create a feature branch -git checkout -b feature/my-change - -# Make changes, following existing code patterns -# Add/update tests for your changes - -# Run validation locally -prek run --all-files -make go-test - -# Commit with descriptive message -git commit -m "feat: add support for X" - -# Push and create PR -git push origin feature/my-change -``` - -### AI-Assisted Development - -When using AI coding agents (Claude Code, GitHub Copilot, Cursor, etc.): - -**Agents MUST:** -- Run `prek run` on changed files before committing -- Execute relevant tests after code changes: `make go-test` -- Preserve existing code style and patterns -- Avoid editing generated files (`**/zz_generated.*.go`, `pkg/dmsclient/mock/mock_dmsclient.go`) -- Never bypass hooks with `--no-verify` -- Never commit secrets, tokens, or credentials -- Reuse existing utilities and abstractions -- Make incremental, focused changes - -**Validation expectations:** -1. Format check: `go fmt ./...` -2. Lint: `make go-check` (or `prek run golangci-lint`) -3. Type safety: Verified by `go build ./...` in pre-commit -4. Tests: `make go-test` for affected packages -5. Secret scan: Automatic via prek gitleaks hook - -**Required checks before PR:** -- [ ] All prek hooks pass -- [ ] Unit tests pass for modified packages -- [ ] No new linter warnings introduced -- [ ] No secrets or credentials in diff -- [ ] Mocks regenerated if interfaces changed: `boilerplate/_lib/container-make generate` - -## Code Style - -Follow existing patterns: -- Standard Go formatting (`gofmt`) -- golangci-lint rules in `boilerplate/openshift/golang-osd-operator/golangci.yml` -- testify/assert for test assertions -- GoMock (`go.uber.org/mock`) for interface mocking - -## Testing Requirements - -- **Unit tests required** for all new functionality -- Use standard Go testing with testify/assert for assertions -- Mock the DMS API client with GoMock (mock at `pkg/dmsclient/mock/`) -- Aim for meaningful test coverage, not just metrics - -See [TESTING.md](./TESTING.md) for testing guidelines. - -## Regenerating Code - -After modifying API types or interfaces: - -```bash -# Regenerate deepcopy, OpenAPI, mocks (in container for consistency) -boilerplate/_lib/container-make generate -``` - -## Security - -**Never commit:** -- API keys, tokens, passwords -- AWS credentials, kubeconfig files -- Private keys, certificates -- `.env` files with secrets -- Debug statements printing sensitive data - -The prek gitleaks hook will block commits containing secrets. - -**High-risk changes** (requiring extra review): -- Authentication/authorization logic -- RBAC manifests with wildcard permissions -- Network policies -- CI/CD pipeline modifications -- Dockerfile changes - -## Commit Message Format - -Use conventional commits style: - -```text -: - - - - -``` - -Types: `feat`, `fix`, `docs`, `test`, `refactor`, `chore`, `ci` - -Examples: -- `feat: add support for fleet notification filtering` -- `fix: correct RBAC permissions for service monitor` -- `test: add unit tests for snitch reconciliation` - -## Pull Request Process - -1. **Title**: Clear, descriptive summary -2. **Description**: Explain what changed and why -3. **Testing**: Describe how you tested the changes -4. **CI**: All Tekton pipeline checks must pass -5. **Review**: Address review feedback promptly - -## Questions? - -- Review similar PRs for patterns -- Ask in PR comments for clarification - -## License - -All contributions are licensed under Apache 2.0. See [LICENSE](./LICENSE). diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 7d3016c9..459110d3 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,116 +1 @@ # Development - -- [Development](#development) - - [Development Environment Setup](#development-environment-setup) - - [golang](#golang) - - [prek (pre-commit hooks)](#prek-pre-commit-hooks) - - [Makefile](#makefile) - - [Code Generation](#code-generation) - - [Build using boilerplate container](#build-using-boilerplate-container) - - [Mocks](#mocks) - -This document covers everything you need to develop this operator locally. - -## Development Environment Setup - -### golang - -Go 1.25.4 or newer is required (see `go.mod`). - -```bash -$ go version -go version go1.25.4 linux/amd64 -``` - -**Note**: `FIPS_ENABLED=true` is set by the Makefile, which requires `GOEXPERIMENT=boringcrypto` and may fail outside the CI container. For local Go builds, use `go build .` directly or `make container-test` to build inside the boilerplate container. - -### prek (pre-commit hooks) - -This project uses [prek](https://prek.j178.dev/) for git hook management. - -```bash -# Install prek -uv tool install prek # recommended -# or: pipx install prek - -# Wire up git hooks -prek install -``` - -## Makefile - -Common make targets: - -```bash -# Build, lint, and test (default) -make - -# Individual targets -make go-check # golangci-lint + other static analysis -make go-test # Unit tests (requires envtest) -make go-build # Build binary (FIPS-enabled, may fail outside container) -make lint # YAML validation + go-check -make validate # Ensure generated code is committed -make generate # CRDs, deepcopy, openapi-gen, mocks -make coverage # Code coverage report -make run # Run operator locally (requires kubeconfig with Hive CRDs) - -# Container-based targets (run inside boilerplate container, matches CI) -make container-test -make container-lint -make container-validate -make container-all -``` - -## Code Generation - -After modifying CRD types (`api/v1alpha1/`) or the DMS client interface (`pkg/dmsclient/dmsclient.go`): - -```bash -# Regenerate CRDs, deepcopy, openapi, mocks (use container for CI parity) -boilerplate/_lib/container-make generate - -# Verify generated files are committed (what CI runs) -make generate-check -``` - -Generated files that must be committed: -- `api/v1alpha1/zz_generated.deepcopy.go` -- `api/v1alpha1/zz_generated.openapi.go` -- `pkg/dmsclient/mock/mock_dmsclient.go` -- `deploy/crds/*.yaml` - -## Build using boilerplate container - -To run lint, test and build in the boilerplate container (matches CI environment): - -```bash -boilerplate/_lib/container-make TARGET -``` - -Examples: - -```bash -# Run unit tests -boilerplate/_lib/container-make go-test - -# Run lint -boilerplate/_lib/container-make go-check - -# Run coverage -boilerplate/_lib/container-make coverage - -# Run all validation -boilerplate/_lib/container-make container-all -``` - -## Mocks - -The DMS API client mock lives at `pkg/dmsclient/mock/mock_dmsclient.go`, generated from the -`Client` interface in `pkg/dmsclient/dmsclient.go` using `go.uber.org/mock/mockgen`. - -**Do not edit the mock directly.** Regenerate it with: - -```bash -boilerplate/_lib/container-make generate -``` diff --git a/README.md b/README.md index fecd2144..a2b84b34 100644 --- a/README.md +++ b/README.md @@ -244,3 +244,4 @@ Structural validation tests that run via `go test ./pkg/pko/...` as part of the | Added a new config field to `manifest.yaml` | Add it to the `test.template` contexts | | Added a new `.gotmpl` file | Existing test contexts cover it automatically; add new contexts if it has conditional logic | | Changed conditional logic | Verify the Go unit tests still pass; add cases for new branches | + diff --git a/TESTING.md b/TESTING.md deleted file mode 100644 index 98dc107a..00000000 --- a/TESTING.md +++ /dev/null @@ -1,269 +0,0 @@ -# Testing Guide - -Testing guidelines for the deadmanssnitch-operator. - -## Framework - -- **testify/assert**: Assertions and test helpers (`github.com/stretchr/testify`) -- **GoMock**: Interface mocking (`go.uber.org/mock/gomock`) -- **controller-runtime fake client**: Kubernetes API simulation for controller tests -- **envtest**: Kubernetes API server for integration-style tests - -## Quick Commands - -```bash -# Run all tests -make go-test - -# Run specific package -go test -v ./controllers/deadmanssnitchintegration/ - -# Run a single test by name -go test -v -run TestReconcileClusterDeployment ./controllers/deadmanssnitchintegration/ - -# Run all packages -go test ./... - -# Container-based (CI parity) -boilerplate/_lib/container-make go-test -``` - -## Writing Tests - -### Test Structure - -Tests use standard Go `testing.T` with testify assertions: - -```go -package deadmanssnitchintegration_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestMyFeature(t *testing.T) { - result, err := MyFunction() - require.NoError(t, err) - assert.Equal(t, expected, result) -} -``` - -### Mocking Interfaces - -The DMS API client is mocked with GoMock. The mock is pre-generated at -`pkg/dmsclient/mock/mock_dmsclient.go`. - -```go -import ( - "testing" - "go.uber.org/mock/gomock" - "github.com/openshift/deadmanssnitch-operator/pkg/dmsclient/mock" -) - -func TestReconcileCreate(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - mockClient := mock.NewMockClient(ctrl) - mockClient.EXPECT(). - CreateSnitch(gomock.Any()). - Return(&dmsclient.Snitch{CheckInURL: "https://nosnch.in/abc"}, nil) - - // inject mock into reconciler and test... -} -``` - -The `setupDefaultMocks()` helper in the controller test creates a standard set of -test objects (DMSI CR, ClusterDeployment, Secrets) for use across tests. - -**Regenerate all mocks:** -```bash -boilerplate/_lib/container-make generate -``` - -## Test Organization - -### Unit Tests -- Test individual functions and methods -- Mock external dependencies (DMS API client) -- Fast execution (<1s per package) -- Located alongside source code - -### Controller Tests -- Test reconciliation logic end-to-end -- Use controller-runtime's fake client -- Test custom resource lifecycle (create, update, delete, finalizers) -- Located in `controllers/deadmanssnitchintegration/` - -### PKO Template Tests -- Located in `pkg/pko/template_test.go` -- **Snapshot tests**: golden files in `deploy_pko/.test-fixtures/`, validated by `kubectl-package validate` -- **Structural tests**: Go assertions on rendered template output (kind, annotations, conditional fields) - -## Agent-Driven Validation - -When AI agents modify code: - -**Minimal validation:** -```bash -# After changing controllers/deadmanssnitchintegration/ -go test ./controllers/deadmanssnitchintegration/ -``` - -**Full validation before commit:** -```bash -make go-test -``` - -**If tests fail:** -1. Read test output carefully -2. Fix the underlying issue (don't skip tests) -3. Rerun to confirm fix -4. Regenerate mocks if interface changed: `boilerplate/_lib/container-make generate` - -## Common Patterns - -### Testing Controllers - -```go -func TestReconcileClusterDeployment(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - // Create mock client and set expectations - mockClient := mock.NewMockClient(ctrl) - mockClient.EXPECT().FindSnitch(gomock.Any()).Return(nil, nil) - mockClient.EXPECT().CreateSnitch(gomock.Any()).Return(&dmsclient.Snitch{}, nil) - - // Create fake k8s client with test objects - scheme := setupScheme() - fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(testObjects...).Build() - - // Run reconciler - r := &DeadmansSnitchIntegrationReconciler{ - Client: fakeClient, - DmsClient: mockClient, - } - result, err := r.Reconcile(context.TODO(), req) - assert.NoError(t, err) - assert.False(t, result.Requeue) -} -``` - -### Testing Error Conditions - -```go -func TestReconcileCreateError(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - mockClient := mock.NewMockClient(ctrl) - mockClient.EXPECT().CreateSnitch(gomock.Any()).Return(nil, fmt.Errorf("API error")) - - // ...verify error is returned and handled correctly -} -``` - -### Using testify Matchers - -```go -// Equality -assert.Equal(t, expected, actual) - -// Nil checks -require.NoError(t, err) -assert.Nil(t, obj) - -// Collections -assert.Contains(t, slice, "item") -assert.Len(t, slice, 3) -assert.Empty(t, slice) - -// Booleans -assert.True(t, condition) -assert.False(t, condition) -``` - -## Coverage - -Generate coverage report: -```bash -go test -coverprofile=coverage.out ./... -go tool cover -html=coverage.out -o coverage.html -``` - -**Note**: Aim for meaningful coverage, not arbitrary percentages. -- Test critical paths and error handling -- Don't test generated code or trivial getters/setters - -## PKO Template Tests - -```bash -# Validate against existing fixtures -kubectl-package validate deploy_pko/ - -# Regenerate fixtures after template changes -rm -rf deploy_pko/.test-fixtures/ -kubectl-package validate deploy_pko/ - -# Run Go-level template assertions -go test ./pkg/pko/... -``` - -## Debugging Tests - -```bash -# Verbose output -go test -v ./controllers/deadmanssnitchintegration/ - -# Run single test -go test -v -run TestReconcileCreate ./controllers/deadmanssnitchintegration/ - -# Race detector -go test -race ./... -``` - -## CI Expectations - -Tests run in Tekton pipeline with: -- Fresh environment -- No cached dependencies -- Strict timeout limits - -**Local CI parity:** -```bash -boilerplate/_lib/container-make go-test -``` - -## Common Issues - -**Mock not found or outdated:** -```bash -# Regenerate mocks -boilerplate/_lib/container-make generate -``` - -**envtest not installed:** -```bash -make setup-envtest -``` - -**Test passes locally, fails in CI:** -```bash -# Run in container environment -boilerplate/_lib/container-make go-test - -# Check for: -# - Time-dependent tests -# - Environment-specific assumptions -# - File path dependencies -``` - -## Further Reading - -- [testify Documentation](https://github.com/stretchr/testify) -- [GoMock Guide](https://pkg.go.dev/go.uber.org/mock/gomock) -- [controller-runtime Testing](https://book.kubebuilder.io/reference/testing.html)