Using Randomization for Pulumi Kubernetes Resources
Apr 12, 2022 | The Pulumi Verdict So Far |
Mar 24, 2022 | Using Randomization for Pulumi Kubernetes Resources |
Mar 24, 2022 | Remove Pending Operations From Pulumi State |
Mar 11, 2022 | Pulumi Secrets |
Jan 28, 2022 | Pulumi Round 2 |
Aug 10, 2021 | First Pass With Pulumi |
Logical Names
Pulumi requires unique logical names (URN) for tracking the state of resources. I ran into an issue with this once I expanded to a multi-cluster deployment since Pulumi began erroring on the logical name not being unique. Let’s say you are defining a service:
|
|
You can normally omit the Metadata.Name
and Pulumi will autogenerate this with a randomized suffix for you.
This allows a style of “create before destroy” deployments you don’t get with native kubectl apply style commands.
Things get a little messy here as overriding the logical name requires a normal String
.
To allow maximum uniqueness, you can concatenate values in the logical name so that it’s unique as you desire, such as resourcename + cluster
for example.
Using Random
Having used Terraform’s random provider in the past, and found it incredibly helpful to stop relying on the uniqueness of names that I have to manage, I tried initially to do this with the Pulumi terraform version of random.
It didn’t work out too well.
In the end, I realized this is where just having the power of the language itself makes perfect sense.
I had a pointer from someone in this github discussion about using the petname
package itself, but I didn’t like that idea as it was a bit unwieldy and not designed for importing as a package.
Trying to use the resource results in a problem as pulumi.StringOutput
can’t be used with string concantenation.
Instead, you have to use the ApplyT
and pass around the string output to other inputs accepting pulumi.StringPtr
type.
|
|
This doesn’t work because the output is still a pulumi.StringOutput
and not a string
.
This would work for things like the physical name, but you can’t get the string output as it’s to be considered like a “promise” and not resolved till the end of the plan.
Logical names require strings, not pulumi.String
.
Go Makes it Simple
I did a little searching for correctly converting strings into int hashes, and with the volume of deployments, a collision risk is ridiculously low (something like 1 in 1 billion?).
Here’s how I went about it.
You can adapt this for your Pulumi plan.
I went back to one of my favorites, gofakeit which provides a fantastic package for generating data.
What’s cool about this is that the generators offer a global Seed
option so you can reliably regenerate the same random data.
Setup
@brianvoe on github did a great job with this gofakeit
package.
|
|
Add this to your imports
|
|
Now for the hashing, I found a great MIT licensed library I grabbed two functions from here: util by @shomali11 on github
|
|
I set up a few methods on a configuration struct.
|
|
Now, once you load a configuration into the struct using the Pulumi configuration package, you can obtain a randomized petname on demand, that will be repeatable and only change if the cluster name is different.
resource+cluster
now needing to be deployed in duplication across another namespace (for example for provisioning development environments on demand)… you can just change the input seed from cluster to a combination of other values and you’ll generate new unique seeds from there.
|
|
Quick and Dirty Option
If you just want to do it all in main()
and ignore the frowning of the “best practice police” just inline it.
|
|
Using in Loop
Note that this would probably have issues if you were trying to update the seed in goroutines as I believe it’s a global variable. However, it works great when you need to do something like this:
|
|
Wrap-Up
I got here thanks to the help of folks in the Pulumi slack + Github discussions. I’ve found it’s a common question. I recommended they beef up some good examples of using the random provider like this. However, I’m not certain it fits Pulumi’s “promise” model quite the same as it was with Terraform. I’m not versed enough in the architecture to understand why it worked for Terraform but not with Pulumi, but this “workaround” using normal Go code seems to work fine. I’m really appreciating the value of having access to a full fledged programming language in my infrastructure work, including Kubernetes, even if this entails a little more complexity up front.
Further Reading
- Using random resource in plans with Go? · Discussion #9207 · pulumi/pulumi · GitHub
- How do you approach passing around ID’s for resources without it becoming a hot mess? · Discussion #9205 · pulumi/pulumi · GitHub
- Unique ComponentResource Names With Random Suffix · Discussion #9216 · pulumi/pulumi · GitHub
- Using ComponentResource as logical namespace · Discussion #9250 · pulumi/pulumi · GitHub
- Cannot use Output as logical resource name · Issue #5234 · pulumi/pulumi · GitHub
- Inputs and Outputs | Pulumi Docs
Webmentions