Skip to content
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

cue: ResolveReferences option not work in method "Syntax" #867

Closed
cueckoo opened this issue Jul 3, 2021 · 39 comments
Closed

cue: ResolveReferences option not work in method "Syntax" #867

cueckoo opened this issue Jul 3, 2021 · 39 comments
Labels
NeedsFix x/user/KubeVela Experimental CUE-user-based labels

Comments

@cueckoo
Copy link
Collaborator

cueckoo commented Jul 3, 2021

Originally opened by @leejanee in cuelang/cue#867

What version of CUE are you using (cue version)?

$ cue version
v0.3.0-beta.8

What did you do?

The code is as follows

       var src =`
t: {name: string}
output: [...t]
`
	var r cue.Runtime
	inst,_:=r.Compile("-",src)
	ct,_:=format.Node(inst.Value().Lookup("output").Syntax(cue.ResolveReferences(true)))
	fmt.Println(string(ct))

After running the code,I get

[...t]

But import the v0.2.2 version, The result is as follows

[...{
        name: string
}]

and, This is what i expected

@cueckoo cueckoo added NeedsFix Triage Requires triage/attention labels Jul 3, 2021
@cueckoo cueckoo added this to the v0.5.0 language changes milestone Jul 3, 2021
@cueckoo
Copy link
Collaborator Author

cueckoo commented Jul 3, 2021

Original reply by @myitcv in cuelang/cue#867 (comment)

@leejanee - thanks for raising this.

Just as an FYI, there are some tips on how to create clean reproducers in https://github.com/cuelang/cue/wiki/Creating-test-or-performance-reproducers. The txtar format is particularly good when it comes to multi-file repros, as is the case here.

For example, here is my reproducer based on the above:

exec go mod tidy
exec go run main.go
cmp stdout stdout.golden

-- go.mod --
module mod.com

go 1.16

require cuelang.org/go v0.3.0-beta.8
-- main.go --
package main

import (
 "fmt"

 "cuelang.org/go/cue"
 "cuelang.org/go/cue/format"
)

func main() {
 var src = `
t: {name: string}
output: [...t]
`
 var r cue.Runtime
 inst, _ := r.Compile("-", src)
 ct, _ := format.Node(inst.Value().Lookup("output").Syntax(cue.ResolveReferences(true)))
 fmt.Println(string(ct))
}
-- stdout.golden --
[...{
	name: string
}]

What did you expect to see?

The test pass.

What did you see instead?

> exec go run main.go
[stdout]
[...t]
> cmp stdout stdout.golden
[diff -stdout +stdout.golden]
-[...t]
+[...{
+       name: string
+}]

Creating such repros makes it much easier to confirm the issue, and indeed prepare a fix (because we have code/an example that can largely be copy-pasted).

That said, this does appear to be a regression compared to v0.2.2 (confirmed a problem at tip, 792da39), bisected to 845df05. Therefore I suspect this will be fixed after the planned v0.3.0 release.

@cueckoo
Copy link
Collaborator Author

cueckoo commented Jul 3, 2021

Original reply by @wonderflow in cuelang/cue#867 (comment)

@mpvl Hi, any progress about this issue, we're really want to upgrade to 0.3 while this issue blocks our needs.

@cueckoo
Copy link
Collaborator Author

cueckoo commented Jul 3, 2021

Original reply by @myitcv in cuelang/cue#867 (comment)

@wonderflow @leejanee - just to check, this remains a priority for you?

@cueckoo
Copy link
Collaborator Author

cueckoo commented Jul 3, 2021

Original reply by @wonderflow in cuelang/cue#867 (comment)

@myitcv yes, our project KubeVela highly relies on this feature, and we love to use CUE very much.

@cueckoo
Copy link
Collaborator Author

cueckoo commented Jul 3, 2021

Original reply by @wonderflow in cuelang/cue#867 (comment)

Now we are still using version 0.2.2 because of this block https://github.com/oam-dev/kubevela/blob/master/go.mod#L6

And our users love to use the feature sets in CUE >= 0.3, so we also very hope to upgrade. But one of our feature relies on it.

@cueckoo
Copy link
Collaborator Author

cueckoo commented Jul 3, 2021

Original reply by @myitcv in cuelang/cue#867 (comment)

Ok, thanks for the update @wonderflow

@cueckoo
Copy link
Collaborator Author

cueckoo commented Jul 3, 2021

Original reply by @myitcv in cuelang/cue#867 (comment)

@wonderflow I've tentatively marked this for v0.5.0.

On a related subject, I would be keen to understand if/how we can add KubeVela to our regression testing setup, unity. Would you be happy for me to email you offline to discuss further?

@cueckoo
Copy link
Collaborator Author

cueckoo commented Jul 3, 2021

Original reply by @wonderflow in cuelang/cue#867 (comment)

Sure, very glad ! My email: [email protected]

@cueckoo
Copy link
Collaborator Author

cueckoo commented Jul 3, 2021

Original reply by @myitcv in cuelang/cue#867 (comment)

@wonderflow @leejanee - when we come to look at this it would be good to understand exactly what semantics you expect from cue.ResolveReferences. My understanding from the example above is that given a CUE Instance/Value, you want to resolve all references to their literal equivalents, whether from within the package or across a package boundary. Does that match your understanding?

@cueckoo
Copy link
Collaborator Author

cueckoo commented Jul 3, 2021

Original reply by @leejanee in cuelang/cue#867 (comment)

@wonderflow @leejanee - when we come to look at this it would be good to understand exactly what semantics you expect from cue.ResolveReferences. My understanding from the example above is that given a CUE Instance/Value, you want to resolve all references to their literal equivalents, whether from within the package or across a package boundary. Does that match your understanding?

Yes it is, I expect that the result returned by the Syntax(cue.All(),cue.ResolveReferences(true)) method can be recompiled

@hprotzek
Copy link

Morning, can we expect this fix in the next version? or is this blocked somehow? A new version of culang would improve the writing of templates in Kubevela very much :-)

@myitcv
Copy link
Member

myitcv commented Oct 28, 2021

@hprotzek thanks for the reminder. We have this issue on our radar for an upcoming release, as discussed with @wonderflow. As we discussed recently in the first CUE community town hall we are in the process of getting back to the project in earnest. So hope to share details of upcoming releases very soon.

@mpvl
Copy link
Member

mpvl commented Nov 18, 2021

One problem with arbitrarily expanding references is that it can lead to a combinatorial explosion.

@wonderflow, @leejanee: It would therefore be good to understand what the objective is. Can you share some more detail on the use case?

For instance, if the objective is to create a self-contained snippet of CUE, then a different solution would be better.

In either case, to fully fix any such solution we need to have a different internal representation for let expressions. We are working towards this.

If there are simple use cases you would like to see covered in the mean time, please let us know.

@leejanee
Copy link

leejanee commented Nov 22, 2021

We use cuelang to render kubernetes resources in kubevela. This process is divided into two stages:

  1. First, perform user-side rendering, the result is transmitted to the server in ast.Node format.
  2. Server-side rendering.

For example:

User 1 and User 2 cooperate to complete resource definition. User 1 define the resource type and default values, and user 2 focus on replicas

First of all, the template on the user side has a consistent format, for example as follows:

parameter: {
    ...
}

resource: {
   ...
}

the parameter is user input, and the resource is the desired resource definition.

  1. The template and input of user 1 is as follows:
----- template.file --------
import "k8s.io/api/core/v1"
parameter: {
    image: string
    replicas: int
}

resource: v1.#Deployment{
    spec: {
       replicas: parameter.replicas
       template: spec: containers: [{ 
                image: parameter.image
                name: "main"
                envs: [...]
        }]
    }
}

----- input.file --------
// set parameter
   image: *"myimage" | string
   // limit replicas scope 
   replicas: *2 | >=1 & <5
  1. The template and input of user 2(focus on replicas) is as follows:
----- template.file --------
parameter: {
    replicas: int
}

// not care about specific resource types here.
resource: {
   spec: replicas: parameter.replicas
}

----- input.file --------
// set parameter
replicas: 3

  1. on the server-side, process like as follows:
resource:  v1.#Deployment & { // user 1
     spec: replicas: *2 | >=1 & <5
     ...
} & {  // user 2
     spec: replicas: 3
}

I expect that the cue execution result on the user side can be recompiled on the server side again.

@mpvl
Copy link
Member

mpvl commented Nov 22, 2021

I see. So what I observe going on here is that the input of the two users is resolved to be self-contained without references (like an OpenAPI equivalent) to be recombined later.

What I believe would suffice here, though, is to be able to produce a self-contained CUE file, which still may maintain references within the file. IOW, remove the imports.

Is this correct?

IOW, would it suffice to have a def command that just expands all imports, but not necessarily all references?

Or would do you still need all references to be expanded?

Note that references may act as constraints themselves.

Right now, evaluating references may result in an exponential blowup, analogous to the "billion laughs" in YAML. So if the goal here is to put the onus of this on the user, this is understandable. Once we introduce structure sharing, though, such blowup can be avoided if the depth of the evaluation is limited.

@verdverm
Copy link

I could imagine import inlining could have unexpected blowup in size. Even with some tree shaking, the file could get quite large with k8s. We'd probably have to account for the cue.mod/{gen,pkg,usr} folders together. Maybe this has already been handled at the point of rendering?

For cue def it would seem like the less often used output. So would this be enabled with a flag?

@leejanee
Copy link

leejanee commented Nov 23, 2021

  1. @mpvl Yes, it is correct that expand v1.#Deployment and remove imports on user 1 side.
    In other words, process on the server-side should be as follows:
resource:  { // user 1
     ...
     metadata: kind: "Deployment"
     spec: replicas: *2 | >=1 & <5
     ...
} & {  // user 2
     spec: replicas: 3
}
  1. But, I sincerely hope that all references to be expanded (though ,the depth of the evaluation is limited). And If the referenced object cannot be found, it can be kept. as follows:
---raw----
x: {}
t: {name: string}
output: [ ... {t & x.value}]

---lookUp(output)---
[... {{name: string} & x.value}]

Although evaluating references may result in an exponential blowup, But if don‘t try to expand the reference, it mean that the reference definition will be lost

@mpvl
Copy link
Member

mpvl commented Nov 29, 2021

@leejanee: CL https://review.gerrithub.io/c/cue-lang/cue/+/528214 hints at a solution where we could do an expansion where we include definitions outside of scope (e.g. from imports) as top-level let expression in the evaluation until all references are closed. This would create a self-contained file that is guaranteed to not have cycles.

An alternative would be to expand until we have a structural cycle.

I guess either of these would work for you. Will give it some more thought, but it seems that one of these, as least, is doable.

@mpvl
Copy link
Member

mpvl commented Nov 29, 2021

And just to confirm: it is not sufficient for you to use, for instance, the cue.Final() option. This would also simplify defaults and close lists.

cueckoo pushed a commit that referenced this issue Nov 30, 2021
When no default resolution is selected.

In the future we may make lists open by default if
they are not in definitions (like with structs).

Issue #867

Signed-off-by: Marcel van Lohuizen <[email protected]>

Change-Id: I2c946b3cc18894f96f83f788684618cd2ebfadfd
Signed-off-by: Marcel van Lohuizen <[email protected]>
cueckoo pushed a commit that referenced this issue Nov 30, 2021
Note that this does not fully implement what was requested
in #867, but this may be sufficient. Feedback welcome.

Issue #867

Change-Id: I54db0a623df69a6bfa1cc164c29032a5b20c4f5e
Signed-off-by: Marcel van Lohuizen <[email protected]>
cueckoo pushed a commit that referenced this issue Dec 1, 2021
When no default resolution is selected.

In the future we may make lists open by default if
they are not in definitions or within the scope of
a close builtin (like with structs).

Issue #867

Signed-off-by: Marcel van Lohuizen <[email protected]>

Change-Id: I2c946b3cc18894f96f83f788684618cd2ebfadfd
Signed-off-by: Marcel van Lohuizen <[email protected]>
cueckoo pushed a commit that referenced this issue Jul 26, 2022
- clean up code
- avoid introducing new label unnecessarily
- allow code to be reused for different sets of features

Used for Issue #867

Signed-off-by: Marcel van Lohuizen <[email protected]>
Change-Id: Ic51268cc22ec24949547959233f9a80627370bdc
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/541492
Reviewed-by: Paul Jolly <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
@wonderflow
Copy link

wonderflow commented Jul 26, 2022

Hi @myitcv @mpvl We really appreciate to see the progress. Our team is trying to upgrade and there're some more compatible issues:

cueckoo pushed a commit that referenced this issue Jul 26, 2022
This is now possible because of the passed down evaluator.

Note that most of it is actually left unimplemented as
revealing a new field may cause other fields to be
shadowed, which Sanatize doesn't seem to handle properly
yet.

Useful for Issue #867

Signed-off-by: Marcel van Lohuizen <[email protected]>
Change-Id: Ibd0f2f75f006cdd6c4e3e3eda428a0166a10b31c
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/541463
Reviewed-by: Paul Jolly <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
Unity-Result: CUEcueckoo <[email protected]>
cueckoo pushed a commit that referenced this issue Jul 26, 2022
This makes the logic for self-containment a bit nicer down
the line.

Issue #867

Signed-off-by: Marcel van Lohuizen <[email protected]>
Change-Id: I1c4ff3e0586b7eb2364f3a12a829bf8982d019a2
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/541543
Reviewed-by: Paul Jolly <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
@myitcv
Copy link
Member

myitcv commented Jul 26, 2022

Our team is trying to upgrade and there're some more compatible issues

Thanks. Will take a look through these today.

@wonderflow
Copy link

#1816 one more case, maybe it's also related with the selfcontained and can be resolved together.

@mpvl
Copy link
Member

mpvl commented Jul 26, 2022

#1816 one more case, maybe it's also related with the selfcontained and can be resolved together.

It indeed solve it, to some extent, but please see my answer there. It may not solve it how you want it, but at least in principle it is able to solve it so it will not be hard to iterate towards something workable.

FogDong pushed a commit to FogDong/cue that referenced this issue Jul 28, 2022
The ensures  that all references pointing outside an exported
value (in Def) are either inlined or included as let, the
latter to avoid a possible exponential blowup.

This does not yet handle the case where a reference points to
an ancestor node of the exported value.

Issue cue-lang#867

Signed-off-by: Marcel van Lohuizen <[email protected]>
Change-Id: Ie5a93949a68565ea60e954417bb2573fd36b0284
FogDong pushed a commit to FogDong/cue that referenced this issue Jul 29, 2022
- never inline core packages (see comments)
- mangle hidden fields of packages

Issue cue-lang#867

Signed-off-by: Marcel van Lohuizen <[email protected]>
Change-Id: Ib2fefb83fb6302770e5177fca97131b47188991c
FogDong pushed a commit to FogDong/cue that referenced this issue Jul 29, 2022
This makes it easier to see where errors originally came
from, as a value that is not inlined is marked with the
original path.

- tighten comprehension handling
- don't inline error values
- don't inline non-concrete values in expressions that
  require a concrete value.

Issue cue-lang#867

Signed-off-by: Marcel van Lohuizen <[email protected]>
Change-Id: I5a9ff86949c0c743fad45280a06b272640c13c7f
FogDong pushed a commit to FogDong/cue that referenced this issue Jul 29, 2022
This means that Syntax now needs to use Vertex
instead of Value. As it is still undesirable to show the
package for data, adding the package is now an option.
(Default for typical schema calls.)

This also deprecates ResolveReferences.

Issue cue-lang#867

Signed-off-by: Marcel van Lohuizen <[email protected]>
Change-Id: I05494b4fed86462df897e041ba90639ab0344bf2
FogDong pushed a commit to FogDong/cue that referenced this issue Jul 29, 2022
Issue cue-lang#867

Signed-off-by: Marcel van Lohuizen <[email protected]>
Change-Id: Iddf31af8da5466c848c89e24b07c5c0549a47b58
cueckoo pushed a commit that referenced this issue Jul 29, 2022
The ensures  that all references pointing outside an exported
value (in Def) are either inlined or included as let, the
latter to avoid a possible exponential blowup.

This does not yet handle the case where a reference points to
an ancestor node of the exported value.

Issue #867

Signed-off-by: Marcel van Lohuizen <[email protected]>
Change-Id: Ie5a93949a68565ea60e954417bb2573fd36b0284
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/541464
Reviewed-by: Paul Jolly <[email protected]>
Unity-Result: CUEcueckoo <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
cueckoo pushed a commit that referenced this issue Jul 29, 2022
- never inline core packages (see comments)
- mangle hidden fields of packages

Issue #867

Signed-off-by: Marcel van Lohuizen <[email protected]>
Change-Id: Ib2fefb83fb6302770e5177fca97131b47188991c
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/541544
Reviewed-by: Paul Jolly <[email protected]>
Unity-Result: CUEcueckoo <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
cueckoo pushed a commit that referenced this issue Jul 29, 2022
This makes it easier to see where errors originally came
from, as a value that is not inlined is marked with the
original path.

- tighten comprehension handling
- don't inline error values
- don't inline non-concrete values in expressions that
  require a concrete value.

Issue #867

Signed-off-by: Marcel van Lohuizen <[email protected]>
Change-Id: I5a9ff86949c0c743fad45280a06b272640c13c7f
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/541545
Reviewed-by: Paul Jolly <[email protected]>
Unity-Result: CUEcueckoo <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
cueckoo pushed a commit that referenced this issue Jul 29, 2022
This means that Syntax now needs to use Vertex
instead of Value. As it is still undesirable to show the
package for data, adding the package is now an option.
(Default for typical schema calls.)

This also deprecates ResolveReferences.

Issue #867

Signed-off-by: Marcel van Lohuizen <[email protected]>
Change-Id: I05494b4fed86462df897e041ba90639ab0344bf2
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/541561
Reviewed-by: Paul Jolly <[email protected]>
Unity-Result: CUEcueckoo <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
cueckoo pushed a commit that referenced this issue Jul 29, 2022
Issue #867

Signed-off-by: Marcel van Lohuizen <[email protected]>
Change-Id: Iddf31af8da5466c848c89e24b07c5c0549a47b58
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/541562
Reviewed-by: Paul Jolly <[email protected]>
Unity-Result: CUEcueckoo <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
@mpvl
Copy link
Member

mpvl commented Jul 29, 2022

The changes that address this particular issue have been checked in. Note that the ultimate implementation is a bit different:

  • There is no SelfContained option, as it is now simply default behavior (without expanding imports). CUE now detects when Syntax is called on part of the tree and then activates the algorithm.
  • To also expand references to imports use InlineImports.

The reason for this approach is that in the old algorithm CUE would generate potentially invalid CUE. So this seems to be strictly better. It will also be better for debugging, as it will be clearer where errors are coming from.

Also, less options is better. :)

In moving from v0.2 to v0.3/v0.4, note that there are some performance issues with disjunctions. We know what it is and are working toward a solution, but just something to be mindful off.

@mpvl
Copy link
Member

mpvl commented Jul 29, 2022

Going forward, there is certainly room to tweak the algorithm.

In my opinion, one of the current behaviors is incorrect. Consider

a: b: {
    c: a.b.d
    d: string
}

Applying the algorithm to a.b results in

c: d
d: string

In this case, though, since a points "outside" of the original structure, the value should arguably be pegged to the original value, not the self-contained one. This is how references would work within a CUE program anyway. So the result in this case should be

c: string
d: string

If the original had been

a: b: {
    c: d
    d: string
}

the result of calling Syntax for a.b would still be

c: d
d: string

of course.

@mpvl
Copy link
Member

mpvl commented Jul 29, 2022

Another possible change is how much is inlined. Right now, there are a lot of cases where let is used where arguably it doesn't have to be.

This should be thought through carefully, though, as inlining indiscriminately can lead to polynomial expansion. See for instance https://en.wikipedia.org/wiki/Billion_laughs_attack.

@mpvl
Copy link
Member

mpvl commented Jul 29, 2022

On final caveat is that the current implementation doesn't handle if a self-contained node points to its root or any of its ancestors (also holds for roots of the "external" values.

We still plan to address that, but the solution depends a bit on some of the design decisions noted above and whether we have value aliases for embeddings. The latter is the reason we now no longer support rewriting them.

@mpvl
Copy link
Member

mpvl commented Jul 29, 2022

I'm keeping this issue open to allow for initial feedback to not get buried.

@wonderflow
Copy link

@myitcv Much appreciate for the work! We're trying but it seems the package is all internal. How can we use it as we're integrating CUE by using the go module?

@myitcv
Copy link
Member

myitcv commented Jul 29, 2022

Mentioning here for the benefit of others too, you need to use the new cue.InlineImports() option. Here's a small example:

https://gist.github.com/myitcv/78bfb51d5061365f429372b8968087c5

@myitcv
Copy link
Member

myitcv commented Jul 29, 2022

@wonderflow @FogDong just following up on the issues linked from above:

Replied in the issue. I'm not clear there is actually an issue here, just a bogus error value being reported. Perhaps you can confirm my understanding in the issue?

Commented in response to @mpvl. I think my conversation with @FogDong helped move this forward, but please let me know if there are any outstanding questions/issues.

Commented. You can work around this for now by casting a value of *cue.Context to *cue.Runtime.

@myitcv
Copy link
Member

myitcv commented Oct 6, 2022

Per our discussions in various other issues, I think we have identified a better approach via the cue.Value API so I will mark this as closed. Please comment if this needs to remain open with details of outstanding issues. Thanks

@myitcv myitcv closed this as completed Oct 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsFix x/user/KubeVela Experimental CUE-user-based labels
Projects
None yet
Development

No branches or pull requests

7 participants