Skip to content

Helm lookup Function Support #5202

Open

Description

@rajivml

Hello,

Happy new year Guys !!

So, I have this requirement to build the imagePath by reading the dockerRegistryIP value from configMap, so that I need not ask user explicitly where the registry is located.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ template "helm-guestbook.fullname" . }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: {{ printf "%s/%s:%s" **$dockerRegistryIP** .Values.image.repository .Values.image.tag }}

Helm3 has introduced support for this where they introduced a lookup function through which configMap can be read at runtime like this,

{{ (lookup "v1" "ConfigMap" "default" "my-configmap").data.registryURL}}

But the lookup function will return nil when templates are rendered using "helm dryrun" or "helm template" as a result when you parse a field on nil, you will see an exception like this,

"nil pointer evaluating interface {}.registryURL Use --debug flag to render out invalid YAML"

The solution which was proposed on stack overflow is to use "helm template --validate" instead of "helm template"

Can you guys add support for this ?

Right now am populating docker-registry-ip like this, but with this kustomize-plugin approach am loosing the ability to render values.yaml file as an config screen through which user can override certain values i.e. the fix to solve one issue has lead to an other issue

kubectl -n argocd get cm argocd-cm -o yaml
apiVersion: v1
data:
  configManagementPlugins: |
    - name: kustomized-helm
      generate:
        command: [sh, -c]
        args: ["DOCKER_REG_IP=$(kubectl -n registry get svc registry -o jsonpath={.spec.clusterIP}) && sed -i \"s/DOCKER_REGISTRY_IP/$DOCKER_REG_IP/g\" kustomization.yaml | helm template $ARGOCD_APP_NAME --namespace $ARGOCD_APP_NAMESPACE . > all.yaml && kustomize build"]
    - name: kustomized
      generate:
        command: [sh, -c]
        args: ["DOCKER_REG_IP=$(kubectl -n registry get svc registry -o jsonpath={.spec.clusterIP}) && sed -i \"s/DOCKER_REGISTRY_IP/$DOCKER_REG_IP/g\" kustomization.yaml | kustomize build"]

Activity

  1. jessesuen commented on Jan 7, 2021

    @jessesuen
    Member

    Note that even if we allowed configuring Argo CD to append the --validate arg when running the helm template command, the repo-server would still need to be given API server credentials (i.e. mount a service account token) in order to perform the lookup. We would not ever allow kubernetes credentials to repo-server as a default (though you are welcome to modify your deployment in your environment) so there would not be a value in adding a --validate option configuration.

    Since you would anyways need a customized repo-server, you can already accomplish this today using a wrapper script around the helm binary which appends the argument in the script (coupled with the service account given to the repo server).

  2. added
    workaroundThere's a workaround, might not be great, but exists
    on Jan 7, 2021
  3. kvaps commented on Jan 7, 2021

    @kvaps

    @jessesuen, I guess this workaround is possible only with in-cluster configuration, and wont work for the external ones.

  4. jessesuen commented on Jan 8, 2021

    @jessesuen
    Member

    Ah yes, you are right about that unfortunately.

  5. Gowiem commented on Feb 16, 2021

    @Gowiem

    @jessesuen just coming across this issue and I'm running into the same. There is duplicate issue here as well: #3640

    You mentioned two things to accomplish as a work around for Argo not supporting this:

    1. Add a service account for Argo + mount the token -- This is straight forward and would be easy to implement.
    2. "using a wrapper script around the helm binary which appends the argument in the script" -- This I don't really get.

    Can you expand more on the wrapper script? How would one inject that into a standard Argo deployment?

  6. dvcanton commented on Apr 14, 2021

    @dvcanton
    Contributor

    Hi @jessesuen, @Gowiem,

    any updates on this?

    Thanks in advance, Dave

  7. Gowiem commented on Apr 14, 2021

    @Gowiem

    @dvcanton I tried the Argo plugin / wrapper script approach that @jessesuen mentioned after asking about it directly in the Argo Slack. You can find more about that by looking at the plugins documentation.

    Unfortunately, that solution seemed overly hacky and pretty esoteric to me and my team. Instead we've now moved towards not using lookup in our charts and copy / pasting certain configuration manually instead. It's not great and I wish Argo would support this, but doesn't seem like there is enough momentum unfortunately.

  8. randrusiak commented on Apr 27, 2021

    @randrusiak

    A lot of charts use build-in objects such as Capabilities to provide backward compatibility for old APIs. Capabilities.APIVersions works properly only with --validate flag because without this flag it returns only API versions without available resources.
    There is an example in grafana chart: https://github.com/grafana/helm-charts/blob/main/charts/grafana/templates/ingress.yaml#L7

  9. kvaps commented on Apr 27, 2021

    @kvaps

    As about Capabilities, helm template command supports setting capabilities manually, ref #3594

  10. randrusiak commented on Apr 28, 2021

    @randrusiak

    @kvaps take a look for an example which I posted.
    {{- $newAPI := .Capabilities.APIVersions.Has "networking.k8s.io/v1/Ingress" -}} it returns false because without --validate flag only APIs versions are returned.

  11. kvaps commented on Apr 28, 2021

    @kvaps

    @randrusiak, it works to me:

    # helm template . --set ingress.enabled=true --include-crds > /tmp/1.yaml
    # helm template . --api-versions networking.k8s.io/v1/Ingress --set ingress.enabled=true --include-crds > /tmp/2.yaml
    # diff -u /tmp/1.yaml /tmp/2.yaml
    @@ -399,7 +399,7 @@
               emptyDir: {}
     ---
     # Source: grafana/templates/ingress.yaml
    -apiVersion: extensions/v1beta1
    +apiVersion: networking.k8s.io/v1
     kind: Ingress
     metadata:
       name: RELEASE-NAME-grafana
    @@ -417,9 +417,12 @@
             paths:
     
               - path: /
    +            pathType: Prefix
                 backend:
    -              serviceName: RELEASE-NAME-grafana
    -              servicePort: 80
    +              service:
    +                name: RELEASE-NAME-grafana
    +                port:
    +                  number: 80

    my idea was that ArgoCD could provide repo-server list of api-versions from destination Kubernets API. eg:

    kubectl api-versions
    

    will return all available apiversions for the cluster.

    Not sure if lookup function support can be implemented with the same simplicity, as it already requires direct access to the cluster.

  12. randrusiak commented on Apr 28, 2021

    @randrusiak

    @kvaps I understand how it works on the helm level, but I don't know how to pass this additional flag--api-versions networking.k8s.io/v1/Ingress via argo manifest. It's still unclear to me. Could you explain that to me? I'd appreciate your help.

  13. kvaps commented on Apr 28, 2021

    @kvaps

    Actually my note was more likely for contributors than for users :)
    They could implement api-versions passing from the argocd-application-controller to the argocd-repo-server via API call.

    On current stage, I think you got nothing to do. The only workaround for you is to add serviceAccount to your repo-server and use --validate flag for helm template (you would need to create small shell wrapper script for helm command, or use custom plugin). Unfortunately this is less secure and would work only with the single cluster (the current one).

    Another option for you is to hardcode those parameters somewhere, eg. save the output of the following command:

    kubectl api-versions | awk '{printf " --api-versions " $1 } END{printf "\n"}' 
    

    And pass it to helm, using any suitable way for you, eg, you can still use wrapper script for helm, something like that:

    cat /usr/local/bin/helm
    #!/bin/sh
    exec /usr/local/bin/helm.bin $HELM_EXTRA_ARGS "$@"

    where:

    • /usr/local/bin/helm.bin - the original helm binary
    • #HELM_EXTRA_ARGS - extra variable for your repo-server

    or using custom-plugin

  14. jgoeres commented on May 7, 2021

    @jgoeres

    We also ran into the issue with the non-working lookup function. Background is that we want to make sure that for a certain service, a secure random password is generated instead of having a hardcoded default. If desired, the use can explicitly set his own password, but most people don't.
    Since we are using Helm's random function, the password is newly generated with each helm upgrade (resp. helm template), so the password would not remain stable. So we use the lookup function to check if the secret holding the password already exists and only generate the password if it doesn't. So effectively, it will only be generated initially on "helm install".
    With the non-working lookup function in Argo, we have the issue that the password will be regenerated on each sync, wreaking quite some havoc, as you might guess.

    Our goal is to keep the helmchart usage as simple as possible and require as little parameters for simple installations. So I would like to keep the "generate a secure (and stable) random password" as the default for "pure" Helm usage.
    Is there a way to find out in the helmchart that we are actually running inside Argo? That would allow me to react to this and add a validation that enforces explicitly setting a password in Argo-based deployments.

  15. 103 remaining items

  16. bennypea commented on Jan 14, 2025

    @bennypea

    @bennypea you can ignore differences on the randomly-generated field and then ask Argo CD to respect the ignored difference. https://argo-cd.readthedocs.io/en/stable/user-guide/sync-options/#respect-ignore-difference-configs

    Great! Thank you

  17. jacob-kuder commented on Feb 12, 2025

    @jacob-kuder

    I know we are closer with the merging of the impersonation work. How's this work going beyond that to resolve this overall issue? Been re-reading everything here and feel that we're closer to a resolution but one hasn't been completed yet. Thanks so much!

  18. niklasfrick commented on Mar 20, 2025

    @niklasfrick

    Any news on this?

  19. vistasunil commented on Apr 4, 2025

    @vistasunil

    Are you also going to enable helm template --dry-run=server option in the enhancement as mentioned in #16367?

  20. mzupan commented on Apr 20, 2025

    @mzupan

    So i got this working sort of

    argocd-repo-server-9b7584d8c-pg59z repo-server time="2025-04-20T14:50:00Z" level=error msg="finished call" grpc.code=Unknown grpc.component=server grpc.error="failed to execute helm template command: failed to get command args to log: `helm template . --name-template lookup --namespace default --kube-version 1.30 <api versions removed> --include-crds --dry-run=server` failed exit status 1: Error: template: lookup/templates/configmap.yaml:8:34: executing \"lookup/templates/configmap.yaml\" at <lookup \"v1\" \"Service\" \"default\" \"\">: error calling lookup: services is forbidden: User \"system:serviceaccount:argocd:argocd-repo-server\" cannot list resource \"services\" in API group \"\" in the namespace \"default\"\n\nUse --debug flag to render out invalid YAML" grpc.method=GenerateManifest grpc.method_type=unary grpc.request.deadline="2025-04-20T14:50:59Z" grpc.service=repository.RepoServerService grpc.start_time="2025-04-20T14:49:59Z" grpc.time_ms=229.935 peer.address="192.168.194.109:57452" protocol=grpc
    

    The issue with this is repo-server is a role and probably needs a clusterrole for it to work and look for things outside of the namespace it lives in. Also I think its a large amount of work if you need to do a lookup on a remote server. That was just on my laptop and a cluster that lives on my laptop.

    I'm not sure if repo-server handles the auth into a remote server or if that's something like application controller.

    Happy to put up a fork if someone wants to take what I did and build on it. It would be super nice to somehow be able to use lookup(). It makes doing things in terraform and have TF drop a configmap somewhere and then helm template picks it up to use pretty nice

  21. JuryA commented on Jul 19, 2025

    @JuryA

    Adding lookup functionality to Helm was a mistake IMO. Would much rather solve the problem inside of Kubernetes or extending Kubernetes. I am not sure if ArgoCD aims to have pairity with all of Helm functionality.

    @acelinkio I tend to agree with you—ideally, all such orchestration and lookup functionality would be solved natively within Kubernetes itself, rather than as ad-hoc extensions in tools like Helm. The challenge, though, is that getting any changes or new features approved and implemented in Kubernetes is a notoriously slow and complex process. This is understandable, given how conservative and stability-focused the project is, but it inevitably leads to “workarounds” like Helm’s lookup function or hooks.

    Frankly, I’d also prefer not to have to use these mechanisms, since they’re almost always awkward hacks, as you pointed out. But to be realistic, sometimes there’s simply no practical alternative, especially when user experience is on the line (as long as it doesn’t compromise healthy security practices). I’ve seen situations where a strictly declarative, “pure” implementation ended up being far less usable than one that made limited use of a hook or lookup.

    So I wouldn’t be too quick to dismiss these features entirely—while they are far from ideal, the alternatives aren’t always as clear-cut or pragmatic in real-world scenarios.

  22. akessner commented on Jul 20, 2025

    @akessner

    Adding lookup functionality to Helm was a mistake IMO. Would much rather solve the problem inside of Kubernetes or extending Kubernetes. I am not sure if ArgoCD aims to have pairity with all of Helm functionality.

    @acelinkio I tend to agree with you—ideally, all such orchestration and lookup functionality would be solved natively within Kubernetes itself, rather than as ad-hoc extensions in tools like Helm. The challenge, though, is that getting any changes or new features approved and implemented in Kubernetes is a notoriously slow and complex process. This is understandable, given how conservative and stability-focused the project is, but it inevitably leads to “workarounds” like Helm’s lookup function or hooks.

    Frankly, I’d also prefer not to have to use these mechanisms, since they’re almost always awkward hacks, as you pointed out. But to be realistic, sometimes there’s simply no practical alternative, especially when user experience is on the line (as long as it doesn’t compromise healthy security practices). I’ve seen situations where a strictly declarative, “pure” implementation ended up being far less usable than one that made limited use of a hook or lookup.

    So I wouldn’t be too quick to dismiss these features entirely—while they are far from ideal, the alternatives aren’t always as clear-cut or pragmatic in real-world scenarios.

    Might want to look into KRO or Crossplane 2.0 for "Server side" configurations.

  23. JuryA commented on Aug 4, 2025

    @JuryA

    Might want to look into KRO or Crossplane 2.0 for "Server side" configurations.

    I hear this argument a lot:

    “Adding lookup to Helm was a mistake. This should be solved in Kubernetes itself. Better to use Crossplane or KRO for this.”

    Okay. Let me respond clearly — not out of ego, but out of operational reality.


    Yes, lookup is a compromise.
    But no, it's not the mistake.
    The real mistake is pretending we live in a world where we always get to choose.

    We don’t.

    Some of us work in environments where:

    • CI is airgapped,
    • every tool must go through security sign-off,
    • and using a new plugin requires six months of paperwork and an internal risk committee.

    In that world, there's no "clean architecture," no "freedom of choice," and definitely no time for “waiting until Kubernetes maybe solves it upstream.”
    There’s just money, uptime, and consequences.


    You know what passed all reviews, audits, and actually made it into production?

    lookup.

    Because it's:

    • part of Helm core,
    • has no dependencies,
    • works in secure, disconnected, and hardened environments,
    • and doesn’t require me to “install one more thing” just to read a Secret.

    "But it's not declarative!"
    No sh*t. You know what else isn't declarative?
    Patching a broken system at 2 AM because someone accidentally rotated a secret.


    I love Crossplane. I actively prefer Crossplane.
    Where I can use it, I absolutely do.

    But lookup? That’s not a substitute. It’s a different tool, solving a specific operational gap inside Helm.
    And it does that safely, cleanly, and predictably, as long as you’re not using it like a monkey with a wrench.


    Let’s ask the real question here:

    ❓ What exactly is “pure”?

    • Putting secrets in Git? → Not pure. That’s a compliance nightmare.
    • Using helm-secrets? → Not pure. That’s a plugin. Which might not even work on your hardened kernel.
    • Preprocessing values in CI? → Not pure. That’s scripting around your tooling instead of using it properly.

    If it’s not integrated, auditable, and deployable by default in standard Helm, it’s not "pure" — it’s just fragile and non-portable.


    You know what’s pure?

    Shipping working software, on time, without breaking production.
    Shipping something that doesn't require 10 external tools, CI mutations, or heroic ops scripting just to survive.


    And here’s the hard truth:

    lookup isn’t a mistake.
    It’s a symptom — of a bigger design gap in Kubernetes and Helm around runtime introspection.

    Until that gap is addressed upstream (if ever), lookup is:

    • not optional,
    • not evil,
    • and definitely not going anywhere.

    So let’s stop pretending this is about taste or style.

    It’s about what works under pressure, under constraints, and under fire.

    And in that world — the real world — lookup wins.

    You want to stop misuse of lookup?
    Great.
    Teach people how not to abuse it.
    That’s the only battle worth fighting.
    Everything else is blogpost theater.


    This is not theoretical. This is delivery.
    And unless you’ve shipped into a live environment with 7-figure liability behind every deploy,
    you might want to hold off on calling working solutions "mistakes."

  24. crenshaw-dev commented on Aug 4, 2025

    @crenshaw-dev
    Member

    Reminder to please balance passion for the subject with empathetic and diplomatic delivery.

  25. Stevenpc3 commented on Aug 4, 2025

    @Stevenpc3

    @JuryA I would up vote this a billion times if I could. I would follow it up with similar examples if I was allowed and I would add more if it wasn't already complete.

    In certain life or death, save the country infrastructures this all applies and it really makes the job hard. Not everything is commercial, not everything is easy and open, not everything can be explained. It's certainly a hard balance and it's tough to get that shared with others.

  26. TheUnableDeveloper commented on Oct 22, 2025

    @TheUnableDeveloper

    @farioas I want to marry you after this, you saved my life 😭

  27. added 2 commits that reference this issue on Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    component:source/helmIssue related to Helm as an Application sourceenhancementNew feature or requestworkaroundThere's a workaround, might not be great, but exists

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions