kuberneteskubernetes-custom-resources

k8s CRD definition: 4 mutually exclusive optional fields


I have created a CRD for a resource and currently unable to make them mutually exclusive and optional at the same time:

optionOne:
  type: string
optionTwo:
  type: string
optionThree:
  type: string
optionFour:
  type: string

How to combine exclusitivity and optionality of these fields?

I tried using

oneOf:
- not:
    required: ["optionOne"]

But it didn't work.


Solution

  • I used Rust and kube-derive to define a FourOption type that takes an optional oneOf value. It generated the following CRD:

    fouroption.crd.yaml:

    apiVersion: apiextensions.k8s.io/v1
    kind: CustomResourceDefinition
    metadata:
      name: fouroptions.example.com
    spec:
      group: example.com
      names:
        categories: []
        kind: FourOption
        plural: fouroptions
        shortNames: []
        singular: fouroption
      scope: Namespaced
      versions:
      - additionalPrinterColumns: []
        name: v1alpha1
        schema:
          openAPIV3Schema:
            description: Stackoverflow 78523396
            properties:
              spec:
                properties:
                  foo:
                    nullable: true
                    oneOf:
                    - required:
                      - option_one
                    - required:
                      - option_two
                    - required:
                      - option_three
                    - required:
                      - option_four
                    properties:
                      option_four:
                        type: string
                      option_one:
                        type: string
                      option_three:
                        type: string
                      option_two:
                        type: string
                    type: object
                type: object
            required:
            - spec
            title: FourOption
            type: object
        served: true
        storage: true
        subresources: {}
    
    NAMESPACE="78523396"
    kubectl create namespace ${NAMESPACE}
    
    kubectl apply --filename=./fouroption.crd.yaml
    
    customresourcedefinition.apiextensions.k8s.io/fouroptions.example.com configured
    

    Then I tested with none; oneof; and multiple:

    kind: List
    apiVersion: v1
    metadata:
    items:
    # Expect: succeed
    - kind: FourOption
      apiVersion: example.com/v1alpha1
      metadata:
        name: none
      spec:
        foo: null
    # Expect: succeed
    - kind: FourOption
      apiVersion: example.com/v1alpha1
      metadata:
        name: option-two
      spec:
        foo:
          option_two: Hello Freddie
    # Expect: fail
    - kind: FourOption
      apiVersion: example.com/v1alpha1
      metadata:
        name: multiple
      spec:
        foo:
          option_two: Hello Freddie
          option_four: Hello Freddie
    

    Yields:

    fouroption.example.com/none created
    fouroption.example.com/option-two created
    The FourOption "multiple" is invalid: <nil>: Invalid value: "": "spec.foo" must validate one and only one schema (oneOf). Found 2 valid alternatives