Package: gryml
GrymlWARNING: Alpha version, might not be suitable for productionWARNING: API is unstable and might change significantlySometimes you just need to substitute a couple of variables in the K8S resource definitions, often using the same
value in multiple files.This tool was born as an attempt to bridge the gap between HELM and Kustomize as they both
are lacking simplicity for trivial but common cases. But while Gryml was designed with k8s in mind it is essentially a general purpose YAML processor, and can be used to
automate the generation of any yaml files when value substitution is needed.We providethe full list of featuresseparately, but in this document we will cover the most important
ones.InstallationGryml is now available in the pypi (Python 3.7+ is required):$pipinstallgrymlAnd you should be able to use CLI version:$gryml--helpGryml supports UNIX pipes for both input and output so something like this is also possible:$echo"{say: something} #{world.greeting}"|gryml---setworld.greeting="hello world!"---
say:helloworld!But generally you'll use Gryml to process existing template files as shown in the following section.BasicsGryml can be used as Unix-style CLI to pipe the incoming file or directory combined with the values file or cmd args
as a stream of K8S resource definitions to stdout.This can be used to apply the modified definitions via kubectl:$gryml<file>|<dir>--setapp.name=sample|kubectlapply-f-Gryml relies on YAML comments instead  of inline templates, which makes it compatible with the tools that can
only work with the native k8s resource definition files.Lets look at the simple example:deployment.gryml.ymlapiVersion:apps/v1kind:Deploymentmetadata:name:"application"#{"app-" ~ common.name ~ "-suffix"}Thenamefield with the default value"application"has the#{"app-" ~ common.name ~ "-suffix"}Gryml tag.We can apply the value using the following command:$grymldeployment.gryml.yml--setcommon.name=simpleThis will result in the following output:apiVersion:apps/v1kind:Deploymentmetadata:name:"app-simple-suffix"Note: if your value is simple and you just to use it as is - you can use the simplified tag version:#{common.name}.Note: Multiple--setparameters can be used to set multiple values if necessary.By default all values including objects and lists are replaced with the value of the simplified tag, but sometimes
it's necessary to combine static values from the resource definition file with some dynamic values. One of the most
common use cases is the container environment variables.To facilitate that, Gryml provides "value strategies" that can be applied using the following tag syntax:#[strategy <argument?>]{expression}where supportedstrategyis one of the following:set- (default) - replaces the whole valueappend- adds new array items to the existing array valuemerge-using <key>- merges two array values containing objects using the values of the fields<key>in both
collections to find and replace existing itemsif <expression>- removes the value or item if expression evaluates asfalseelse- can be used rigth afterifstrategy to output the value if that strategy was evaluated asfalserepeat <key:value?>- iterates over values from theexpressionand repeats the array itemstemplate jinja- processes the existing value as Jinja2 template using current values tree as the contextValue filesIf the configuration is complex in addition to--setflags Gryml supportsvalues file. You can use the-f <path_to_values_file>argument to use the YAML value file in combination with the--setarguments. Note that--setarguments will override values file.Additionally, values file provide two important features, making Gryml capable of producing quite complex
configurations similar to HELM charts viaGryml directives.Gryml directives can be defined in the list with the key"gryml"in a values file.Importing other value filesIt is possible to include additional values files using theincludefield of thegrymlmetadata object,
for example:gryml:include:-"another.values.yml"-"/workspace/root.values.yml"Note: Additional values files are importedbeforethe current file and values are merged, you can useoverridefield of thegrymlmetadata object if you need to apply additional valuesafterthe current file was
evaluated.OutputIt is possible to reference yaml files that will be also processed and included in the outputafterall values
files and--setarguments are processed and a final value tree has been built.In combination with theincludeandoverridedirectives this allows different outputs to be generated from a single
codebase based on the different initial values files. Moreover, resource definitions can be logically organized into
libraries and artifacts in a single or even multiple repositories and then combined together explicitly in a values file.This basically provides a way to generate "charts" dynamically, significantly reducing the amount of duplicated
code between different resource definitions, while deriving the related parts from same values.Value transformation pipesGryml value expressions support Jinja2 filters (we call them value transformation pipes though). Gryml also
defines a couple of additional pipes suitable for use with k8s.Currently Gryml Core defines the following pipes:lowercase- AAA -> aaalimit(<n>)- limits the length of the sting to n symbolsk8sName- limits the length of the string to 64 symbolsb64enc- converts the value to base64 encoded stringrandstr- uses the value as length for the generated alphanumeric stringsource- uses the value as file name that will be loaded as string (relative to the current context)sha256- converts the value to sha256 hashvalmap(<pipe>)- when value is dictionary applies the pipe to the each field-value, preserving keysChart management and migrating from HELMUnlike HELM, Gryml currently does not add any labels into generated resource definitions and is not capable of managing
the release versions.Yet it's still possible to generate and use the "common" labels and annotations in the every output yaml definition,
so that they can all be filtered and deleted at once.We might introducegrymlctlutility in future to manage kubernetes cluster directly and handle gryml packages.Advanced exampleLets look at thisDeploymentdefinition with already added Gryml com-tags.deployment.gryml.ymlapiVersion:apps/v1#{apiVersion.deployment}kind:Deploymentmetadata:name:"application"#{"test-" ~ name ~ "-suffix"}spec:replicas:1#{replicas}strategy:type:Recreate#{strategy}selector:matchLabels:application:"application"#{name}template:metadata:labels:application:"application"#{name}spec:serviceAccountName:serviceAccount#{serviceAccountName}containers:-name:main#{"main-" ~ roleContainerSuffix}image:image#{imageRef}env:#[merge-using name]{env.common}-name:DEMO_GREETINGvalue:"Hellofromtheenvironment"-name:DEMO_FAREWELLvalue:"Suchasweetsorrow"#[if useSecondary]-name:secondary#{"secondary-" ~ roleContainerSuffix}image:image#{imageRef}env:#[append]{env.common}-name:DEMO_FAREWELLvalue:"Suchasweetsorrow"Now lets create values files:base.gryml.ymlapiVersion:deployment:'apps/v1'values.gryml.ymlgryml:include:-base.gryml.ymloverride:-context.gryml.ymloutput:-deployment.gryml.ymlname:'custom-name'role:'test'image:'custom-image'tag:'latest'useSecondary:falsereplicas:2serviceAccountName:'custom-serviceAccount'env:common:-name:"COMMON_GREETING"value:"Commonhello"-name:"DEMO_GREETING"value:"Hellofromthecustomenvironment"context.gryml.yml# Note: dynamic derived values are supported using the com-tagsimageRef:=#{image ~ ":" ~ tag}roleContainerSuffix:=#{name ~ "-" ~ role ~ "-container"}Now you can just exec:gryml -f values.gryml.yml, as a result you should be able to see contents of thedeployment.gryml.ymlfile with the substituted values.Best practicesAvoid complex logic in the output yaml files and instead implement this logic in the values filesSeparate configuration between multiple values files instead of combining it into a single oneLibraryGryml can easily be used as a python module without CLI:fromgryml.coreimportGrymlfrompathlibimportPathgryml=Gryml()values=gryml.load_values(Path("values-file.yaml"),base_values=None,process=True,mutable=True,load_nested=True,load_sources=True)