Add support for caller to determine origin for union_replace#184
Add support for caller to determine origin for union_replace#184dplore wants to merge 11 commits into
Conversation
Coverage Report for CI Build 25770024131Coverage increased (+0.03%) to 4.469%Details
Uncovered Changes
Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
robshakir
left a comment
There was a problem hiding this comment.
I'm not sure I actually see the implementation of the description here (or at least it's not tested).
Can you please add unit tests that demonstrate the setting of the value of the origin to the value set by the caller?
| // then we know that the type is a node lower than it should be | ||
| // as far as the JSON is concerned. | ||
| modifyTypedValueFn = func(tv *gpb.TypedValue) error { return wrapJSONIETF(tv, compressInfo.PostRelPath) } | ||
| if s, ok := val.(string); ok && op == unionreplacePath { |
There was a problem hiding this comment.
This seems a bit obtuse, why is it that setting val to string MUST mean that we are specifying a union replace with CLI? If I am setting /system/config/hostname then can't the value be a string here?
There was a problem hiding this comment.
True, /system/config/hostname takes a string! This logic (or similar) is needed ygnmi's use of pointers under the hood for OpenConfig scalar leaf values compared to plain values for CLI text.
From what I can understand, this is how ygnmi distinguishes the two:
1. Origin openconfig schema Leaf Updates (e.g., /system/config/hostname)
When you invoke set or BatchUnionReplace on an OpenConfig leaf like /system/config/hostname:
- The framework checks if this query target is a leaf and scalar:
q.isLeaf() && q.isScalar(). - Because OpenConfig fields in generated GoStructs use pointers to represent potentially unset values, the framework converts the value to a pointer type:
if q.isLeaf() && q.isScalar() {
setVal = &val
}-
Next when
populateSetRequestis called, the incoming dynamicvalparameter is a*string, not a plain string. -
Inside
gnmi.goline 520-523 block, the type assertions, ok := val.(string)performs a lookup against the concrete string type. Because the type is*string, okevaluates tofalse. -
Thus, the CLI-override block is bypassed, leaving
isCLIUnionReplace = false. The request then correctly proceeds to translate*stringinto its JSON/JSON-IETF representation (e.g., "baz").
2. Origin cli Union Replace
On the other hand, native CLI configuration isn't in the OpenConfig schema tree. When you queue a CLI replacement using BatchUnionReplaceCLI:
You pass the raw CLI as a plain string (e.g., "interface Ethernet1\n mtu 1500").
Since the target is not a structured schema leaf, the framework leaves val as a plain string instead of converting it to a pointer.
In populateSetRequest, when the block evaluates:
if s, ok := val.(string); ok && op == unionreplacePath {
isCLIUnionReplace = true
cliUnionReplaceVal = s
}the dynamic type is a plain string, making ok evaluate to true.
- This triggers isCLIUnionReplace = true, which correctly outputs a schema-free gpb.TypedValue_AsciiVal wrapping the raw CLI command.
There was a problem hiding this comment.
Resolving, but feel free to provide suggestions on a different way to approach this. I am not sure what would be more elegant.
There was a problem hiding this comment.
It seems quite fragile to me to have this assumption be made -- how do we guarantee that all callers are aware that setting val to string means one thing and *string means another.
What is the value of path being set to when one is creating such an update? It seems like the tuple of path and val really specifies what one wants to set, rather than the type of val.
There was a problem hiding this comment.
It seems the implementation took a shortcut by exploiting a difference in how
BatchUnionReplace and BatchUnionReplaceCLI populate their values:
In BatchUnionReplace, if a query is a scalar leaf (q.isLeaf() && q.isScalar()), its value is passed as a pointer (&val -> *string).
CLI Configs: In BatchUnionReplaceCLI, the ASCII snippet is passed as a raw, non-pointer string.
I will try having BatchUnionReplaceCLI wrap the CLI string in a dedicated custom type (type cliAsciiConfig string). Then populateSetRequest can type-assert on val.(cliAsciiConfig) without any risk of colliding with normal Go string values.
I'll raise a separate PR for this one, standby |
…e type-sniffing assumption between string and *string.
Similar to #176 we've also run into several scenarios where it is more convenient for the caller to specify the origin when using union_replace batch functions. This changes origin to match whatever is passed in by the caller.