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

[K8s Contrib CRD] CRD conversion fails if enum is used #24

Closed
Pythoner6 opened this issue Feb 23, 2024 · 4 comments
Closed

[K8s Contrib CRD] CRD conversion fails if enum is used #24

Pythoner6 opened this issue Feb 23, 2024 · 4 comments

Comments

@Pythoner6
Copy link
Contributor

If a CRD uses an enum, k8s.contrib.crd will fail, and with a rather unhelpful error message to the user:

–– Pkl Error ––
No member of union type matched value 'new Mapping { ["apiVersion"] = "apiextensions.k8s.io/v1"; ["kind"] = "CustomResourceDefinition"; ["metadata"] { ["annotations"] { ["controller-gen.kubebuilder.io/version"] = "v0.11.3"; ["helm.sh/resource-policy"] = "keep" }; ["name"] = "cephbucketnotifications.ceph.rook.io" }; ["spec"] { ["group"] = "ceph.rook.io"; ["names"] { ["kind"] = "CephBucketNotification"; ["listKind"] = "CephBucketNotificationList"; ["plural"] = "cephbucketnotifications"; ["singular"] = "cephbucketnotification" }; ["scope"] = "Namespaced"; ["versions"] { new Mapping { ["name"] = "v1"; ["schema"] { ["openAPIV3Schema"] { ["description"] = "CephBucketNotification represents a Bucket Notifications"; ["properties"] { ["apiVersion"] { ["description"] = "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources"; ["type"] = "string" }; ["kind"] { ["description"] = "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds"; ["type"] = "string" }; ["metadata"] { ["type"] = "object" }; ["spec"] { ["description"] = "BucketNotificationSpec represent the spec of a Bucket Notification"; ["properties"] { ["events"] { ["description"] = "List of events that should trigger the notification"; ["items"] { ["description"] = "BucketNotificationSpec represent the event type of the bucket notification"; ["enum"] { "s3:ObjectCreated:*"; "s3:ObjectCreated:Put"; "s3:ObjectCreated:Post"; "s3:ObjectCreated:Copy"; "s3:ObjectCreated:CompleteMultipartUpload"; "s3:ObjectRemoved:*"; "s3:ObjectRemoved:Delete"; "s3:ObjectRemoved:DeleteMarkerCreated" }; ["type"] = "string" }; ["type"] = "array" }; ["filter"] { ["description"] = "Spec of notification filter"; ["properties"] { ["keyFilters"] { ["description"] = "Filters based on the object's key"; ["items"] { ["description"] = "NotificationKeyFilterRule represent a single key rule in the Notification Filter spec"; ["properties"] { ["name"] { ["description"] = "Name of the filter - prefix/suffix/regex"; ["enum"] { "prefix"; "suffix"; "regex" }; ["type"] = "string" }; ["value"] { ["description"] = "Value to filter on"; ["type"] = "string" } }; ["required"] { "name"; "value" }; ["type"] = "object" }; ["type"] = "array" }; ["metadataFilters"] { ["description"] = "Filters based on the object's metadata"; ["items"] { ["description"] = "NotificationFilterRule represent a single rule in the Notification Filter spec"; ["properties"] { ["name"] { ["description"] = "Name of the metadata or tag"; ["minLength"] = 1; ["type"] = "string" }; ["value"] { ["description"] = "Value to filter on"; ["type"] = "string" } }; ["required"] { "name"; "value" }; ["type"] = "object" }; ["type"] = "array" }; ["tagFilters"] { ["description"] = "Filters based on the object's tags"; ["items"] { ["description"] = "NotificationFilterRule represent a single rule in the Notification Filter spec"; ["properties"] { ["name"] { ["description"] = "Name of the metadata or tag"; ["minLength"] = 1; ["type"] = "string" }; ["value"] { ["description"] = "Value to filter on"; ["type"] = "string" } }; ["required"] { "name"; "value" }; ["type"] = "object" }; ["type"] = "array" } }; ["type"] = "object" }; ["topic"] { ["description"] = "The name of the topic associated with this notification"; ["minLength"] = 1; ["type"] = "string" } }; ["required"] { "topic" }; ["type"] = "object" }; ["status"] { ["description"] = "Status represents the status of an object"; ["properties"] { ["conditions"] { ["items"] { ["description"] = "Condition represents a status condition on any Rook-Ceph Custom Resource."; ["properties"] { ["lastHeartbeatTime"] { ["format"] = "date-time"; ["type"] = "string" }; ["lastTransitionTime"] { ["format"] = "date-time"; ["type"] = "string" }; ["message"] { ["type"] = "string" }; ["reason"] { ["description"] = "ConditionReason is a reason for a condition"; ["type"] = "string" }; ["status"] { ["type"] = "string" }; ["type"] { ["description"] = "ConditionType represent a resource's status"; ["type"] = "string" } }; ["type"] = "object" }; ["type"] = "array" }; ["observedGeneration"] { ["description"] = "ObservedGeneration is the latest generation observed by the controller."; ["format"] = "int64"; ["type"] = "integer" }; ["phase"] { ["type"] = "string" } }; ["type"] = "object"; ["x-kubernetes-preserve-unknown-fields"] = true } }; ["required"] { "metadata"; "spec" }; ["type"] = "object" } }; ["served"] = true; ["storage"] = true; ["subresources"] { ["status"] {} } } } } }'

36 | if (result is ConversionFailure) throw(result.message) else result
                                      ^^^^^^^^^^^^^^^^^^^^^
at pkl.experimental.deepToTyped.deepToTyped#apply.<function#1> (https://github.com/apple/pkl-pantry/blob/pkl.experimental.deepToTyped@1.0.0/packages/pkl.experimental.deepToTyped/deepToTyped.pkl#L36-36)

30 | let (result =
     ^^^^^^^^^^^^^
at pkl.experimental.deepToTyped.deepToTyped#apply (https://github.com/apple/pkl-pantry/blob/pkl.experimental.deepToTyped@1.0.0/packages/pkl.experimental.deepToTyped/deepToTyped.pkl#L30-36)

105 | deepToTyped.apply(ModuleGenerator.CRD, crd) as ModuleGenerator.CRD
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at k8s.contrib.crd.generate#crds.<function#1>[#1] (https://github.com/apple/pkl-pantry/blob/k8s.contrib.crd@1.0.0/packages/k8s.contrib.crd/generate.pkl#L105-105)

101 | let (parser = new yaml.Parser { useMapping = true })
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at k8s.contrib.crd.generate#crds (https://github.com/apple/pkl-pantry/blob/k8s.contrib.crd@1.0.0/packages/k8s.contrib.crd/generate.pkl#L101-108)

128 | for (_crd in crds) {
                   ^^^^
at k8s.contrib.crd.generate#modules (https://github.com/apple/pkl-pantry/blob/k8s.contrib.crd@1.0.0/packages/k8s.contrib.crd/generate.pkl#L128-128)

142 | for (mod in modules) {
                  ^^^^^^^
at k8s.contrib.crd.generate#output.files (https://github.com/apple/pkl-pantry/blob/k8s.contrib.crd@1.0.0/packages/k8s.contrib.crd/generate.pkl#L142-142)

Manually parsing and then calling deep to typed on the offending CRD (but without the type union used by k8s.contrib.crd) lead to a slightly better error message:

–– Pkl Error ––
Unsupported type for conversion: Any

36 | if (result is ConversionFailure) throw(result.message) else result
                                      ^^^^^^^^^^^^^^^^^^^^^
at pkl.experimental.deepToTyped.deepToTyped#apply.<function#1> (projectpackage://pkg.pkl-lang.org/pkl-pantry/pkl.experimental.deepToTyped@1.0.0#/deepToTyped.pkl)

30 | let (result =
     ^^^^^^^^^^^^^
at pkl.experimental.deepToTyped.deepToTyped#apply (projectpackage://pkg.pkl-lang.org/pkl-pantry/pkl.experimental.deepToTyped@1.0.0#/deepToTyped.pkl)

11 | crd: CustomResourceDefinition = deepToTyped.apply(CRD, parsed)
                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at test#crd (file:///home/joseph/dev/nix-pkl/test.pkl, line 11)

106 | text = renderer.renderDocument(value)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (https://github.com/apple/pkl/blob/0.25.2/stdlib/base.pkl#L106)

Which eventually lead me to the offending property, which is enum as defined here https://github.com/apple/pkl-k8s/blob/main/generated-package/apiextensions-apiserver/pkg/apis/apiextensions/v1/CustomResourceDefinition.pkl#L272 (there's also two other properties in there that use the type Any: default and example)

An example minimal CRD which reproduces the issue:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
spec:
  group: foo.example.com
  names:
    kind: Foo
    plural: foos
  scope: Namespaced
  versions:
    - name: v1
      schema:
        openAPIV3Schema:
          properties:
            doesntwork:
              enum:
                - foo
              type: string
          type: object
      served: true
      storage: true
@mruoss
Copy link

mruoss commented Feb 24, 2024

I stumbled upon the same issue. Also, the same exception is raised if default is used (same CRD as above but enum replaced with default:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
spec:
  group: foo.example.com
  names:
    kind: Foo
    plural: foos
  scope: Namespaced
  versions:
    - name: v1
      schema:
        openAPIV3Schema:
          properties:
            doesntwork:
              default: foo
              type: string
          type: object
      served: true
      storage: true

@jackkleeman
Copy link
Contributor

I think I stumbled into this as well; thank you for tracking it down and your fix makes sense to me

@mruoss
Copy link

mruoss commented Mar 5, 2024

Is this fixed with #29?

@Pythoner6
Copy link
Contributor Author

Yep, looks like this is now working with k8s.contrib.crd 1.0.1 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants