Install and operate HAPTIC with GitOps¶
Use stable credential and certificate Secrets, and keep the chart's hooks enabled.
Argo CD maps the CRD, validation, and agent-certificate bootstrap hooks to
PreSync. Flux runs them as Helm hooks. Both stop before applying the release's
configuration if preflight validation fails.
These examples install release haptic in namespace haptic. Use one GitOps
controller for the release. Adopting a release already managed by another tool
requires transferring ownership; that migration isn't covered here.
Development chart
credentials.existingSecret and automatic agent-certificate renewal were
added after 0.2.0-alpha.3. Until the next release, use a matching snapshot
chart and controller image containing these changes.
Prepare stable Secrets¶
You need an installed Argo CD or Flux controller, Bash, kubectl, jq, and OpenSSL.
The examples keep the default automatic agent-certificate renewal, which doesn't
require cert-manager. Webhook and frontend certificates use one of the two
sources below.
-
Create the namespace.
-
Create the credential Secret if it doesn't exist.
set -e existing=$(kubectl get secret haptic-gitops-credentials -n haptic --ignore-not-found -o name) if [ -z "$existing" ]; then password=$(openssl rand -base64 32) kubectl create secret generic haptic-gitops-credentials -n haptic \ --from-literal=dataplane_username=admin \ --from-literal=dataplane_password="$password" unset password fiYour Secret-management system can create this Secret instead. It must provide
dataplane_usernameanddataplane_password. Keep its plaintext contents out of Git. When migrating from chart-generated credentials, use a new Secret name so removal of the old chart resource can't delete your replacement. -
Select a certificate source.
Obtain a webhook server certificate for
haptic-webhook.haptic.svcand a default frontend certificate for your application domains. This path useswebhook.crt,webhook.key,webhook-ca.crt,frontend.crt, andfrontend.keyfrom your issuer. The.crtfiles must contain the leaf and any intermediate certificates.kubectl create secret generic haptic-gitops-webhook -n haptic \ --type=kubernetes.io/tls \ --from-file=tls.crt=webhook.crt \ --from-file=tls.key=webhook.key \ --from-file=ca.crt=webhook-ca.crt \ --dry-run=client -o yaml | kubectl apply -f - kubectl create secret tls haptic-gitops-frontend -n haptic \ --cert=frontend.crt --key=frontend.key \ --dry-run=client -o yaml | kubectl apply -f - jq -n --rawfile ca webhook-ca.crt '{ credentials: {existingSecret: "haptic-gitops-credentials"}, controller: {webhook: { secretName: "haptic-gitops-webhook", caBundle: ($ca | @base64), certManager: {enabled: false} }}, defaultSSLCertificate: { secretName: "haptic-gitops-frontend", certManager: {enabled: false} } }' > haptic-values.jsonYour issuer must renew these certificates and update their Secrets before expiry. The controller reloads the webhook certificate; the frontend certificate follows normal configuration deployment. Changing the webhook's CA also requires updating
controller.webhook.caBundlein Git. Automatic agent renewal doesn't renew these external certificates.Install cert-manager before applying this configuration. The chart uses it for webhook and default frontend certificates; agent certificates retain their independent default renewal mechanism.
kubectl get crd certificates.cert-manager.io jq -n '{ credentials: {existingSecret: "haptic-gitops-credentials"}, controller: {webhook: {certManager: {enabled: true}}} }' > haptic-values.jsonThis creates a self-signed default frontend certificate for
localdev.me. Configure a production certificate before exposing application HTTPS; see SSL certificates.
The values file contains Secret names and public trust material. Store it with your GitOps configuration. The chart hashes an external credential Secret's name, so unchanged rendering doesn't generate a password or roll the pods. With explicit legacy Basic authentication, rotating that Secret also requires replacing the HAProxy pods because their password comes from the environment. Default mutual TLS doesn't use that password for agent authentication.
Argo CD installation¶
Use an Argo CD project that permits HAPTIC's namespaced resources, CRDs, cluster
RBAC, IngressClass, and admission webhook. The example uses the standard default
project. Argo CD Core installations need an AppProject created separately.
-
Generate the Application.
read -r -p "Chart version to install: " HAPTIC_CHART_VERSION test -n "$HAPTIC_CHART_VERSION" jq -n --arg version "$HAPTIC_CHART_VERSION" --slurpfile values haptic-values.json '{ apiVersion: "argoproj.io/v1alpha1", kind: "Application", metadata: {name: "haptic", namespace: "argocd"}, spec: { project: "default", destination: {server: "https://kubernetes.default.svc", namespace: "haptic"}, source: { repoURL: "registry.gitlab.com/haproxy-haptic/haptic/charts", chart: "haptic", targetRevision: $version, helm: {releaseName: "haptic", valuesObject: $values[0]} }, syncPolicy: { automated: {enabled: true, prune: true, selfHeal: true}, retry: {limit: 1}, syncOptions: ["ServerSideApply=true", "DisableClientSideApplyMigration=true"] } } }' > haptic-application.jsonKeep the Application name equal to the Helm release name when Argo CD uses its default label-based resource tracking. This preserves the instance labels used by the chart's selectors. The OCI Helm repository URL has no
oci://prefix in Argo CD's Helm source format. -
Apply the Application.
Commit the generated Application to the configuration repository watched by your Argo CD bootstrap application for subsequent changes.
-
Inspect the result.
Use full syncs. Argo CD selective resource syncs don't run hooks. Don't add Argo-specific hook annotations to individual chart resources: Argo CD ignores Helm hooks when it finds Argo-specific hooks in the application. The chart already sets hook ordering. See Argo CD's Helm hook mapping.
Flux installation¶
The example uses an OCIRepository and a HelmRelease. RetryOnFailure retries a
failed operation without uninstalling the existing release or attempting a
rollback over the chart's CRDs.
-
Generate the source and release.
read -r -p "Chart version to install: " HAPTIC_CHART_VERSION test -n "$HAPTIC_CHART_VERSION" jq -n --arg version "$HAPTIC_CHART_VERSION" '{ apiVersion: "source.toolkit.fluxcd.io/v1", kind: "OCIRepository", metadata: {name: "haptic", namespace: "flux-system"}, spec: { interval: "10m", url: "oci://registry.gitlab.com/haproxy-haptic/haptic/charts/haptic", ref: {tag: $version}, layerSelector: {mediaType: "application/vnd.cncf.helm.chart.content.v1.tar+gzip", operation: "copy"} } }' > haptic-source.json jq -n --slurpfile values haptic-values.json '{ apiVersion: "helm.toolkit.fluxcd.io/v2", kind: "HelmRelease", metadata: {name: "haptic", namespace: "flux-system"}, spec: { interval: "10m", timeout: "15m", releaseName: "haptic", targetNamespace: "haptic", chartRef: {kind: "OCIRepository", name: "haptic"}, install: {strategy: {name: "RetryOnFailure", retryInterval: "5m"}}, upgrade: {strategy: {name: "RetryOnFailure", retryInterval: "5m"}}, values: $values[0] } }' > haptic-release.json -
Apply both resources.
Commit them to the repository watched by your Flux
Kustomization. If the same repository provisions the Secrets, order theirKustomizationbefore this release withdependsOnand a Secret readiness check. -
Inspect the result.
Flux runs Helm install and upgrade actions, including live lookup calls. Argo
CD renders Helm templates offline. External Secrets make the examples stable in
both paths. See Flux HelmRelease behavior.
Upgrade and recover a rejected change¶
Pin a chart version and keep the chart and controller image from the same build. The validation Job renders the chart embedded in that image and checks its version before validating the complete future configuration. The CRD hook runs first; CRD changes may therefore have applied even when later validation fails.
When validation rejects a sync, inspect its retained Job:
kubectl get job haptic-haptic-pre-rollout -n haptic
kubectl logs job/haptic-haptic-pre-rollout -n haptic
Fix the rejected values or select a corrected release in Git. A full Argo CD sync or Flux reconciliation reruns the hooks. Existing HAProxy workers keep their last configuration while preflight blocks the rejected release.
Once the corrected version and values appear in the GitOps resources, you can request immediate reconciliation with the Argo CD or Flux CLI:
Don't use --no-hooks, selective sync, or disabled validation to force the bad
release through. Helm rollback doesn't reverse CRD evolution and bypasses these
pre-upgrade hooks. HAPTIC doesn't claim compatibility for downgrades across its
configuration API changes; recover with a corrected forward release. See
the 0.2 upgrade contract.
Reproduce lifecycle verification¶
The lifecycle suite uses fresh Kubernetes 1.33 clusters with Argo CD 3.5.3 or Flux 2.9.5. It tests external certificates with no cert-manager installation and an explicit cert-manager 1.21.2 profile. Four synthetic chart versions exercise install, upgrade, rejection, and forward recovery against the candidate binary; the separate upgrade suite covers published release baselines.
make test-gitops-lifecycle GITOPS_PROVIDER=argo
make test-gitops-lifecycle GITOPS_PROVIDER=flux
make test-gitops-lifecycle GITOPS_PROVIDER=argo GITOPS_CERTIFICATES=cert-manager
make test-gitops-lifecycle GITOPS_PROVIDER=flux GITOPS_CERTIFICATES=cert-manager
Each run checks both HTTP and HTTPS on both HAProxy replicas, unchanged Secret contents, stable pods after an unchanged sync, and preservation of the live configuration after a rejected release. Artifacts include chart and image digests, Kubernetes and GitOps versions, hook results, and traffic checks. The test uses a private HTTP Helm repository and applies desired-state resources directly; it doesn't test a Git provider, OCI registry authentication, or cross-version CRD downgrades.