Thursday, June 28, 2018

Joys of New DSLs

Note: this document is very much a "for my own knowledge"/rant post. Don't try to use it as a reference unless you're really freaking desperate. Terms are likely wrong or inaccurate. However, I needed to capture the gist for posterity. I lost two days of my life teasing this shit out of Google (and across a few dozen only very obliquely-related search-hits) — particularly the last paragraph's contents — and don't wish to do so ever again .


One of the projects I'm working on, they requested that I take all of the previously-delivered CloudFormation templates and wrap them in Jenkins pipeline jobs. They wanted to be able to reliably an easily re-deploy things, have the deployment-options saved as code, and they wanted to be able to easily check whether a given deployment had errors or not. All are good, valid goals.

That said, the manner in which I'd constructed the CFn templates, I included a lot of built-in break-points and error-reporting. As far as easily redeploying, when you use the AWS CLI, one need only pass the --parameters flag to be able to feed a file of parameter values and settings to the command. Place those parm-files under revision control and all of their stated goals are satisfied. But, "whatever".

Main problem for me is/was, "I'm not a Jenkins guy." While it was easy enough how to deploy Jenkins via CloudFormation, doing so via Jenkins meant (at least partially) learning the Jenkins equivalent methods to using a parameters-file. When first assigned the task, I'd indicated, "I don't know Jenkins: got any examples you can point me to?" All of the examples they had were GUI-authored "Freestyle" Jenkins jobs. Not a GUI-jock, but figured, "if this is what they want, I can probably ape their current job definitions to create ones for these templates."

In a matter of a few days, I'd banged out the requisite Freestyle jobs. Pointed my customer's technical PoC at them to review. He gives me an incredulous, "what are these??" I stated that they were the Jenkins jobs. He stated that he'd wanted them as pipeline jobs. To which I asked, "then why did you point me at Freestyle jobs as examples to follow?" We discussed things a bit further so I was better assured that what I was understanding as the desired outcome was the actual desired outcome.

So, I set to the task of learning how to author jobs using the pipeline DSL. It's been... "unfun". Jenkins is composed of a lot of "contributed" parts. While the core of Jenkins has something in the neighborhood of "ok" documentation, many of the contributed parts' documentation varies from "slightly better than ok" (rare), down through "awful" (common) and, in some cases utterly lacking to the point that you have to read the components' code to see how to make it go (annoyingly far from uncommon). Worse, because so much is "contributed" and the contribution-sources so varied, what works in one context may or may not work (or at least not work the same way) in other contributed contexts.

Further exacerbating things is that there are also multiple methods for denoting action-blocks. And, depending on which method you usde for denoting an action-block, variable interpretation and interpolation is processed differently. This totally glosses over the fact that one can reference a variable in multiple ways: naked "VARNAME"; shell-ish "$VARNAME" or "${VARNAME}"; and, when embedded in a shell-block, "\$VARNAME" or "\${VARNAME}". For those still following along but not aware of the plain "$" and "\$" methods, the former is for Jenkins environmental variables and the latter is for standard shell variables.

Further, if one wants to fetch the value of a parameter, one can use "${PARAMNAME}",  "params.PARAMNAME" or even  "env.PARAMNAME" (or, within the context of a script-block, the latter two become  "${env.PARAMNAME}" or  "${params.PARAMNAME}"). Use of straight-up  "${PARAMNAME}" is frequently fine. When it becomes not fine is if you want to allow PARAMNAME to have a value or be null. If it has a value, all is golden. If you try to pass a null value, it blows the hell up. Instead, you need to use the "env.PARAMNAME"/"${env.PARAMNAME}" to refer to it. Using that method, Jenkins understands, "oh, you wanted to process this as an empty value, not as an undefined parameter".