Closed andaryjo closed 6 months ago
Thank you for reporting this and huge respect for already spending so much time debugging this ❤️. I was able to reproduce the bug with your example.
You were, right about the fact that the variable replacement is random. And that is also the reason this happens. The underlying issue is that variable replacement is rather naively implemented by simply brute-forcing and replacing variables as long as variables can be found. Hence, the two cases arise.
ReplaceVariables
is calledFindVariables
is called --------------------------------------${a:location}
at spokes
${b:spokes}
at b.spokes.0.location
replaceVariable
is called for variable a
SetPath()
which in turn calls yaml.Marshal
westeurope
is yaml marshalled, which doesn't change the type (string) replaceVariable
is called for variable b
SetPath
which again calls yaml.Marshal
b.spokes
is a slice of skipper.Data
maps (Data{"location": "westeurope"}}
)map[string]interface{}
(map[string]interface{}{"location": "westeurope"}
)
because the yaml marshaller obviously doesnt use skipper Data (which is only a map[string]interface{} anyway).FindVariables
is called --------------------------------------ReplaceVariables
is calledFindVariables
is called --------------------------------------${b:spokes}
at b.spokes.0.location
${a:location}
at spokes
replaceVariable
is called for variable a
map[string]interface{}{"location": "${a:location}"}
because the location variable has not yet been replacedreplaceVariable
is called for variable b
spokes.0.location
now has the value westeurope
FindVariables
is called --------------------------------------${a:location}
at spokes.0.location
spokes.0.location
is a map[string]interface{}
and not skipper.Data
. This is significant in that neither GetPath
nor SetPath
support that type which in turn causes them to fall-back into the default
case, which is where the error originates from.Ultimately the issue arises from variables which point to variables are not properly resolved and replaced in order. This would've prevented that situation from happening.
I've implemented a fix here: https://github.com/lukasjarosch/skipper/pull/79 Can you verify if that fix works for you?
This sounds complicated, because it is. Let me explain.
Imagine you have to classes A and B:
And finally a target which uses both of these classes:
Compilation of this setup would sometimes pass and sometimes fail with this error:
@alxndr13 and myself spent together around 10 hours debugging this and so far this is what we got:
GetPath
function when invoked from theReplaceVariables
function.b.spokes[0].location
gets replaced before the value oftarget.spokes
, the compile would pass.target.spokes
gets replaced before the value ofb.spokes[0].location
, the compile fails.target.spokes[0].location
.GetPath
function then throws an error because the type of the value oftarget.spokes[0].location
ismap[string]interface {}
and for some reason notskipper.Data
.setPath
function somehow messes up the data type of the map value that gets inserted into the tree. More specifically, the YAML marshalling and unmarshalling seems to be the issue. If you remove that, the compile always passes, regardless of the order the variables get replaced in.FindValues
function determines to be a value.We are planning to continue investigating what exactly the problem is here since we don't fully understand it yet. But maybe you have already an idea, @lukasjarosch.