pulumi / pulumi-kubernetes

A Pulumi resource provider for Kubernetes to manage API resources and workloads in running clusters
https://www.pulumi.com/docs/reference/clouds/kubernetes/
Apache License 2.0
410 stars 117 forks source link

Helm chart v4: no more getResource & how to use chart.resources #3251

Open awoimbee opened 1 month ago

awoimbee commented 1 month ago

Hi, When transitioning from Helm chart v3 to helm chart v4, we loose access to getResource. The docs surrounding the resources output of helm chart v4 are very bare: resources any[] Resources created by the Chart..

From https://github.com/pulumi/pulumi-kubernetes/issues/3110#issuecomment-2393940142:

Wanted to wonder if there's any guidelines on how to manage outputs better with the v4 Chart? The v3 chart had getResource typed API which was very helpful.

From the docs it's not very clear how I'm supposed to do it. I switched some deployments from the v3 to the v4 chart and I end up with this sort of diff:

-  const clusterCrd = operatorChart.getResource("apiextensions.k8s.io/v1/CustomResourceDefinition", "rabbitmqclusters.rabbitmq.com").apply(c => notNull(c));
+  const clusterCrd = operatorChart.resources.apply(resources => {
+    for (const r of resources) {
+      if (r.__pulumiType === "kubernetes:apiextensions.k8s.io/v1:CustomResourceDefinition" && r.__name.endsWith("rabbitmqclusters.rabbitmq.com")) {
+        return r;
+      }
+    }
+    throw new Error("Could not find rabbitmq operator CRD");
+  });  

I previously posted a message on slack: https://pulumi-community.slack.com/archives/CRFURDVQB/p1728239416448769

rquitales commented 1 month ago

@awoimbee Apologies that you're having difficulties getting a resource with Chart v4. This is still an area that we're improving the UX on. For now, the following snippet might be helpful in replicating a getResource type functionality.

import * as k8s from "@pulumi/kubernetes"

// create a mapping table from `apiVersion/Kind/Namespace/Name` to kubernetes resource
const lookupTable = operatorChart.resources.apply(resources => {
    return resources.reduce((table, r) => {
        const ident = pulumi.interpolate`${r.apiVersion}/${r.kind}/${r.metadata.namespace}/${r.metadata.name}`;
        return pulumi.all([table, ident]).apply(([tbl, id]) => {
            tbl[id] = (r as pulumi.Resource);
            return tbl;
        });
    }, {});
})

// now we can look up the desired resource by ident
const svc = lookupTable.apply(table => {
    return table["apiextensions.k8s.io/v1/CustomResourceDefinition/rabbitmqclusters.rabbitmq.com"]
}) as unknown as k8s.apiextensions.v1.CustomResourceDefinition;

We're considering having better support with a utility function similar to getResource and this is on our backlog.

awoimbee commented 1 month ago

Thanks for the idea, I ended up with this generic solution:

export function getResource<T extends pulumi.Resource=pulumi.Resource> (
  chart: k8s.helm.v4.Chart,
  match: pulumi.Input<string>
): pulumi.Output<T> {
  const resources = chart.resources.apply(
    resources => pulumi.all(
      resources.map(
        r => {
          const namespace = output(r.metadata?.namespace).apply(n => n || "");
          return pulumi.all([
            r as pulumi.Resource,
            pulumi.interpolate`${r.apiVersion}:${r.kind}:${namespace}:${r.metadata?.name}`
          ]);
        }
      )
    )
  );
  return pulumi.all([resources, match]).apply(([resources, match]) => {
    const matchingResources = resources.filter(([_, name]) => name === match);
    const matchingResourceNames = matchingResources.map(([_, n]) => n);
    assert.equal(matchingResources.length, 1, `Exactly 1 resource should match '${match}', matched: '${matchingResourceNames}'`);
    return matchingResources[0][0] as T;
  });
}

The full code is here: https://github.com/pulumi/pulumi-kubernetes/issues/3110#issuecomment-2414011466

jkodroff commented 1 day ago

Could we at least add something to the docs that this is a known limitation and how to get around it?