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

Support complex enums in CRDs #779

Merged
merged 8 commits into from
Jan 15, 2022

Conversation

nightkr
Copy link
Member

@nightkr nightkr commented Jan 13, 2022

Motivation

See GREsau/schemars#84

Currently #[derive(CustomResource)] generates invalid CRD objects for enum variants with fields, because the generated schema is rejected by K8s as not being structural.

Solution

This PR adds a Visitor that transparently rewrites enum schemas from:

properties:
  enum:
    oneOf:
      - type: object
      - properties:
          variantOne:
            # properties...
          required: [variantOne]
      - type: object
        properties:
          variantTwo:
            # properties...
        required: [variantTwo]

to:

properties:
  enum:
    type: object
    properties:
      variantOne:
        # properties...
      variantTwo:
        # properties...
    oneOf:
      - required: [variantOne]
      - required: [variantTwo]

See GREsau/schemars#84

Signed-off-by: Teo Klestrup Röijezon <teo@nullable.se>
@nightkr nightkr added the core generic apimachinery style work label Jan 13, 2022
@nightkr nightkr requested a review from a team January 13, 2022 16:06
@nightkr nightkr self-assigned this Jan 13, 2022
Signed-off-by: Teo Klestrup Röijezon <teo@nullable.se>
Signed-off-by: Teo Klestrup Röijezon <teo@nullable.se>
@codecov-commenter
Copy link

codecov-commenter commented Jan 13, 2022

Codecov Report

Merging #779 (aef0969) into master (5e6762d) will increase coverage by 0.10%.
The diff coverage is 88.46%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #779      +/-   ##
==========================================
+ Coverage   71.89%   72.00%   +0.10%     
==========================================
  Files          54       55       +1     
  Lines        3700     3725      +25     
==========================================
+ Hits         2660     2682      +22     
- Misses       1040     1043       +3     
Impacted Files Coverage Δ
kube-derive/src/custom_resource.rs 11.80% <ø> (ø)
kube-core/src/schema.rs 87.50% <87.50%> (ø)
kube-client/src/api/util.rs 81.57% <100.00%> (ø)
kube-derive/tests/crd_schema_test.rs 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 5e6762d...aef0969. Read the comment docs.

@nightkr

This comment has been minimized.

@nightkr
Copy link
Member Author

nightkr commented Jan 13, 2022

cc @cassandracomar @LukeMathWalker @clux @webern who have expressed interest in GREsau/schemars#84
and @thedodd who reported this issue in #129 (comment)

Copy link
Member

@clux clux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great! And the api-server accepts this? Beautiful.
Left some quick comments/questions.

kube-core/src/schema.rs Outdated Show resolved Hide resolved
kube-core/src/schema.rs Show resolved Hide resolved
kube-core/src/schema.rs Show resolved Hide resolved
@nightkr
Copy link
Member Author

nightkr commented Jan 13, 2022

And the api-server accepts this?

Yes :)

kube-core/src/lib.rs Outdated Show resolved Hide resolved
Co-authored-by: kazk <kazk.dev@gmail.com>
Signed-off-by: Teo Klestrup Röijezon <teo@nullable.se>
@soenkeliebau
Copy link
Contributor

I just ran a few simple tests as well and it worked like a charm!

nightkr and others added 3 commits January 14, 2022 11:27
Co-authored-by: Eirik A <sszynrae@gmail.com>
Signed-off-by: Teo Klestrup Röijezon <teo@nullable.se>
Signed-off-by: Teo Klestrup Röijezon <teo@nullable.se>
Signed-off-by: Teo Klestrup Röijezon <teo@nullable.se>
Copy link

@webern webern left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't checked the code (yet), but I thought of a couple of edge cases. You may have already tested these.

Does an enum like this work? The Empty variant would be some kind of empty object with no properties?

enum DoesThisWork {
    Empty,
    NotEmpty(MyStruct),
}

If we do the same thing with some nesting?

enum InnerEnum {
    Nope,
    Yep(SomeStruct)
}

enum Outer {
    Inner(Inner),
    Foo(AnotherStruct),
    Nothing,
}

I normally avoid having a variant and struct by the same name (even though that's actually what I want, but my IDE gets confused). But in the above example I thought having the same name for variant and struct makes for a good edge case.

@nightkr
Copy link
Member Author

nightkr commented Jan 14, 2022

@webern

Does an enum like this work? The Empty variant would be some kind of empty object with no properties?

enum DoesThisWork {
   Empty,
    NotEmpty(MyStruct),
}

Not directly, no. That would serialize as:

# assuming this is wrapped in a field does_this_work
doesThisWork: empty
# or...
doesThisWork:
  notEmpty:
    # MyStruct contents

This violates the rule that all variants must have the same type. Changing Empty to an empty struct variant would ensure that both cases are serialized as objects:

enum DoesThisWork {
    Empty {},
    NotEmpty(MyStruct),
}

That causes the serialization to look like:

doesThisWork:
  empty: {}
  # or...
  notEmpty:
    # MyStruct contents

If we do the same thing with some nesting?

enum InnerEnum {
    Nope,
    Yep(SomeStruct)
}

enum Outer {
    Inner(Inner),
    Foo(AnotherStruct),
    Nothing,
}

Same thing here, it should work as long as you change the Nope and Nothing unit variants to empty struct variants instead.

I normally avoid having a variant and struct by the same name (even though that's actually what I want, but my IDE gets confused). But in the above example I thought having the same name for variant and struct makes for a good edge case.

Type names are ignored here anyway, only variant names matter.

@nightkr nightkr linked an issue Jan 14, 2022 that may be closed by this pull request
@webern
Copy link

webern commented Jan 14, 2022

This violates the rule that all variants must have the same type.

I see, thank you for explaining this. So all of the following would work?

enum {
    A(String),
    B(String),
}
enum {
    A(u8),
    B(u8),
}
enum {
    A(SomeStruct{}),
    B(DifferentStruct{}),
    C{},
}

And this would not work?

enum {
    A(u8),
    B(String),
}

@nightkr
Copy link
Member Author

nightkr commented Jan 14, 2022

I see, thank you for explaining this. So all of the following would work?

enum {
    A(String),
    B(String),
}
enum {
    A(u8),
    B(u8),
}

Yes.

enum {
    A(SomeStruct{}),
    B(DifferentStruct{}),
    C{},
}

After fixing SomeStruct{} -> SomeStruct, yes.

And this would not work?

enum {
    A(u8),
    B(String),
}

Yes it would, this would serialize as:

foo:
  a: 5
  # or...
  b: foo

Which is valid.


I think I explained this poorly, the enum itself must always serialize to the same JSON "type" (bool, number, string, array, object). For externally tagged enums (the default), the result of this is that either all or none of the variants must be unit variants (A,).

Each non-unit variant gets serialized as an object with a unique key, so there is no limit on what type those variants' values can have (apart from the usual constraint that they must also have a JsonSchema that we can turn structural).

@clux clux added the changelog-add changelog added category for prs label Jan 15, 2022
@nightkr nightkr merged commit 912cabd into kube-rs:master Jan 15, 2022
@clux clux added this to the 0.66.0 milestone Jan 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
changelog-add changelog added category for prs core generic apimachinery style work
Projects
None yet
Development

Successfully merging this pull request may close these issues.

cannot use complex enums in kube-derive schemas via schemars
6 participants