This file provides guidance to AI agents when working with code in this repository.
concierge is a Go utility for provisioning charm development and testing machines. It installs "craft" tools (charmcraft, snapcraft, rockcraft), configures providers (LXD, MicroK8s, K8s, Google Cloud), and bootstraps Juju controllers onto those providers.
# Build the binary
go build
# Build a snapshot release with goreleaser (output in ./dist)
goreleaser build --clean --snapshot
# Run directly
go run .
# Run unit tests
go test ./...
# Run all integration tests (requires LXD)
spread -v lxd:
# Run specific integration test
spread -v lxd:ubuntu-24.04:tests/juju-model-defaults
# Run integration tests on local machine
spread -v github-ci:Note: The binary must be run with sudo for most operations since it installs system packages and configures providers.
- Command Layer (
cmd/): Cobra commands (prepare,restore,status) parse flags and load configuration - Manager (
internal/concierge/manager.go): Orchestrates the overall execution flow - Plan (
internal/concierge/plan.go): Represents the complete set of actions to execute - Handlers: Execute actions concurrently using errgroups
-
cmd/: Command-line interface definitionsprepare.go: Provisions the machine according to configrestore.go: Reverses the provisioning processstatus.go: Reports concierge status on the machine
-
internal/concierge/: Core orchestration logicmanager.go: Creates plans, records runtime config to~/.cache/concierge/concierge.yamlplan.go: Builds execution plan from config, runs providers/packages/juju in sequenceplan_validators.go: Validates plans before execution
-
internal/config/: Configuration managementconfig.go: Loads config from files, presets, or flagspresets.go: Defines built-in presets (dev,k8s,microk8s,machine,crafts)overrides.go: Handles CLI flags and environment variable overrides
-
internal/providers/: Provider implementationsproviders.go: Provider interface definition- Each provider (LXD, MicroK8s, K8s, Google) implements:
Prepare(),Restore(),Bootstrap(),CloudName(),Credentials(),ModelDefaults(),BootstrapConstraints()
-
internal/packages/: Package handlerssnap_handler.go: Installs/removes snapsdeb_handler.go: Installs/removes apt packages
-
internal/juju/: Juju controller management- Bootstraps Juju controllers onto configured providers
- Applies model-defaults and bootstrap-constraints
-
internal/system/: System abstraction layerinterface.go: Worker interface for system operationsrunner.go: Executes shell commandssnap.go: Snap-specific utilities
-
Action Pattern: Operations implement
Prepare()andRestore()methods. TheDoAction()function calls the appropriate method based on the action string. -
Concurrent Execution: Plan execution uses
errgroupto run independent operations concurrently:- Snaps and Debs are installed in parallel
- Providers are prepared/restored in parallel
- Juju bootstrap runs after all providers are ready
-
Worker Interface: All system operations go through the
system.Workerinterface, enabling:- Testability via mock implementations
- Consistent command execution with package-level helpers (e.g.
system.RunExclusive(w, cmd),system.RunWithRetries(w, cmd, d),system.RunMany(w, cmds...)) - Safe file operations via helper functions (e.g.
system.WriteHomeDirFile,system.ReadHomeDirFile)
-
Runtime Config Caching: During
prepare, the merged configuration (including all overrides) is saved to~/.cache/concierge/concierge.yaml. Therestorecommand reads this file to undo exactly what was provisioned. -
Channel Overrides: Snap channels specified in config can be overridden by CLI flags or environment variables (e.g.,
--juju-channelorCONCIERGE_JUJU_CHANNEL).
Configuration is loaded in this order (later overrides earlier):
- Preset (if specified with
-p) - Config file (
concierge.yamlor path from-c) - Environment variables (e.g.,
CONCIERGE_JUJU_CHANNEL) - CLI flags (e.g.,
--juju-channel)
- Unit Tests: Standard Go tests in
*_test.gofiles. Most business logic is tested via unit tests. - Integration Tests: The
spreadframework runs full end-to-end tests in LXD VMs or on pre-provisioned machines. Tests are in thetests/directory. - Mock System:
internal/system/mock_system.goprovides test doubles for system operations.
To add a new provider:
- Create a new file in
internal/providers/(e.g.,newprovider.go) - Implement the
Providerinterface with all required methods - Add the provider name to
SupportedProvidersinproviders.go - Update
NewProvider()factory function to instantiate your provider - Add provider configuration to
internal/config/config.gostruct - Add integration tests in
tests/