Skip to content
Development

Development

This page covers the development workflow for contributing to SRE Portal.

Prerequisites

  • Go 1.25+
  • Docker
  • kubectl with access to a Kubernetes cluster
  • Node.js (for the web UI)
  • Buf CLI (for protobuf codegen)

Make Targets

Build and Run

CommandDescription
make buildBuild the manager binary to bin/manager
make runRun the controller locally using the current kubeconfig
make docker-build IMG=<image>Build the container image
make docker-push IMG=<image>Push the container image

Code Generation

CommandDescription
make manifestsGenerate CRDs, RBAC, and webhook manifests from kubebuilder markers
make generateGenerate DeepCopy methods (zz_generated.deepcopy.go)
make protoGenerate Go and TypeScript code from protobuf definitions (Buf)
make proto-lintLint protobuf files with Buf

Testing

CommandDescription
make testRun unit tests with envtest (in-memory K8s API + etcd)
make test-e2eRun end-to-end tests on an isolated Kind cluster
go test ./path/to/package -run TestName -vRun a single test

Linting

CommandDescription
make lintRun golangci-lint
make lint-fixAuto-fix lint issues

Web UI

CommandDescription
make install-webInstall npm dependencies (npm install)
make build-webBuild the React app for production (Vite)
npm test --prefix webRun web UI unit tests (Vitest)

Deployment

CommandDescription
make installInstall CRDs into the cluster
make deploy IMG=<image>Deploy the operator to the cluster

Testing

Unit Tests

Unit tests use Ginkgo v2 and Gomega with envtest, which runs an in-memory Kubernetes API server and etcd.

Each test package has a suite_test.go that sets up the envtest environment in BeforeSuite and tears it down in AfterSuite.

Tests follow a BDD structure:

var _ = Describe("Controller", func() {
    Context("when resource is created", func() {
        It("should reconcile", func() {
            Eventually(func(g Gomega) {
                // assertions
            }).Should(Succeed())
        })
    })
})

End-to-End Tests

E2E tests run against a real Kind cluster and are guarded by the e2e build tag:

make test-e2e

Adding New CRDs

Always use the kubebuilder CLI to scaffold new APIs and webhooks:

# Create a new API (controller + types)
kubebuilder create api --group sreportal --version v1alpha1 --kind <Kind>

# Create a webhook
kubebuilder create webhook --group sreportal --version v1alpha1 --kind <Kind> \
  --defaulting --programmatic-validation

After editing *_types.go files or kubebuilder markers, regenerate manifests:

make manifests generate

Protobuf Codegen

Proto definitions live in proto/sreportal/v1/. After editing .proto files:

make proto

This generates:

  • Go code in internal/grpc/gen/
  • TypeScript code in web/src/gen/
  • OpenAPI v2 (Swagger) spec in api/openapi/

The Swagger UI is served at /swagger by the web server.

Connect handlers

Connect RPC handlers use a shared unary interceptor in internal/grpc/interceptor.go to log procedure errors (see Architecture). When adding new Connect services, register them with the same connect.WithInterceptors(grpc.LoggingInterceptor()) options as existing handlers in internal/webserver/server.go.

Critical Rules

Never edit auto-generated files:

  • config/crd/bases/*.yaml
  • config/rbac/role.yaml
  • config/webhook/manifests.yaml
  • **/zz_generated.*.go
  • internal/grpc/gen/*
  • web/src/gen/*
  • PROJECT

Never remove scaffold markers:

// +kubebuilder:scaffold:*

These markers are used by kubebuilder to insert new code when scaffolding additional APIs.