From 5f020d15656dadf1421e1fb711394173c3823c6d Mon Sep 17 00:00:00 2001 From: Fabio Burzigotti Date: Fri, 27 Dec 2024 11:13:54 +0100 Subject: [PATCH 1/4] [issue-212] - Adding the Open Data Hub CRDs manifests (ODH) --- provisioners/pom.xml | 29 +- ...er.opendatahub.io_datascienceclusters.yaml | 972 ++++++++++++++++++ ...ion.opendatahub.io_dscinitializations.yaml | 315 ++++++ ...atures.opendatahub.io_featuretrackers.yaml | 101 ++ ...s.platform.opendatahub.io_monitorings.yaml | 141 +++ 5 files changed, 1544 insertions(+), 14 deletions(-) create mode 100644 provisioners/src/main/resources/crds/datasciencecluster.opendatahub.io_datascienceclusters.yaml create mode 100644 provisioners/src/main/resources/crds/dscinitialization.opendatahub.io_dscinitializations.yaml create mode 100644 provisioners/src/main/resources/crds/features.opendatahub.io_featuretrackers.yaml create mode 100644 provisioners/src/main/resources/crds/services.platform.opendatahub.io_monitorings.yaml diff --git a/provisioners/pom.xml b/provisioners/pom.xml index a4b9ee85..ebd2cc18 100644 --- a/provisioners/pom.xml +++ b/provisioners/pom.xml @@ -236,25 +236,26 @@ java-generator-maven-plugin + default-crd-generation generate + + target/generated-sources + src/main/resources/crds + + false + + true + - - target/generated-sources - src/main/resources/crds - - false - - true - diff --git a/provisioners/src/main/resources/crds/datasciencecluster.opendatahub.io_datascienceclusters.yaml b/provisioners/src/main/resources/crds/datasciencecluster.opendatahub.io_datascienceclusters.yaml new file mode 100644 index 00000000..7c06b2c7 --- /dev/null +++ b/provisioners/src/main/resources/crds/datasciencecluster.opendatahub.io_datascienceclusters.yaml @@ -0,0 +1,972 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: datascienceclusters.datasciencecluster.opendatahub.io +spec: + group: datasciencecluster.opendatahub.io + names: + kind: DataScienceCluster + listKind: DataScienceClusterList + plural: datascienceclusters + shortNames: + - dsc + singular: datasciencecluster + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + description: DataScienceCluster is the Schema for the datascienceclusters + API. + 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: DataScienceClusterSpec defines the desired state of the cluster. + properties: + components: + description: Override and fine tune specific component configurations. + properties: + codeflare: + description: |- + CodeFlare component configuration. + If CodeFlare Operator has been installed in the cluster, it should be uninstalled first before enabling component. + properties: + devFlags: + description: Add developer fields + properties: + manifests: + description: List of custom manifests for the given component + items: + properties: + contextDir: + default: manifests + description: contextDir is the relative path to + the folder containing manifests in a repository, + default value "manifests" + type: string + sourcePath: + default: "" + description: 'sourcePath is the subpath within contextDir + where kustomize builds start. Examples include + any sub-folder or path: `base`, `overlays/dev`, + `default`, `odh` etc.' + type: string + uri: + default: "" + description: uri is the URI point to a git repo + with tag/branch. e.g. https://github.com/org/repo/tarball/ + type: string + type: object + type: array + type: object + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + dashboard: + description: Dashboard component configuration. + properties: + devFlags: + description: Add developer fields + properties: + manifests: + description: List of custom manifests for the given component + items: + properties: + contextDir: + default: manifests + description: contextDir is the relative path to + the folder containing manifests in a repository, + default value "manifests" + type: string + sourcePath: + default: "" + description: 'sourcePath is the subpath within contextDir + where kustomize builds start. Examples include + any sub-folder or path: `base`, `overlays/dev`, + `default`, `odh` etc.' + type: string + uri: + default: "" + description: uri is the URI point to a git repo + with tag/branch. e.g. https://github.com/org/repo/tarball/ + type: string + type: object + type: array + type: object + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + datasciencepipelines: + description: |- + DataSciencePipeline component configuration. + Requires OpenShift Pipelines Operator to be installed before enable component + properties: + devFlags: + description: Add developer fields + properties: + manifests: + description: List of custom manifests for the given component + items: + properties: + contextDir: + default: manifests + description: contextDir is the relative path to + the folder containing manifests in a repository, + default value "manifests" + type: string + sourcePath: + default: "" + description: 'sourcePath is the subpath within contextDir + where kustomize builds start. Examples include + any sub-folder or path: `base`, `overlays/dev`, + `default`, `odh` etc.' + type: string + uri: + default: "" + description: uri is the URI point to a git repo + with tag/branch. e.g. https://github.com/org/repo/tarball/ + type: string + type: object + type: array + type: object + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + kserve: + description: |- + Kserve component configuration. + Requires OpenShift Serverless and OpenShift Service Mesh Operators to be installed before enable component + Does not support enabled ModelMeshServing at the same time + properties: + defaultDeploymentMode: + description: |- + Configures the default deployment mode for Kserve. This can be set to 'Serverless' or 'RawDeployment'. + The value specified in this field will be used to set the default deployment mode in the 'inferenceservice-config' configmap for Kserve. + This field is optional. If no default deployment mode is specified, Kserve will use Serverless mode. + enum: + - Serverless + - RawDeployment + pattern: ^(Serverless|RawDeployment)$ + type: string + devFlags: + description: Add developer fields + properties: + manifests: + description: List of custom manifests for the given component + items: + properties: + contextDir: + default: manifests + description: contextDir is the relative path to + the folder containing manifests in a repository, + default value "manifests" + type: string + sourcePath: + default: "" + description: 'sourcePath is the subpath within contextDir + where kustomize builds start. Examples include + any sub-folder or path: `base`, `overlays/dev`, + `default`, `odh` etc.' + type: string + uri: + default: "" + description: uri is the URI point to a git repo + with tag/branch. e.g. https://github.com/org/repo/tarball/ + type: string + type: object + type: array + type: object + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + nim: + description: Configures and enables NVIDIA NIM integration + properties: + managementState: + default: Managed + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + serving: + description: |- + Serving configures the KNative-Serving stack used for model serving. A Service + Mesh (Istio) is prerequisite, since it is used as networking layer. + properties: + ingressGateway: + description: |- + IngressGateway allows to customize some parameters for the Istio Ingress Gateway + that is bound to KNative-Serving. + properties: + certificate: + description: |- + Certificate specifies configuration of the TLS certificate securing communication + for the gateway. + properties: + secretName: + description: |- + SecretName specifies the name of the Kubernetes Secret resource that contains a + TLS certificate secure HTTP communications for the KNative network. + type: string + type: + default: OpenshiftDefaultIngress + description: |- + Type specifies if the TLS certificate should be generated automatically, or if the certificate + is provided by the user. Allowed values are: + * SelfSigned: A certificate is going to be generated using an own private key. + * Provided: Pre-existence of the TLS Secret (see SecretName) with a valid certificate is assumed. + * OpenshiftDefaultIngress: Default ingress certificate configured for OpenShift + enum: + - SelfSigned + - Provided + - OpenshiftDefaultIngress + type: string + type: object + domain: + description: |- + Domain specifies the host name for intercepting incoming requests. + Most likely, you will want to use a wildcard name, like *.example.com. + If not set, the domain of the OpenShift Ingress is used. + If you choose to generate a certificate, this is the domain used for the certificate request. + type: string + type: object + managementState: + default: Managed + enum: + - Managed + - Unmanaged + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + name: + default: knative-serving + description: |- + Name specifies the name of the KNativeServing resource that is going to be + created to instruct the KNative Operator to deploy KNative serving components. + This resource is created in the "knative-serving" namespace. + type: string + type: object + type: object + kueue: + description: Kueue component configuration. + properties: + devFlags: + description: Add developer fields + properties: + manifests: + description: List of custom manifests for the given component + items: + properties: + contextDir: + default: manifests + description: contextDir is the relative path to + the folder containing manifests in a repository, + default value "manifests" + type: string + sourcePath: + default: "" + description: 'sourcePath is the subpath within contextDir + where kustomize builds start. Examples include + any sub-folder or path: `base`, `overlays/dev`, + `default`, `odh` etc.' + type: string + uri: + default: "" + description: uri is the URI point to a git repo + with tag/branch. e.g. https://github.com/org/repo/tarball/ + type: string + type: object + type: array + type: object + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + modelmeshserving: + description: ModelMeshServing component configuration. + properties: + devFlags: + description: Add developer fields + properties: + manifests: + description: List of custom manifests for the given component + items: + properties: + contextDir: + default: manifests + description: contextDir is the relative path to + the folder containing manifests in a repository, + default value "manifests" + type: string + sourcePath: + default: "" + description: 'sourcePath is the subpath within contextDir + where kustomize builds start. Examples include + any sub-folder or path: `base`, `overlays/dev`, + `default`, `odh` etc.' + type: string + uri: + default: "" + description: uri is the URI point to a git repo + with tag/branch. e.g. https://github.com/org/repo/tarball/ + type: string + type: object + type: array + type: object + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + modelregistry: + description: ModelRegistry component configuration. + properties: + devFlags: + description: Add developer fields + properties: + manifests: + description: List of custom manifests for the given component + items: + properties: + contextDir: + default: manifests + description: contextDir is the relative path to + the folder containing manifests in a repository, + default value "manifests" + type: string + sourcePath: + default: "" + description: 'sourcePath is the subpath within contextDir + where kustomize builds start. Examples include + any sub-folder or path: `base`, `overlays/dev`, + `default`, `odh` etc.' + type: string + uri: + default: "" + description: uri is the URI point to a git repo + with tag/branch. e.g. https://github.com/org/repo/tarball/ + type: string + type: object + type: array + type: object + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + registriesNamespace: + default: odh-model-registries + description: Namespace for model registries to be installed, + configurable only once when model registry is enabled, defaults + to "odh-model-registries" + maxLength: 63 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?)?$ + type: string + type: object + x-kubernetes-validations: + - message: RegistriesNamespace is immutable when model registry + is Managed + rule: (self.managementState != 'Managed') || (oldSelf.registriesNamespace + == '') || (oldSelf.managementState != 'Managed')|| (self.registriesNamespace + == oldSelf.registriesNamespace) + ray: + description: Ray component configuration. + properties: + devFlags: + description: Add developer fields + properties: + manifests: + description: List of custom manifests for the given component + items: + properties: + contextDir: + default: manifests + description: contextDir is the relative path to + the folder containing manifests in a repository, + default value "manifests" + type: string + sourcePath: + default: "" + description: 'sourcePath is the subpath within contextDir + where kustomize builds start. Examples include + any sub-folder or path: `base`, `overlays/dev`, + `default`, `odh` etc.' + type: string + uri: + default: "" + description: uri is the URI point to a git repo + with tag/branch. e.g. https://github.com/org/repo/tarball/ + type: string + type: object + type: array + type: object + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + trainingoperator: + description: Training Operator component configuration. + properties: + devFlags: + description: Add developer fields + properties: + manifests: + description: List of custom manifests for the given component + items: + properties: + contextDir: + default: manifests + description: contextDir is the relative path to + the folder containing manifests in a repository, + default value "manifests" + type: string + sourcePath: + default: "" + description: 'sourcePath is the subpath within contextDir + where kustomize builds start. Examples include + any sub-folder or path: `base`, `overlays/dev`, + `default`, `odh` etc.' + type: string + uri: + default: "" + description: uri is the URI point to a git repo + with tag/branch. e.g. https://github.com/org/repo/tarball/ + type: string + type: object + type: array + type: object + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + trustyai: + description: TrustyAI component configuration. + properties: + devFlags: + description: Add developer fields + properties: + manifests: + description: List of custom manifests for the given component + items: + properties: + contextDir: + default: manifests + description: contextDir is the relative path to + the folder containing manifests in a repository, + default value "manifests" + type: string + sourcePath: + default: "" + description: 'sourcePath is the subpath within contextDir + where kustomize builds start. Examples include + any sub-folder or path: `base`, `overlays/dev`, + `default`, `odh` etc.' + type: string + uri: + default: "" + description: uri is the URI point to a git repo + with tag/branch. e.g. https://github.com/org/repo/tarball/ + type: string + type: object + type: array + type: object + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + workbenches: + description: Workbenches component configuration. + properties: + devFlags: + description: Add developer fields + properties: + manifests: + description: List of custom manifests for the given component + items: + properties: + contextDir: + default: manifests + description: contextDir is the relative path to + the folder containing manifests in a repository, + default value "manifests" + type: string + sourcePath: + default: "" + description: 'sourcePath is the subpath within contextDir + where kustomize builds start. Examples include + any sub-folder or path: `base`, `overlays/dev`, + `default`, `odh` etc.' + type: string + uri: + default: "" + description: uri is the URI point to a git repo + with tag/branch. e.g. https://github.com/org/repo/tarball/ + type: string + type: object + type: array + type: object + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + type: object + type: object + status: + description: DataScienceClusterStatus defines the observed state of DataScienceCluster. + properties: + components: + description: Expose component's specific status + properties: + codeflare: + description: CodeFlare component status. + properties: + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + dashboard: + description: Dashboard component status. + properties: + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + url: + type: string + type: object + datasciencepipelines: + description: DataSciencePipeline component status. + properties: + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + kserve: + description: Kserve component status. + properties: + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + kueue: + description: Kueue component status. + properties: + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + modelmeshserving: + description: ModelMeshServing component status. + properties: + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + modelregistry: + description: ModelRegistry component status. + properties: + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + registriesNamespace: + type: string + type: object + ray: + description: Ray component status. + properties: + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + trainingoperator: + description: Training Operator component status. + properties: + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + trustyai: + description: TrustyAI component status. + properties: + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + workbenches: + description: Workbenches component status. + properties: + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + type: object + conditions: + description: Conditions describes the state of the DataScienceCluster + resource. + items: + description: |- + Condition represents the state of the operator's + reconciliation functionality. + properties: + lastHeartbeatTime: + format: date-time + type: string + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + description: ConditionType is the state of the operator's reconciliation + functionality. + type: string + required: + - status + - type + type: object + type: array + errorMessage: + type: string + installedComponents: + additionalProperties: + type: boolean + description: List of components with status if installed or not + type: object + observedGeneration: + description: The generation observed by the deployment controller. + format: int64 + type: integer + phase: + description: |- + Phase describes the Phase of DataScienceCluster reconciliation state + This is used by OLM UI to provide status information to the user + type: string + relatedObjects: + description: |- + RelatedObjects is a list of objects created and maintained by this operator. + Object references will be added to this list after they have been created AND found in the cluster. + items: + description: ObjectReference contains enough information to let + you inspect or modify the referred object. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + 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 + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic + type: array + release: + description: Version and release type + properties: + name: + type: string + version: + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/provisioners/src/main/resources/crds/dscinitialization.opendatahub.io_dscinitializations.yaml b/provisioners/src/main/resources/crds/dscinitialization.opendatahub.io_dscinitializations.yaml new file mode 100644 index 00000000..d1750a44 --- /dev/null +++ b/provisioners/src/main/resources/crds/dscinitialization.opendatahub.io_dscinitializations.yaml @@ -0,0 +1,315 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: dscinitializations.dscinitialization.opendatahub.io +spec: + group: dscinitialization.opendatahub.io + names: + kind: DSCInitialization + listKind: DSCInitializationList + plural: dscinitializations + shortNames: + - dsci + singular: dscinitialization + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - description: Current Phase + jsonPath: .status.phase + name: Phase + type: string + - jsonPath: .metadata.creationTimestamp + name: Created At + type: string + name: v1 + schema: + openAPIV3Schema: + description: DSCInitialization is the Schema for the dscinitializations API. + 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: DSCInitializationSpec defines the desired state of DSCInitialization. + properties: + applicationsNamespace: + default: opendatahub + description: Namespace for applications to be installed, non-configurable, + default to "opendatahub" + maxLength: 63 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?)?$ + type: string + x-kubernetes-validations: + - message: ApplicationsNamespace is immutable + rule: self == oldSelf + devFlags: + description: |- + Internal development useful field to test customizations. + This is not recommended to be used in production environment. + properties: + logLevel: + description: Override Zap log level. Can be "debug", "info", "error" + or a number (more verbose). + type: string + logmode: + default: production + description: '## DEPRECATED ##: Ignored, use LogLevel instead' + enum: + - devel + - development + - prod + - production + - default + type: string + manifestsUri: + description: Custom manifests uri for odh-manifests + type: string + type: object + monitoring: + description: Enable monitoring on specified namespace + properties: + managementState: + description: |- + Set to one of the following values: + + - "Managed" : the operator is actively managing the component and trying to keep it active. + It will only upgrade the component if it is safe to do so + + - "Removed" : the operator is actively managing the component and will not install it, + or if it is installed, the operator will try to remove it + enum: + - Managed + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + namespace: + default: opendatahub + description: |- + monitoring spec exposed to DSCI api + Namespace for monitoring if it is enabled + maxLength: 63 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?)?$ + type: string + type: object + serviceMesh: + description: |- + Configures Service Mesh as networking layer for Data Science Clusters components. + The Service Mesh is a mandatory prerequisite for single model serving (KServe) and + you should review this configuration if you are planning to use KServe. + For other components, it enhances user experience; e.g. it provides unified + authentication giving a Single Sign On experience. + properties: + auth: + description: |- + Auth holds configuration of authentication and authorization services + used by Service Mesh in Opendatahub. + properties: + audiences: + default: + - https://kubernetes.default.svc + description: |- + Audiences is a list of the identifiers that the resource server presented + with the token identifies as. Audience-aware token authenticators will verify + that the token was intended for at least one of the audiences in this list. + If no audiences are provided, the audience will default to the audience of the + Kubernetes apiserver (kubernetes.default.svc). + items: + type: string + type: array + namespace: + description: |- + Namespace where it is deployed. If not provided, the default is to + use '-auth-provider' suffix on the ApplicationsNamespace of the DSCI. + maxLength: 63 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?)?$ + type: string + type: object + controlPlane: + description: ControlPlane holds configuration of Service Mesh + used by Opendatahub. + properties: + metricsCollection: + default: Istio + description: |- + MetricsCollection specifies if metrics from components on the Mesh namespace + should be collected. Setting the value to "Istio" will collect metrics from the + control plane and any proxies on the Mesh namespace (like gateway pods). Setting + to "None" will disable metrics collection. + enum: + - Istio + - None + type: string + name: + default: data-science-smcp + description: Name is a name Service Mesh Control Plane. Defaults + to "data-science-smcp". + type: string + namespace: + default: istio-system + description: Namespace is a namespace where Service Mesh is + deployed. Defaults to "istio-system". + maxLength: 63 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?)?$ + type: string + type: object + managementState: + default: Removed + enum: + - Managed + - Unmanaged + - Removed + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + type: object + trustedCABundle: + description: |- + When set to `Managed`, adds odh-trusted-ca-bundle Configmap to all namespaces that includes + cluster-wide Trusted CA Bundle in .data["ca-bundle.crt"]. + Additionally, this fields allows admins to add custom CA bundles to the configmap using the .CustomCABundle field. + properties: + customCABundle: + default: "" + description: |- + A custom CA bundle that will be available for all components in the + Data Science Cluster(DSC). This bundle will be stored in odh-trusted-ca-bundle + ConfigMap .data.odh-ca-bundle.crt . + type: string + managementState: + default: Removed + description: managementState indicates whether and how the operator + should manage customized CA bundle + enum: + - Managed + - Removed + - Unmanaged + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + required: + - customCABundle + - managementState + type: object + required: + - applicationsNamespace + type: object + status: + description: DSCInitializationStatus defines the observed state of DSCInitialization. + properties: + conditions: + description: Conditions describes the state of the DSCInitializationStatus + resource + items: + description: |- + Condition represents the state of the operator's + reconciliation functionality. + properties: + lastHeartbeatTime: + format: date-time + type: string + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + description: ConditionType is the state of the operator's reconciliation + functionality. + type: string + required: + - status + - type + type: object + type: array + errorMessage: + type: string + phase: + description: |- + Phase describes the Phase of DSCInitializationStatus + This is used by OLM UI to provide status information to the user + type: string + relatedObjects: + description: |- + RelatedObjects is a list of objects created and maintained by this operator. + Object references will be added to this list after they have been created AND found in the cluster + items: + description: ObjectReference contains enough information to let + you inspect or modify the referred object. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + 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 + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic + type: array + release: + description: Version and release type + properties: + name: + type: string + version: + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/provisioners/src/main/resources/crds/features.opendatahub.io_featuretrackers.yaml b/provisioners/src/main/resources/crds/features.opendatahub.io_featuretrackers.yaml new file mode 100644 index 00000000..c0110c7f --- /dev/null +++ b/provisioners/src/main/resources/crds/features.opendatahub.io_featuretrackers.yaml @@ -0,0 +1,101 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: featuretrackers.features.opendatahub.io +spec: + group: features.opendatahub.io + names: + kind: FeatureTracker + listKind: FeatureTrackerList + plural: featuretrackers + singular: featuretracker + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + description: |- + FeatureTracker represents a cluster-scoped resource in the Data Science Cluster, + specifically designed for monitoring and managing objects created via the internal Features API. + This resource serves a crucial role in cross-namespace resource management, acting as + an owner reference for various resources. The primary purpose of the FeatureTracker + is to enable efficient garbage collection by Kubernetes. This is essential for + ensuring that resources are automatically cleaned up and reclaimed when they are + no longer required. + 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: FeatureTrackerSpec defines the desired state of FeatureTracker. + properties: + appNamespace: + type: string + source: + description: Source describes the type of object that created the + related Feature to this FeatureTracker. + properties: + name: + type: string + type: + type: string + type: object + type: object + status: + description: FeatureTrackerStatus defines the observed state of FeatureTracker. + properties: + conditions: + items: + description: |- + Condition represents the state of the operator's + reconciliation functionality. + properties: + lastHeartbeatTime: + format: date-time + type: string + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + description: ConditionType is the state of the operator's reconciliation + functionality. + type: string + required: + - status + - type + type: object + type: array + phase: + description: |- + Phase describes the Phase of FeatureTracker reconciliation state. + This is used by OLM UI to provide status information to the user. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/provisioners/src/main/resources/crds/services.platform.opendatahub.io_monitorings.yaml b/provisioners/src/main/resources/crds/services.platform.opendatahub.io_monitorings.yaml new file mode 100644 index 00000000..56e4caec --- /dev/null +++ b/provisioners/src/main/resources/crds/services.platform.opendatahub.io_monitorings.yaml @@ -0,0 +1,141 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: monitorings.services.platform.opendatahub.io +spec: + group: services.platform.opendatahub.io + names: + kind: Monitoring + listKind: MonitoringList + plural: monitorings + singular: monitoring + scope: Cluster + versions: + - additionalPrinterColumns: + - description: Ready + jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - description: Reason + jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Reason + type: string + - description: URL + jsonPath: .status.url + name: URL + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Monitoring is the Schema for the monitorings API + 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: MonitoringSpec defines the desired state of Monitoring + properties: + namespace: + default: opendatahub + description: |- + monitoring spec exposed to DSCI api + Namespace for monitoring if it is enabled + maxLength: 63 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?)?$ + type: string + type: object + status: + description: MonitoringStatus defines the observed state of Monitoring + properties: + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + observedGeneration: + format: int64 + type: integer + phase: + type: string + url: + type: string + type: object + type: object + x-kubernetes-validations: + - message: Monitoring name must be default-monitoring + rule: self.metadata.name == 'default-monitoring' + served: true + storage: true + subresources: + status: {} From c8e632776447281e3d7ccdbdde342fd6906ba446 Mon Sep 17 00:00:00 2001 From: Fabio Burzigotti Date: Mon, 6 Jan 2025 11:31:58 +0100 Subject: [PATCH 2/4] [issue-212] - Make subscription and CSV related behavior overridable in OperaotrProvisioner, e.g. to let ODH provisioning deal with all namespaces --- .../operator/OperatorProvisioner.java | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/jboss/intersmash/provision/operator/OperatorProvisioner.java b/core/src/main/java/org/jboss/intersmash/provision/operator/OperatorProvisioner.java index 951d94f4..476ea923 100644 --- a/core/src/main/java/org/jboss/intersmash/provision/operator/OperatorProvisioner.java +++ b/core/src/main/java/org/jboss/intersmash/provision/operator/OperatorProvisioner.java @@ -64,8 +64,8 @@ public abstract class OperatorProvisioner implements Provisioner, Scalable { // cache the current csv and list of provided custom resource definitions - static String currentCSV; - final String packageManifestName; + protected static String currentCSV; + protected final String packageManifestName; private CatalogSource catalogSource; private final A operatorApplication; private PackageManifest packageManifest; @@ -180,6 +180,16 @@ private String getCatalogSourceNamespace() { return namespace; } + /** + * The CatalogSource is in the "openshift-marketplace" namespace by default on OpenShift, in the "olm" one on K8s. + * When a custom operator image must be used, then a custom CatalogSource will be created in the current namespace. + * + * @return namespace where the custom CatalogSource is located + */ + protected String getTargetNamespace() { + return this.client().getNamespace(); + } + /** * Initialize a reference to a proper catalog source which will be used to pull the required operator image. * @@ -350,7 +360,7 @@ public void subscribe(String installPlanApproval, Map envVariabl log.info("Subscribing the {} operator", packageManifestName); // oc get packagemanifest wildfly -o template --template {{.status.defaultChannel}} final String catalogSourceNamespace = getCatalogSourceNamespace(); - final String targetNamespace = this.client().getNamespace(); + final String targetNamespace = getTargetNamespace(); Subscription operatorSubscription = (envVariables == null || envVariables.isEmpty()) ? new Subscription(catalogSourceNamespace, targetNamespace, getOperatorCatalogSource(), packageManifestName, @@ -462,7 +472,7 @@ protected void waitForOperatorPod() { if (operatorSpec.length != 3) { throw new IllegalStateException("Failed to get operator deployment spec from csvs!"); } - BooleanSupplier bs = () -> this.client().inNamespace(this.client().getNamespace()).pods().list().getItems().stream() + BooleanSupplier bs = () -> this.client().inNamespace(this.getTargetNamespace()).pods().list().getItems().stream() .filter(p -> !com.google.common.base.Strings.isNullOrEmpty(p.getMetadata().getLabels().get(operatorSpec[1])) && p.getMetadata().getLabels().get(operatorSpec[1]).equals(operatorSpec[2]) && p.getStatus().getContainerStatuses().size() > 0 @@ -486,8 +496,8 @@ protected FailFastCheck getFailFastCheck() { * Documentation: https://docs.openshift.com/container-platform/4.4/operators/olm-deleting-operators-from-cluster.html#olm-deleting-operator-from-a-cluster-using-cli_olm-deleting-operators-from-a-cluster */ public void unsubscribe() { - this.execute("delete", "subscription", packageManifestName, "--ignore-not-found"); - this.execute("delete", "csvs", currentCSV, "--ignore-not-found"); + removeSubscription(); + removeClusterServiceVersion(); for (String customResource : getCustomResourceDefinitions()) { final String crds = this.execute("get", "crd", customResource, "--ignore-not-found"); if (crds != null && !crds.isEmpty()) { @@ -496,6 +506,14 @@ public void unsubscribe() { } } + protected void removeClusterServiceVersion() { + this.execute("delete", "csvs", currentCSV, "--ignore-not-found"); + } + + protected void removeSubscription() { + this.execute("delete", "subscription", packageManifestName, "--ignore-not-found"); + } + public String getCurrentCSV() { return currentCSV; } From 8efb462bbecbcb20330c09d22fc1856b74de351e Mon Sep 17 00:00:00 2001 From: Fabio Burzigotti Date: Mon, 6 Jan 2025 11:32:33 +0100 Subject: [PATCH 3/4] [issue-212] - Add ODH application, provisioning and tests --- .../jboss/intersmash/IntersmashConfig.java | 46 ++++ .../OpenDataHubOperatorApplication.java | 44 +++ ...enDataHubOpenShiftOperatorProvisioner.java | 116 ++++++++ .../OpenDataHubOperatorProvisioner.java | 257 ++++++++++++++++++ ...OpenDataHubOperatorProvisionerFactory.java | 46 ++++ .../model/odh/DSCInitializationList.java | 26 ++ .../model/odh/DataScienceClusterList.java | 26 ++ .../model/odh/FeatureTrackerList.java | 26 ++ .../operator/model/odh/MonitoringList.java | 26 ++ ...ss.intersmash.provision.ProvisionerFactory | 3 +- ...taHubOpenShiftOperatorProvisionerTest.java | 172 ++++++++++++ .../OpenShiftProvisionerTestBase.java | 46 +++- .../openshift/ProvisionerCleanupTestCase.java | 5 +- 13 files changed, 835 insertions(+), 4 deletions(-) create mode 100644 provisioners/src/main/java/org/jboss/intersmash/application/operator/OpenDataHubOperatorApplication.java create mode 100644 provisioners/src/main/java/org/jboss/intersmash/provision/openshift/OpenDataHubOpenShiftOperatorProvisioner.java create mode 100644 provisioners/src/main/java/org/jboss/intersmash/provision/operator/OpenDataHubOperatorProvisioner.java create mode 100644 provisioners/src/main/java/org/jboss/intersmash/provision/operator/OpenDataHubOperatorProvisionerFactory.java create mode 100644 provisioners/src/main/java/org/jboss/intersmash/provision/operator/model/odh/DSCInitializationList.java create mode 100644 provisioners/src/main/java/org/jboss/intersmash/provision/operator/model/odh/DataScienceClusterList.java create mode 100644 provisioners/src/main/java/org/jboss/intersmash/provision/operator/model/odh/FeatureTrackerList.java create mode 100644 provisioners/src/main/java/org/jboss/intersmash/provision/operator/model/odh/MonitoringList.java create mode 100644 testsuite/integration-tests/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/OpenDataHubOpenShiftOperatorProvisionerTest.java diff --git a/core/src/main/java/org/jboss/intersmash/IntersmashConfig.java b/core/src/main/java/org/jboss/intersmash/IntersmashConfig.java index 55a6a0a0..4623a4f4 100644 --- a/core/src/main/java/org/jboss/intersmash/IntersmashConfig.java +++ b/core/src/main/java/org/jboss/intersmash/IntersmashConfig.java @@ -85,6 +85,13 @@ public class IntersmashConfig { private static final String HYPERFOIL_OPERATOR_PACKAGE_MANIFEST = "intersmash.hyperfoil.operators.package_manifest"; private static final String COMMUNITY_HYPERFOIL_OPERATOR_PACKAGE_MANIFEST = "hyperfoil-bundle"; private static final String DEFAULT_HYPERFOIL_OPERATOR_PACKAGE_MANIFEST = COMMUNITY_HYPERFOIL_OPERATOR_PACKAGE_MANIFEST; + private static final String OPEN_DATA_HUB_OPERATOR_CATALOG_SOURCE_NAME = "intersmash.odh.operators.catalog_source"; + private static final String OPEN_DATA_HUB_OPERATOR_INDEX_IMAGE = "intersmash.odh.operators.index_image"; + private static final String OPEN_DATA_HUB_OPERATOR_CHANNEL = "intersmash.odh.operators.channel"; + private static final String OPEN_DATA_HUB_OPERATOR_PACKAGE_MANIFEST = "intersmash.odh.operators.package_manifest"; + private static final String COMMUNITY_OPEN_DATA_HUB_OPERATOR_PACKAGE_MANIFEST = "opendatahub-operator"; + private static final String DEFAULT_OPEN_DATA_HUB_OPERATOR_PACKAGE_MANIFEST = COMMUNITY_OPEN_DATA_HUB_OPERATOR_PACKAGE_MANIFEST; + // Bootable Jar private static final String BOOTABLE_JAR_IMAGE_URL = "intersmash.bootable.jar.image"; @@ -407,4 +414,43 @@ public static String keycloakOperatorChannel() { public static String keycloakOperatorPackageManifest() { return XTFConfig.get(KEYCLOAK_OPERATOR_PACKAGE_MANIFEST, DEFAULT_KEYCLOAK_OPERATOR_PACKAGE_MANIFEST); } + + /** + * Read the configuration property for the Open Data Hub Operator catalog source, i.e. {@code intersmash.odh.operators.catalog_source}. + * + * @return The value for the {@code intersmash.odh.operators.catalog_source} property or the default catalog source, + * i.e. the value of {@code intersmash.olm.operators.catalog_source} property. + */ + public static String openDataHubOperatorCatalogSource() { + return XTFConfig.get(OPEN_DATA_HUB_OPERATOR_CATALOG_SOURCE_NAME, defaultOperatorCatalogSourceName()); + } + + /** + * Read the configuration property for the Open Data Hub Operator index image, i.e. {@code intersmash.odh.operators.index_image}. + * + * @return The value for the {@code intersmash.odh.operators.index_image} property, representing a custom index image. + */ + public static String openDataHubOperatorIndexImage() { + return XTFConfig.get(OPEN_DATA_HUB_OPERATOR_INDEX_IMAGE); + } + + /** + * Read the configuration property for the Open Data Hub Operator channel to be used, i.e. {@code intersmash.odh.operators.channel}. + * + * @return The value for the {@code intersmash.odh.operators.channel} property. If not provided the default operator + * channel is used. + */ + public static String openDataHubOperatorChannel() { + return XTFConfig.get(OPEN_DATA_HUB_OPERATOR_CHANNEL); + } + + /** + * Read the configuration property for the Open Data Hub Operator channel to be used, i.e. {@code intersmash.odh.operators.package_manifest}. + * + * @return The value for the {@code intersmash.odh.operators.package_manifest} property or the default package manifest + * that should be used for this operator, i.e. {@code opendatahub-operator} property. + */ + public static String openDataHubOperatorPackageManifest() { + return XTFConfig.get(OPEN_DATA_HUB_OPERATOR_PACKAGE_MANIFEST, DEFAULT_OPEN_DATA_HUB_OPERATOR_PACKAGE_MANIFEST); + } } diff --git a/provisioners/src/main/java/org/jboss/intersmash/application/operator/OpenDataHubOperatorApplication.java b/provisioners/src/main/java/org/jboss/intersmash/application/operator/OpenDataHubOperatorApplication.java new file mode 100644 index 00000000..b37b9f2d --- /dev/null +++ b/provisioners/src/main/java/org/jboss/intersmash/application/operator/OpenDataHubOperatorApplication.java @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2024 Red Hat, Inc. + * + * 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. + */ +package org.jboss.intersmash.application.operator; + +import io.opendatahub.datasciencecluster.v1.DataScienceCluster; +import io.opendatahub.dscinitialization.v1.DSCInitialization; + +/** + * End user Application interface which presents an Open Data Hub/OpenShift AI operator application. + * Only relevant model APIs are currently exposed, more would be added on demand + * + * The application will be deployed by: + *
    + *
  • {@link org.jboss.intersmash.provision.operator.OpenDataHubOperatorProvisioner}
  • + *
+ */ +public interface OpenDataHubOperatorApplication extends OperatorApplication { + + /** + * Provides read access to the deployed {@link DataScienceCluster} instance. + * @return A {@link DataScienceCluster} instance that is deployed. + */ + DataScienceCluster getDataScienceCluster(); + + /** + * Provides read access to the {@link DSCInitialization} instance that should be used to initialize the + * deployed {@link DataScienceCluster}, represented by {@link #getDataScienceCluster} getter. + * @return A {@link DSCInitialization} instance that configures the deployed {@link DataScienceCluster} + */ + DSCInitialization getDSCInitialization(); +} diff --git a/provisioners/src/main/java/org/jboss/intersmash/provision/openshift/OpenDataHubOpenShiftOperatorProvisioner.java b/provisioners/src/main/java/org/jboss/intersmash/provision/openshift/OpenDataHubOpenShiftOperatorProvisioner.java new file mode 100644 index 00000000..a134d12b --- /dev/null +++ b/provisioners/src/main/java/org/jboss/intersmash/provision/openshift/OpenDataHubOpenShiftOperatorProvisioner.java @@ -0,0 +1,116 @@ +/** + * Copyright (C) 2023 Red Hat, Inc. + * + * 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. + */ +package org.jboss.intersmash.provision.openshift; + +import org.jboss.intersmash.application.operator.OpenDataHubOperatorApplication; +import org.jboss.intersmash.provision.operator.OpenDataHubOperatorProvisioner; +import org.jboss.intersmash.provision.operator.model.odh.DSCInitializationList; +import org.jboss.intersmash.provision.operator.model.odh.DataScienceClusterList; +import org.jboss.intersmash.provision.operator.model.odh.FeatureTrackerList; +import org.jboss.intersmash.provision.operator.model.odh.MonitoringList; + +import cz.xtf.core.openshift.OpenShifts; +import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition; +import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinitionList; +import io.fabric8.kubernetes.client.NamespacedKubernetesClientAdapter; +import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; +import io.fabric8.kubernetes.client.dsl.Resource; +import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext; +import io.fabric8.kubernetes.client.dsl.internal.HasMetadataOperationsImpl; +import io.fabric8.openshift.client.NamespacedOpenShiftClient; +import io.opendatahub.datasciencecluster.v1.DataScienceCluster; +import io.opendatahub.dscinitialization.v1.DSCInitialization; +import io.opendatahub.features.v1.FeatureTracker; +import io.opendatahub.platform.services.v1alpha1.Monitoring; +import lombok.NonNull; + +/** + * A {@link org.jboss.intersmash.provision.Provisioner} that extends {@link OpenDataHubOperatorProvisioner} and + * is able to deploy the Open Data Hub Operator on OpenShift specifically. + */ +public class OpenDataHubOpenShiftOperatorProvisioner + // leverage Open Data Hub common Operator based provisioner behavior + extends OpenDataHubOperatorProvisioner + // ... and common OpenShift provisioning logic, too + implements OpenShiftProvisioner { + + private static final String OPENSHIFT_OPERATORS_NAMESPACE = "openshift-operators"; + + public OpenDataHubOpenShiftOperatorProvisioner( + @NonNull OpenDataHubOperatorApplication application) { + super(application); + } + + @Override + protected String getTargetNamespace() { + return OPENSHIFT_OPERATORS_NAMESPACE; + } + + @Override + public NamespacedKubernetesClientAdapter client() { + return OpenShifts.admin(); + } + + @Override + public String execute(String... args) { + return OpenShiftProvisioner.super.execute(args); + } + + // ================================================================================================================= + // Related to generic provisioning behavior + // ================================================================================================================= + @Override + protected void removeClusterServiceVersion() { + this.execute("delete", "csvs", currentCSV, "-n", this.getTargetNamespace(), "--ignore-not-found"); + } + + @Override + protected void removeSubscription() { + this.execute("delete", "subscription", packageManifestName, "-n", this.getTargetNamespace(), "--ignore-not-found"); + } + + // ================================================================================================================= + // Client related + // ================================================================================================================= + @Override + public NonNamespaceOperation> customResourceDefinitionsClient() { + return OpenShifts.admin().apiextensions().v1().customResourceDefinitions(); + } + + @Override + protected HasMetadataOperationsImpl dataScienceClusterCustomResourcesClient( + CustomResourceDefinitionContext crdc) { + return OpenShifts.admin().newHasMetadataOperation(crdc, DataScienceCluster.class, DataScienceClusterList.class); + } + + @Override + protected HasMetadataOperationsImpl dscInitializationCustomResourcesClient( + CustomResourceDefinitionContext crdc) { + return OpenShifts.admin().newHasMetadataOperation(crdc, DSCInitialization.class, DSCInitializationList.class); + } + + @Override + protected HasMetadataOperationsImpl featureTrackerCustomResourcesClient( + CustomResourceDefinitionContext crdc) { + return OpenShifts.admin().newHasMetadataOperation(crdc, FeatureTracker.class, FeatureTrackerList.class); + } + + @Override + protected HasMetadataOperationsImpl monitoringCustomResourcesClient( + CustomResourceDefinitionContext crdc) { + return OpenShifts.admin().newHasMetadataOperation(crdc, Monitoring.class, MonitoringList.class); + } +} diff --git a/provisioners/src/main/java/org/jboss/intersmash/provision/operator/OpenDataHubOperatorProvisioner.java b/provisioners/src/main/java/org/jboss/intersmash/provision/operator/OpenDataHubOperatorProvisioner.java new file mode 100644 index 00000000..6cbdff48 --- /dev/null +++ b/provisioners/src/main/java/org/jboss/intersmash/provision/operator/OpenDataHubOperatorProvisioner.java @@ -0,0 +1,257 @@ +/** + * Copyright (C) 2024 Red Hat, Inc. + * + * 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. + */ +package org.jboss.intersmash.provision.operator; + +import java.util.List; + +import org.jboss.intersmash.IntersmashConfig; +import org.jboss.intersmash.application.operator.OpenDataHubOperatorApplication; +import org.jboss.intersmash.provision.Provisioner; +import org.jboss.intersmash.provision.operator.model.odh.DSCInitializationList; +import org.jboss.intersmash.provision.operator.model.odh.DataScienceClusterList; +import org.jboss.intersmash.provision.operator.model.odh.FeatureTrackerList; +import org.jboss.intersmash.provision.operator.model.odh.MonitoringList; +import org.slf4j.event.Level; + +import cz.xtf.core.waiting.SimpleWaiter; +import cz.xtf.core.waiting.failfast.FailFastCheck; +import io.fabric8.kubernetes.api.model.StatusDetails; +import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition; +import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinitionList; +import io.fabric8.kubernetes.client.NamespacedKubernetesClient; +import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; +import io.fabric8.kubernetes.client.dsl.Resource; +import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext; +import io.fabric8.kubernetes.client.dsl.internal.HasMetadataOperationsImpl; +import io.opendatahub.datasciencecluster.v1.DataScienceCluster; +import io.opendatahub.dscinitialization.v1.DSCInitialization; +import io.opendatahub.features.v1.FeatureTracker; +import io.opendatahub.platform.services.v1alpha1.Monitoring; +import lombok.extern.slf4j.Slf4j; + +/** + * Defines the contract and default behavior of an Operator based provisioner for the Open Data Hub Operator + */ +@Slf4j +public abstract class OpenDataHubOperatorProvisioner extends + OperatorProvisioner implements Provisioner { + + public OpenDataHubOperatorProvisioner(OpenDataHubOperatorApplication application) { + super(application, OpenDataHubOperatorProvisioner.OPERATOR_ID); + } + + // ================================================================================================================= + // Related to generic provisioning behavior + // ================================================================================================================= + @Override + public String getOperatorCatalogSource() { + return IntersmashConfig.openDataHubOperatorCatalogSource(); + } + + @Override + public String getOperatorIndexImage() { + return IntersmashConfig.openDataHubOperatorIndexImage(); + } + + @Override + public String getOperatorChannel() { + return IntersmashConfig.openDataHubOperatorChannel(); + } + + @Override + public void deploy() { + FailFastCheck ffCheck = getFailFastCheck(); + + subscribe(); + + dataScienceClusterClient().createOrReplace(getApplication().getDataScienceCluster()); + new SimpleWaiter(() -> dataScienceCluster().get().getStatus() != null) + .failFast(ffCheck) + .reason("Wait for status field to be initialized.") + .level(Level.DEBUG) + .waitFor(); + + dscInitializationClient().createOrReplace(getApplication().getDSCInitialization()); + } + + @Override + public void undeploy() { + // remove the CRs + List deletionDetails = dataScienceCluster().delete(); + boolean deleted = deletionDetails.stream().allMatch(d -> d.getCauses().isEmpty()); + if (!deleted) { + log.warn("Wasn't able to remove the 'DataScienceCluster' resource"); + } + new SimpleWaiter(() -> dataScienceCluster().get() == null) + .reason("Waiting for the the 'DataScienceCluster' resource to be removed.") + .level(Level.DEBUG) + .waitFor(); + + if (getApplication().getDSCInitialization() != null) { + final String appName = getApplication().getName(); + deletionDetails = dscInitializationClient().withName(appName).delete(); + deleted = deletionDetails.stream().allMatch(d -> d.getCauses().isEmpty()); + if (!deleted) { + log.warn("Wasn't able to remove the 'DSCInitialization' resources created for '{}' instance!", + appName); + } + new SimpleWaiter(() -> dscInitializationClient().list().getItems().isEmpty()).level(Level.DEBUG).waitFor(); + } + // delete the OLM subscription + unsubscribe(); + new SimpleWaiter(() -> getPods().isEmpty()) + .reason("Waiting for the all the HyperFoil pods to be stopped.") + .level(Level.DEBUG) + .waitFor(); + } + + @Override + public void scale(int replicas, boolean wait) { + throw new UnsupportedOperationException("Scaling is not implemented by Open Data Hub operator based provisioning"); + } + + // ================================================================================================================= + // Client related + // ================================================================================================================= + // this is the packagemanifest for the operator; + // you can get it with command: + // oc get packagemanifest -o template --template='{{ .metadata.name }}' + public static String OPERATOR_ID = IntersmashConfig.openDataHubOperatorPackageManifest(); + + // this is the name of the CustomResourceDefinition(s) + // you can get it with command: + // oc get crd > -o template --template='{{ .metadata.name }}' + protected static String ODH_DATA_SCIENCE_CLUSTER_CRD_NAME = "datascienceclusters.datasciencecluster.opendatahub.io"; + + protected static String ODH_DSC_INITIALIZATION_CRD_NAME = "dscinitializations.dscinitialization.opendatahub.io"; + + protected static String ODH_FEATURE_TRACKER_CRD_NAME = "featuretrackers.features.opendatahub.io"; + + protected static String ODH_MONITORING_CRD_NAME = "monitorings.services.platform.opendatahub.io"; + + /** + * Generic CRD client which is used by client builders default implementation to build the CRDs client + * + * @return A {@link NonNamespaceOperation} instance that represents a + */ + protected abstract NonNamespaceOperation> customResourceDefinitionsClient(); + + // datascienceclusters.datasciencecluster.opendatahub.io + protected abstract HasMetadataOperationsImpl dataScienceClusterCustomResourcesClient( + CustomResourceDefinitionContext crdc); + + // dscinitializations.dscinitialization.opendatahub.io + protected abstract HasMetadataOperationsImpl dscInitializationCustomResourcesClient( + CustomResourceDefinitionContext crdc); + + // featuretrackers.features.opendatahub.io + protected abstract HasMetadataOperationsImpl featureTrackerCustomResourcesClient( + CustomResourceDefinitionContext crdc); + + // monitorings.services.platform.opendatahub.io + protected abstract HasMetadataOperationsImpl monitoringCustomResourcesClient( + CustomResourceDefinitionContext crdc); + + private static NonNamespaceOperation> ODH_DATA_SCIENCE_CLUSTER_CLIENT; + private static NonNamespaceOperation> ODH_DSC_INITIALIZATION_CLIENT; + private static NonNamespaceOperation> ODH_FEATURE_TRACKER_CLIENT; + private static NonNamespaceOperation> ODH_MONITORING_CLIENT; + + /** + * Get a client capable of working with {@link OpenDataHubOperatorProvisioner#ODH_DATA_SCIENCE_CLUSTER_CLIENT} custom resource. + * + * @return client for operations with {@link OpenDataHubOperatorProvisioner#ODH_DATA_SCIENCE_CLUSTER_CLIENT} custom resource + */ + public NonNamespaceOperation> dataScienceClusterClient() { + if (ODH_DATA_SCIENCE_CLUSTER_CLIENT == null) { + CustomResourceDefinition crd = customResourceDefinitionsClient() + .withName(ODH_DATA_SCIENCE_CLUSTER_CRD_NAME).get(); + if (crd == null) { + throw new RuntimeException(String.format("[%s] custom resource is not provided by [%s] operator.", + ODH_DATA_SCIENCE_CLUSTER_CRD_NAME, OPERATOR_ID)); + } + ODH_DATA_SCIENCE_CLUSTER_CLIENT = dataScienceClusterCustomResourcesClient( + CustomResourceDefinitionContext.fromCrd(crd)); + } + return ODH_DATA_SCIENCE_CLUSTER_CLIENT; + } + + /** + * Get a client capable of working with {@link OpenDataHubOperatorProvisioner#ODH_DSC_INITIALIZATION_CLIENT} custom resource. + * + * @return client for operations with {@link OpenDataHubOperatorProvisioner#ODH_DSC_INITIALIZATION_CLIENT} custom resource + */ + public NonNamespaceOperation> dscInitializationClient() { + if (ODH_DSC_INITIALIZATION_CLIENT == null) { + CustomResourceDefinition crd = customResourceDefinitionsClient() + .withName(ODH_DSC_INITIALIZATION_CRD_NAME).get(); + if (crd == null) { + throw new RuntimeException(String.format("[%s] custom resource is not provided by [%s] operator.", + ODH_DSC_INITIALIZATION_CRD_NAME, OPERATOR_ID)); + } + ODH_DSC_INITIALIZATION_CLIENT = dscInitializationCustomResourcesClient( + CustomResourceDefinitionContext.fromCrd(crd)); + } + return ODH_DSC_INITIALIZATION_CLIENT; + } + + /** + * Get a client capable of working with {@link OpenDataHubOperatorProvisioner#ODH_FEATURE_TRACKER_CLIENT} custom resource. + * + * @return client for operations with {@link OpenDataHubOperatorProvisioner#ODH_FEATURE_TRACKER_CLIENT} custom resource + */ + public NonNamespaceOperation> featureTrackerClient() { + if (ODH_FEATURE_TRACKER_CLIENT == null) { + CustomResourceDefinition crd = customResourceDefinitionsClient() + .withName(ODH_FEATURE_TRACKER_CRD_NAME).get(); + if (crd == null) { + throw new RuntimeException(String.format("[%s] custom resource is not provided by [%s] operator.", + ODH_FEATURE_TRACKER_CRD_NAME, OPERATOR_ID)); + } + ODH_FEATURE_TRACKER_CLIENT = featureTrackerCustomResourcesClient(CustomResourceDefinitionContext.fromCrd(crd)); + } + return ODH_FEATURE_TRACKER_CLIENT; + } + + /** + * Get a client capable of working with {@link OpenDataHubOperatorProvisioner#ODH_MONITORING_CLIENT} custom resource. + * + * @return client for operations with {@link OpenDataHubOperatorProvisioner#ODH_MONITORING_CLIENT} custom resource + */ + public NonNamespaceOperation> monitoringClient() { + if (ODH_MONITORING_CLIENT == null) { + CustomResourceDefinition crd = customResourceDefinitionsClient() + .withName(ODH_MONITORING_CRD_NAME).get(); + if (crd == null) { + throw new RuntimeException(String.format("[%s] custom resource is not provided by [%s] operator.", + ODH_MONITORING_CRD_NAME, OPERATOR_ID)); + } + ODH_MONITORING_CLIENT = monitoringCustomResourcesClient(CustomResourceDefinitionContext.fromCrd(crd)); + } + return ODH_MONITORING_CLIENT; + } + + /** + * Get a reference to an {@link DataScienceCluster} instance. + * Use get() to obtain the actual object, or null in case it does not exist on tested cluster. + * + * @return A concrete {@link Resource} instance representing the {@link DataScienceCluster} resource definition + */ + public Resource dataScienceCluster() { + return dataScienceClusterClient().withName(getApplication().getName()); + } + +} diff --git a/provisioners/src/main/java/org/jboss/intersmash/provision/operator/OpenDataHubOperatorProvisionerFactory.java b/provisioners/src/main/java/org/jboss/intersmash/provision/operator/OpenDataHubOperatorProvisionerFactory.java new file mode 100644 index 00000000..d88ed7f5 --- /dev/null +++ b/provisioners/src/main/java/org/jboss/intersmash/provision/operator/OpenDataHubOperatorProvisionerFactory.java @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2023 Red Hat, Inc. + * + * 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. + */ +package org.jboss.intersmash.provision.operator; + +import org.jboss.intersmash.application.Application; +import org.jboss.intersmash.application.openshift.OpenShiftApplication; +import org.jboss.intersmash.application.operator.OpenDataHubOperatorApplication; +import org.jboss.intersmash.provision.ProvisionerFactory; +import org.jboss.intersmash.provision.openshift.OpenDataHubOpenShiftOperatorProvisioner; + +import lombok.extern.slf4j.Slf4j; + +/** + * This factory is loaded by the Service loader via SPI and will release a concrete instance of + * {@link OpenDataHubOperatorProvisioner} that is responsible for deploying an Open Data Hub application service + * represented by {@link OpenDataHubOperatorApplication} instances. + */ +@Slf4j +public class OpenDataHubOperatorProvisionerFactory + implements ProvisionerFactory { + + @Override + public OpenDataHubOperatorProvisioner getProvisioner(Application application) { + if (OpenDataHubOperatorApplication.class.isAssignableFrom(application.getClass())) { + if (OpenShiftApplication.class.isAssignableFrom(application.getClass())) { + return new OpenDataHubOpenShiftOperatorProvisioner((OpenDataHubOperatorApplication) application); + } + throw new UnsupportedOperationException( + "Open Data Hub operator based provisioner requires OpenShift"); + } + return null; + } +} diff --git a/provisioners/src/main/java/org/jboss/intersmash/provision/operator/model/odh/DSCInitializationList.java b/provisioners/src/main/java/org/jboss/intersmash/provision/operator/model/odh/DSCInitializationList.java new file mode 100644 index 00000000..c59117a8 --- /dev/null +++ b/provisioners/src/main/java/org/jboss/intersmash/provision/operator/model/odh/DSCInitializationList.java @@ -0,0 +1,26 @@ +/** + * Copyright (C) 2024 Red Hat, Inc. + * + * 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. + */ +package org.jboss.intersmash.provision.operator.model.odh; + +import io.fabric8.kubernetes.client.CustomResourceList; +import io.opendatahub.dscinitialization.v1.DSCInitialization; + +/** + * Used by {@link org.jboss.intersmash.provision.operator.OpenDataHubOperatorProvisioner} client methods, + * this class represents a concrete {@link java.util.List} of {@link DSCInitialization} instances. + */ +public class DSCInitializationList extends CustomResourceList { +} diff --git a/provisioners/src/main/java/org/jboss/intersmash/provision/operator/model/odh/DataScienceClusterList.java b/provisioners/src/main/java/org/jboss/intersmash/provision/operator/model/odh/DataScienceClusterList.java new file mode 100644 index 00000000..51856bcf --- /dev/null +++ b/provisioners/src/main/java/org/jboss/intersmash/provision/operator/model/odh/DataScienceClusterList.java @@ -0,0 +1,26 @@ +/** + * Copyright (C) 2024 Red Hat, Inc. + * + * 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. + */ +package org.jboss.intersmash.provision.operator.model.odh; + +import io.fabric8.kubernetes.client.CustomResourceList; +import io.opendatahub.datasciencecluster.v1.DataScienceCluster; + +/** + * Used by {@link org.jboss.intersmash.provision.operator.OpenDataHubOperatorProvisioner} client methods, + * this class represents a concrete {@link java.util.List} of {@link DataScienceCluster} instances. + */ +public class DataScienceClusterList extends CustomResourceList { +} diff --git a/provisioners/src/main/java/org/jboss/intersmash/provision/operator/model/odh/FeatureTrackerList.java b/provisioners/src/main/java/org/jboss/intersmash/provision/operator/model/odh/FeatureTrackerList.java new file mode 100644 index 00000000..115d20b4 --- /dev/null +++ b/provisioners/src/main/java/org/jboss/intersmash/provision/operator/model/odh/FeatureTrackerList.java @@ -0,0 +1,26 @@ +/** + * Copyright (C) 2024 Red Hat, Inc. + * + * 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. + */ +package org.jboss.intersmash.provision.operator.model.odh; + +import io.fabric8.kubernetes.client.CustomResourceList; +import io.opendatahub.features.v1.FeatureTracker; + +/** + * Used by {@link org.jboss.intersmash.provision.operator.OpenDataHubOperatorProvisioner} client methods, + * this class represents a concrete {@link java.util.List} of {@link FeatureTracker} instances. + */ +public class FeatureTrackerList extends CustomResourceList { +} diff --git a/provisioners/src/main/java/org/jboss/intersmash/provision/operator/model/odh/MonitoringList.java b/provisioners/src/main/java/org/jboss/intersmash/provision/operator/model/odh/MonitoringList.java new file mode 100644 index 00000000..b366daf3 --- /dev/null +++ b/provisioners/src/main/java/org/jboss/intersmash/provision/operator/model/odh/MonitoringList.java @@ -0,0 +1,26 @@ +/** + * Copyright (C) 2024 Red Hat, Inc. + * + * 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. + */ +package org.jboss.intersmash.provision.operator.model.odh; + +import io.fabric8.kubernetes.client.CustomResourceList; +import io.opendatahub.platform.services.v1alpha1.Monitoring; + +/** + * Used by {@link org.jboss.intersmash.provision.operator.OpenDataHubOperatorProvisioner} client methods, + * this class represents a concrete {@link java.util.List} of {@link Monitoring} instances. + */ +public class MonitoringList extends CustomResourceList { +} diff --git a/provisioners/src/main/resources/META-INF/services/org.jboss.intersmash.provision.ProvisionerFactory b/provisioners/src/main/resources/META-INF/services/org.jboss.intersmash.provision.ProvisionerFactory index fb869619..f5351e30 100644 --- a/provisioners/src/main/resources/META-INF/services/org.jboss.intersmash.provision.ProvisionerFactory +++ b/provisioners/src/main/resources/META-INF/services/org.jboss.intersmash.provision.ProvisionerFactory @@ -16,5 +16,4 @@ org.jboss.intersmash.provision.openshift.Eap7LegacyS2iBuildTemplateProvisionerFa org.jboss.intersmash.provision.openshift.Eap7TemplateOpenShiftProvisionerFactory org.jboss.intersmash.provision.operator.HyperfoilOperatorProvisionerFactory org.jboss.intersmash.provision.openshift.auto.OpenShiftAutoProvisionerFactory - - +org.jboss.intersmash.provision.operator.OpenDataHubOperatorProvisionerFactory diff --git a/testsuite/integration-tests/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/OpenDataHubOpenShiftOperatorProvisionerTest.java b/testsuite/integration-tests/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/OpenDataHubOpenShiftOperatorProvisionerTest.java new file mode 100644 index 00000000..67c876b2 --- /dev/null +++ b/testsuite/integration-tests/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/OpenDataHubOpenShiftOperatorProvisionerTest.java @@ -0,0 +1,172 @@ +package org.jboss.intersmash.testsuite.provision.openshift; + +import java.io.IOException; + +import org.jboss.intersmash.application.operator.OpenDataHubOperatorApplication; +import org.jboss.intersmash.junit5.IntersmashExtension; +import org.jboss.intersmash.provision.olm.OperatorGroup; +import org.jboss.intersmash.provision.openshift.OpenDataHubOpenShiftOperatorProvisioner; +import org.jboss.intersmash.testsuite.junit5.categories.NotForProductizedExecutionProfile; +import org.jboss.intersmash.testsuite.junit5.categories.OpenShiftTest; +import org.jboss.intersmash.testsuite.openshift.ProjectCreationCapable; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.event.Level; + +import cz.xtf.core.config.OpenShiftConfig; +import cz.xtf.core.openshift.OpenShifts; +import cz.xtf.core.waiting.SimpleWaiter; +import cz.xtf.junit5.annotations.CleanBeforeAll; +import io.fabric8.kubernetes.api.model.DeletionPropagation; +import io.opendatahub.datasciencecluster.v1.DataScienceCluster; +import io.opendatahub.datasciencecluster.v1.DataScienceClusterBuilder; +import io.opendatahub.datasciencecluster.v1.datascienceclusterspec.components.kserve.Nim; +import io.opendatahub.datasciencecluster.v1.datascienceclusterspec.components.kserve.Serving; +import io.opendatahub.datasciencecluster.v1.datascienceclusterspec.components.kserve.serving.ingressgateway.Certificate; +import io.opendatahub.dscinitialization.v1.DSCInitialization; +import io.opendatahub.dscinitialization.v1.DSCInitializationBuilder; +import lombok.extern.slf4j.Slf4j; + +/** + * Integration test that validates the functionality of the {@link OpenDataHubOpenShiftOperatorProvisioner} class. + * The test subscribes the Open Data Hub operator, then defines CRs instances and compare them with the ones returned by + * OpenShift upon creation. + */ +@Slf4j +@CleanBeforeAll +@OpenShiftTest +@NotForProductizedExecutionProfile +public class OpenDataHubOpenShiftOperatorProvisionerTest implements ProjectCreationCapable { + private static final OpenDataHubOpenShiftOperatorProvisioner operatorProvisioner = initializeOperatorProvisioner(); + + private static OpenDataHubOpenShiftOperatorProvisioner initializeOperatorProvisioner() { + OpenDataHubOpenShiftOperatorProvisioner operatorProvisioner = new OpenDataHubOpenShiftOperatorProvisioner( + new OpenDataHubOperatorApplication() { + + private static final String APP_NAME = "example-odh"; + + @Override + public DataScienceCluster getDataScienceCluster() { + return new DataScienceClusterBuilder() + .withNewMetadata() + .withName(APP_NAME) + .endMetadata() + .withNewSpec() + .withNewComponents() + .withNewKserve() + .withNewNim() + .withManagementState(Nim.ManagementState.Managed) + .endNim() + .withNewServing() + .withNewIngressGateway() + .withNewCertificate() + .withType(Certificate.Type.OpenshiftDefaultIngress) + .endCertificate() + .endIngressGateway() + .withManagementState(Serving.ManagementState.Managed) + .endServing() + .endKserve() + .withNewModelregistry() + .withRegistriesNamespace("odh-model-registries") + .endModelregistry() + .endComponents() + .endSpec() + .build(); + } + + @Override + public DSCInitialization getDSCInitialization() { + return new DSCInitializationBuilder() + .withNewMetadata() + .withName(APP_NAME) + .endMetadata() + .withNewSpec() + .withApplicationsNamespace(OpenShifts.master().getNamespace()) + .endSpec() + .build(); + } + + @Override + public String getName() { + return APP_NAME; + } + }); + return operatorProvisioner; + } + + @BeforeAll + public static void createOperatorGroup() throws IOException { + operatorProvisioner.configure(); + IntersmashExtension.operatorCleanup(false, true); + // create operator group - this should be done by InteropExtension + OpenShifts.adminBinary().execute("apply", "-f", + new OperatorGroup(OpenShiftConfig.namespace()).save().getAbsolutePath()); + // clean any leftovers + operatorProvisioner.unsubscribe(); + // Let's skip subscribe operation here since we use regular deploy/undeploy where subscribe is called anyway. + } + + @AfterAll + public static void removeOperatorGroup() { + // clean any leftovers + operatorProvisioner.unsubscribe(); + OpenShifts.adminBinary().execute("delete", "operatorgroup", "--all"); + operatorProvisioner.dismiss(); + } + + @AfterEach + public void customResourcesCleanup() { + final String appName = operatorProvisioner.getApplication().getName(); + operatorProvisioner.dataScienceClusterClient().withName(appName).withPropagationPolicy(DeletionPropagation.FOREGROUND) + .delete(); + operatorProvisioner.dscInitializationClient().withName(appName).withPropagationPolicy(DeletionPropagation.FOREGROUND) + .delete(); + } + + /** + * This test case creates and validates minimal {@link DataScienceCluster} and {@link DSCInitialization} CRs + * + * This is not an integration test, the goal here is to assess that the created CRs are configured as per the + * model specification. + */ + @Test + public void testMinimalDataScienceCluster() { + operatorProvisioner.subscribe(); + try { + verifyMinimalDataScienceCluster(operatorProvisioner.getApplication().getDataScienceCluster(), + operatorProvisioner.getApplication().getDSCInitialization()); + } finally { + operatorProvisioner.unsubscribe(); + } + } + + private void verifyMinimalDataScienceCluster(final DataScienceCluster dataScienceCluster, + final DSCInitialization dscInitialization) { + // create and verify that objects exist + operatorProvisioner.dataScienceClusterClient().resource(dataScienceCluster).create(); + new SimpleWaiter(() -> operatorProvisioner.dataScienceClusterClient().list().getItems().size() == 1) + .level(Level.DEBUG) + .waitFor(); + operatorProvisioner.dscInitializationClient().resource(dscInitialization).create(); + new SimpleWaiter(() -> operatorProvisioner.dscInitializationClient().list().getItems().size() == 1) + .level(Level.DEBUG) + .waitFor(); + final DataScienceCluster createdDataScienceCluster = operatorProvisioner.dataScienceCluster().get(); + Assertions.assertNotNull(createdDataScienceCluster); + // the DataScienceCluster spec gets populated on creation, so we just check that it is not null + Assertions.assertNotNull(createdDataScienceCluster.getSpec()); + Assertions.assertNotNull(operatorProvisioner.dataScienceCluster().get().getSpec()); + // the Monitoring spec gets populated on creation, so we verify 1st level resources individually + final DSCInitialization createdDscInitialization = operatorProvisioner.dscInitializationClient() + .withName(dscInitialization.getMetadata().getName()).get(); + Assertions.assertEquals(dscInitialization.getSpec().getDevFlags(), createdDscInitialization.getSpec().getDevFlags()); + Assertions.assertNotNull(createdDscInitialization.getSpec().getMonitoring()); + Assertions.assertEquals(dscInitialization.getSpec().getServiceMesh(), + createdDscInitialization.getSpec().getServiceMesh()); + Assertions.assertEquals(dscInitialization.getSpec().getTrustedCABundle(), + createdDscInitialization.getSpec().getTrustedCABundle()); + } +} diff --git a/testsuite/integration-tests/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/OpenShiftProvisionerTestBase.java b/testsuite/integration-tests/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/OpenShiftProvisionerTestBase.java index f473f142..4bb4b262 100644 --- a/testsuite/integration-tests/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/OpenShiftProvisionerTestBase.java +++ b/testsuite/integration-tests/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/OpenShiftProvisionerTestBase.java @@ -49,11 +49,11 @@ import org.jboss.intersmash.application.operator.InfinispanOperatorApplication; import org.jboss.intersmash.application.operator.KafkaOperatorApplication; import org.jboss.intersmash.application.operator.KeycloakOperatorApplication; +import org.jboss.intersmash.application.operator.OpenDataHubOperatorApplication; import org.jboss.intersmash.provision.operator.model.infinispan.infinispan.InfinispanBuilder; import org.jboss.intersmash.test.deployments.DeploymentsProvider; import org.jboss.intersmash.test.deployments.TestDeploymentProperties; import org.jboss.intersmash.test.deployments.WildflyDeploymentApplicationConfiguration; -import org.jboss.intersmash.testsuite.IntersmashTestsuiteProperties; import org.jboss.intersmash.testsuite.junit5.categories.OpenShiftTest; import org.jboss.intersmash.util.CommandLineBasedKeystoreGenerator; import org.jboss.intersmash.util.openshift.WildflyOpenShiftUtils; @@ -70,6 +70,10 @@ import io.fabric8.kubernetes.api.model.EnvVar; import io.fabric8.kubernetes.api.model.EnvVarBuilder; import io.fabric8.kubernetes.api.model.Secret; +import io.opendatahub.datasciencecluster.v1.DataScienceCluster; +import io.opendatahub.datasciencecluster.v1.DataScienceClusterBuilder; +import io.opendatahub.dscinitialization.v1.DSCInitialization; +import io.opendatahub.dscinitialization.v1.DSCInitializationBuilder; import io.strimzi.api.kafka.model.AclOperation; import io.strimzi.api.kafka.model.AclResourcePatternType; import io.strimzi.api.kafka.model.AclRule; @@ -780,4 +784,44 @@ public String getName() { } }; } + + /** + * Provide an instance of {@link OpenDataHubOperatorApplication}, that represents a minimal Open Data Hub + * application service that is used for instance by {@link ProvisionerCleanupTestCase} + * + * @return A concreate instance of {@link OpenDataHubOperatorApplication}, that represents a minimal Open Data Hub + * application service. + */ + static OpenDataHubOperatorApplication getOpenDataHubOperatorApplication() { + return new OpenDataHubOperatorApplication() { + + private static final String APP_NAME = "example-odh"; + + @Override + public DataScienceCluster getDataScienceCluster() { + return new DataScienceClusterBuilder() + .withNewMetadata() + .withName(APP_NAME) + .endMetadata() + .build(); + } + + @Override + public DSCInitialization getDSCInitialization() { + return new DSCInitializationBuilder() + .withNewMetadata() + .withName(APP_NAME) + .endMetadata() + .withNewSpec() + .withApplicationsNamespace(OpenShifts.master().getNamespace()) + .endSpec() + .build(); + } + + @Override + public String getName() { + return APP_NAME; + } + }; + } } diff --git a/testsuite/integration-tests/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/ProvisionerCleanupTestCase.java b/testsuite/integration-tests/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/ProvisionerCleanupTestCase.java index 3ca1f853..50f994c8 100644 --- a/testsuite/integration-tests/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/ProvisionerCleanupTestCase.java +++ b/testsuite/integration-tests/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/ProvisionerCleanupTestCase.java @@ -62,7 +62,10 @@ private static Stream provisionerProvider() { , new PostgreSQLImageOpenShiftProvisioner( OpenShiftProvisionerTestBase.getPostgreSQLImageOpenShiftApplication()), new PostgreSQLTemplateOpenShiftProvisioner( - OpenShiftProvisionerTestBase.getPostgreSQLTemplateOpenShiftApplication())); + OpenShiftProvisionerTestBase.getPostgreSQLTemplateOpenShiftApplication()) + // ODH + , new OpenDataHubOpenShiftOperatorProvisioner( + OpenShiftProvisionerTestBase.getOpenDataHubOperatorApplication())); } else if (IntersmashTestsuiteProperties.isProductizedTestExecutionProfileEnabled()) { return Stream.of( // RHDG From 33c9f0ad4674bfc35e948f1a4517a760d306c20f Mon Sep 17 00:00:00 2001 From: Fabio Burzigotti Date: Mon, 6 Jan 2025 13:08:01 +0100 Subject: [PATCH 4/4] [issue-212] - Add documentation for Open Data Hub operator based provisioning supported version --- README.md | 4 +++- docs/Operator-Based-Provisioning.md | 3 ++- docs/Provisioner-by-Product.md | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3f2842a6..76710f00 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,8 @@ Feel free to submit an issue in such a case, Intersmash welcomes community contr | Red Hat JBoss EAP 7 | JBoss EAP 7.4.z (and XP 4.z) | | | | | | | Hyperfoil | 0.24.2 | Supports provisioning via the Operator, both on **Kubernetes** and **OpenShift** | +| | | | +| Open Data Hub | 2.22.0 | Supports provisioning on OpenShift via the Operator | Since multiple deliverables can be bound to a given service version, e.g.: container images, operator CRs, or Helm Charts, more information can be found in [the provisioners' documentation](./provisioners/README.md), or in the resources there linked. @@ -175,7 +177,7 @@ We welcome community contributions to other Kubernetes implementations. ## Future goals -* OpenShift AI provisioning +* Full support for OpenShift AI provisioning * Documentation and examples enhancements see the [current milestone](https://github.com/Intersmash/intersmash/milestone/2) for a complete list of issues. diff --git a/docs/Operator-Based-Provisioning.md b/docs/Operator-Based-Provisioning.md index c6c859dc..7bd56cae 100644 --- a/docs/Operator-Based-Provisioning.md +++ b/docs/Operator-Based-Provisioning.md @@ -19,8 +19,9 @@ extended easily, since Intersmash _provisioners_ are pluggable components. | Red Hat Build of keycloak (RHBK) | 24.0.3-opr.1 | stable-v24 | https://github.com/keycloak/keycloak/tree/main/operator | Latest Keycloak, based on Quarkus. | | Red Hat SSO - **DEPRECATED** | 7.6.8-opr-001 | stable | https://github.com/keycloak/keycloak-operator | Latest Red Hat SSO Operator, based on legacy Keycloak | | WildFly | 1.0.0 | alpha | https://github.com/wildfly/wildfly-operator | As available on https://operatorhub.io/operator/wildfly | -| Red Hat JBoss EAP | 3.0.0 | stable | https://github.com/wildfly/wildfly-operator | As available on the OpenShift OperatorHub | +| Red Hat JBoss EAP | 3.0.0 | stable | https://github.com/wildfly/wildfly-operator | As available from the OpenShift OperatorHub | | Hyperfoil | 0.24.2 | alpha | https://github.com/Hyperfoil/hyperfoil-operator | Available for both **Kubernetes** and **OpenShift**. We force the CRs version for the used Hyperfoil runtime to be 0.24.2, see https://github.com/Hyperfoil/hyperfoil-operator/issues/18 | +| Open Data Hub | 2.22.0 | fast | https://github.com/opendatahub-io/opendatahub-operator | As available from the OpenShift OperatorHub | Intersmash operator-based provisioners implement a common contract and high level behavior which is defined by the [OperatorProvisioner](../core/src/main/java/org/jboss/intersmash/provision/openshift/operator/OperatorProvisioner.java) diff --git a/docs/Provisioner-by-Product.md b/docs/Provisioner-by-Product.md index 9b02bc50..bdc90804 100644 --- a/docs/Provisioner-by-Product.md +++ b/docs/Provisioner-by-Product.md @@ -20,6 +20,7 @@ The provisioner is selected by a factory, based on the _Application class_ type | **Keycloak & Red Hat Build of Keycloak Operator** (see KeycloakOperatorApplication) | KeycloakOperatorProvisioner | :heavy_check_mark: | :heavy_check_mark: | | **Red Hat SSO Operator** (see RhSsoOperatorApplication) - **DEPRECATED** | RhSsoOperatorProvisioner | :x: | :heavy_check_mark: | | **WildFly & JBoss EAP 8 Operator** (see WildflyOperatorApplication) | WildflyOperatorProvisioner | :heavy_check_mark: | :heavy_check_mark: | +| **Open Data Hub operator** (see OpenDataHubOperatorApplication) | OpenDataHubOpenShiftOperatorProvisioner | :heavy_check_mark: | :x: | | **Template based services** | | | | | **JBoss EAP 7 Legacy s2i Build Template** (see Eap7LegacyS2iBuildTemplateApplication) | Eap7LegacyS2iBuildTemplateProvisioner | :x: | :heavy_check_mark: | | **JBoss EAP 7 Legacy s2i Deployment Template** (see Eap7TemplateOpenShiftApplication) | Eap7TemplateOpenShiftProvisioner | :x: | :heavy_check_mark: |