Discussion: Add a way to check if an external variable is defined
I'm making an issue to discuss this because I'm not entirely sure if the idea I'm proposing is aligned with Tanka's goals. What I'd like to add is a native method that checks if an external variable was passed in, the intention being to allow external variables to assume a default value if not otherwise specified.
Example Use Case - default value for image tags
main.jsonnet
{
imageTag:: if std.native('hasExtVar')('imageTag') then std.extVar('imageTag') else 'latest',
apiVersion: 'tanka.dev/v1alpha1',
kind: 'Environment',
metadata: {
name: "testing",
},
spec: {
apiServer: 'https://localhost',
namespace: 'default',
},
data: {
apiVersion: 'v1',
kind: 'Deployment',
metadata: {
name: 'MyDeployment',
},
spec: {
replicas: 3,
template: {
spec: {
containers: [
{
name: 'MyContainer',
image: 'myImage:' + $.imageTag,
},
],
},
},
}
},
}
My organization is in the midst of migrating a very large number of manifests from kustomize to tanka, and while we have really appreciated its breadth of features, there are times having this would make the flexibility we're trying to achieve a lot cleaner. So much so that we're considering maintaining a fork of tanka for use internally that has this added.
From reading the Jsonnet Language Specification's section on external variables and some discussion in issues it seems to me that this is a feature intentionally missing from jsonnet, is that also the case for Tanka?
I've created an example PR against a fork implementing the changes necessary, but I didn't want to be "that guy" and just create a PR upstream advocating for something the project is philosophically opposed to without checking in with the maintainers.
Top-Level Arguments (TLAs) are much better suited for this, they take the form of function arguments which can also have a default value.
When all your files are in a single/simple tanka project, I'd 100% agree. I've actually got a PR open right now against tanka that facilitates that use case (its currently broken in some subtle ways when using defaults) because thats how we're doing it right now.
The problem we run into is that I'm seeing this become unmaintainable in two related ways:
- Our project has ~50 possible inputs, and that number is growing as we port more over from kustomize. For any given
tk exportwe only want to override a handful of defaults (often 0). Maintaining this as a flat list of arguments in main.jsonnet and plumbing it through everywhere we want it to go is tedious and error prone. - We maintain a jsonnet library in a private repo for things like a base
Deployment,Ingress, etc which need to make use of the TLA's values. This means each of our projects that imports the TLA's needs to have it plumbed through to the base library. So when we add a new variable in the base library, we also have to plumb it through from the main.jsonnet in each of the N entrypoints form each tanka project that uses it. Its tedious and error prone to reproduce this logic all over the place, a loose coupling withhasExtVarwould let us inherit new functionality from the base library without having to plumb it through.
So in my contrived example above, 100% agree a TLA is the way to go. In my org's real use-case where the manifests are spread across many different repos/projects and a lot of flex is needed to handle all the different use cases (eg different monitoring annotations, various states of migration from one backend to another, discrepancies between different cluster versions, etc) I think the native method would be a lot easier to maintain.
If there are that many inputs, then I think extVars are not your best solution here, how are you ever going to reproduce something in a reasonable way?
We also have many environments, the way we handle differences is by storing them as values in a file (JSON or jsonnet), that way we can track what has changed.
Can you expand further on what the source is for these extVar values? The data must be stored somewhere that could be imported in a more conventional way into jsonnet.