Skip to content

Latest commit

 

History

History
169 lines (118 loc) · 9.02 KB

File metadata and controls

169 lines (118 loc) · 9.02 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

k3d creates containerized k3s (lightweight Kubernetes) clusters using Docker. It runs multi-node k3s clusters on a single machine. The Go module is github.com/k3d-io/k3d/v5.

Build & Development Commands

make build              # Build for local platform -> bin/k3d
make build-debug        # Build with debug symbols
make build-cross        # Cross-compile for all platforms (needs gox)
make install-tools      # Install required tools (golangci-lint, gox)

Dependencies are vendored (-mod=vendor). The go.work workspace includes ., ./docgen, and ./tools.

Local Helper Image Overrides

When developing helper images locally, use these env vars to point k3d at your local builds instead of the published ones (see pkg/types/env.go):

K3D_IMAGE_LOADBALANCER=ghcr.io/k3d-io/k3d-proxy:dev   # Override the load balancer (proxy) image
K3D_IMAGE_TOOLS=ghcr.io/k3d-io/k3d-tools:dev           # Override the tools helper image

Testing

make test                                           # Run all unit tests
go test ./pkg/config/... -run TestProcessConfig     # Run a specific test
make e2e                                            # Run E2E tests (requires Docker, uses DIND)
make e2e -e E2E_INCLUDE="test_basic"                # Run specific E2E test
make e2e -e E2E_LOG_LEVEL=trace -e E2E_FAIL_FAST=true  # E2E with debug output

E2E tests are bash scripts in tests/ that run inside Docker-in-Docker containers. Unit tests use testify for assertions and go-test/deep for deep equality. Config test assets are in pkg/config/test_assets/.

E2E Test Framework

Execution chain: make e2e -> tests/dind.sh (builds DIND image, starts privileged container) -> tests/runner.sh (discovers and batches tests) -> test_*.sh scripts.

Writing a new E2E test:

  1. Create tests/test_<name>.sh (the test_ prefix is required for auto-discovery)
  2. Make it executable (chmod +x)
  3. Follow the standard boilerplate:
    #!/bin/bash
    CURR_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
    [ -d "$CURR_DIR" ] || { echo "FATAL: no current dir (maybe running in zsh?)"; exit 1; }
    source "$CURR_DIR/common.sh"
    
    ### Step Setup ###
    LOG_FILE="$TEST_OUTPUT_DIR/$( basename "${BASH_SOURCE[0]}" ).log"
    exec >${LOG_FILE} 2>&1
    export LOG_FILE
    KUBECONFIG="$KUBECONFIG_ROOT/$( basename "${BASH_SOURCE[0]}" ).yaml"
    export KUBECONFIG
    ### Step Setup ###
    
    export CURRENT_STAGE="Test | <name>"
    # ... test logic using $EXE, info, failed, check_clusters, etc.
    exit 0
  4. Use unique cluster/resource names to avoid collisions with parallel tests
  5. Always clean up resources (cluster delete, registry delete) at the end

Key helpers from tests/common.sh: info, failed, passed, highlight, check_clusters, check_multi_node, check_registry, wait_for_pod_running_by_name, wait_for_pod_running_by_label, exec_in_node, docker_assert_container_label, k3s_assert_node_label.

E2E environment variables: E2E_INCLUDE (run only named tests), E2E_EXCLUDE (skip named tests), E2E_PARALLEL (default: 4), E2E_EXTRA (run extra_test_* files), E2E_FAIL_FAST, E2E_LOG_LEVEL, E2E_K3S_VERSION, E2E_DIND_VERSION, E2E_KEEP (keep runner container).

Linting & Formatting

make fmt        # Fix formatting (gofmt)
make check-fmt  # Check formatting
make lint       # Run golangci-lint (v2.4.0)
make ci-lint    # Same with 5-minute timeout (CI)
make check      # check-fmt + lint

Architecture

Three-layer design:

  1. CLI layer (cmd/) - Cobra commands for cluster, node, registry, kubeconfig, image, config, debug subcommands. Entry point: cmd/root.go -> NewCmdK3d().

  2. Client layer (pkg/client/) - Business logic and orchestration. Key files: cluster.go (ClusterCreate/Start/Delete/Run), node.go (NodeCreate/Start/Delete), registry.go, loadbalancer.go, kubeconfig.go.

  3. Runtime layer (pkg/runtimes/) - Container runtime abstraction via the Runtime interface in runtime.go. Only Docker is implemented (pkg/runtimes/docker/). The Docker runtime translates k3d types to Docker API calls (translate.go).

Key Packages

  • pkg/types/ - Core domain types: Cluster, Node, Registry, Role (ServerRole, AgentRole, LoadBalancerRole, RegistryRole). Extensive label system (k3d.cluster, k3d.role, etc.) used to track Docker container metadata.
  • pkg/config/ - Versioned config system (current: v1alpha5, API version k3d.io/v1alpha5). Includes migration (migrate.go), JSON schema validation (schema.json embedded via //go:embed), and config processing/transformation pipeline.
  • pkg/actions/ - Node lifecycle hooks (e.g., WriteFileAction).
  • pkg/logger/ - Logging via logrus.

Helper Components

  • proxy/ - nginx-based load balancer with confd for dynamic upstream config. Built as ghcr.io/k3d-io/k3d-proxy.
  • tools/ - Separate Go module (tools/) for the k3d-tools helper binary. Built as ghcr.io/k3d-io/k3d-tools.

Config System

Config files are YAML with apiVersion: k3d.io/v1alpha5 and kind: Simple. The processing pipeline: read YAML -> validate JSON schema -> migrate from older versions if needed -> transform to internal types -> merge with defaults. Config versions: v1alpha2 (legacy) -> v1alpha3 -> v1alpha4 -> v1alpha5 (current).

Version & LDFLAGS

Version info is injected at build time via LDFLAGS into version/version.go:

  • version.Version - git tag
  • version.K3sVersion - latest stable k3s version (fetched from k3s update channel)
  • version.HelperVersionOverride - optional override for helper image versions

License & Contributing

This project is MIT licensed (Copyright 2019-2023 Thorsten Klein). All new Go source files must include the MIT copyright header present in existing files. See CONTRIBUTING.md for guidelines — check existing issues/PRs before opening new ones to avoid duplicates. The project follows the Contributor Covenant Code of Conduct (CODE_OF_CONDUCT.md).

Conventions

  • All Go source files have the MIT license copyright header
  • Import alias: k3d "github.com/k3d-io/k3d/v5/pkg/types" is used throughout as the canonical import for the types package
  • Docker is the only runtime, but all container operations go through the Runtime interface
  • Node types are identified by Role and tracked via Docker container labels

Issue & PR Handling

Before starting work on any issue:

  1. Check for duplicates — search open and closed issues for the same topic (gh issue list -S "keyword" / gh issue list -S "keyword" --state closed)
  2. Check for existing PRs — look for open PRs that already address the issue (gh pr list -S "keyword")
  3. Check if it's a k3d issue — many reports are actually k3s, Docker, or CNI issues. If so, redirect the reporter to the appropriate upstream repo
  4. Read the full thread — issues often evolve through discussion; the original request may have been refined or scoped down

When replying to issues:

  • For questions: answer concisely, point to env vars/flags/docs, close if resolved
  • For bugs: confirm reproducibility scope, check if already fixed on main
  • For features: check if there's a workaround, note if a PR would be welcome

When working on PRs:

  1. Check the linked issue — understand the full context and any decisions made in discussion
  2. Check the PR's CI status — don't start reviewing or building on a PR that's failing CI for unrelated reasons
  3. Check for related/conflicting PRs — look for other open PRs touching the same files (gh pr list + review changed files)
  4. Verify the branch is up to date with main before investing effort

Scope awareness:

  • k3d only controls the container orchestration layer around k3s. Issues about k3s behavior, CNI plugins, Kubernetes internals, or Docker engine bugs are upstream concerns
  • The proxy (k3d-proxy) and tools (k3d-tools) images are part of this project — issues about those are in scope
  • Registry issues may involve the upstream registry image vs k3d's registry wiring — distinguish which layer is at fault

Deep Dive References

For detailed analysis beyond this summary, see .planning/codebase/:

Document Contents
STACK.md Full dependency inventory, Go version, build toolchain, Docker SDK usage
ARCHITECTURE.md Layer diagrams, data flow through CLI->Client->Runtime, abstraction boundaries
STRUCTURE.md Complete directory layout, file naming conventions, package organization
CONVENTIONS.md Error handling patterns, logging conventions, code style, naming rules
TESTING.md Unit test patterns, E2E test framework details, test helpers, fixture locations
INTEGRATIONS.md Docker API interaction, k3s channel server, registry (wharfie), kubeconfig handling
CONCERNS.md Technical debt, single-runtime limitation, config migration complexity, known fragile areas