kubegen icon indicating copy to clipboard operation
kubegen copied to clipboard

basic selector expressions

Open errordeveloper opened this issue 7 years ago • 3 comments

It would be convenient to take an object (e.g. one read from an imported file), and use only a part of it.

kubegen.Object.Select([0]["something"]):
  kubegen.Array.ImportFromFile: "stuff.json"

errordeveloper avatar Jan 27 '18 09:01 errordeveloper

We could re-use k8s.io/client-go/util/jsonpath, but it seems like there is no clear need for full-blow jsonpath expressions, and they aren't even as powerful as jq (plus we would need to find ways of validating the expressions and provide friendly error messages etc (kubectl get -o jsonpath=... doesn't seem to produce any useful error message for invalid expressions, so it's hard to tell how jsonpath package works with respect to that). All we really need to implement is parsing of macro arguments (which is already needed for kubegen.String.Join and a path expression extractor that takes a string and returns []interface{} for use with *Tree.Get).

errordeveloper avatar Feb 01 '18 09:02 errordeveloper

Alternative syntax:

kubegen.Object.Select: '[0]["something"]'
kubegen.Object.From:
  kubegen.Array.ImportFromFile: "stuff.json"

It may be easier to implement for the purpose of the MVP, and it's arguably more readable and doesn't add unusual characters in keys, as kubegen.Object.Select([0]["something"]) would be "kubegen.Object.Select([0][\"something\"])" or "kubegen.Object.Select([0]['something'])" in JSON.

With this in mind, It's uncertain what kubegen.String.Join would look like...

kubegen.String.Join: ["foo", "bar"]
kubegen.String.WIth: ":"

errordeveloper avatar Feb 01 '18 09:02 errordeveloper

It'd be not unreasonable to use more explicit syntax, e.g. kubegen.Object.SelectFrom and kubegen.Object.SelectWith & kubegen.Object.JoinFrom and kubegen.Object.JoinWith... Or, even expand into another level like this:

--- 
kubegen.String.Join:
  .With: ":"
  .From: ["foo", "bar"]
--- 
kubegen.Object.Select:
  .With: '[0]["something"]' 
  .From:
    kubegen.Array.ImportFile: "stuff.json"

In fact, .With: [0, something] seems like a good option that avoid the introduction of string-based expressions with a special syntax (although the syntax proposed earlier is quite faimilar).

Currently, some expressions may seem a little inconsistent (mostly due to simplicity of the implementation), let's consider a few options with a new perspective.

# Given the following set of structural macro expressions
--- 
<key1>: 
  kubegen.Object:
   .Select: [0, something]
   .From: [{"something": {<objectValue1>}}]
<key2>: 
  kubegen.String:
   .Select: [something, 0]
   .From: {"something": [<stringValue1>]}

# It would yield
--- 
<key1>: {<objectValue1>}
<key2>: "<stringValue1>"
# Given the following set of structural macro expressions
--- 
<key1>: 
  kubegen.Object:
   .Lookup: objectValueRef2
<key2>: 
  kubegen.String:
     .Join:
       .With: ":"
       .From:
         .Array:
           .Lookup: arrayValueRef1
<key3>: 
  kubegen.String:
     .Join:
       .With:
         .String:
           .Lookup: stringJoinValueRef1
       .From:
         .Array:
           .Select:
             .With: [<topKey1>, [[0], [2, <subKey1>]]]
             .From: [<stringValue3>, <stringValue4>, {<subKey>: <stringValue5>}, <stringValue6>]

# With the following attributes:
# - `objectValueRef2: {<objectValue2>}`
# - `stringJoinValueRef1: ","`
# - `arrayValueRef: [<stringValue2>, <stringValue3>]`
# it would yield
--- 
<key1>: {<objectValue>}
<key2>: "<stringValue1>:<stringValue2>"
<key3>: "<stringValue3>,<stringValue5>"

The last set shows an idea of how composing selector could be expressed, which probably could be achieved somehow differently, yet seems like a convenient way of dealing with complex imported structures that should be fairly easy to implement.

However, short-hand syntax becomes much more important, so let's consider the following equivalent expression in compressed form.

<key3>: 
  kubegen.String.Join:
     .With.String.Lookup: stringJoinValueRef1
     .From.Array.Select:
        .With: [<topKey1>, [[0], [2, <subKey1>]]]
        .From: [<stringValue3>, <stringValue4>, {<subKey>: <stringValue5>}, <stringValue6>]

If we did a lookup, the last line would appear as .From.Object.Lookup: objectValueRef3, because when we do reference lookups we do not know what type of value we must get, yet when we deal with a literal expression, we can see what it is based on the syntax, however for consitency .From.Array: [ ... ] should be equivalent to .From: [ ... ].

We may need to consider using different delimiters to distinguish types from verbs, e.g. kubegen//Array.Select, or kubegen//String.Join, or //String.Join in compressed form.

So the example above can be re-written as:

<key3>: 
  kubegen//String.Join:
     .With//String.Lookup: stringJoinValueRef1
     .From//Array.Select:
        .With: [<topKey1>, [[0], [2, <subKey1>]]]
        .From: [<stringValue3>, <stringValue4>, {<subKey>: <stringValue5>}, <stringValue6>]

Or, with slightly more expanded style:

<key3>: 
  kubegen//String.Join:
     .With:
       //String.Lookup: stringJoinValueRef1
     .From:
        //Array.Select:
          .With: [<topKey1>, [[0], [2, <subKey1>]]]
          .From: [<stringValue3>, <stringValue4>, {<subKey>: <stringValue5>}, <stringValue6>]

With all this in mind, we only need to detect certain fragments of expressions, e.g. //Array.Select would need to be evaluated first (and it'd be expected to match an object that has.With and .From or otherwise it'd be seen as an invalid expression), then //String.Lookup would be evaluated (expected to resolve stringJoinValueRef1), and only after that kubegen//String.Join would be evaluated (where keys .With and From would be expected).

So, essentially, instead of matching full-names macro keys like it is done, we will need to match for (kubegen)//<type>(.<verb>), (<parentExpression>)//<type>(.<verb>), and any .<verb> keys would be expected to get evaluated by parenting //<type>. Also, (<parentExpression>) is expected to be a argument to an expression above it, e.g. .With//String.Lookup: stringJoinValueRef1 would be transformed to .With: "," as that's how the //String.Lookup macro is intended to evaluate. Keys with more than one occurrence of double-backslash (//) should be treated as invalid, as it would make the syntax too complex to read and, in fact, potentially meaningless. Also, to avoid ambiguities, we will need to catch expression look-alikes properly and throw warnings or errors.

Let's consider another, more complex, case – lookup with branching.

--- 
<key4>:
   kubegen//String.Join:
     .With//String:
       .If: stringJoinValueRef1
       .Then//String.Lookup: stringJoinValueRef1
       .Else: " "
     .From:
        //Array.Select:
          .From: [<stringValue3>, <stringValue4>, {<subKey>: <stringValue5>}, <stringValue6>]
          .If: boolRef1
            .With: [<topKey1>, [[0], [2, <subKey1>]]]            
          .Else:
            .With:
              .If: objectValueRef3
              .Then:
                //Array.Lookup: arrayValueRef2
            .Else: [[0], [1]]

errordeveloper avatar Feb 01 '18 14:02 errordeveloper