|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +The Policy Generator is a Kustomize exec plugin that constructs Open Cluster Management policies from Kubernetes YAML files. It's a Go binary that processes PolicyGenerator Custom Resources and outputs Policy, Placement, and PlacementBinding manifests. |
| 8 | + |
| 9 | +## Build Commands |
| 10 | + |
| 11 | +```bash |
| 12 | +# Build the plugin (installs to ~/.config/kustomize/plugin/...) |
| 13 | +make build |
| 14 | + |
| 15 | +# Build binary in repository root (for standalone use) |
| 16 | +make build-binary |
| 17 | + |
| 18 | +# Run all tests |
| 19 | +make test |
| 20 | + |
| 21 | +# Run tests with coverage |
| 22 | +make test-coverage |
| 23 | + |
| 24 | +# Run single test |
| 25 | +go test -v -run TestName ./internal |
| 26 | + |
| 27 | +# Build release binaries for all platforms |
| 28 | +make build-release |
| 29 | +``` |
| 30 | + |
| 31 | +## Testing |
| 32 | + |
| 33 | +Tests use the standard Go testing framework. Test files are colocated with source files (e.g., `plugin.go` has `plugin_test.go`). |
| 34 | + |
| 35 | +## Development Workflow |
| 36 | + |
| 37 | +Before submitting PRs: |
| 38 | +```bash |
| 39 | +make fmt |
| 40 | +make lint |
| 41 | +make test |
| 42 | +``` |
| 43 | + |
| 44 | +## High-Level Architecture |
| 45 | + |
| 46 | +### Core Flow |
| 47 | + |
| 48 | +1. **Input**: PolicyGenerator YAML manifest (custom resource) |
| 49 | +2. **Processing**: Plugin.Config() validates and applies defaults → Plugin.Generate() creates policies |
| 50 | +3. **Output**: YAML stream with Policy, Placement, and PlacementBinding manifests |
| 51 | + |
| 52 | +### Key Components |
| 53 | + |
| 54 | +**cmd/PolicyGenerator/main.go** |
| 55 | +- Entry point that reads PolicyGenerator YAML files |
| 56 | +- Calls Plugin.Config() then Plugin.Generate() |
| 57 | +- Outputs YAML to stdout (for Kustomize consumption) |
| 58 | + |
| 59 | +**internal/plugin.go** (Plugin struct) |
| 60 | +- `Config()`: Validates input, applies defaults, sets base directory |
| 61 | +- `Generate()`: Main generation orchestrator |
| 62 | + - Creates policies via `createPolicy()` |
| 63 | + - Creates policy sets via `createPolicySet()` |
| 64 | + - Creates placements (consolidated where possible) |
| 65 | + - Creates placement bindings |
| 66 | +- Placement consolidation: Tracks cluster/label selectors in `csToPlc` map to reuse placements when selectors match |
| 67 | + |
| 68 | +**internal/types/types.go** |
| 69 | +- Defines all configuration structs: |
| 70 | + - `PolicyConfig`: Individual policy specification |
| 71 | + - `PolicyDefaults`: Default values applied to all policies |
| 72 | + - `PolicySetConfig`: Policy set groupings |
| 73 | + - `PlacementConfig`: Cluster targeting configuration |
| 74 | + - `Manifest`: References to Kubernetes manifests to wrap in policies |
| 75 | + |
| 76 | +**internal/expanders/** |
| 77 | +- Policy expanders create additional policies for specific policy engines (Gatekeeper, Kyverno) |
| 78 | +- Each expander implements the interface in `expanders.go` |
| 79 | +- Enabled by default via `InformGatekeeperPolicies` and `InformKyvernoPolicies` flags |
| 80 | + |
| 81 | +**internal/patches.go** |
| 82 | +- Handles Kustomize-style patching of manifests using strategic merge or JSON patches |
| 83 | +- OpenAPI schema support for patching non-Kubernetes CRs with list fields |
| 84 | + |
| 85 | +**internal/utils.go** |
| 86 | +- Helper functions for manifest processing, YAML/JSON conversion, file operations |
| 87 | + |
| 88 | +### Default Handling |
| 89 | + |
| 90 | +The plugin has a sophisticated defaults system: |
| 91 | +- Hard-coded defaults in `defaults` variable (internal/plugin.go:77) |
| 92 | +- User-specified `policyDefaults` in PolicyGenerator YAML |
| 93 | +- Per-policy overrides in `policies` array |
| 94 | +- Per-manifest overrides in `manifests` array |
| 95 | + |
| 96 | +The `applyDefaults()` method (internal/plugin.go:446) cascades these defaults in order, considering: |
| 97 | +- Explicit false values (requires checking raw YAML via `unmarshaledConfig`) |
| 98 | +- Special relationships (e.g., `orderManifests=true` forces `consolidateManifests=false`) |
| 99 | + |
| 100 | +### Placement Logic |
| 101 | + |
| 102 | +**Consolidation**: Multiple policies can share a Placement if they have identical cluster/label selectors. The `csToPlc` map tracks selector → placement name mappings. |
| 103 | + |
| 104 | +**Kind Selection**: Plugin supports both Placement (cluster.open-cluster-management.io/v1beta1) and deprecated PlacementRule (apps.open-cluster-management.io/v1). Cannot mix in single PolicyGenerator. |
| 105 | + |
| 106 | +**Placement Sources**: |
| 107 | +1. External file via `placementPath` or `placementRulePath` |
| 108 | +2. Referenced by name via `placementName` or `placementRuleName` |
| 109 | +3. Generated from inline `labelSelector` or `clusterSelector` |
| 110 | + |
| 111 | +### Manifest Processing |
| 112 | + |
| 113 | +Manifests can be: |
| 114 | +- Individual YAML/JSON files |
| 115 | +- Directories (processed recursively) |
| 116 | +- Kustomize directories (if not disabled via env var) |
| 117 | + |
| 118 | +The generator wraps each manifest in a ConfigurationPolicy, which is then wrapped in a Policy template. Policy expanders may create additional inform-only policies for Gatekeeper/Kyverno resources. |
| 119 | + |
| 120 | +## Environment Variables |
| 121 | + |
| 122 | +- `POLICY_GEN_ENABLE_HELM`: Set to "true" to enable Helm processing in Kustomize directories |
| 123 | +- `POLICY_GEN_DISABLE_LOAD_RESTRICTORS`: Set to "true" to allow Helm directories outside Kustomize path |
| 124 | + |
| 125 | +## Important Validation Rules |
| 126 | + |
| 127 | +- Policy names must be DNS-compliant (RFC 1123) |
| 128 | +- Policy namespace + name must be ≤ 63 characters |
| 129 | +- Cannot mix Placement and PlacementRule kinds |
| 130 | +- `consolidateManifests` and `orderManifests` are mutually exclusive |
| 131 | +- `orderManifests` incompatible with `extraDependencies` |
| 132 | +- When consolidating manifests, all ConfigurationPolicy options must match at policy level |
| 133 | + |
| 134 | +## Code Modification Guidance |
| 135 | + |
| 136 | +**Adding a Policy Expander**: |
| 137 | +1. Create file in `internal/expanders/` |
| 138 | +2. Implement the expander interface |
| 139 | +3. Register in `getExpanders()` (expanders/expanders.go) |
| 140 | +4. Add boolean flag to `PolicyDefaults` and `PolicyConfig` structs (types/types.go) |
| 141 | +5. Add default handling in `applyDefaults()` (plugin.go) |
| 142 | +6. Update docs/policygenerator-reference.yaml |
| 143 | + |
| 144 | +**Modifying Defaults**: |
| 145 | +- Edit `defaults` variable in internal/plugin.go |
| 146 | +- Update `applyDefaults()` method for cascade logic |
| 147 | +- Check both `PolicyDefaults` and `PolicyConfig` structs |
| 148 | + |
| 149 | +**Adding Manifest Processing**: |
| 150 | +- Most logic is in `getPolicyTemplates()` (internal/utils.go) |
| 151 | +- Patching handled in internal/patches.go |
| 152 | +- Use `unmarshalManifestFile()` for reading YAML/JSON files |
0 commit comments