Skip to content

Defaults for top-level arguments don't appear to work using inline environments #522

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
craigfurman opened this issue Feb 26, 2021 · 9 comments
Labels
component/jsonnet Everything regarding the jsonnet language keepalive kind/bug Something isn't working

Comments

@craigfurman
Copy link
Contributor

Running tk show foo/bar --name NAME or tk diff foo/bar --name NAME against a main.jsonnet that contains a function with a default argument, e.g.

function(tag='latest') {
   // return a tanka.Environment
}

Returns an error "Error: Found no environments in ...". I haven't validated yet whether or not top-level argument defaults are still working in traditional, non-inline environments with a spec.json. I can look into making a PR, but wanted to raise an issue first in case it was known.

@sh0rez
Copy link
Member

sh0rez commented Feb 26, 2021

Ugh, good catch. This is related to the TLA hack put in place here:

if len(tla) != 0 {
tlaJoin := strings.Join(tla, ", ")
evalScript = fmt.Sprintf(`
function(%s)
local main = (import '%s')(%s);
%s
`, tlaJoin, entrypoint, tlaJoin, opts.EvalScript)
}

Tanka uses internal Jsonnet snippets to only evaluate subsets of the Jsonnet tree when applicable, which gives massive speedup (e.g. only evaluating Environment metadata but omitting data is what makes tk env list fast)

We thought above would make TLA work, but yeah, because we create a new function which does not have these defaults, it fails.

@Duologic
Copy link
Member

This is an interesting use case, similarly it doesn't show up in tk env list.

@craigfurman
Copy link
Contributor Author

In my tanka travels I hadn't seen the parts of the codebase that actually evaluate jsonnet yet 😅. I can see the use case for pruning out data, for subcommands that only need metadata, and I'm guessing you'll want to preserve the performance offered by the current code here?

One way to do this that occurs to me, is to parse the jsonnet AST (but not evaluate the jsonnet) and delete the data subtrees from each environment object. This is just an avenue to explore, I have no idea if this will work, because:

  1. I haven't yet researched what (go-)jsonnet-ast libraries exist, short of trying to reuse internals of go-jsonnet.
  2. Even if they exist, I don't know how much speed we'll sacrifice over the current implementation.

wdyt?

@craigfurman
Copy link
Contributor Author

craigfurman commented Feb 26, 2021

It looks like it might be worth at least exploring an AST implementation: https://github.com/google/go-jsonnet/blob/7d810911490e86edee423dbf2cfb24f7d8b9dde8/vm.go#L517

Do you have any particular benchmarks you run? I have a large-ish inline environment (https://gitlab.com/gitlab-com/gl-infra/k8s-workloads/tanka-deployments/-/tree/master/environments/thanos) that anecdotally takes a good few seconds to evaluate, that I can use to gather some data. I might explore an AST impl over the next couple of weeks if I find some time.

@Duologic
Copy link
Member

We have ~230 Tanka environments large and small, running tk env list and tk export against these show performance issues really well.

@sh0rez
Copy link
Member

sh0rez commented Feb 26, 2021

@craigfurman you're right, we would very much like to keep the performance benefits of the current implementation.

That being said, the current implementation has always been a glorified hack (Jsonnet snippets doing reflection is a real subtle thing), so yeah, I'd be very much for a more permanent solution, aka written in Go.

Given the Jsonnet our "optimizations" are heavy load Jsonnet (full tree traversal), this might turn out even faster :D

@sh0rez sh0rez added component/jsonnet Everything regarding the jsonnet language kind/bug Something isn't working labels Feb 26, 2021
@craigfurman
Copy link
Contributor Author

craigfurman commented Feb 27, 2021

I've been poking around a bit with the go-jsonnet functions around ASTs just now, and while it's early days I'm now skeptical that an AST-pruning implementation has legs.

For example, if you have a simple tanka main.jsonnet like this:

function(foo='bar') {
  example: {
    apiVersion: 'tanka.dev/v1alpha1',
    kind: 'Environment',
    metadata: {
      name: 'example',
    },
    spec: {
      apiServer: 'https://127.0.0.1:16443',
      injectLabels: true,
      namespace: 'example',
    },
    data: {},
  },
}

It's possible to walk the AST to the function's return value, traverse down the tree until you find an object with an apiVersion and Kind field indicative of a tanka environment, and prune the data field out, before evaluating the jsonnet.

However, as soon as any library-based indirection is introduced, we have a harder problem. Take this example main.jsonnet:

local tanka = import 'tanka_helpers.libsonnet';

function(foo='bar') {
  example: tanka.environment('example'),
}

Let's assume tanka.environment returns a tanka environment object. We can't perform the AST pruning without effectively mimicking most of what jsonnet evaluation already does: delving into function calls etc. That wouldn't really be a viable solution.

I've got quite a busy week ahead so I'm not sure how much time I'll be able to spend on this, but wanted to update this issue with some more early thoughts. I suppose the next avenue to explore is fixing the tlaCode generator functions and/or the eval scripts in https://github.com/grafana/tanka/blob/master/pkg/tanka/evaluators.go to preserve tla defaults.

@stale
Copy link

stale bot commented Apr 18, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Apr 18, 2021
@stale stale bot removed the stale label May 20, 2021
@Elfo404 Elfo404 added this to Tanka May 27, 2024
@github-project-automation github-project-automation bot moved this to Triage in Tanka May 27, 2024
@Elfo404 Elfo404 moved this from Triage to Backlog in Tanka May 27, 2024
@zerok
Copy link
Contributor

zerok commented Jul 30, 2024

Would the following environment config represent the original issue?

local tanka = import 'github.com/grafana/jsonnet-libs/tanka-util/main.libsonnet';

function(namespace='var') {
  env: tanka.environment.new(
         name='namespace',
         namespace=namespace,
         apiserver='https://localhost:6443',
       )
       + tanka.environment.withData({
         namespace: {
           apiVersion: 'v1',
           kind: 'Namespace',
           metadata: {
             name: namespace,
           },
         },
       }),
}

This works now:

> tk show environments/dummy
apiVersion: v1
kind: Namespace
metadata:
  name: var

Or did I misunderstand the issue? 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component/jsonnet Everything regarding the jsonnet language keepalive kind/bug Something isn't working
Projects
Status: Backlog
Development

No branches or pull requests

4 participants