This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Assisted Service is a REST/Kubernetes API service that installs OpenShift clusters with minimal infrastructure prerequisites. It supports highly-available control planes (3+ nodes) and Single-Node OpenShift (SNO). The service can operate in two modes:
- REST API mode: Standalone service exposing REST endpoints
- Kube-API mode: Operator exposing Kubernetes Custom Resources
-
bminventory (
internal/bminventory/) - Main business logic and REST API handlers- Central orchestrator coordinating cluster and host operations
- Implements REST API endpoints defined in
swagger.yaml
-
Cluster Management (
internal/cluster/) - Cluster lifecycle state machine- State transitions and validations for cluster installation
- Works with cluster validation logic
-
Host Management (
internal/host/) - Host lifecycle state machine- Manages individual host states and validations
- Coordinates with hardware validation
-
Controllers (
internal/controller/) - Kubernetes operator controllers- Reconcile CRs: AgentServiceConfig, InfraEnv, Agent, etc.
- Only active in Kube-API mode
-
Storage Layer - PostgreSQL database via GORM
- Models defined in
models/(auto-generated fromswagger.yaml) - Migration scripts in
internal/migrations/
- Models defined in
-
internal/- Core business logic (not importable by other projects)bminventory/- Main API implementationcluster/,host/,infraenv/- Domain logichardware/,network/,connectivity/- Validation logicoperators/- Operator support (OLM, LSO, etc.)controller/- Kubernetes controllers
-
pkg/- Reusable utilities (importable by other projects)auth/,db/,s3wrapper/,k8sclient/- Infrastructurevalidations/,conversions/- Domain utilities
-
restapi/- Auto-generated REST server code (fromswagger.yaml) -
client/- Auto-generated REST client code -
api/- Kubernetes API definitions (CRDs) -
models/- Auto-generated data models
# Build everything (runs lint + unit tests + build)
skipper make all
# Build service binary only (skip validation)
skipper make build-minimal
# Build service container image
SERVICE=quay.io/<username>/assisted-service:<tag> skipper make build-image
# Build in current environment (no container)
make build-assisted-serviceAfter modifying swagger.yaml, regenerate code:
skipper make generate-from-swaggerThis regenerates:
restapi/- REST server codeclient/- REST client codemodels/- Data models
After modifying CRD definitions in api/:
make generate # Generates deepcopy, CRD manifests, etc.skipper make lint # Run all linters
skipper make format # Auto-format codeFor comprehensive testing documentation, see:
-
Go (1.19+)
- Check:
go version
- Check:
-
Container Runtime (for full unit tests)
- Docker or Podman
- Check:
docker --versionorpodman --version - Used to run PostgreSQL test containers
-
nmstate Development Headers (for network state management tests)
# Fedora/RHEL sudo dnf install -y nmstate-devel # Ubuntu/Debian sudo apt-get install -y libnmstate-dev
Note: When using Claude Code, package installation commands must be run unsandboxed. Claude Code will prompt for permission when needed.
-
gotestsum (required for make test targets)
- Required for
make unit-testandmake run-unit-test - Check:
which gotestsum - Install:
go install gotest.tools/gotestsum@latest export PATH=$PATH:$(go env GOPATH)/bin
- Note: Tests will run for a long time before failing if gotestsum is not installed. Always verify it's available before running make test targets.
- Required for
-
Optional Tools
skipper- Container-based build environmentkind- Kubernetes in Docker (for subsystem tests)
Before running the make test targets, verify your environment:
1. Check for container runtime (Docker or Podman)
Unit tests require a PostgreSQL database. The test framework can automatically create one in:
- A Kubernetes cluster (if available)
- A local container via testcontainers (if Docker/Podman available)
Check container runtime availability:
# Check for Podman
which podman && podman --version
# OR check for Docker
which docker && docker --versionChoose the appropriate make target based on availability:
- If Podman/Docker is available: Use
make unit-test(automatically manages database lifecycle) - If NO container runtime: Tests will fail unless you manually run a PostgreSQL container first (see workaround below)
2. Check for gotestsum
The make targets depend on gotestsum, and tests will run for a long time before failing if it's missing:
# Check if gotestsum is installed
which gotestsum
# If not found, install it
go install gotest.tools/gotestsum@latest
# Ensure it's in your PATH
export PATH=$PATH:$(go env GOPATH)/bin# Requires Docker/Podman running
make unit-testThis will:
- Start a PostgreSQL container at localhost:5433
- Run all unit tests (with SKIP_UT_DB=1, which tells tests to use localhost:5433)
- Kill the PostgreSQL container
- Generate coverage reports
Important: Despite its confusing name, SKIP_UT_DB does NOT skip database-dependent tests!
SKIP_UT_DB=1: Tests assume PostgreSQL is already running atlocalhost:5433(they skip creating it)- Without
SKIP_UT_DB: Tests will try to create PostgreSQL automatically (K8s pod → testcontainer fallback)
When to use SKIP_UT_DB=1:
- You've already started PostgreSQL at localhost:5433 (e.g., via
make run-db-container) - You want to run tests multiple times without recreating the database each time
Running tests without a database will fail:
# This will FAIL with connection errors:
SKIP_UT_DB=1 make run-unit-test
# Tests fail in BeforeSuite (~5 seconds) with:
# "failed to connect to `host=127.0.0.1 user=postgres database=`:
# dial tcp 127.0.0.1:5433: connect: connection refused"If you want to run tests repeatedly without restarting the database each time:
# Terminal 1: Start and keep database running
make run-db-container
# Terminal 2: Run tests multiple times (fast iterations)
SKIP_UT_DB=1 go test -v ./pkg/validations
SKIP_UT_DB=1 go test -v ./internal/cluster
# When done, kill the database
make kill-db-container| Target | Description | Requirements |
|---|---|---|
make unit-test |
Run all unit tests with DB | Docker/Podman, gotestsum |
make run-unit-test |
Run unit tests (with SKIP_UT_DB=1) | Go, gotestsum |
make ci-unit-test |
Run unit tests in CI mode | Docker/Podman, gotestsum |
Important: All make test targets require gotestsum. Check with which gotestsum before running.
# Run tests with coverage
make unit-test
# View coverage report
make display-coverage
# Coverage files are saved to:
# - reports/unit_coverage.out
# - reports/unit_coverage.xml (CI mode)# Single package
go test -v ./pkg/validations
# Multiple packages
go test -v ./pkg/validations ./pkg/webhooks/...
# All packages under a directory
go test -v ./internal/cluster/...# Using -run flag (regex pattern)
go test -v ./pkg/validations -run TestValidateCluster
# Using Ginkgo focus
FOCUS="install_cluster" make run-unit-test# Using Ginkgo skip
SKIP="slow_test" make run-unit-test
# Skip subsystem tests
go test ./... -count=1 -short | grep -v subsystemThese packages typically don't require database or nmstate:
go test -v \
./pkg/app \
./pkg/conversions \
./pkg/error \
./pkg/filemiddleware \
./pkg/jq \
./pkg/kafka \
./pkg/log \
./pkg/mirrorregistries \
./pkg/requestid \
./pkg/s3wrapper \
./pkg/secretdump \
./pkg/thread \
./pkg/validations \
./pkg/webhooks/agentinstall/v1beta1 \
./pkg/webhooks/hiveextension/v1beta1 \
./internal/cluster/validationsSubsystem tests require a running Kubernetes cluster with the assisted-service deployed.
podmanandkindin $PATHskippertoolgotestsum(required for running tests)
# Install kind if needed
make install-kind-if-needed
# Deploy service for subsystem testing
make deploy-service-for-subsystem-test
# Run subsystem tests (REST-API mode)
skipper make subsystem-test
# Run subsystem tests (Kube-API mode)
ENABLE_KUBE_API=true make deploy-service-for-subsystem-test
skipper make subsystem-test-kube-apimake destroy-hub-clusterSee Running Subsystem Tests for more details.
The project has three main test categories:
-
Unit Tests (this guide)
- Fast, isolated tests
- Mock external dependencies
- Run with
make unit-test
-
Subsystem Tests (running-test.md)
- Test service with mocked agent responses
- Require Kubernetes cluster
- Run with
skipper make subsystem-test
-
E2E Tests (External repositories)
- Full integration tests
- Upstream: assisted-test-infra
- Downstream: QE maintained tests
When writing unit tests in internal/ or pkg/ packages, reference: .claude/skills/assisted-service-writing-unit-tests.md
Note: This skill covers unit tests only. Subsystem tests follow different patterns (documented separately).
| Variable | Description | Default |
|---|---|---|
SKIP_UT_DB |
Skip auto-creating database; use localhost:5433 | unset |
TEST |
Specific test package(s) to run | all non-subsystem |
FOCUS |
Ginkgo focused specs (regex) | "" |
SKIP |
Ginkgo skip specs (regex) | "" |
VERBOSE |
Enable verbose output | false |
TIMEOUT |
Test timeout | 30m (unit), 120m (subsystem) |
| Variable | Description | Default |
|---|---|---|
CI |
Enable CI mode (XML reports) | false |
COVER_PROFILE |
Coverage output file | reports/unit_coverage.out |
REPORTS |
Reports directory | ./reports |
| Variable | Description | Default |
|---|---|---|
SERVICE_IMAGE |
Custom service image | (local build) |
DEBUG_SERVICE |
Deploy in debug mode | false |
ENABLE_KUBE_API |
Enable Kube-API mode | false |
# Run specific package with verbose output
VERBOSE=true go test -v ./pkg/validations
# Run tests with custom timeout
TIMEOUT=60m make run-unit-test
# Run subsystem tests with focus
FOCUS="installation" skipper make subsystem-test
# Skip slow tests
SKIP="slow" make run-unit-testError:
fatal error: nmstate.h: No such file or directory
Solution:
# Fedora/RHEL
sudo dnf install -y nmstate-devel
# Ubuntu/Debian
sudo apt-get install -y libnmstate-devNote: When using Claude Code, these package installation commands must be run unsandboxed. Claude Code will prompt for permission when needed.
Workaround: Tests for packages that don't depend on nmstate will still pass.
Error:
Cannot connect to the Docker daemon at unix:///var/run/docker.sock
Solutions:
- Start Docker/Podman daemon
- If Docker/Podman is unavailable, start PostgreSQL another way (system service, remote instance) listening at localhost:5433
- If you can't run PostgreSQL at all, test individual packages that don't require database (see below)
Cause: No PostgreSQL running at localhost:5433
Symptoms: Tests fail in BeforeSuite with error message:
failed to connect to `host=127.0.0.1 user=postgres database=`:
dial tcp 127.0.0.1:5433: connect: connection refused
What to expect:
- Without
SKIP_UT_DB: Tests will attempt to create PostgreSQL in K8s or via testcontainers, then fail if neither is available - With
SKIP_UT_DB=1: Tests immediately try localhost:5433, fail fast (~5 seconds)
If you cannot run PostgreSQL at all:
- Many packages don't require database and can be tested individually
- No make target skips database tests - you must run packages individually with
go test - Packages that initialize database in BeforeSuite will fail fast with connection errors
Example packages that work without database:
go test -v \
./pkg/app \
./pkg/conversions \
./pkg/validations \
./pkg/webhooks/agentinstall/v1beta1 \
./internal/cluster/validations
# See "Packages Known to Pass Without Dependencies" section above for full listSolutions:
- Use
make unit-test(requires Docker/Podman) - Manually start PostgreSQL at localhost:5433 with credentials
postgres/admin:# Example: using system PostgreSQL or remote instance # Configure it to listen on port 5433 # Then run: SKIP_UT_DB=1 go test ./...
- Test individual packages without database (shown above)
Cause: gotestsum not installed
Solutions:
- Install:
go install gotest.tools/gotestsum@latest - Use Go directly:
go test ./... - Update PATH:
export PATH=$PATH:$(go env GOPATH)/bin
Cause: User not in docker group
Solutions:
- Add user to docker group:
sudo usermod -aG docker $USER(requires logout/login) - Use podman instead:
export CONTAINER_COMMAND=podman - Test individual packages that don't require database (see "Database Connection Errors" section above)
Cause: Missing C dependencies (usually nmstate.h)
Solutions:
- Install nmstate-devel:
sudo dnf install -y nmstate-devel- Note: When using Claude Code, this command must be run unsandboxed. Claude Code will prompt for permission when needed.
- Run tests for specific packages that don't need nmstate (see list above)
- Use build tags to exclude problematic packages (advanced)
Cause: Default timeout too short for slow machines
Solution:
TIMEOUT=60m make run-unit-testmake unit-test
go tool cover -html=reports/unit_coverage.out -o reports/coverage.html
# Open reports/coverage.html in browserCI=true make unit-test
# Generates XML reports in reports/ directory# Deploy service in debug mode
DEBUG_SERVICE=true make deploy-service-for-subsystem-test
# Run with verbose Ginkgo output
VERBOSE=true make run-unit-test# Terminal 1: Keep database running
make run-db-container
# Terminal 2: Run tests quickly
SKIP_UT_DB=1 go test -v ./pkg/your-package
# When done
make kill-db-containerLightest environment for REST API testing:
make deploy-onprem # Starts DB, service, image-service, UI in pods
# Access UI: http://localhost:8080
# Access API: http://localhost:8090/api/assisted-install/v2/Configuration in deploy/podman/configmap.yml.
Local Kubernetes environment with operator:
make deploy-dev-infra # Creates kind cluster, deploys operator and services# Deploy with ingress
skipper make deploy-all TARGET=oc-ingress
# Optional parameters:
# APPLY_NAMESPACE=False - Skip namespace creation
# INGRESS_DOMAIN=apps.example.com - Specify domain
# DISABLE_TLS=true - Use HTTP routesSee docs/dev/README.md for all deployment scenarios.
Cluster and Host entities use explicit state machines:
- Cluster states:
models.ClusterStatus*constants - Host states:
models.HostStatus*constants - Transitions handled by
cluster.APIandhost.APIinterfaces - State changes emit events via
eventsapi.Handler
- Use
pkg/errorfor common error types - Add context with
errors.Wrap()fromgitlite.zycloud.tk/pkg/errors - Log before returning errors:
log.WithError(err).Error("message")
- Use GORM ORM:
db.Model(&model).Where(...).Find(&results) - Transactions via
pkg/transactionwrapper - Always use preloading for associations:
.Preload("Hosts") - Soft deletes enabled on most models (check
DeletedAtfield)
- Use logrus structured logging:
log.WithFields(logrus.Fields{...}).Info("message") - Request-scoped logger via
logutil.FromContext(ctx) - Include request ID:
pkg/requestid.FromContext(ctx)
- Pre-flight validations in
internal/cluster/validations,internal/host/validations - Hardware requirements in
data/default_hw_requirements.json - Network validations via
internal/connectivity,internal/network
- REST API: v1 (deprecated), v2 (current) - see
swagger.yaml - Kubernetes API:
v1beta1- seeapi/v1beta1/
Commit messages should describe the change clearly. Pull request titles must reference a JIRA/GitHub issue (e.g., MGMT-1234:) or use NO-ISSUE: prefix.
For complete guidelines and examples, see CONTRIBUTING.md.
swagger.yaml- REST API specification (source of truth for API)Makefile- Build targetsskipper.yaml- Container build environment configdata/default_hw_requirements.json- Hardware validation rulesdata/default_must_gather_versions.json- Must-gather image versionsdeploy/- Deployment manifests for various environmentsdocs/dev/- Developer documentationdocs/user-guide/- User-facing documentation
The project uses Prow (primary) and Jenkins (secondary) for CI/CD. For details on CI jobs, debugging failures, and adding new jobs, see docs/dev/testing.md.
- Testing Overview - Complete testing documentation
- Running Subsystem Tests - Detailed subsystem test guide
- Debug Guide - Debugging assisted-service
- Development Scenarios - podman, kind, CRC, etc.
- User Guide - API usage and workflows
- Contributing Guide - PR process and commit guidelines