hey team, what's the right way to make sure export waits on all values in the array? currently i'm g...
o
hey team, what's the right way to make sure export waits on all values in the array? currently i'm getting 4 values in
urls.public
whereas I would expect 6.
Copy code
const records: pulumi.Output<cloudflare.Record>[] = [];
for (const service of ["ga", "hr", "wp", "api"]) {
  records.push(
    pulumi.output(
      new cloudflare.Record(
        `${service}-dns-record`,
        {
          zoneId: process.env.CLOUDFLARE_ZONE_ID!,
          type: "CNAME",
          name: pulumi.interpolate`${service}-${suffix}`,
          value: traefikService.status.loadBalancer.ingress[0].hostname,
        },
        { provider }
      )
    )
  );
}

sipNodes.apply((nodes) => {
  nodes.forEach((node, index) => {
    records.push(
      pulumi.output(
        new cloudflare.Record(
          `sip-dns-record-${index}`,
          {
            zoneId: process.env.CLOUDFLARE_ZONE_ID!,
            type: "A",
            name: pulumi.interpolate`sip-${suffix}`,
            value: node.publicIp,
          },
          { provider }
        )
      )
    );
  });
});

export const urls = pulumi.all([records]).apply(([records]) => {
  return {
    lb: traefikService.status.loadBalancer.ingress[0].hostname,
    public: records.map((r) => r.hostname),
  };
});
l
That first loop is running immediately, creating 4 items. The second loop runs in the future, creating more items (inferring 2 more items, since you're expecting 6 in total). The export runs immediately, when there are still 4 items. I can't tell what the type of
nodes
is, but if it was an array of outputs, you could move the apply inside the loop, and you'd get a result of the right size.
Are you using
urls.public
in the Pulumi program, or via the program outputs? I would expect the program outputs to be correct, since (as I understand it) Pulumi waits for all `Output`s to be applied before building the program outpus.
But if you're looking at the values inside the program, then I expect it to be wrong, since you're not waiting for the
apply
to finish.
o
pulumi outputs, so it should wait?
this is the sipNodes
Copy code
const sipNodes = sipNodegroup.nodeGroupName.apply(async (name) => {
  const instances = await aws.ec2.getInstances({
    filters: [
      {
        name: "tag:eks:nodegroup-name",
        values: [name],
      },
    ],
  });

  return instances.ids.map((id, index) => ({
    instanceId: id,
    privateIp: instances.privateIps[index],
    publicIp: instances.publicIps[index],
  }));
});
l
This all seems very odd. There's very rarely a need to create resources in an
apply()
, and it's strongly recommended that you don't. In your first block of code, it seems like the only reason you're wrapping the Records in
pulumi.output()
is as a side-effect of having an
Output<string[]>
that you want to build into the loop. However, you can avoid that by getting those EC2 instance ids where they're needed, instead of earlier. Essentially: don't grab the properties now because you will need them later, and build Output<YourOwnDataType[]> to contain those little pieces of information. Just pass the actual resources around, and let Pulumi worry about the outputs.
Creating a CloudFlare Record using the publicIp directly from the EC2 instance. Don't attempt to store it anywhere else. That way, you can have Pulumi resolve your outputs while the engine is deploying things. When you resource outputs, you're doing it during the state-building phase, and the outputs just aren't available then.
And if you feel you really really need to do anything like this (and generally, you shouldn't), then you should prefer to store outputs in collections, instead of having an output containing a collection of values. Individual values wrapped in an Output can be passed to resource constructors.
And if you really, really need to store a collection in an Output wrapper, then you should resolve it inside the call to a resource constructor. Don't wrap the call to the resource constructor in an apply.
As an example, this is bad;
Copy code
outputArrayOfString.apply((array) => array.forEach((str) => new Resource("x", { value: str })));
This is less bad:
Copy code
for (const i = 0; i <= sizeOfArray; ++i) {
   new Resource("x", { value: outputArrayOfString.apply((array) => array[i].str) });
}