Hi All, I have been learning Pulumi for a few wee...
# general
f
Hi All, I have been learning Pulumi for a few weeks. I am working with dotnet deploying GCP and Kubernetes resources. I have run into an issue where when running pulumi up, the preview does not display all of the operations that I am trying to create. But when the actual update operation happens, it does the correct thing and creates all the correct items. This started occurring after I wanted to make the stack a bit easier to add DNS items for cloud-dns for GCP. I pulled the details of each record into the pulumi config in a json object, then on start up I parsed the config and generated all of the dns records. When I run Pulumi Up, the preview does not show any of the cloud dns records I am creating. If by chance I have already ran the operation once and created the records, the preview says it wants to delete the records. Does anyone happen to know why the preview would break by doing this? Example: (quick snippet on pulling dns records from config and creating them. )
Copy code
var data = config.RequireObject<Models.DNS.DNSConfigModel>("dns");

foreach (Models.DNS.ZoneConfigModel zone in data.zones)
            {
                ManagedZone requestedZone = createManagedZone(zone, new List<Resource>() {});

                foreach (Models.DNS.RecordConfigModel record in zone.records)
                {

                    Output.All(requestedZone.Name, ingressIp.Apply(t => t)).Apply(x =>
                    {
                        createRecordSet(x[0], record, new List<Resource>() { requestedZone }, x[1]);
                        return "";
                    });
                }
            }
Example: (config)
Copy code
<project>:dns:
    zones:
    - name: website-com
      projectKey: networking
      dnsName: <http://website.com|website.com>.
      description: "Zone for website-com"
      protect: true
      records:
      - name: <http://api.website.com|api.website.com>.
        isIngressEndpoint: false
        projectKey: networking
        type: CNAME
        ttl: 300
        protect: true
        rrDatas:
        - website.hosted.com.
e
the preview does not display all of the operations that I am trying to create. But when the actual update operation happens, it does the correct thing and creates all the correct items.
That's because your creating resources inside an Apply call.
Copy code
Output.All(requestedZone.Name, ingressIp.Apply(t => t)).Apply(x =>
{
    createRecordSet(x[0], record, new List<Resource>() { requestedZone }, x[1]);
    return "";
});
The engine can't always run the code inside an Apply at preview because it doesn't have a value to pass in. We've been thinking about how we could better support cases like this see https://github.com/pulumi/pulumi/issues/5464 Also
output.Apply(x => x)
is a no-op, you should never need to write that.
f
Fraser, Thank you so much for your response. Do you suggest a better way this could be written for this situation? Or would you say the current best practices is to not create resources dynamically by config? Also thanks about the no-op, I believe I did that because it was an Input at first, so I did that to convert it to an output.
e
There isn't a better way currently, it's a tricky problem but we do keep thinking about. Creating resources dynamically like this is mostly fine, update will handle it fine it just means your previews don't match. Up to you how much you care about that.
f
Ahh okay. Well thank you for the feedback. We have found that the previews are pretty important for us to maintain. In this situation, we have found that because it doesnt process the records during the preview it tries to tell us its going to delete the records if they already exist from an update. Which can be confusing for other users. So we went back to more statically typed.
w
What does the code for
createManagedZone
and
createRecordSet
look like? Where is
ingressIp
initialized? You might be able to pass the outputs directly to input properties used in these methods and thereby avoid creating the resources inside apply.
e
We have found that the previews are pretty important for us to maintain. In this situation, we have found that because it doesnt process the records during the preview it tries to tell us its going to delete the records if they already exist from an update
If your not changing the objects that
requestedZone
and
ingressIp
came from they should be "known" at preview time and the apply should of ran. If they we're stable in your updates and you were still seeing the apply not run that looks like a bug, and you should open an issue at https://github.com/pulumi/pulumi/issues about it. Also fools point is a good one here. If
createRecordSet
could take
Output<T>
instead of just plain
T
you wouldn't need the apply.
f
@worried-city-86458 Thats a good point. IngressIp is an output from another CustomResource. (TLDR: deploy nginx ingress in k8s on gcp, get the public ip from the service and pass that back out as an output) Sample of createRecordSet (Nuked the createManagedZone when I changed the pattern):
Copy code
private RecordSet createRecordSet(string zoneName, Models.DNS.RecordConfigModel record, InputList<Resource> dependsOn, string ingressIp="")
        {
            var recordArgs = new RecordSetArgs();
            recordArgs.Name = record.name;
            recordArgs.Type = record.type;
            recordArgs.Ttl = record.ttl;
            recordArgs.ManagedZone = zoneName;
            recordArgs.Project = _projectManager.GetProject(record.projectKey);

            if (record.isIngressEndpoint)
            {
                recordArgs.Rrdatas = new List<string>() { ingressIp };
            }
            else
            {
                recordArgs.Rrdatas = record.rrDatas;
            }
            
            var customOptions = new CustomResourceOptions();
            customOptions.DependsOn = dependsOn;
            customOptions.Protect = record.protect;
            customOptions.Parent = this;

            var recordRequest = new RecordSet($"{record.name.ToLower().Replace(".", "-")}{record.type.ToLower()}-record-set", recordArgs, customOptions);

            return recordRequest;
        }
If your not changing the objects that  
requestedZone
 and 
ingressIp
 came from they should be "known" at preview time and the apply should of ran. If they we're stable in your updates and you were still seeing the apply not run that looks like a bug, and you should open an issue at https://github.com/pulumi/pulumi/issues about it.
They do not change between runs unless we recreate the nginx-ingress or k8s cluster. Which we are not doing very often at all. A better way to explain: Before anything is created in the cloud provider, when I run "pulumi up" the preview does not show it will create the dns objects. But if you tell it to continue, it does the right thing and creates the records. If you run "pulumi up" again, the preview has not executed the code that was happening in an Output.Apply, so it doesnt think that the records should exist anymore. So it tells the user that its going to delete them if you let it continue. If you let it continue, it does the correct thing. It just no-ops because nothing actually changed now that it could execute the full code path. I understand the behavior now and can see why that happens. I will play around and see if the suggestions that you and fool have made will change that behavior. 🙂
w
Yeah, you can change
zoneName
and
ingressIp
parameters from
string
to
Output<string>
types, then no need for apply.
f
Good point, thank you fool. I will give that a try.
Another thought:
If your not changing the objects that  
requestedZone
 and 
ingressIp
 came from they should be "known" at preview time
I am not recording the IPAddress or the Zone as actual stack outputs, but just outputs from resources that I reference in the same stack. Could that be why it might not be "known"
e
No I think they should still be known, I'll have a look into this at some point sounds suspect.