Development Setup
This guide covers setting up a development environment for Butler components.
Prerequisites
Required Tools
| Tool | Version | Purpose | Install |
|---|---|---|---|
| Go | 1.24+ | Controller and CLI development | go.dev |
| Node.js | 20+ | Console frontend development | nodejs.org |
| Docker | Latest | Container builds, KIND clusters | docker.com |
| kubectl | 1.28+ | Kubernetes interaction | kubernetes.io |
| kind | Latest | Local test clusters | go install sigs.k8s.io/kind@latest |
| helm | 3.x | Chart development | helm.sh |
| make | Latest | Build automation | Usually pre-installed |
Optional Tools
| Tool | Purpose |
|---|---|
| k9s | TUI for Kubernetes |
| stern | Multi-pod log tailing |
| kubebuilder | Controller scaffolding |
| golangci-lint | Go linting |
Verify Installation
go version # go version go1.24.x
node --version # v20.x.x
docker version # Client and Server info
kubectl version # Client Version: v1.28+
kind version # kind v0.x.x
helm version # version.BuildInfo{Version:"v3.x.x"...}
Repository Setup
Clone Repositories
# Create workspace
mkdir -p ~/src/butler && cd ~/src/butler
# Clone core repositories
git clone https://github.com/butlerdotdev/butler-api
git clone https://github.com/butlerdotdev/butler-controller
git clone https://github.com/butlerdotdev/butler-bootstrap
git clone https://github.com/butlerdotdev/butler-cli
git clone https://github.com/butlerdotdev/butler-server
git clone https://github.com/butlerdotdev/butler-console
git clone https://github.com/butlerdotdev/butler-charts
Install Dependencies
Go Projects (controller, bootstrap, cli, server)
cd butler-controller
make deps
# Or: go mod download
Console Frontend
cd butler-console
npm install
Local Development
Go Controllers
Build
cd butler-controller
make build
# Binary at bin/manager
Run Locally
Run against a KIND cluster:
# Create cluster
kind create cluster --name butler-dev
# Install CRDs
kubectl apply -f ../butler-api/config/crd/bases/
# Run controller
make run
# Or with specific kubeconfig:
# go run ./cmd/main.go --kubeconfig ~/.kube/config
Code Generation
After modifying *_types.go files:
# Generate DeepCopy methods
make generate
# Update CRD manifests
make manifests
CLI Development
cd butler-cli
# Build both CLIs
make build
# Run directly
go run ./cmd/butlerctl/main.go cluster list
go run ./cmd/butleradm/main.go version
# Install locally
make install
# Installs to $GOPATH/bin
Console Frontend
cd butler-console
# Development server (hot reload)
npm run dev
# Access at http://localhost:5173
# Build for production
npm run build
# Preview production build
npm run preview
Server Backend
cd butler-server
# Build
make build
# Run locally
make run
# Or with environment:
# BUTLER_KUBECONFIG=~/.kube/config go run ./cmd/server/main.go
# Server runs on http://localhost:8080
Testing
Unit Tests
Go Projects
# Run all tests
make test
# With coverage
make test-coverage
go tool cover -html=coverage.out
# Specific package
go test ./internal/controller/... -v
Frontend
cd butler-console
# Run tests
npm test
# With coverage
npm run test:coverage
Integration Tests (envtest)
Controllers use envtest for integration testing:
cd butler-controller
# Install envtest binaries
make envtest
# Run integration tests
make test
E2E Tests
cd butler-controller
# Create KIND cluster with CRDs
make e2e-setup
# Run E2E tests
make e2e-test
# Cleanup
make e2e-cleanup
Manual Testing
# Create KIND cluster
kind create cluster --name butler-test
# Install CRDs
kubectl apply -f butler-api/config/crd/bases/
# Install controller (from local build)
make docker-build IMG=butler-controller:dev
kind load docker-image butler-controller:dev --name butler-test
kubectl apply -f config/manager/manager.yaml
# Create test resources
kubectl apply -f config/samples/
Debugging
VS Code Launch Configuration
Create .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Controller",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/cmd/main.go",
"args": ["--kubeconfig", "${env:HOME}/.kube/config"],
"env": {
"ENABLE_WEBHOOKS": "false"
}
},
{
"name": "Debug CLI",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/cmd/butlerctl/main.go",
"args": ["cluster", "list"]
}
]
}
Delve (CLI Debugging)
# Install delve
go install github.com/go-delve/delve/cmd/dlv@latest
# Debug controller
dlv debug ./cmd/main.go -- --kubeconfig ~/.kube/config
# Debug test
dlv test ./internal/controller/tenantcluster/
Logging
Controllers use structured logging via controller-runtime's log package. In code, use:
import logf "sigs.k8s.io/controller-runtime/pkg/log"
log := logf.FromContext(ctx)
log.Info("Creating resource", "name", obj.Name)
log.Error(err, "Failed to create resource")
Increase verbosity at runtime:
# Run with debug logging
go run ./cmd/main.go --zap-log-level=debug
# Or set environment
export LOG_LEVEL=debug
make run
Kubernetes Resources
# Watch CRD status
kubectl get tenantcluster -A -w
# Describe for events
kubectl describe tenantcluster my-cluster -n default
# Controller logs
kubectl logs -n butler-system deploy/butler-controller -f
# Check events
kubectl get events --sort-by='.lastTimestamp' -A
Docker Builds
Build Image
# Build with default tag
make docker-build
# Custom tag
make docker-build IMG=ghcr.io/butlerdotdev/butler-controller:dev
Multi-Architecture
# Build for multiple platforms
make docker-buildx IMG=ghcr.io/butlerdotdev/butler-controller:dev \
PLATFORMS=linux/amd64,linux/arm64
Load to KIND
kind load docker-image butler-controller:dev --name butler-test
Helm Development
Lint Charts
cd butler-charts/charts/butler-controller
helm lint .
Template Rendering
helm template butler-controller . \
--namespace butler-system \
--set image.tag=dev
Local Install
# From chart directory
helm upgrade --install butler-controller . \
--namespace butler-system \
--create-namespace \
--set image.tag=dev
Common Tasks
Update Dependencies
# Go modules
go get -u ./...
go mod tidy
# npm
npm update
Add New CRD
- Edit types in
butler-api/api/v1alpha1/ - Run
make generate manifestsin butler-api - Run CRD sync workflow
- Update controller to reconcile new type
Add New Command
- Create command file in
internal/cmd/ - Register in root command
- Add tests
- Update documentation
Getting Help
- Questions: Open a GitHub Discussion
- Bugs: Open an issue in the relevant repository
- Chat: See community channels in CONTRIBUTING.md