cue-lang / cue

The home of the CUE language! Validate and define text-based and dynamic configuration
https://cuelang.org
Apache License 2.0
5.14k stars 294 forks source link

Confusion Regarding File Hierarchies #557

Open cueckoo opened 3 years ago

cueckoo commented 3 years ago

Originally opened by @nyarly in https://github.com/cuelang/cue/issues/557

I have a peculiar use case for Cue that has been generally working well, but I'm finding myself in something of a bind for next steps.

I am targeting Kustomize, so the structure of the output is significant, which means that a simple export would mean I couldn't use instance constructions to introduce common features of my configuration. My solution there has been a straightforward tool script to generate files into fields named by a large struct. The general form is { "a/path/\(feature)/part.yaml": { ... } } - fields are filenames, and their values are the contents.

What I would like to do is extract all the variations in the structure - this works out to less than 1%!o(MISSING)f the overall files, and put them in one file near the top level, so that the aspects of the configuration I generate that are intended to vary are cohesive and discoverable. There are several variations of my target configuration, and each of them can be expressed (today, at least) in about a dozen lines of Cue.

I'm resistant to the design in the Kubernetes tutorial, of putting these variations at the leaves of the directory tree for a couple of reasons. In part, this sacrifices my intuition the the "knobs" on this configuration will be on the "top panel," so to speak. There are also a few places in the target configuration that are coupled to common configuration, and aligning that with the instance-collection mechanism would mean dissolving any relationship between the Cue layout and the target.

My first approach was to simply break out the file structure and run by tool like cue gen ./structure/... ./config/x.cue. These are all in the same module and with the same package name. However, it appears that the instances are evaluated separately per argument.

An aside: what's the design rationale there? My intuition of ./structure/... is as a shorthand for enumerating all the files, but that's plainly not so - replacing cue ... ./structure/... with find -type f -regex '.*.cue' | xargs cue ... {} wouldn't behave anything like the same. I'm having trouble, I admit, forming of mental model for why the collection of instances that can reference each other should be constrained to be entire subtrees of a module. Candidly, it feels like an accident of the instance selection syntax.

My second thought was that perhaps this would be a good application of include - since what I want is to "rearrange" the hierarchy of these files, I could include the configuration into the structure. This has some sense, and works okay, so long as there's only one configuration. Since I want to be managing several variant configurations, this doesn't work well, since it hard codes the related configuration.

Conversely, I considered that, although the base structure would want to include one of many variants, each variant cares only for the one structure. Perhaps I could include the structure to the variants? However, because of the files-to-contents base structure, there's a lot of string interpolation based on the configurations; I can't see how to parameterize that into the included structure.

I'm currently contemplating a solution where I'd include all the Cue files (i.e. cue ... ./...) and use a tag to select the variant I want. I can see how that would work, but it feels clunky in a number of ways. At a minimum it would mean reproducing variant tags in a few places (constraints on the @tag, @if blocks in the configuration, the variant files themselves...) It also seems like the wrong "match" for "choose one of these" to then have several files that each say "in this case..." But I suppose this might be my best option.

cueckoo commented 3 years ago

Original reply by @myitcv in https://github.com/cuelang/cue/issues/557#issuecomment-711076638

I'm struggling somewhat to understand what you're getting at here. What might be easier would be if you can share a cut down version of the inputs you are working with, and the outputs you need want?

To answer a couple of points.

The convention by which packages can be made more specific in subdirectories is not the only way of solving problems; you are certainly not required to follow it. As you observed, it's one that lends itself well to the Kubernetes setup (and indeed many others) but it's by no means the only way of solving the problem.

Exactly the same effect can be achieved by embedding a package. Consider:

-- cue.mod/module.cue --
module: "mod.com"
-- x.cue --
package x

x: 5
-- y/y.cue --
package x

y: 4
-- z/z.cue --
package z

import "mod.com:x"

x

z: 3

Then:

$ cue eval .
x: 5
$ cue eval ./y
x: 5
y: 4
$ cue eval ./z
z: 3
x: 5

There are also a few places in the target configuration that are coupled to common configuration, and aligning that with the instance-collection mechanism would mean dissolving any relationship between the Cue layout and the target.

I don't understand what you mean here. Please can you explain?

My intuition of ./structure/... is as a shorthand for enumerating all the files, but that's plainly not so - replacing cue ... ./structure/...

As I linked on Slack, cue help inputs explains what's going on here. Also see the following:

https://cuelang.org/docs/concepts/packages/

I'm currently contemplating a solution where I'd include all the Cue files (i.e. cue ... ./...) and use a tag to select the variant I want

Again, I'm slightly confused as to what you're trying to do here (a cut down example really would help), not least because earlier you seem an approach where you do appear to be using subdirectories (at least in the output) for the specific instances:

The general form is { "a/path/\(feature)/part.yaml": { ... } } - fields are filenames, and their values are the contents.

If you can include a cut down version of what you are trying to do that will help massively. From the gist of what you are trying to solve it absolutely sounds like CUE would be a good solution. As @jlongtine and others have remarked, however, it sometimes requires thinking about things in a slightly different way.