KubeBuilder:validation:AnyOf[] support

1,246 views
Skip to first unread message

bal809...@gmail.com

unread,
Jul 30, 2020, 5:55:36 PM7/30/20
to kubebuilder
Hello,

I have a Go type which can take boolean/string/number and this type has implemented Marshal and UnMarshal functions. But when I generate the CRD by running "make manifests", the CRD has type "Object" which gives me validation error when creating a resource.

Is there any support for "anyOf" in KubeBuilder? If not, is there a way to achieve what I want ?

This is my Go type:

type AdvancedFilterValue struct {
    Type        AdvancedFilterValueType `json:"-"`
    BoolValue   bool                    `json:"-"`
    StringValue string                  `json:"-"`
    NumberValue float64                 `json:"-"`
}

// AdvancedFilterValueType represents the stored type of AdvancedFilterValue.
type AdvancedFilterValueType int64

const (
    Bool    AdvancedFilterValueType = iota // The AdvancedFilterValue holds an bool.
    String                                 // The AdvancedFilterValue holds a string.
    Float64                                // The AdvancedFilterValue holds a float64
)

func (filterValue *AdvancedFilterValue) UnmarshalJSON(value []byteerror {
    if value[0] == '"' {
        filterValue.Type = String
        return json.Unmarshal(value, &filterValue.StringValue)
    }

    if value[0] == 't' || value[0] == 'T' || value[0] == 'f' || value[0] == 'F' {
        filterValue.Type = Bool
        return json.Unmarshal(value, &filterValue.BoolValue)
    }

    filterValue.Type = Float64
    return json.Unmarshal(value, &filterValue.NumberValue)
}

func (filterValue *AdvancedFilterValue) MarshalJSON() ([]byteerror) {
    switch filterValue.Type {
    case Bool:
        return json.Marshal(filterValue.BoolValue)
    case String:
        return json.Marshal(filterValue.StringValue)
    case Float64:
        return json.Marshal(filterValue.NumberValue)
    default:
        return []byte{}, fmt.Errorf("Invalid AdvancedFilterValue.Type")
    }
}

Thanks
Balmukund

Solly Ross

unread,
Jul 30, 2020, 6:16:26 PM7/30/20
to bal809...@gmail.com, kubebuilder
Recently, we merged a patch to controller-tools that will mark any type with custom serialization as "type: Any".  From there, you can theoretically apply extra validation.  However, the type, as you've defined it, is not valid within a Kubernetes structural schema, so if you use in an a CRD type definition, you'll be locked out of using newer CRD features, and your CRD won't be valid for the v1 CRD Kind: https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#specifying-a-structural-schema.  I'd advise you to rethink that type.

Best Regards,
Solly

--
You received this message because you are subscribed to the Google Groups "kubebuilder" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kubebuilder...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kubebuilder/58a25667-c062-417b-bb96-91f49644062co%40googlegroups.com.

bal809...@gmail.com

unread,
Jul 30, 2020, 6:36:55 PM7/30/20
to kubebuilder
Thank you Solly! 

Right now, I am blank on how to re-model this type. The value of this type depends on the value of another variable. As you can see, it's a filter value and it's type depends on the operator used to filter. One way I can think of is using string data type for this value and then parse it but that can lead to bugs. 
To unsubscribe from this group and stop receiving emails from it, send an email to kubeb...@googlegroups.com.

Solly Ross

unread,
Jul 30, 2020, 7:17:52 PM7/30/20
to bal809...@gmail.com, kubebuilder
So, you've got two options here that I see.

The first is to mark the field as string, add additional string validation (e.g. via a regex or something), and then serialize/deserialize from more strongly typed Go code.  Kinda what we do for `resource.Quantity` -- that's a string (waves hands dramatically to distract from legacy validation also allowing it to be other things) that has a specific structured format, and then deserializes into basically a bignum.

The second is to use that other value and follow the enum pattern that we do in kubernetes.  Note that "oneOf"s are allowed, it's just that any given field must have the same type across all branches.  So

```
properties:
  bool: {type: boolean},
  str: {type: string},
  float: {type: float},
  oneOf:
  - {properties: {bool: {type: boolean}}}
  - {properties: {int: {type: integer}}}
  - {properties: {str: {type: string}}}
```
  
Is allowed.  If you wanted to, depending on how your object is structured, you could also work in the other field into the oneOf to get validation that the right type is specified according to that field.

Unfortunately, you'd have to "hand-overlay" that validation at the moment (we have an open PR for allowing markers for that, but we're still figuring out the details), but it's a valid structural schema.

Does that make sense?

Best Regards,
Solly

To unsubscribe from this group and stop receiving emails from it, send an email to kubebuilder...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kubebuilder/336d85e5-49f9-41e5-946b-303492bc8c10o%40googlegroups.com.

David Sharp

unread,
Aug 4, 2020, 1:02:25 PM8/4/20
to bal809...@gmail.com, Solly Ross, kubebuilder
We recently went through how to implement a union type as well. One thing we found that I think is worth noting is that there's an "implementable" KEP for unions. It recommends adding an optional discriminator field, which would be set by the apiserver (once the KEP is implemented).



From: 'Solly Ross' via kubebuilder <kubeb...@googlegroups.com>
Sent: Thursday, July 30, 2020 4:17 PM
To: bal809...@gmail.com <bal809...@gmail.com>
Cc: kubebuilder <kubeb...@googlegroups.com>
Subject: Re: KubeBuilder:validation:AnyOf[] support
 
Reply all
Reply to author
Forward
0 new messages