stefanprodan / timoni

Timoni is a package manager for Kubernetes, powered by CUE and inspired by Helm.
https://timoni.sh
Apache License 2.0
1.55k stars 68 forks source link

fix: vendor CRDs with no spec #230

Closed b4nst closed 1 year ago

b4nst commented 1 year ago

On CRD with no Spec in the version schema, timoni mod vendor crds was failing with a segfault. This PR introduce a simple fix by ignoring the spec when empty, but still generating the definition of the CR.

Example with crd_computesharedvpchostprojects.compute.cnrm.cloud.google.com.yaml

# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    cnrm.cloud.google.com/version: 1.110.0
  creationTimestamp: null
  labels:
    cnrm.cloud.google.com/managed-by-kcc: "true"
    cnrm.cloud.google.com/stability-level: alpha
    cnrm.cloud.google.com/system: "true"
    cnrm.cloud.google.com/tf2crd: "true"
  name: accesscontextmanageraccesslevelconditions.accesscontextmanager.cnrm.cloud.google.com
spec:
  group: accesscontextmanager.cnrm.cloud.google.com
  names:
    categories:
      - gcp
    kind: AccessContextManagerAccessLevelCondition
    plural: accesscontextmanageraccesslevelconditions
    shortNames:
      - gcpaccesscontextmanageraccesslevelcondition
      - gcpaccesscontextmanageraccesslevelconditions
    singular: accesscontextmanageraccesslevelcondition
  preserveUnknownFields: false
  scope: Namespaced
  versions:
    - additionalPrinterColumns:
        - jsonPath: .metadata.creationTimestamp
          name: Age
          type: date
        - description: When 'True', the most recent reconcile of the resource succeeded
          jsonPath: .status.conditions[?(@.type=='Ready')].status
          name: Ready
          type: string
        - description: The reason for the value in 'Ready'
          jsonPath: .status.conditions[?(@.type=='Ready')].reason
          name: Status
          type: string
        - description: The last transition time for the value in 'Status'
          jsonPath: .status.conditions[?(@.type=='Ready')].lastTransitionTime
          name: Status Age
          type: date
      name: v1alpha1
      schema:
        openAPIV3Schema:
          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/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/api-conventions.md#types-kinds'
              type: string
            metadata:
              type: object
            spec:
              properties:
                accessLevelRef:
                  oneOf:
                    - not:
                        required:
                          - external
                      required:
                        - name
                    - not:
                        anyOf:
                          - required:
                              - name
                          - required:
                              - namespace
                      required:
                        - external
                  properties:
                    external:
                      description: 'Allowed value: The `name` field of an `AccessContextManagerAccessLevel` resource.'
                      type: string
                    name:
                      description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
                      type: string
                    namespace:
                      description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
                      type: string
                  type: object
                devicePolicy:
                  description: |-
                    Immutable. Device specific restrictions, all restrictions must hold for
                    the Condition to be true. If not specified, all devices are
                    allowed.
                  properties:
                    allowedDeviceManagementLevels:
                      description: |-
                        Immutable. A list of allowed device management levels.
                        An empty list allows all management levels. Possible values: ["MANAGEMENT_UNSPECIFIED", "NONE", "BASIC", "COMPLETE"].
                      items:
                        type: string
                      type: array
                    allowedEncryptionStatuses:
                      description: |-
                        Immutable. A list of allowed encryptions statuses.
                        An empty list allows all statuses. Possible values: ["ENCRYPTION_UNSPECIFIED", "ENCRYPTION_UNSUPPORTED", "UNENCRYPTED", "ENCRYPTED"].
                      items:
                        type: string
                      type: array
                    osConstraints:
                      description: |-
                        Immutable. A list of allowed OS versions.
                        An empty list allows all types and all versions.
                      items:
                        properties:
                          minimumVersion:
                            description: |-
                              Immutable. The minimum allowed OS version. If not set, any version
                              of this OS satisfies the constraint.
                              Format: "major.minor.patch" such as "10.5.301", "9.2.1".
                            type: string
                          osType:
                            description: 'Immutable. The operating system type of the device. Possible values: ["OS_UNSPECIFIED", "DESKTOP_MAC", "DESKTOP_WINDOWS", "DESKTOP_LINUX", "DESKTOP_CHROME_OS", "ANDROID", "IOS"].'
                            type: string
                        required:
                          - osType
                        type: object
                      type: array
                    requireAdminApproval:
                      description: Immutable. Whether the device needs to be approved by the customer admin.
                      type: boolean
                    requireCorpOwned:
                      description: Immutable. Whether the device needs to be corp owned.
                      type: boolean
                    requireScreenLock:
                      description: |-
                        Immutable. Whether or not screenlock is required for the DevicePolicy
                        to be true. Defaults to false.
                      type: boolean
                  type: object
                ipSubnetworks:
                  description: |-
                    Immutable. A list of CIDR block IP subnetwork specification. May be IPv4
                    or IPv6.
                    Note that for a CIDR IP address block, the specified IP address
                    portion must be properly truncated (i.e. all the host bits must
                    be zero) or the input is considered malformed. For example,
                    "192.0.2.0/24" is accepted but "192.0.2.1/24" is not. Similarly,
                    for IPv6, "2001:db8::/32" is accepted whereas "2001:db8::1/32"
                    is not. The originating IP of a request must be in one of the
                    listed subnets in order for this Condition to be true.
                    If empty, all IP addresses are allowed.
                  items:
                    type: string
                  type: array
                members:
                  description: |-
                    Immutable. An allowed list of members (users, service accounts).
                    Using groups is not supported yet.

                    The signed-in user originating the request must be a part of one
                    of the provided members. If not specified, a request may come
                    from any user (logged in/not logged in, not present in any
                    groups, etc.).
                    Formats: 'user:{emailid}', 'serviceAccount:{emailid}'.
                  items:
                    type: string
                  type: array
                negate:
                  description: |-
                    Immutable. Whether to negate the Condition. If true, the Condition becomes
                    a NAND over its non-empty fields, each field must be false for
                    the Condition overall to be satisfied. Defaults to false.
                  type: boolean
                regions:
                  description: |-
                    Immutable. The request must originate from one of the provided
                    countries/regions.
                    Format: A valid ISO 3166-1 alpha-2 code.
                  items:
                    type: string
                  type: array
                requiredAccessLevels:
                  description: |-
                    Immutable. A list of other access levels defined in the same Policy,
                    referenced by resource name. Referencing an AccessLevel which
                    does not exist is an error. All access levels listed must be
                    granted for the Condition to be true.
                    Format: accessPolicies/{policy_id}/accessLevels/{short_name}.
                  items:
                    type: string
                  type: array
                resourceID:
                  description: Immutable. Optional. The accessLevel of the resource. Used for creation and acquisition. When unset, the value of `metadata.name` is used as the default.
                  type: string
              type: object
            status:
              properties:
                conditions:
                  description: Conditions represent the latest available observation of the resource's current state.
                  items:
                    properties:
                      lastTransitionTime:
                        description: Last time the condition transitioned from one status to another.
                        type: string
                      message:
                        description: Human-readable message indicating details about last transition.
                        type: string
                      reason:
                        description: Unique, one-word, CamelCase reason for the condition's last transition.
                        type: string
                      status:
                        description: Status is the status of the condition. Can be True, False, Unknown.
                        type: string
                      type:
                        description: Type is the type of the condition.
                        type: string
                    type: object
                  type: array
                observedGeneration:
                  description: ObservedGeneration is the generation of the resource that was most recently observed by the Config Connector controller. If this is equal to metadata.generation, then that means that the current reported status reflects the most recent desired state of the resource.
                  type: integer
              type: object
          type: object
      served: true
      storage: true
      subresources:
        status: {}
status:
  acceptedNames:
    kind: ""
    plural: ""
  conditions: []
  storedVersions: []
timoni mod vendor crds -f crd_computesharedvpchostprojects.compute.cnrm.cloud.google.com.yaml
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x40 pc=0x101883c08]

goroutine 1 [running]:
github.com/stefanprodan/timoni/internal/engine.convertCRD({0x140004c30f8?, 0x14000250e10?, 0x0?})
    /home/runner/work/timoni/timoni/internal/engine/importer.go:330 +0xa28
github.com/stefanprodan/timoni/internal/engine.(*Importer).fromYAML(0x1400048fb98, {0x14000347300?, 0x1400048f7d8?, 0x10060a6d4?})
    /home/runner/work/timoni/timoni/internal/engine/importer.go:86 +0x2cc
github.com/stefanprodan/timoni/internal/engine.(*Importer).Generate(0x1400061bb98, {0x14000347300, 0x101d, 0x1300})
    /home/runner/work/timoni/timoni/internal/engine/importer.go:38 +0x4c
main.runVendorCrdCmd(0x102ea0f80?, {0x140004d41c0?, 0x4?, 0x10198d8bb?})
    /home/runner/work/timoni/timoni/cmd/timoni/mod_vendor_crd.go:147 +0xb50
github.com/spf13/cobra.(*Command).execute(0x102ea0f80, {0x140004d41a0, 0x2, 0x2})
    /home/runner/go/pkg/mod/github.com/spf13/cobra@v1.7.0/command.go:940 +0x658
github.com/spf13/cobra.(*Command).ExecuteC(0x102e9c7a0)
    /home/runner/go/pkg/mod/github.com/spf13/cobra@v1.7.0/command.go:1068 +0x320
github.com/spf13/cobra.(*Command).Execute(...)
    /home/runner/go/pkg/mod/github.com/spf13/cobra@v1.7.0/command.go:992
main.main()
    /home/runner/work/timoni/timoni/cmd/timoni/main.go:88 +0x24