adopt existing helm resources

Every once in a while, you need to install a helm chart that corresponds with already existing resources. Usually, when trying to use a helm chart to have more control over a kube-system daemonset or operator: aws-node, coreDNS, or kube-proxy for instance. But if you try to install their helm chart you can get Error: INSTALLATION FAILED: rendered manifests contain a resource that already exists. You can get helm to adopt resources by using the correct annotation/labels.

tldr

For each pre-existing resource run:

  kubectl annotate $OBJ meta.helm.sh/release-namespace=${NAMESPACE}
  kubectl annotate $OBJ meta.helm.sh/release-name=${RELEASE_NAME}
  kubectl label $OBJ app.kubernetes.io/managed-by=Helm

then run helm install or helm upgrade

Setting the stage

Let’s create an example deployment and service. Using kubernetes yaml. Then we will attempt to wrap these resources into a helm chart.

http-echo.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: http-echo
  labels:
    app: http-echo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: http-echo
  template:
    metadata:
      labels:
        app: http-echo
    spec:
      containers:
      - name: echo
        image: hashicorp/http-echo
        args:
        - "-text=success"
---
kind: Service
apiVersion: v1
metadata:
  name: echo-service
  labels:
    app: http-echo
spec:
  type: LoadBalancer
  selector:
    app: http-echo
  ports:
  # Default port used by the image
  - port: 5678

Apply and verify that everything works

kubectl apply -f http-echo.yaml
kubectl get pods,deployment,service -l app=http-echo
NAME                             READY   STATUS    RESTARTS   AGE
pod/http-echo-58c8855bc6-62zjv   1/1     Running   0          5m
pod/http-echo-58c8855bc6-rbgqm   1/1     Running   0          5m
pod/http-echo-58c8855bc6-vrd2d   1/1     Running   0          5m

NAME                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/http-echo   3/3     3            3           5m

NAME                   TYPE           CLUSTER-IP     EXTERNAL-IP
service/echo-service   LoadBalancer   10.96.182.49   172.18.0.150

Wrapping in a helm chart

We can use a minimal Chart.yaml file for this example.

Chart.yaml

name: http-echo
version: 0.1.0

And setup a basic helm chart directory

http-echo/
├── Chart.yaml
└── templates
    └── http-echo.yaml

1 directory, 2 files

The problem is that all of the resources that are specified in templetes/http-echo.yaml already exist. When we try to install this chart via helm install http-echo http-echo/ we get met with:

Error: INSTALLATION FAILED: rendered manifests contain a resource 
that already exists. Unable to continue with install: Service 
"echo-service" in namespace "default" exists and cannot be 
imported into the current release: invalid ownership metadata; 
label validation error: missing key 
"app.kubernetes.io/managed-by": must be set to "Helm"; annotation
validation error: missing key "meta.helm.sh/release-name": must
be set to "http-echo"; annotation validation error: missing key
"meta.helm.sh/release-namespace": must be set to "default" 

Annotate and label existing resources

Each resource needs two annoations and a label to be adopted by Helm.

annotations

  • meta.helm.sh/release-namespace
  • meta.helm.sh/release-name

label

  • app.kubernetes.io/managed-by
for OBJ in deployment/http-echo service/echo-service
do
  kubectl annotate $OBJ meta.helm.sh/release-namespace=default
  kubectl annotate $OBJ meta.helm.sh/release-name=http-echo
  kubectl label $OBJ app.kubernetes.io/managed-by=Helm
done

Run helm install --dry-run http-echo http-echo/ to show the error no longer exist; without actually installing the chart.

More Information

This doesn’t work with CRD objects

It does! You need to annotate/label the CRDs as usual. But then set --skip-crds. Most charts have a crd.create = false setting.

Can I see the code that makes this adopt thing work?

helm issue #7649

Written on October 30, 2021