Skip to content

Commit

Permalink
Add yaml example that does math and uses pulumi-std functions
Browse files Browse the repository at this point in the history
  • Loading branch information
MitchellGerdisch committed Dec 11, 2024
1 parent 549de02 commit c34d467
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 0 deletions.
118 changes: 118 additions & 0 deletions yaml-math-and-objects/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
name: yaml-math-and-objects
description: A Pulumi YAML program that demonstrates how to do math and manipulate objects in YAML.
runtime: yaml

## Stack Config
config:
# The length of the string to generate.
length:
type: integer
default: 16
# When this config is set to true, we then want to implement some logic related to the string generation.
useLowerCase:
type: boolean
default: true
# An array of key-value pairs as strings used to build the keepers map array.
configKeepersArray:
type: array
items:
type: string
default:
- "configKey1:configValue1"
- "configKey2:configValue2"


## Variables used by the RandomString resource below constructed from the config and functions and a little help from the Command resource.
variables:
# Get the length from the config.
stringLength: ${length}

# Build keepersArrayPart1 using the structured config (which is limited to an array of strings) by leveraging pulumi-std functions
# to build the requisite map array.
keepersArrayPart1:
# Build the required map array from an array of strings such that each pair of strings is used to create the key-value pair.
fn::invoke:
function: std:map
arguments:
args:
# Split the string of strings created by joining the array of strings from the config.
# This is needed for the map function to build the array of key-value pairs.
fn::invoke:
function: std:split
arguments:
separator: ":"
text:
# Build one big string of strings from the array of strings in the config.
fn::invoke:
function: std:join
arguments:
# The array of strings from the config.
input: ${configKeepersArray}
separator: ":"
return: result
return: result
return: result

# Hard-coded map for the second part of the keepers array to have something to merge.
keepersArrayPart2:
part2aKey: "part2aValue"
part2bKey: "part2bValue"

# Combine the two parts of the keepers map arrays into a single map array to be used in the RandomString resource.
keepersArrayCombined:
fn::invoke:
function: std:merge
arguments:
input:
- ${keepersArrayPart1}
- ${keepersArrayPart2}
return: result

# Lowercase settings object to use for the RandomString resource.
lowerCaseSettings:
# Just passing along the config value as part of this variable.
uselower: ${useLowerCase}
# Want to only set minLower based on length and only set it if useLowerCase is true.
# Since we don't have a way to run logic and math in YAML, we can use the Pulumi Command resource (see below) to do this.
# But we need to use the srd-pareint function to convert the string output from the Command resource to an integer for the RandomString resource.
minLower:
fn::invoke:
function: std:parseint
arguments:
input: ${calculateMinLower.stdout}
return: result

## Resources
resources:
# Creating a RandomString resource (https://www.pulumi.com/registry/packages/random/api-docs/randomstring/)
randomString:
type: random:RandomString
properties:
# Must be a number. Taken from config.
length: ${stringLength}
# Must be an array of key-value pairs. Built from the config and hard-coded values (see above).
keepers: ${keepersArrayCombined}
# Must be a boolean. Taken from config.
lower: ${lowerCaseSettings.uselower}
# Must be a number. Calculated value based on boolean logic and math using the Command provider (see below).
minLower: ${lowerCaseSettings.minLower}

# Since we don't yet have math operations (other than `sum`) in YAML,
# see https://github.com/pulumi/pulumi-std/issues/70
# we can use the Pulumi Command resource to do math.
# The output is then referenced by the `lowerCaseSettings` variable.
calculateMinLower:
type: command:local:Command
properties:
# we want about a quarter of the password to be lowercase letters if useLowerCase is true.
create: if ${useLowerCase}; then echo "${stringLength} / 4" | bc; else echo 0; fi


# Stack outputs
outputs:
# Generated string
randomString: ${randomString.result}
# Lowercase settings reflecting the config-based value and the calculated minLower value.
lowerCaseSettings: ${lowerCaseSettings}
# The keepers array that was built using the config and hard-coded values.
keepersArrayCombined: ${keepersArrayCombined}
54 changes: 54 additions & 0 deletions yaml-math-and-objects/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Math and Collection and String Manipulation in YAML

In addition to programming languages such as Python, Go, Typescript, C#, Pulumi also supports YAML.
However, YAML is limited when it comes to constructs such as loops and conditionals and it is not always obvious how one can do math in a Pulumi YAML program.
Similarly, using the `pulumi-std` library can be a little tricky.

This project is meant to provide some YAML program examples for these use-cases.

## References
* [Pulumi YAML Docs Page](https://www.pulumi.com/docs/iac/languages-sdks/yaml/)
* [Pulumi YAML Language Reference](https://www.pulumi.com/docs/iac/languages-sdks/yaml/yaml-language-reference/)
* [pulum-std Function List](https://github.com/pulumi/pulumi-std/blob/master/FUNCTION_LIST.md)
* These are functions that are available to YAML programs.

## Tips and Tricks for Using the pulumi-std Functions
Start with the functions list page in the `pulumi-std` repo, [pulum-std Function List](https://github.com/pulumi/pulumi-std/blob/master/FUNCTION_LIST.md)
Here you will find the list of functions that are available.

To understand how to use a given function, click on the link from the function list page.
This will take you to the go code for the function.
From here you can learn two important things:
* What the function does.
* What the function's input parameters are.

To understand what the function does, look for the `Annotate` function code block. This will provide an explanation of what the function does.

To understand the function's inputs, look for the `...Args` structure code block. This will list the inputs' names and types (e.g. string, array, etc).

For example, look at [std-merge](https://github.com/pulumi/pulumi-std/blob/master/std/merge.go).
You'll see it expects a single parameter named `input` that is an array of map of strings.
So something like: `[goo:"foo", moo:"boo"]` and if you look at the Pulumi YAML program in this folder you'll see how that is represented in YAML.

Now, look at [std-split](https://github.com/pulumi/pulumi-std/blob/master/std/split.go) and you'll see it wants two parameters: `separator` and `text`.

## Using the Pulumi Program
Read the `Pulumi.yaml` file to see what the program does and how it does it.

Then, from the folder containing the program:
```bash
$ pulumi stack init dev
$ pulumi up
```

This creates a random string and outputs some values used in the code.

Now, tinker with the stack config values in `Pulumi.yaml` or via `pulumi config set` commands to see what happens when you:
* Change the length of the string.
* Change `length` to, say, `32`
* Run `pulumi up` and you should see that `minLower` is now set to one-fourth(ish) of whatever value you set for the length and there are at least that many lowercase letters in the string.
* Disable using lowercase characters.
* Change `useLowerCase` to `false`
* Run `pulumi up` and you should see that there are no lowercase characters and the stack output show `minLower` set to 0.
* Change one of the key-value pairs in `configKeepersArray`.
* Run `pulumi up` and see that the array map is updated and that a new string is generated since that's what the `keepers` property is for - to trigger a regeneration of the key even though no property directly related to the resource like length, etc is changed..

0 comments on commit c34d467

Please sign in to comment.