Version: v0.3.0
Helm Operator is a Kubernetes controller for managing the lifecycle of Helm releases. It provides declarative Helm release management capabilities through Kubernetes Custom Resource Definitions (CRDs) with intelligent automation and advanced features.
helm-operator/
├── api/v1alpha1/ # CRD definitions
│ ├── helmrelease_types.go # HelmRelease resource definition
│ └── helmrepository_types.go # HelmRepository resource definition
├── internal/
│ ├── controller/ # Controller logic
│ │ ├── helmrelease_controller.go
│ │ └── helmrepository_controller.go
│ ├── helm/ # Helm client wrapper
│ │ ├── client.go
│ │ ├── release.go
│ │ └── repository.go
│ └── utils/ # Utility functions
├── deploy/ # Deployment configurations
├── samples/ # Sample resources
└── cmd/main.go # Program entry point- HelmRepository: Defines Helm repository configuration
- HelmRelease: Defines Helm release configuration
- Go 1.21+
- Docker
- Kubernetes cluster (local or remote)
- kubectl
- Helm 3.x
- kubebuilder
# Clone the project
git clone https://github.com/ketches/helm-operator.git
cd helm-operator
# Install dependencies
go mod download# Build binary
make build
# Build Docker image
make docker-build
# Run tests
make testHelmRepository defines the configuration information for Helm repositories and supports multiple types of repositories:
OCI (Open Container Initiative) registries are the recommended way to distribute Helm charts due to better performance, security, and standardization.
Public OCI Registry:
apiVersion: helm-operator.ketches.cn/v1alpha1
kind: HelmRepository
metadata:
name: ghcr-charts
namespace: default
spec:
url: "oci://ghcr.io/myorg/charts"
type: "oci"
interval: "1h"
timeout: "10m"
# Optimize resource usage
valuesConfigMapPolicy: disabled # RecommendedPrivate OCI Registry with Authentication:
# Create Docker registry secret
apiVersion: v1
kind: Secret
metadata:
name: oci-registry-auth
namespace: default
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: <base64-encoded-docker-config>
---
apiVersion: helm-operator.ketches.cn/v1alpha1
kind: HelmRepository
metadata:
name: acr-private
namespace: default
spec:
url: "oci://myregistry.azurecr.io/helm"
type: "oci"
auth:
secretRef:
name: oci-registry-auth
interval: "1h"
valuesConfigMapPolicy: disabledOCI Registry Examples:
# GitHub Container Registry (GHCR)
spec:
url: "oci://ghcr.io/myorg/charts"
# Azure Container Registry (ACR)
spec:
url: "oci://myregistry.azurecr.io/helm"
# Google Artifact Registry (GAR)
spec:
url: "oci://us-docker.pkg.dev/project-id/helm-charts"
# Amazon Elastic Container Registry (ECR)
spec:
url: "oci://123456789012.dkr.ecr.us-east-1.amazonaws.com/helm-charts"
# Harbor
spec:
url: "oci://harbor.example.com/library"apiVersion: helm-operator.ketches.cn/v1alpha1
kind: HelmRepository
metadata:
name: bitnami
namespace: default
spec:
url: "https://charts.bitnami.com/bitnami"
type: "helm"
interval: "30m"
timeout: "5m"
suspend: false
valuesConfigMapPolicy: disabled # RecommendedapiVersion: helm-operator.ketches.cn/v1alpha1
kind: HelmRepository
metadata:
name: private-repo
namespace: default
spec:
url: "https://private.charts.example.com"
type: "helm"
interval: "1h"
timeout: "10m"
auth:
basic:
secretRef:
name: "private-repo-auth"
namespace: "default"
suspend: falseapiVersion: helm-operator.ketches.cn/v1alpha1
kind: HelmRepository
metadata:
name: internal-repo
namespace: default
spec:
url: "http://internal-charts.company.local:8080"
type: "helm"
interval: "30m"
timeout: "10m"
auth:
basic:
secretRef:
name: "internal-repo-auth"
namespace: "default"
tls:
insecureSkipVerify: true
suspend: falseKey Field Descriptions:
url: Helm repository URL, supportshttps://andhttp://protocolstype: Repository type (currently only supportshelm, may addocisupport in the future)interval: Synchronization intervaltimeout: Operation timeoutauth: Authentication configuration (optional)basic: Basic authentication (username/password)tls: TLS configuration
suspend: Whether to suspend synchronization
HelmRelease defines the configuration for Helm releases:
apiVersion: helm-operator.ketches.cn/v1alpha1
kind: HelmRelease
metadata:
name: nginx
namespace: default
spec:
chart:
name: "nginx-charts"
version: "0.1.0"
repository:
name: "nginx-charts"
namespace: "default"
release:
name: "nginx"
namespace: "default"
createNamespace: true
values: |
replicaCount: 2
install:
timeout: "10m"
wait: true
upgrade:
timeout: "10m"
wait: true
interval: "1h"Key Field Descriptions:
chart: Chart configuration (name, version, repository)release: Release configuration (name, namespace)values: Helm values configurationinstall/upgrade: Install/upgrade optionsinterval: Reconciliation interval
-
Modify CRD definitions
# Edit API type definitions vim api/v1alpha1/helmrelease_types.go # Regenerate code make generate # Update CRDs make manifests
-
Update controller logic
# Edit controller vim internal/controller/helmrelease_controller.go # Add business logic # Update reconciliation loop
-
Add tests
# Add unit tests vim internal/controller/helmrelease_controller_test.go # Run tests make test
func (r *HelmReleaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
// 1. Get resource
release := &helmoperatorv1alpha1.HelmRelease{}
if err := r.Get(ctx, req.NamespacedName, release); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// 2. Handle deletion logic
if !release.DeletionTimestamp.IsZero() {
return r.reconcileDelete(ctx, release)
}
// 3. Add Finalizer
if !controllerutil.ContainsFinalizer(release, utils.HelmReleaseFinalizer) {
controllerutil.AddFinalizer(release, utils.HelmReleaseFinalizer)
return ctrl.Result{}, r.Update(ctx, release)
}
// 4. Execute main logic
return r.reconcileNormal(ctx, release)
}// Update status condition
condition := utils.NewReleaseReadyCondition(metav1.ConditionTrue, "InstallCompleted", "Release is ready")
meta.SetStatusCondition(&release.Status.Conditions, condition)
// Update status
return r.Status().Update(ctx, release)helmClient, err := helm.NewClient("default")
if err != nil {
return fmt.Errorf("failed to create helm client: %w", err)
}installReq := &helm.InstallRequest{
Name: "my-release",
Namespace: "default",
Chart: "bitnami/nginx",
Version: "1.0.0",
Values: "replicaCount: 2",
CreateNamespace: true,
Wait: true,
Timeout: 10 * time.Minute,
}
releaseInfo, err := helmClient.InstallRelease(ctx, installReq)Chart references support multiple formats:
-
Repository reference:
repository_name/chart_namechartRef := fmt.Sprintf("%s/%s", repoName, chartName) // Example: "bitnami/nginx", "private-repo/myapp"
-
Direct URL: Use
repositoryURLfieldspec: chart: name: "nginx" repositoryURL: "https://charts.bitnami.com/bitnami"
-
Local Chart: Use chart name directly
spec: chart: name: "./local-chart"
| Repository Type | URL Format | Example | Use Case |
|---|---|---|---|
| Public HTTPS | https:// |
https://charts.bitnami.com/bitnami |
Public Helm repositories |
| Private HTTPS | https:// |
https://private.charts.example.com |
Enterprise private repositories |
| Internal HTTP | http:// |
http://charts.internal:8080 |
Internal private repositories |
For private repositories, multiple authentication methods are supported:
-
Basic Authentication (username/password)
auth: basic: secretRef: name: "repo-credentials" namespace: "default"
-
TLS Configuration
auth: tls: insecureSkipVerify: true # Skip certificate verification (for HTTP repositories) secretRef: name: "tls-config" namespace: "default"
-
Authentication Secret Format
apiVersion: v1 kind: Secret metadata: name: repo-credentials type: Opaque data: username: <base64-encoded-username> password: <base64-encoded-password>
# Run all tests
make test
# Run tests for specific packages
go test ./internal/controller/...
# Run tests with coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out# Start test environment
make test-integration
# Run end-to-end tests
make test-e2e-
Run controller
# Set KUBECONFIG export KUBECONFIG=~/.kube/config # Run controller go run cmd/main.go
-
Apply test resources
# Apply HelmRepository kubectl apply -f samples/helm_repository.yaml # Apply HelmRelease kubectl apply -f samples/helm_release.yaml
-
View logs
# View controller logs kubectl logs -f deployment/helm-operator -n ketches
-
Chart reference error
Error: non-absolute URLs should be in form of repo_name/path_to_chart, got: myapp Solution: Ensure correct chart reference format "repository_name/chart_name"
-
Kubernetes client initialization failure
Error: kubernetes client not initialized in Helm configuration Solution: Check KUBECONFIG settings and cluster connection
-
Insufficient permissions
Error: forbidden: User cannot create resource Solution: Check RBAC configuration and service account permissions
-
Enable verbose logging
# Set log level export LOG_LEVEL=debug go run cmd/main.go
-
Check resource status
# Check HelmRelease status kubectl describe helmrelease myapp-sample # View events kubectl get events --sort-by=.metadata.creationTimestamp
-
Verify using Helm CLI
# List releases helm list -A # View release details helm get all <release-name> -n <namespace>
-
Go code style
- Follow
gofmtformatting - Use
golintfor code quality checks - Add appropriate comments and documentation
- Follow
-
Commit message format
<type>(<scope>): <subject> <body> <footer>
Example:
feat(controller): add support for OCI repositories - Add OCI repository type support - Update chart reference handling - Add integration tests Fixes #123
-
Testing requirements
- New features must include unit tests
- Test coverage should not be below 80%
- Integration tests to verify end-to-end functionality
-
Version tagging
git tag -a v1.0.0 -m "Release v1.0.0" git push origin v1.0.0 -
Build release
make release VERSION=v1.0.0
This project is licensed under the Apache 2.0 License. See the LICENSE file for details.