Is there any benefit to component resources compar...
# general
w
Is there any benefit to component resources compared to a function that creates multiple resources? I personally find the hierarchical URN's a pain in the butt. Having to prepend the parent name to all the children to make it unique is silly. It's also not possible to import a resources as a child of a component resource that doesn't exist yet which is really problematic when that component comes from a package.
l
There are benefits like encapsulation, opts-inheritance and auto-calculation of dependencies.
It sounds like you want un-encapsulated resources, so ComponentResources wouldn't be a great choice.
If you want to do once-off imports of child resources, you can: 1. import is as a top-level resource (no component resource) (pulumi import, pulumi up) 2. add the component resource, remove the top-level resource, and add an alias for it, (pulumi up) 3. remove the alias. (pulumi up)
Which is a pain.
w
You can still encapsulate the resources in the type that you choose to return from your function. Opts-inheritance is not that great since you have to explicitly create the child opts from the parent which is actually more work. With a function this wouldn't be necessary and would avoid bugs where you accidentally pass the parent opts to the child (which I have done a bunch of times). Wouldn't auto-calculation of dependencies still happen? I thought that was done using the input/output types which are passed exactly the same to a constructor vs a function. I do want them to be logically grouped together. I like how they are shown as being indented under the parent (although sometimes the diff is missing the parent and the indentation is all messed up but I think that is a bug). I don't think that workaround will work when you can't edit the code for the component resource. It would be nice if there was something really lightweight for grouping such as a
group
parameter which is just a slash separated string. This would be really flexible. You could even have two separate packages adding resources into the same group.
l
Yep, you can use all your preferred language's features. I use opts inheritance and it's perfect for my use cases. I don't have to configure child opts, I just pass in either the top-level opts or default inherited opts, based on well-known rules we have established internally. (We almost always use top-level opts with overridden parent, so I know what you mean.. what can't that be the default? But we do use the Pulumi-provided default too, on occasion.). Yes, dependencies based on properties still happen. And the extra ComponentResource dependency stuff is exactly the same as the implicit dependency you get by grouping resource creating into your own function or class, so you're right, there's no particular advantage to ComponentResources in this regard, except compared to creating the resources separately in your index.ts.
One new extra feature provided by ComponentResources is that they can be exported to other client languages using the new Package feature.
w
Hmm that's a good point about being able to export them to other client languages. Perhaps the simplest change is to allow child resources to be imported without their parent existing yet. My more drastic suggestion was to change the URN format. I'm sure there is a bunch of reasons why it is the way it is but it seems a bit of an odd format. Does it need to be hierarchical? If it does then why doesn't it contain the parent name, as in
<parent-urn>$<resource-type>::<resource-name>
? I was thinking
<provider-name>:<provider-unique-resource-id>
would suffice.
l
We have passed the limit of my knowledge at this point...
b
You can still encapsulate the resources in the type that you choose to return from your function.
I don't really get this. I mean you're encapsulating them in the sense of OOP but not from the perspective of Pulumi, which IMO is the entire purpose of component resources. If you need to call your function more than once, aren't you doing something to make the URNs unique on each call anyways?
l
Yes, that's what I meant. ✔️
b
Sorry @little-cartoon-10569, was responding to @worried-queen-62794 function usage, not correcting anything you said 🙂
👍 1
w
@bored-oyster-3147
I mean you're encapsulating them in the sense of OOP but not from the perspective of Pulumi, which IMO is the entire purpose of component resources.
How is Pulumi's perspective of encapsulation different to the OOP sense? AFAIK pulumi allows you to import, delete, unprotect, refresh child resources. The child resources are still visible from the outside (in the Pulumi sense). They don't update atomically either so they aren't treated as a single unit. It doesn't seem like they really are encapsulated in the Pulumi sense. I'm not saying that this is a bad thing. I'm just not seeing any additional benefit beyond the OOP reasons for encapsulating resources.
If you need to call your function more than once, aren't you doing something to make the URNs unique on each call anyways?
Yes, you would do it just the same as for component resources. But that's my point, component resources isn't making this easier. IMO the names of the children within a component resource should only have to worry about being unique amongst themselves not globally. If the URN of the child was prefixed with the URN of the parent and not just the type of the parent then this would work.
b
Yea that’s fair. You’re right that a component resource doesn’t make the URN thing any easier. I guess I am placing a lot of value on your higher order component being able to be declared in the same way as you would declare any other resource. I think this is a limitation of the architectural decision that defining resources in Pulumi be declarative. Like we just declare an object and statically in the background Pulumi is tracking that instantiation, instead of for instance declaring a Stack and doing something like Stack.AddResource(...) for everything we want to deploy. Because of that decision Pulumi is not able to make any inferences about your resource based on the fact that it was declared inside a ComponentResource constructor, without being told. There could be an AddResource(...) function accessible in the ComponentResource constructor that automatically pre-pended the name & set the parent, but it wouldn’t jive with that declarative decision that was made early on. I do remember reading an open issue about making Pulumi more functional though
w
There must be something that tells Pulumi that a resource is a child of a ComponentResource since Pulumi knows to prepend the parent type to the child's URN. Presumably that is by setting the parent in the child opts. Prefixing the parent name, no matter whether it is done auto-magically or manually, is still just a bandaid to the real problem that the URN format is not a true hierarchical identifier. There is not enough information in the child URN to be able to identify what parent it belongs to. Given this fact, it seems like it might be possible to change the URN format to be hierarchical and be backwards compatible with old URNs.
b
Wouldn't the URN then need to include that entire branch of the dependency tree all the way up to the stack node?
meaning a heavily nested dependency tree would have ever-growing URN lengths
l
And would make moving resources around (refactoring components, etc.) more likely to involve manual steps, aliasing or similar.
b
yea unfortunately I think that would add a lot of pain points, and I'm sure that's why it wasn't done that way. Like if changing a resource's parent triggered a re-create on every one of its children
w
Yes it would get longer but I think in practice this would actually make renames a lot easier. Currently if you use the prefix method for making child resource names unique then if you rename the parent you now have to add alias to the parent and for all the children. With a hierarchical URN you would only need to add the alias to the parent and Pulumi would be able to automatically alias the children.