This document provides comprehensive guidelines for AI coding agents working on the kube-router project. It consolidates project-specific coding standards, best practices, and workflows to ensure consistent, high-quality contributions.
- Project Overview
- Project Structure
- Build System and Makefile
- Go Coding Standards
- Error Handling
- Logging Practices
- Testing
- Refactoring Guidelines
- Code Review Process
- Development Workflow
- Planning Guidelines
- Markdown Formatting
- Kubernetes-Specific Considerations
- Additional Best Practices
- References
- Support
kube-router is a turnkey solution for Kubernetes networking that provides:
- IPVS/LVS-based Service Proxy: High-performance load balancing using Linux kernel features
- Pod Networking: Direct routing with BGP protocol via GoBGP
- Network Policy Controller: Firewall rules using ipsets and iptables
- Advanced BGP Capabilities: Integration with external networking devices
- Language: Go
- Kubernetes API: v0.34.2 or latest
- Key Libraries: GoBGP, Netlink, iptables, ipset, IPVS
- Logging: klog/v2
- Testing: Testify, Ginkgo, Gomega
.
├── cmd/ # Main applications
│ └── kube-router/ # Main application entry point
├── pkg/ # Public library code
│ ├── healthcheck/ # Logic for monitoring control loops
│ └── controllers/ # Main control loop logic
│ ├── lballoc/ # LoadBalancer controller IPAM logic
│ ├── netpol/ # NetworkPolicy enforcement logic
│ ├── proxy/ # Services controller logic
│ └── routing/ # Pod & Service routing logic
├── internal/ # Private implementation code
│ ├── nlretry/ # Netlink retry logic
│ └── testutils/ # Internal test utilities
├── testdata/ # Test fixtures and data
├── daemonset/ # Kubernetes deployment manifests
├── cni/ # Kubernetes CNI configuration files
└── docs/ # Documentation in markdown format
- Use snake_case for file names:
// Good
api_handler.go
user_service.go
// Bad
apiHandler.go
UserService.go- Test files should use _test suffix:
// Good
api_handler_test.go
// Bad
api_handler_tests.go- Package names should be short and clear:
// Good
package api
package validator
// Bad
package apihandlers
package utility_functionskube-router uses a comprehensive Makefile for building, testing, linting, and releasing. Always use Makefile targets instead of running commands directly when available.
make kube-router- Build the kube-router binarymake container- Build the container imagemake gobgp- Build the gobgp binarymake all- Default target: lint, test, build binaries and images
make test- Run all tests with verbose outputmake test-pretty- Run tests with formatted output using gotestsummake gofmt- Check which files need formattingmake gofmt-fix- Automatically fix formatting issues
make lint- Run golangci-lint and markdownlintmake markdownlint- Run markdown linting on README and docs
make gomoqs- Generate all mock files using moq- Individual mocks:
make pkg/controllers/proxy/linux_networking_moq.go
make update-deps- Update all dependency versions, resolve digests, and pin SHAsmake update-deps-dry- Preview what dependency updates would be made (dry-run with diff output)
The update-deps target runs the build/dependency-updater Go tool, which:
- Discovers all managed files dynamically (no static file list)
- Updates Docker image tags to their latest versions within constraints defined in
build/dependency-updater/versions.lock.yaml - Resolves Docker image tags to
image:tag@sha256:digestformat for reproducibility - Updates tool version variables (e.g.
GOBGP_VERSION) to their latest GitHub releases - Updates GitHub Action
uses:lines from bare tags (@v6) or existing SHA pins to the latest SHA-pinned form (@sha256... # vX.Y.Z) - Updates the
toolchaindirective ingo.mod
Set GITHUB_TOKEN in your environment to avoid GitHub API rate limits (60/hr unauthenticated
vs 5000/hr authenticated):
GITHUB_TOKEN=ghp_... make update-depsmake prep-release- Full release preparation: update deps, run all checks, build container
This is the single command to run before tagging a new release. It calls update-deps first,
then runs the same steps as all (doctoc lint test-pretty kube-router container).
make push- Push container image to registrymake release- Create and push a releasemake clean- Remove build artifacts
make help- Display all available targets with descriptions
The Makefile supports several environment variables:
BUILD_IN_DOCKER(default: true) - Build inside Docker containerGOARCH- Target architecture (amd64, arm64, arm, s390x, ppc64le, riscv64)IMG_NAMESPACE- Container registry namespaceIMG_TAG- Container image tagDOCKER_BUILD_IMAGE- Docker image for buildingGO_CACHE- Go build cache directoryGO_MOD_CACHE- Go module cache directory
By default, builds run inside Docker containers for consistency:
make kube-router # Builds in Docker
make test # Tests in DockerTo build locally without Docker:
BUILD_IN_DOCKER=false make kube-router
BUILD_IN_DOCKER=false make testkube-router supports multiple architectures. Specify with GOARCH:
GOARCH=arm64 make kube-router
GOARCH=s390x make containerSupported architectures:
- amd64 (default)
- arm64
- arm
- s390x
- ppc64le
- riscv64
The build system automatically injects version information:
GIT_COMMIT- Git commit hash/tagBUILD_DATE- Build timestampGIT_BRANCH- Current Git branch
When modifying interfaces that have mocks:
- Update the interface in the source file
- Ensure the file has the
//go:generate moqdirective - Run
make gomoqsto regenerate all mocks - Or regenerate a specific mock:
make pkg/controllers/proxy/linux_networking_moq.go
Example from code:
//go:generate moq -out linux_networking_moq.go . LinuxNetworking
type LinuxNetworking interface {
ipvsCalls
netlinkCalls
}The Makefile pins specific versions of tools for reproducibility:
- Go: 1.25.1 or latest
- golangci-lint: v2.4.0 or latest
- GoBGP: v3.37.0 or latest
- moq: v0.6.0 or latest
- CNI plugins: v1.8.0 or latest
- Always use Makefile targets - Don't run
go build,go test, etc. directly - Run tests before committing -
make test-prettyormake test - Lint before submitting -
make lintto catch issues early - Clean before release builds -
make cleanthenmake all - Check formatting -
make gofmtto see what needs fixing - Auto-fix formatting -
make gofmt-fixbefore committing - Regenerate mocks after interface changes -
make gomoqs
# Start development
git checkout -b feature_x
# Make code changes
# ...
# Format code
make gofmt-fix
# Run linters
make lint
# Run tests
make test-pretty
# Build binary
make kube-router
# Build container (if needed)
make container
# Clean up
make clean- Group related files in packages
- Keep packages focused and cohesive
- Follow standard Go project layout
- Use internal/ for private implementation
- Place reusable code in pkg/
Organize imports in the following order:
- Standard library packages
- External dependencies
- Kubernetes dependencies
- Project-internal packages
import (
"context"
"fmt"
"github.com/vishvananda/netlink"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog/v2"
"github.com/cloudnativelabs/kube-router/v2/pkg/utils"
)- Custom error types for specific errors:
// Good
type ValidationError struct {
Field string
Error string
}
func (v *ValidationError) Error() string {
return fmt.Sprintf("validation failed on %s: %s", v.Field, v.Error)
}
// Usage
if !isValid {
return &ValidationError{
Field: "email",
Error: "invalid format",
}
}- Error wrapping for context:
// Good
if err := db.QueryRow(query, id).Scan(&user); err != nil {
return fmt.Errorf("fetching user %d: %w", id, err)
}
// Bad
if err := db.QueryRow(query, id).Scan(&user); err != nil {
return err // Lost context
}- Handle errors immediately:
// Good
result, err := someFunction()
if err != nil {
return fmt.Errorf("failed to execute someFunction: %w", err)
}
// Use result
// Bad
result, err := someFunction()
// Do other things...
if err != nil { // Too late
return err
}- Don't panic in libraries:
// Good
func ProcessData(data []byte) error {
if len(data) == 0 {
return errors.New("empty data provided")
}
// Process data
return nil
}
// Bad
func ProcessData(data []byte) {
if len(data) == 0 {
panic("empty data provided")
}
// Process data
}- Use %v for logging errors, not %w or %s:
// Good
result, err := someFunction()
if err != nil {
klog.Errorf("failed to execute someFunction: %v", err)
}
// Bad
result, err := someFunction()
if err != nil {
klog.Errorf("failed to execute someFunction: %w", err)
}Use sentinel errors for expected error conditions:
var (
ErrNotFound = errors.New("resource not found")
ErrInvalid = errors.New("invalid input")
)Use error groups for concurrent operations:
g := new(errgroup.Group)
for _, item := range items {
item := item
g.Go(func() error {
return processItem(item)
})
}
if err := g.Wait(); err != nil {
return fmt.Errorf("processing items: %w", err)
}kube-router uses klog from the Kubernetes project for logging.
import (
"k8s.io/klog/v2"
)
func main() {
klog.Info("Application started")
klog.Infof("User created: %v", userID)
klog.V(1).Infof("Found %d routes for interface %s, deleting them...", len(routes), iface.Attrs().Name)
}- Add the logger at the top of the file or as a package/global variable
- Do not use global context for sensitive or user-specific data
- Avoid logging sensitive information (passwords, tokens, keys)
- Use appropriate log levels:
klog.Info(): General informational messagesklog.V(1).Info(): Verbose/debug informationklog.Warning(): Warning messagesklog.Error(): Error messagesklog.Fatal(): Fatal errors (use sparingly)
- Use table-driven tests:
func TestCalculate(t *testing.T) {
tests := []struct {
name string
input int
want int
wantErr bool
}{
{
name: "positive number",
input: 5,
want: 10,
wantErr: false,
},
{
name: "negative number",
input: -5,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Calculate(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("Calculate() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && got != tt.want {
t.Errorf("Calculate() = %v, want %v", got, tt.want)
}
})
}
}- Use subtests for better organization:
func TestService(t *testing.T) {
t.Run("Create", func(t *testing.T) {
// Test creation
})
t.Run("Update", func(t *testing.T) {
// Test update
})
t.Run("Delete", func(t *testing.T) {
// Test deletion
})
}Create test utilities with proper helper marking:
func setupTestDB(t *testing.T) (*sql.DB, func()) {
t.Helper() // Mark as helper function
db, err := sql.Open("postgres", "test-connection-string")
if err != nil {
t.Fatalf("setting up test db: %v", err)
}
return db, func() {
db.Close()
}
}
// Usage
func TestDatabase(t *testing.T) {
db, cleanup := setupTestDB(t)
defer cleanup()
// Run tests
}- Use interfaces for testability:
type UserRepository interface {
GetUser(id int) (*User, error)
}
// Mock implementation
type mockUserRepo struct {
users map[int]*User
}
func (m *mockUserRepo) GetUser(id int) (*User, error) {
user, ok := m.users[id]
if !ok {
return nil, ErrNotFound
}
return user, nil
}- Small mocks (< 200 lines): Use testify mock library
import "github.com/stretchr/testify/mock"See pkg/utils/node_test.go for examples.
- Large mocks (> 200 lines): Use moq to generate mock files
//go:generate moq -out linux_networking_moq.go . LinuxNetworking
type LinuxNetworking interface {
ipvsCalls
netlinkCalls
}Use the Testify library for assertions:
import "github.com/stretchr/testify/assert"
if testcase.err != nil {
assert.EqualError(t, err, testcase.err.Error())
}Use t.Fatalf() for conditions that would stop a test from executing:
t.Run(testcase.name, func(t *testing.T) {
clientset := fake.NewSimpleClientset()
_, err := clientset.CoreV1().Nodes().Create(context.Background(), testcase.existingNode, metav1.CreateOptions{})
if err != nil {
t.Fatalf("failed to create existing nodes for test: %v", err)
}
})Use test fixtures for large amounts of test data (>100 lines):
// testdata/users.json
// Place test data in testdata directorySee testdata/ipset_test_1/ for examples.
Use parallel tests when possible:
func TestParallel(t *testing.T) {
tests := []struct{
name string
// test cases
}{
// test cases here
}
for _, tt := range tests {
tt := tt // Capture range variable
t.Run(tt.name, func(t *testing.T) {
t.Parallel() // Mark test as parallel
// Test implementation
})
}
}- Run tests with coverage:
go test -cover ./...- Generate coverage report:
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out- Test race conditions:
go test -race ./...- Target 70% test coverage for new code with emphasis on:
- Business logic cases
- Corner cases
- Boundary conditions
- Poorly defined input handling
Use build tags for integration tests:
// +build integration
package integration
func TestIntegration(t *testing.T) {
// Integration test implementation
}When refactoring code, ensure the following:
- Try to ensure functions are a maximum of 50 lines
- Prefer function sizes of approximately 30 lines or less
- Don't create functions for code that is less than 5 lines long
- Ensure that all functions created encapsulate related logic while obeying max lengths
- If you can't follow these guidelines, ask for guidance rather than assuming
- For any hard-coded variables encountered during a refactor, consider whether they should be added as constants
- Prioritize keeping code as DRY (Don't Repeat Yourself) as possible and minimize repetition
- If you find elements of code that are reused in the current file, refactor those as well to use the refactored functions
Extract magic numbers and repeated strings:
// Good
const (
MaxRetries = 3
DefaultTimeout = 30 * time.Second
)
// Bad
if retries > 3 { // Magic number
// ...
}When conducting code reviews (when explicitly asked), address the following sections:
Review the code changes in the indicated range and summarize in four sentences or less the general purpose of the code.
Identify any obvious problems:
- Anything that would stop it from running or accomplishing its intent
- Obvious violations of programming design or functionality
Format as a bulleted list with sub-bullets for recommended remediation.
Compare the changed code with surrounding code and other committed code in the repository. Provide a brief assessment of how well the code fits with the structure of the existing code.
Provide a score (1-10): 1 = not matching at all, 10 = matching very well.
Audit the rest of the codebase for any code duplication introduced by the changes. Focus on utility functions that might not have been used.
Identify obvious wins that would:
- Greatly improve readability
- Be significantly more conformant with generally accepted standards
- Greatly improve performance/memory structure
Don't be pedantic; focus on obvious improvements.
Identify any obvious tests that are missing. New code should seek 70% test coverage, with emphasis on:
- Business logic cases
- Corner cases
- Boundary conditions
- Poorly defined input handling
Provide a test coverage score (1-10): 1 = no unit tests, 10 = 100% test coverage.
List any obvious security problems related to the indicated code. Format as a bulleted list with sub-bullets for recommended remediation.
Review existing documentation in /docs and check if:
- Code changes require documentation updates that weren't made
- Any documentation has spelling/grammar issues or readability problems
Format as a bulleted list with sub-bullets for recommended remediation.
If a URL is provided (likely a related issue or PR):
- Fetch the content remotely and analyze it
- If it's a PR with comments/feedback, check if they were resolved
Format as a bulleted list with sub-bullets for recommended remediation.
Provide a code quality score (1-10): 1 = lowest quality, 10 = highest quality.
Check if a previous review exists in .reviews/<branch_name>/. If it exists, compare the current review to it in terms
of:
- Fixed issues
- Remaining issues
- New issues found
Save all review output to:
.reviews/<branch_name>/<branch_name>-<integer_increment>.md
Where:
<branch_name>is the name of the current git branch<integer_increment>is an integer to ensure unique filenames
- Fork the repository
- Create a feature branch:
git checkout -b feature_x - Make changes and commit with descriptive messages
- Keep branch up-to-date with upstream:
git checkout master
git fetch upstream
git rebase upstream/master
git checkout feature_x
git rebase master- Push to your fork:
git push origin feature_x - Create a pull request
This project uses Conventional Commits. Every commit message must follow this format:
<type>(<scope>): <description>
Where <type> is one of:
- feat: A new feature or enhancement
- fix: A bug fix
- doc: Documentation-only changes
- test: Adding or updating tests
- build: Changes to the build system or dependencies (e.g.
build(deps)) - chore: Maintenance tasks that don't modify src or test files
- fact (refactor): Code restructuring without behavior changes
The <scope> is optional but encouraged. It should identify the component or file affected (e.g. gobgp,
aws.go, Makefile). Use the following abbreviations for the main controllers:
- NSC -- Network Services Controller (
pkg/controllers/proxy/) - NRC -- Network Routes Controller (
pkg/controllers/routing/) - NPC -- Network Policy Controller (
pkg/controllers/netpol/)
Additional guidelines:
- Keep the description concise and lowercase
- Explain the "why" rather than the "what"
- Keep commits focused on a single logical change
Examples from this repository:
feat(gobgp): add kube_router_bgp_peer_info metric
fix(aws.go): load region before attempting to assume a role
test(NSC): add comprehensive TCPMSS unit tests
build(deps): bump golang.org/x/net from 0.49.0 to 0.51.0
doc(metrics.md): replace controller_bgp_peers -> bgp_peer_info
Run the following before submitting:
# Linting
golangci-lint run
# Static analysis
staticcheck ./...
# Error checking
errcheck ./...
# Tests
go test ./...
# Race detection
go test -race ./...When asked to create plans for complex features or changes, follow this structured approach to ensure clarity and thoroughness.
Create a new directory inside <git_root>/.plans. The name of the directory should be 3 words or less that briefly
summarizes the task. It should be camel case and not contain any spaces.
Example:
.plans/IpsetRefactor/.plans/BgpPeerConfig/.plans/MetricsController/
The primary artifact is a PLAN.md file that contains markdown documenting the plan. This is a living document that
should be continuously updated during execution. It should contain the following sections with each top level list item
being a heading:
This should be the original prompt or request verbatim.
A paragraph summary of the approach that will be taken.
A markdown list of what will be done in order. Later during implementation, check off items as they are completed and keep the list updated.
Example:
## Detail
- [x] Analyze existing ipset implementation
- [x] Identify refactoring opportunities
- [ ] Create new ipset helper functions
- [ ] Update network policy controller to use new helpers
- [ ] Add unit tests for new functionality
- [ ] Update integration testsThis section is filled out during implementation to describe any particular problems encountered while trying to execute the plan as provided in the details section.
Example:
## Problems Encountered
- **Netlink compatibility**: Found that some ipset operations don't work consistently across kernel versions.
Implemented fallback logic to handle this.
- **Test fixtures**: Existing test fixtures didn't cover edge cases. Created new fixtures in testdata/ipset_test_2/List the files that are expected to need changes. Update this list as the plan evolves.
Example:
## Files Changed
- pkg/controllers/netpol/ipset.go
- pkg/utils/ipset.go
- pkg/controllers/netpol/network_policy_controller.go
- pkg/controllers/netpol/ipset_test.goIn general, tests should be additive to ensure compatibility is maintained. However, if existing test changes are unavoidable, detail what will be changed and why.
Example:
## Tests Changed
- pkg/controllers/netpol/ipset_test.go
- Updated TestIpsetCreate to use new helper functions
- Added new test cases for error handling
- No breaking changes to existing testsAll plans must abide by the markdown formatting standards described in the Markdown Formatting section. Lines should be no more than 120 characters long.
If any questions arise when creating the plan, ask for clarification rather than making assumptions unless the assumptions are small and clearly reasonable.
As implementation progresses:
- Check off completed items in the Detail section
- Add any new tasks discovered during implementation
- Document problems in the Problems Encountered section
- Update Files Changed and Tests Changed as needed
- Keep the plan synchronized with actual work
Create plans for:
- Complex multi-file refactorings
- New feature implementations
- Major architectural changes
- Changes that affect multiple controllers or subsystems
- Work that will take multiple sessions to complete
Do not create plans for:
- Simple bug fixes
- Single-file changes
- Documentation-only updates
- Trivial refactorings
kube-router uses markdownlint to enforce consistent markdown formatting across all documentation. All markdown files must pass markdownlint checks before being committed.
Use the Makefile target to check markdown files:
make markdownlintThis will lint both the README and all files in the /docs directory.
The project's markdownlint rules are defined in .markdownlint.yaml:
- MD046: Code blocks must use fenced style (triple backticks) instead of indented style
- MD013: Line length limited to 120 characters (200 for code blocks)
- MD045: Images must have alt text (disabled in this project)
- Keep lines under 120 characters
- Code blocks can extend to 200 characters
- Break long sentences and paragraphs naturally at punctuation
Good:
This is a reasonably long sentence that explains a complex concept but stays within the 120 character limit by
breaking at a natural point.Bad:
This is an extremely long sentence that goes on and on without any breaks and exceeds the maximum line length of 120 characters which will cause markdownlint to fail.Always use fenced code blocks with language identifiers:
Good:
```bash
make test
```
```golang
func main() {
fmt.Println("Hello")
}
```Bad:
make test
func main() {
fmt.Println("Hello")
}- Use ATX-style headings (with #):
# Heading 1
## Heading 2
### Heading 3-
One H1 per document - Only use a single
#heading at the top of the file -
No skipping levels - Don't jump from
##to#### -
Space after hash - Always include a space:
## Headingnot##Heading
- Consistent markers - Use
-for unordered lists throughout:
Good:
- Item 1
- Item 2
- Nested item
- Another nested item
- Item 3Bad:
* Item 1
- Item 2
+ Item 3- Proper indentation - Use 2 spaces for nested lists:
- Top level
- Nested level
- Double nested- Ordered lists - Use
1.for all items (auto-numbering):
1. First item
1. Second item
1. Third item- No bare URLs - Always use link syntax:
Good:
See the [User Guide](/docs/user-guide.md) for details.
Visit [GitHub](https://github.com/cloudnativelabs/kube-router).Bad:
See /docs/user-guide.md for details.
Visit https://github.com/cloudnativelabs/kube-router.- Reference-style links - Use for repeated or long URLs:
Check out [kube-router and [Kubernetes][k8s].
[kr]: https://github.com/cloudnativelabs/kube-router
[k8s]: https://kubernetes.io- Bold - Use
**double asterisks**:
**Important**: This is critical information.- Italic - Use
*single asterisks*:
This is *emphasized* text.- Code - Use backticks for inline code:
The `kubectl` command is used to interact with Kubernetes.
Use the `--help` flag for more information.Ensure tables are properly formatted:
| Header 1 | Header 2 | Header 3 |
|----------|----------|----------|
| Cell 1 | Cell 2 | Cell 3 |
| Cell 4 | Cell 5 | Cell 6 |- Around headings - One blank line before and after:
Some text here.
## New Section
More text here.- Around code blocks - One blank line before and after:
Some explanation here.
` ``bash
command here
` ``
More explanation here.- Around lists - One blank line before and after:
Paragraph text.
- List item 1
- List item 2
More paragraph text.- Start with H1 - Every markdown file should start with a level 1 heading
- No trailing spaces - Remove spaces at the end of lines
- End with newline - Files should end with a single newline character
- No multiple blank lines - Use only single blank lines
Problem: Lines ending with spaces
Fix: Remove trailing whitespace from all lines
Problem: More than one consecutive blank line
Fix: Replace multiple blank lines with a single blank line
Problem: No blank line before/after heading
Fix: Add blank lines around all headings
Problem: More than one # heading in the document
Fix: Use only one H1 at the document start; use ## for sections
Problem: No blank line before/after code block
Fix: Add blank lines around all code blocks
Problem: Code block without language identifier
Fix: Add language after opening backticks:
```bash
command here
```- Always run markdownlint before committing - Use
make markdownlint - Use language identifiers on all code blocks - Helps with syntax highlighting
- Keep lines under 120 characters - Improves readability in all editors
- Use fenced code blocks - Never use indented code blocks
- Consistent list markers - Always use
-for unordered lists - One blank line between sections - Don't use multiple blank lines
- Proper heading hierarchy - Don't skip heading levels
- No trailing whitespace - Configure your editor to remove it automatically
Configure your editor to show/remove:
- Line length guides at 120 characters
- Trailing whitespace
- End-of-file newlines
Popular editors with markdownlint support:
- VS Code: Install the "markdownlint" extension
- Vim: Use ALE or vim-markdown
- Emacs: Use markdown-mode with flycheck
- Use typed clients when possible:
clientset.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{})- Handle API errors properly:
node, err := clientset.CoreV1().Nodes().Get(context.Background(), nodeName, metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
return fmt.Errorf("node %s not found: %w", nodeName, err)
}
return fmt.Errorf("failed to get node %s: %w", nodeName, err)
}- Use informers for watching resources:
// Prefer informers over direct watches
informerFactory.Core().V1().Nodes().Informer().AddEventHandler(...)- Use netlink library for Linux networking:
import "github.com/vishvananda/netlink"
link, err := netlink.LinkByName("eth0")
if err != nil {
return fmt.Errorf("failed to get link: %w", err)
}- Handle iptables carefully:
import "github.com/coreos/go-iptables/iptables"
ipt, err := iptables.New()
if err != nil {
return fmt.Errorf("failed to initialize iptables: %w", err)
}- Use ipsets efficiently:
// Batch operations when possible
// Clean up sets when no longer needed- Minimize API calls - use informers and caching
- Batch netlink operations when possible
- Use ipsets for large rule sets instead of individual iptables rules
- Profile code for performance bottlenecks in hot paths
- Be mindful of goroutine leaks in long-running controllers
Always clean up resources properly:
func (c *Controller) Run(stopCh <-chan struct{}) {
defer c.cleanup()
// Controller logic
<-stopCh
}
func (c *Controller) cleanup() {
// Clean up iptables rules
// Clean up ipsets
// Clean up netlink resources
}Always propagate context for cancellation and timeouts:
func (s *Service) ProcessItem(ctx context.Context, item Item) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
// Process item
}
}- Use sync primitives appropriately
- Avoid goroutine leaks
- Handle context cancellation
- Use errgroup for managing multiple goroutines
- Keep dependencies up to date
- Be mindful of Kubernetes version compatibility
- Test with multiple Kubernetes versions when making API changes
- Document exported functions and types
- Keep documentation up to date with code changes
- Use godoc conventions
- Update
/docsfor user-facing changes
- Slack: #kube-router on Kubernetes Slack
- Issues: GitHub Issues