Add support for managing multiple trustee releases through versioned configuration templates. Each TrusteeConfig can select a specific trustee release version, which determines both the configuration templates used and the container image tags deployed.
- ✅ Single TrusteeConfig can select release version via
spec.versionfield - ✅ Templates organized by trustee release (e.g.,
config/templates/v0.10.0/) - ✅ Automatic image selection based on version
config/templates/
├── latest/ # Default templates (current behavior)
│ ├── kbs-config-permissive.toml
│ ├── kbs-config-restricted.toml
│ ├── attestation-policy.rego
│ ├── resource-policy-permissive.rego
│ ├── resource-policy-restrictive.rego
│ ├── rvps-reference-values.json
│ └── tdx-config.json
├── v0.10.0/ # Trustee v0.10.0 specific templates
│ └── (same 7 files)
├── v0.11.0/ # Trustee v0.11.0 specific templates
│ └── (same 7 files)
└── versions.json # Version metadata and image mappings
{
"supported_versions": ["v0.10.0", "v0.11.0", "latest"],
"default_version": "latest",
"version_mappings": {
"v0.10.0": {
"kbs_image": "ghcr.io/confidential-containers/key-broker-service:v0.10.0",
"as_image": "ghcr.io/confidential-containers/attestation-service:v0.10.0",
"rvps_image": "ghcr.io/confidential-containers/reference-value-provider-service:v0.10.0"
},
"latest": {
"kbs_image": "ghcr.io/confidential-containers/key-broker-service:latest",
"as_image": "ghcr.io/confidential-containers/attestation-service:latest",
"rvps_image": "ghcr.io/confidential-containers/reference-value-provider-service:latest"
}
}
}- Environment variables (highest priority) - for testing/dev overrides
- Version-based images from versions.json - production usage
- Default constants - fallback
File: api/v1alpha1/kbsconfig_types.go (line ~225)
Add to TrusteeConfigSpec:
// Version specifies the trustee release version to deploy
// Determines which configuration templates and container images to use
// Valid values: "v0.10.0", "v0.11.0", or "latest"
// Defaults to "latest" if not specified
// +optional
// +kubebuilder:validation:Pattern=`^(v[0-9]+\.[0-9]+\.[0-9]+|latest)$`
Version string `json:"version,omitempty"`Run make generate and make manifests to update CRDs.
- Create
config/templates/latest/directory - Copy existing 7 template files to
latest/ - Create
config/templates/versions.jsonwith metadata - Update
Dockerfileif needed (already copies entire directory)
New file: internal/controller/template_manager.go
Core responsibilities:
- Load and validate version from
versions.json - Construct version-specific template paths
- Load templates with error handling and fallback to "latest"
- Provide image tag mappings based on version
- Validate requested version is supported
Key functions:
type TemplateManager struct {
version string
basePath string
metadata VersionMetadata
}
func NewTemplateManager(version string) (*TemplateManager, error)
func (tm *TemplateManager) LoadTemplate(templateName string) (string, error)
func (tm *TemplateManager) GetImageVersions() (kbs, as, rvps string, err error)
func (tm *TemplateManager) ValidateVersion() errorNew file: internal/controller/template_manager_test.go
- Test version validation
- Test template loading for each version
- Test image mapping
- Test fallback behavior
File: internal/controller/trusteeconfig_controller.go
Change 1: Add field to reconciler struct (line ~41)
type TrusteeConfigReconciler struct {
client.Client
Scheme *runtime.Scheme
trusteeConfig *confidentialcontainersorgv1alpha1.TrusteeConfig
log logr.Logger
namespace string
templateManager *TemplateManager // NEW
}Change 2: Initialize template manager in buildKbsConfigSpec() (line ~332)
// Get version from TrusteeConfig, default to "latest"
version := r.trusteeConfig.Spec.Version
if version == "" {
version = "latest"
}
// Create version-aware template manager
templateMgr, err := NewTemplateManager(version)
if err != nil {
r.log.Error(err, "Failed to create template manager, using latest")
templateMgr, _ = NewTemplateManager("latest")
}
r.templateManager = templateMgrChange 3: Update createOrUpdateKbsConfig() to pass version info (line ~144)
// Get image versions from template manager
kbsImage, asImage, rvpsImage, err := r.templateManager.GetImageVersions()
// Create KbsConfig with version annotations
kbsConfig := &confidentialcontainersorgv1alpha1.KbsConfig{
ObjectMeta: metav1.ObjectMeta{
Name: kbsConfigName,
Namespace: r.namespace,
Annotations: map[string]string{
"trustee-operator.confidentialcontainers.org/version": version,
"trustee-operator.confidentialcontainers.org/kbs-image": kbsImage,
"trustee-operator.confidentialcontainers.org/as-image": asImage,
"trustee-operator.confidentialcontainers.org/rvps-image": rvpsImage,
},
},
Spec: spec,
}Files:
internal/controller/resource_policy_helper.go(lines 24-59)internal/controller/rvps_helper.go(lines 23-34)internal/controller/tdx_helper.go(lines 23-34)
Change standalone functions to receiver methods on TrusteeConfigReconciler:
// Before: func generateResourcePolicyRego(profileType string)
// After: func (r *TrusteeConfigReconciler) generateResourcePolicyRego(profileType string)
// Use template manager:
return r.templateManager.LoadTemplate("resource-policy-restrictive.rego")Update generateKbsTomlConfig() in trusteeconfig_controller.go (line ~568) similarly.
File: internal/controller/kbsconfig_controller.go
Add helper function:
func (r *KbsConfigReconciler) getImageForVersion(envVar, defaultImage, versionedImage string) string {
// 1. Environment variable (highest priority)
if envImage := os.Getenv(envVar); envImage != "" {
return envImage
}
// 2. Versioned image from annotation (medium priority)
if versionedImage != "" {
return versionedImage
}
// 3. Default constant (lowest priority)
return defaultImage
}Update container builders (lines 602, 635, 670):
func (r *KbsConfigReconciler) buildAsContainer(...) corev1.Container {
versionedImage := r.kbsConfig.Annotations["trustee-operator.confidentialcontainers.org/as-image"]
asImageName := r.getImageForVersion("AS_IMAGE_NAME", DefaultAsImageName, versionedImage)
// ... rest of function
}Similar changes for buildKbsContainer() and buildRvpsContainer().
template_manager_test.go: Test all template manager functionstrusteeconfig_controller_test.go: Test version handling, default behavior, image selection- Test backward compatibility (no version specified → uses latest)
New directory: test/e2e/multi-version/
Test cases:
- Deploy with
version: v0.10.0, verify v0.10.0 images used - Deploy with
version: latest, verify latest images - Deploy without version field, verify defaults to latest
- Update version, verify ConfigMaps preserved but deployment updated
- Invalid version handling
- CLAUDE.md: Document new
versionfield, template structure, version management - README.md: Add multi-version support section
- config/samples/: Add examples with version field
- No action required for existing deployments (defaults to "latest")
- To pin version: Add
spec.version: v0.11.0to TrusteeConfig - To upgrade versions: Update version field, optionally delete ConfigMaps to regenerate with new templates
For each trustee release (v0.10.0, v0.11.0, etc.):
- Create
config/templates/vX.Y.Z/directory - Copy 7 template files from that release
- Update
versions.jsonwith version and image mappings - Test deployment with that version
| File | Changes | Lines |
|---|---|---|
api/v1alpha1/kbsconfig_types.go |
Add version field to TrusteeConfigSpec | ~225 |
internal/controller/template_manager.go |
NEW: Version-aware template loading | All |
internal/controller/trusteeconfig_controller.go |
Add templateManager field, initialize it, pass version to KbsConfig | 41, 144, 332, 568+ |
internal/controller/kbsconfig_controller.go |
Read version annotations, update image selection | 602-698 |
internal/controller/resource_policy_helper.go |
Convert to methods, use template manager | 24-59 |
internal/controller/rvps_helper.go |
Convert to methods, use template manager | 23-34 |
internal/controller/tdx_helper.go |
Convert to methods, use template manager | 23-34 |
config/templates/versions.json |
NEW: Version metadata and image mappings | All |
After implementation:
-
Build & Deploy:
make docker-build IMG=<registry>/trustee-operator:test make docker-push IMG=<registry>/trustee-operator:test make deploy IMG=<registry>/trustee-operator:test
-
Test Default Behavior (backward compatibility):
# Deploy without version field kubectl apply -f config/samples/microservices/trusteeconfig_sample.yaml # Verify uses "latest" templates and images kubectl get deployment -o yaml | grep image:
-
Test Version Selection:
# Deploy with specific version cat <<EOF | kubectl apply -f - apiVersion: confidentialcontainers.org/v1alpha1 kind: TrusteeConfig metadata: name: test-v0100 spec: version: v0.10.0 profileType: Permissive EOF # Verify correct images kubectl get deployment -o yaml | grep "v0.10.0"
-
Test Version Upgrade:
# Update version kubectl patch trusteeconfig test-v0100 --type=merge -p '{"spec":{"version":"v0.11.0"}}' # Verify ConfigMaps preserved kubectl get configmap -o yaml | grep "kbs-config" # Verify deployment uses new images kubectl get deployment -o yaml | grep "v0.11.0"
-
Run Tests:
make test # Unit tests make test-e2e # Integration tests
✅ Fully backward compatible:
- Existing TrusteeConfigs without
versionfield continue working - Default behavior: use "latest" templates and images
- ConfigMaps never regenerated (current preservation logic maintained)
- Environment variable overrides still work
- No breaking changes to API
Overall looks good.
I have few questions to better understand some aspects