diff --git a/.ci/cico_updates_openshift.sh b/.ci/cico_updates_openshift.sh index 5568538f7..c51a20832 100755 --- a/.ci/cico_updates_openshift.sh +++ b/.ci/cico_updates_openshift.sh @@ -15,7 +15,7 @@ set -e set -x export OPERATOR_REPO=$(dirname $(dirname $(readlink -f "$0"))); -source "${OPERATOR_REPO}"/.github/bin/common.sh +source "${OPERATOR_REPO}/.github/bin/common.sh" #Stop execution on any error trap "catchFinish" EXIT SIGINT diff --git a/.ci/oci-devworkspace-happy-path.sh b/.ci/oci-devworkspace-happy-path.sh index febdd7427..1089d84fa 100755 --- a/.ci/oci-devworkspace-happy-path.sh +++ b/.ci/oci-devworkspace-happy-path.sh @@ -17,7 +17,7 @@ set -x export CHE_REPO_BRANCH="main" export OPERATOR_REPO=$(dirname $(dirname $(readlink -f "$0"))); -source "${OPERATOR_REPO}"/.github/bin/common.sh +source "${OPERATOR_REPO}/.github/bin/common.sh" source <(curl -s https://raw.githubusercontent.com/eclipse/che/${CHE_REPO_BRANCH}/tests/devworkspace-happy-path/common.sh) #Stop execution on any error @@ -29,7 +29,7 @@ overrideDefaults() { } deployChe() { - deployEclipseCheOnWithOperator "chectl" "openshift" ${CURRENT_OPERATOR_VERSION_TEMPLATE_PATH} "true" + deployEclipseCheWithOperator "chectl" "openshift" ${CURRENT_OPERATOR_VERSION_TEMPLATE_PATH} "true" } initDefaults diff --git a/.ci/oci-disconnected.sh b/.ci/oci-disconnected.sh index 99e6bd26d..54e285c44 100755 --- a/.ci/oci-disconnected.sh +++ b/.ci/oci-disconnected.sh @@ -19,7 +19,7 @@ set -o pipefail set -u export OPERATOR_REPO=$(dirname $(dirname $(readlink -f "$0"))); -source "${OPERATOR_REPO}"/.github/bin/common.sh +source "${OPERATOR_REPO}/.github/bin/common.sh" # Define Disconnected tests environment export INTERNAL_REGISTRY_URL=${INTERNAL_REGISTRY_URL-"UNDEFINED"} diff --git a/.ci/oci-nightly-update.sh b/.ci/oci-nightly-update.sh index b41c22e4c..c54902e6c 100755 --- a/.ci/oci-nightly-update.sh +++ b/.ci/oci-nightly-update.sh @@ -20,8 +20,7 @@ set -e set -x export OPERATOR_REPO=$(dirname $(dirname $(readlink -f "$0"))); -source "${OPERATOR_REPO}"/olm/olm.sh -source "${OPERATOR_REPO}"/.github/bin/common.sh +source "${OPERATOR_REPO}/.github/bin/common.sh" #Stop execution on any error trap "catchFinish" EXIT SIGINT @@ -33,7 +32,7 @@ overrideDefaults() { runTests() { deployDevWorkspaceOperator "stable" - deployEclipseCheOnWithOperator "chectl" "openshift" ${LAST_OPERATOR_VERSION_TEMPLATE_PATH} "false" + deployEclipseCheWithOperator "chectl" "openshift" ${LAST_OPERATOR_VERSION_TEMPLATE_PATH} "false" updateEclipseChe "chectl" "openshift" ${CURRENT_OPERATOR_VERSION_TEMPLATE_PATH} "true" } diff --git a/.ci/oci-proxy.sh b/.ci/oci-proxy.sh index 1cf685d4d..b8be436d6 100755 --- a/.ci/oci-proxy.sh +++ b/.ci/oci-proxy.sh @@ -19,7 +19,7 @@ set -o pipefail set -u export OPERATOR_REPO=$(dirname $(dirname $(readlink -f "$0"))); -source "${OPERATOR_REPO}"/.github/bin/common.sh +source "${OPERATOR_REPO}/.github/bin/common.sh" #Stop execution on any error trap "catchFinish" EXIT SIGINT @@ -29,7 +29,7 @@ overrideDefaults() { } runTests() { - deployEclipseCheOnWithOperator "chectl" "openshift" ${CURRENT_OPERATOR_VERSION_TEMPLATE_PATH} "true" + deployEclipseCheWithOperator "chectl" "openshift" ${CURRENT_OPERATOR_VERSION_TEMPLATE_PATH} "true" } initDefaults diff --git a/.ci/oci-single-host.sh b/.ci/oci-single-host.sh index 520d4330c..0b10d70ef 100755 --- a/.ci/oci-single-host.sh +++ b/.ci/oci-single-host.sh @@ -20,7 +20,7 @@ set -e set -x export OPERATOR_REPO=$(dirname $(dirname $(readlink -f "$0"))); -source "${OPERATOR_REPO}"/.github/bin/common.sh +source "${OPERATOR_REPO}/.github/bin/common.sh" #Stop execution on any error trap "catchFinish" EXIT SIGINT @@ -31,12 +31,12 @@ overrideDefaults() { } runTests() { - createNamespace ${NAMESPACE} + createNamespace "${NAMESPACE}" - useCustomOperatorImageInCSV ${OPERATOR_IMAGE} + useCustomOperatorImageInCSV "${OPERATOR_IMAGE}" - echo "$(getCheClusterCRFromExistedCSV)" | oc apply -n "${NAMESPACE}" -f - - waitEclipseCheDeployed $(getCheVersionFromExistedCSV) + getCheClusterCRFromExistedCSV | oc apply -n "${NAMESPACE}" -f - + waitEclipseCheDeployed "$(getCheVersionFromExistedCSV)" waitDevWorkspaceControllerStarted } diff --git a/.github/bin/check-bundle-version.sh b/.github/bin/check-bundle-version.sh index 522f89ab8..3221caead 100755 --- a/.github/bin/check-bundle-version.sh +++ b/.github/bin/check-bundle-version.sh @@ -55,7 +55,7 @@ compareVersions() { if (( $VERSION_NEW_NUMBER <= $VERSION_CURRENT_NUMBER )); then echo "[ERROR] New next bundle version is less than the current one." - echo "[ERROR] Please update next bundle with script 'make update-resources -s'" + echo "[ERROR] Please update next bundle with script 'make update-dev-resources'" exit 1 fi } diff --git a/.github/bin/check-resources.sh b/.github/bin/check-resources.sh index 1a272e5bd..bb9fc5f75 100755 --- a/.github/bin/check-resources.sh +++ b/.github/bin/check-resources.sh @@ -11,151 +11,29 @@ # Red Hat, Inc. - initial API and implementation # -# Checks if repository resources are up to date: -# - CRDs -# - next olm bundle -# - Dockerfile & operator.yaml -# - DW resources -# - Helm charts - set -e ROOT_PROJECT_DIR="${GITHUB_WORKSPACE}" if [ -z "${ROOT_PROJECT_DIR}" ]; then SCRIPT=$(readlink -f "${BASH_SOURCE[0]}") - ROOT_PROJECT_DIR=$(dirname $(dirname $(dirname ${SCRIPT}))) + ROOT_PROJECT_DIR=$(dirname $(dirname $(dirname "${SCRIPT}"))) fi -installOperatorSDK() { - OPERATOR_SDK_BINARY=$(command -v operator-sdk) || true - if [[ ! -x "${OPERATOR_SDK_BINARY}" ]]; then - OPERATOR_SDK_TEMP_DIR="$(mktemp -q -d -t "OPERATOR_SDK_XXXXXX" 2>/dev/null || mktemp -q -d)" - - pushd "${ROOT_PROJECT_DIR}" || exit - make download-operator-sdk OP_SDK_DIR="${OPERATOR_SDK_TEMP_DIR}" - export OPERATOR_SDK_BINARY="${OPERATOR_SDK_TEMP_DIR}/operator-sdk" - popd || exit - fi -} - -updateResources() { - echo "[INFO] Update resources with skipping version incrementation and timestamp..." - - pushd "${ROOT_PROJECT_DIR}" || exit - make update-resources NO_DATE_UPDATE="true" NO_INCREMENT="true" -s - popd || exit -} - -# check_che_types function check first if api/v1/checluster_types.go file suffer modifications and -# in case of modification should exist also modifications in config/crd/bases/* folder. -checkCRDs() { - echo "[INFO] Checking CRDs" - - # files to check - local checluster_CRD_V1="config/crd/bases/org_v1_che_crd.yaml" - local chebackupserverconfiguration_CRD_V1="config/crd/bases/org.eclipse.che_chebackupserverconfigurations_crd.yaml" - local checlusterbackup_CRD_V1="config/crd/bases/org.eclipse.che_checlusterbackups_crd.yaml" - local checlusterrestore_CRD_V1="config/crd/bases/org.eclipse.che_checlusterrestores_crd.yaml" - - changedFiles=($(cd ${ROOT_PROJECT_DIR}; git diff --name-only)) - # Check if there are any difference in the crds. If yes, then fail check. - if [[ " ${changedFiles[*]} " =~ $checluster_CRD_V1 ]] || \ - [[ " ${changedFiles[*]} " =~ $chebackupserverconfiguration_CRD_V1 ]] || \ - [[ " ${changedFiles[*]} " =~ $checlusterbackup_CRD_V1 ]] || \ - [[ " ${changedFiles[*]} " =~ $checlusterrestore_CRD_V1 ]] - then - echo "[ERROR] CRD file is not up to date: ${BASH_REMATCH}" - echo "[ERROR] Run 'make update-resources -s' to regenerate CRD files." - exit 1 - else - echo "[INFO] CRDs files are up to date." - fi -} - -checkNextOlmBundle() { - # files to check - local CSV_OPENSHIFT="bundle/next/eclipse-che-preview-openshift/manifests" - - changedFiles=($(cd ${ROOT_PROJECT_DIR}; git diff --name-only)) - if [[ " ${changedFiles[*]} " =~ $CSV_OPENSHIFT ]]; then - echo "[ERROR] Nighlty bundle is not up to date: ${BASH_REMATCH}" - echo "[ERROR] Run 'make update-resources -s' to regenerate next bundle files." - exit 1 - else - echo "[INFO] Next bundles are up to date." - fi -} - -checkDockerfile() { - # files to check - local Dockerfile="Dockerfile" - - changedFiles=($(cd ${ROOT_PROJECT_DIR}; git diff --name-only)) - if [[ " ${changedFiles[*]} " =~ $Dockerfile ]]; then - echo "[ERROR] Dockerfile is not up to date" - echo "[ERROR] Run 'make update-resources -s' to update Dockerfile" - exit 1 - else - echo "[INFO] Dockerfile is up to date." - fi -} - -checkOperatorYaml() { - # files to check - local managerYaml="config/manager/manager.yaml" - - changedFiles=($(cd ${ROOT_PROJECT_DIR}; git diff --name-only)) - if [[ " ${changedFiles[*]} " =~ $managerYaml ]]; then - echo "[ERROR] $managerYaml is not up to date" - echo "[ERROR] Run 'make update-resources -s' to update $managerYaml" - exit 1 - else - echo "[INFO] $managerYaml is up to date." - fi -} - -checkRoles() { - # files to check - local RoleYaml="config/rbac/role.yaml" - local ClusterRoleYaml="config/rbac/cluster_role.yaml" - - changedFiles=( - $(git diff --name-only) - ) - if [[ " ${changedFiles[*]} " =~ $RoleYaml ]] || [[ " ${changedFiles[*]} " =~ $ClusterRoleYaml ]]; then - echo "[ERROR] Roles are not up to date: ${BASH_REMATCH}" - echo "[ERROR] Run 'make update-resources -s' to update them." - exit 1 - else - echo "[INFO] Roles are up to date." - fi -} - -checkHelmCharts() { - changedFiles=( - $(git diff --name-only) - ) - if [[ " ${changedFiles[*]} " =~ helmcharts ]]; then - echo "[ERROR] Helm Charts are not up to date" - echo "[ERROR] Run 'make update-resources -s' to update them." - exit 1 - else - echo "[INFO] Helm Charts are up to date." - fi -} - -installOperatorSDK - pushd "${ROOT_PROJECT_DIR}" || true -updateResources -checkCRDs -checkRoles -checkNextOlmBundle -checkDockerfile -checkOperatorYaml -checkHelmCharts +# Update resources +make update-dev-resources INCREMENT_BUNDLE_VERSION=false + +if [[ $(git diff --name-only | wc -l) != 0 ]]; then + # Print difference + git --no-pager diff + + echo "[ERROR] Resources are not up to date." + echo "[ERROR] Run 'make update-dev-resources' to update them." + exit 1 +else + echo "[INFO] Done." +fi popd || true -echo "[INFO] Done." diff --git a/.github/bin/common.sh b/.github/bin/common.sh index cc42b3b23..11754955c 100755 --- a/.github/bin/common.sh +++ b/.github/bin/common.sh @@ -11,6 +11,11 @@ # Red Hat, Inc. - initial API and implementation # +export ECLIPSE_CHE_PACKAGE_NAME="eclipse-che-preview-openshift" +export ECLIPSE_CHE_CATALOG_SOURCE_NAME="eclipse-che-custom-catalog-source" +export ECLIPSE_CHE_SUBSCRIPTION_NAME="eclipse-che-subscription" +export DEV_WORKSPACE_CATALOG_SOURCE_NAME="custom-devworkspace-operator-catalog" + catchFinish() { result=$? @@ -21,6 +26,8 @@ catchFinish() { echo "[INFO] Job completed successfully." fi + rm -rf ${OPERATOR_REPO}/tmp + echo "[INFO] Please check github actions artifacts." exit $result } @@ -54,9 +61,11 @@ initTemplates() { export PREVIOUS_OPERATOR_VERSION_TEMPLATE_PATH=${CHECTL_TEMPLATES_BASE_DIR}/${PREVIOUS_PACKAGE_VERSION} export LAST_OPERATOR_VERSION_TEMPLATE_PATH=${CHECTL_TEMPLATES_BASE_DIR}/${LAST_PACKAGE_VERSION} - copyChectlTemplates "${PREVIOUS_OPERATOR_VERSION_CLONE_PATH}" "${PREVIOUS_OPERATOR_VERSION_TEMPLATE_PATH}/che-operator" - copyChectlTemplates "${LAST_OPERATOR_VERSION_CLONE_PATH}" "${LAST_OPERATOR_VERSION_TEMPLATE_PATH}/che-operator" - copyChectlTemplates "${OPERATOR_REPO}" "${CURRENT_OPERATOR_VERSION_TEMPLATE_PATH}/che-operator" + pushd "${OPERATOR_REPO}" || exit 1 + make gen-chectl-tmpl SOURCE="${PREVIOUS_OPERATOR_VERSION_CLONE_PATH}" TARGET="${PREVIOUS_OPERATOR_VERSION_TEMPLATE_PATH}" + make gen-chectl-tmpl SOURCE="${LAST_OPERATOR_VERSION_CLONE_PATH}" TARGET="${LAST_OPERATOR_VERSION_TEMPLATE_PATH}" + make gen-chectl-tmpl SOURCE="${OPERATOR_REPO}" TARGET="${CURRENT_OPERATOR_VERSION_TEMPLATE_PATH}" + popd || exit 1 } getLatestStableVersions() { @@ -68,12 +77,6 @@ getLatestStableVersions() { git remote remove operator } -copyChectlTemplates() { - pushd "${OPERATOR_REPO}" || exit - make chectl-templ "SRC=${1}" "TARGET=${2}" - popd || exit -} - collectLogs() { mkdir -p ${ARTIFACTS_DIR} @@ -85,7 +88,7 @@ collectLogs() { # additionally grab server logs for fast access chectl server:logs -n $NAMESPACE -d $ARTIFACTS_DIR - set -ex + set -e } RESOURCES_DIR_NAME='resources' @@ -181,39 +184,94 @@ collectClusterScopeResources() { } buildCheOperatorImage() { - docker build -t "${OPERATOR_IMAGE}" -f Dockerfile --build-arg TESTS=false . && docker save "${OPERATOR_IMAGE}" > /tmp/operator.tar + docker build -t "${OPERATOR_IMAGE}" -f Dockerfile --build-arg SKIP_TESTS=true . && docker save "${OPERATOR_IMAGE}" > /tmp/operator.tar } copyCheOperatorImageToMinikube() { eval $(minikube docker-env) && docker load -i /tmp/operator.tar && rm /tmp/operator.tar } -deployEclipseCheOnWithOperator() { +deployEclipseCheWithHelm() { + local chectlbin=$1 + local platform=$2 + local templates=$3 + local customimage=$4 + local channel="next" + + # Deploy Eclipse Che to have Cert Manager and Dex installed + deployEclipseCheWithOperator "${chectlbin}" "${platform}" "${templates}" "${customimage}" + + # Get configuration + local identityProvider=$(kubectl get checluster/eclipse-che -n ${NAMESPACE} -o jsonpath='{.spec.networking.auth.identityProviderURL}') + local oAuthSecret=$(kubectl get checluster/eclipse-che -n ${NAMESPACE} -o jsonpath='{.spec.networking.auth.oAuthSecret}') + local oAuthClientName=$(kubectl get checluster/eclipse-che -n ${NAMESPACE} -o jsonpath='{.spec.networking.auth.oAuthClientName}') + local domain=$(kubectl get checluster/eclipse-che -n ${NAMESPACE} -o jsonpath='{.spec.networking.domain}') + + # Delete Eclipse Che (Cert Manager and Dex are still there) + ${chectlbin} server:delete -y -n ${NAMESPACE} + + # Prepare HelmCharts + HELMCHART_DIR=/tmp/chectl-helmcharts + OPERATOR_DEPLOYMENT="${HELMCHART_DIR}"/templates/che-operator.Deployment.yaml + + rm -rf "${HELMCHART_DIR}" && cp -r "${OPERATOR_REPO}/helmcharts/${channel}" "${HELMCHART_DIR}" + + if [[ ${customimage} == "true" ]]; then + yq -riSY '.spec.template.spec.containers[0].image = "'${OPERATOR_IMAGE}'"' "${OPERATOR_DEPLOYMENT}" + yq -riSY '.spec.template.spec.containers[0].imagePullPolicy = "IfNotPresent"' "${OPERATOR_DEPLOYMENT}" + fi + + # Deploy Eclipse Che with Helm + pushd "${HELMCHART_DIR}" || exit 1 + helm install che \ + --create-namespace \ + --namespace eclipse-che \ + --set networking.domain="${domain}" \ + --set networking.auth.oAuthSecret="${oAuthSecret}" \ + --set networking.auth.oAuthClientName="${oAuthClientName}" \ + --set networking.auth.identityProviderURL="${identityProvider}" . + popd + + local cheVersion=$(yq -r '.spec.template.spec.containers[0].env[] | select(.name == "CHE_VERSION") | .value' < "${OPERATOR_DEPLOYMENT}") + waitEclipseCheDeployed "${cheVersion}" + + waitDevWorkspaceControllerStarted +} + +deployEclipseCheWithOperator() { local chectlbin=$1 local platform=$2 local templates=$3 local customimage=$4 - local domainFlag="" - if [[ ${platform} == "minikube" ]]; then - domainFlag="--domain $(minikube ip).nip.io" - fi - if [[ ${customimage} == "true" ]]; then if [[ ${platform} == "minikube" ]]; then buildCheOperatorImage copyCheOperatorImageToMinikube + yq -riSY '.spec.template.spec.containers[0].image = "'${OPERATOR_IMAGE}'"' "${templates}"/che-operator/kubernetes/operator.yaml + yq -riSY '.spec.template.spec.containers[0].imagePullPolicy = "IfNotPresent"' "${templates}"/che-operator/kubernetes/operator.yaml + else + yq -riSY '.spec.template.spec.containers[0].image = "'${OPERATOR_IMAGE}'"' "${templates}"/che-operator/openshift/operator.yaml + yq -riSY '.spec.template.spec.containers[0].imagePullPolicy = "IfNotPresent"' "${templates}"/che-operator/openshift/operator.yaml fi + fi - yq -riSY '.spec.template.spec.containers[0].image = "'${OPERATOR_IMAGE}'"' ${templates}/che-operator/operator.yaml - yq -riSY '.spec.template.spec.containers[0].imagePullPolicy = "IfNotPresent"' ${templates}/che-operator/operator.yaml + if [[ ${platform} == "minikube" ]]; then + checluster=$(grep -rlx "kind: CheCluster" /tmp/chectl-templates/che-operator/) + apiVersion=$(yq -r '.apiVersion' ${checluster}) + if [[ ${apiVersion} == "org.eclipse.che/v2" ]]; then + yq -riY '.spec.networking.domain = "'$(minikube ip).nip.io'"' ${checluster} + yq -riY '.spec.networking.tlsSecretName = "che-tls"' ${checluster} + else + yq -riY '.spec.k8s.ingressDomain = "'$(minikube ip).nip.io'"' ${checluster} + fi fi ${chectlbin} server:deploy \ --batch \ --platform ${platform} \ --installer operator \ - --templates ${templates} ${domainFlag} + --templates ${templates} waitDevWorkspaceControllerStarted } @@ -228,24 +286,34 @@ updateEclipseChe() { if [[ ${platform} == "minikube" ]]; then buildCheOperatorImage copyCheOperatorImageToMinikube + yq -riSY '.spec.template.spec.containers[0].image = "'${OPERATOR_IMAGE}'"' ${templates}/che-operator/kubernetes/operator.yaml + yq -riSY '.spec.template.spec.containers[0].imagePullPolicy = "IfNotPresent"' ${templates}/che-operator/kubernetes/operator.yaml + else + yq -riSY '.spec.template.spec.containers[0].image = "'${OPERATOR_IMAGE}'"' ${templates}/che-operator/openshift/operator.yaml + yq -riSY '.spec.template.spec.containers[0].imagePullPolicy = "IfNotPresent"' ${templates}/che-operator/openshift/operator.yaml fi - - yq -riSY '.spec.template.spec.containers[0].image = "'${OPERATOR_IMAGE}'"' ${templates}/che-operator/operator.yaml - yq -riSY '.spec.template.spec.containers[0].imagePullPolicy = "IfNotPresent"' ${templates}/che-operator/operator.yaml fi ${chectlbin} server:update \ --batch \ --templates ${templates} - local cheVersion=$(cat ${templates}/che-operator/operator.yaml | yq -r '.spec.template.spec.containers[0].env[] | select(.name == "CHE_VERSION") | .value') + local configManagerPath="" + if [[ -f ${templates}/che-operator/operator.yaml ]]; then + configManagerPath="${templates}/che-operator/operator.yaml" + elif [[ ${platform} == "minikube" ]]; then + configManagerPath="${templates}/che-operator/kubernetes/operator.yaml" + else + configManagerPath="${templates}/che-operator/openshift/operator.yaml" + fi + + local cheVersion=$(cat "${configManagerPath}" | yq -r '.spec.template.spec.containers[0].env[] | select(.name == "CHE_VERSION") | .value') waitEclipseCheDeployed ${cheVersion} waitDevWorkspaceControllerStarted } waitEclipseCheDeployed() { - set -x local version=$1 echo "[INFO] Wait for Eclipse Che '${version}' version" @@ -253,11 +321,11 @@ waitEclipseCheDeployed() { while [ $n -le 500 ] do cheVersion=$(oc get checluster/eclipse-che -n "${NAMESPACE}" -o "jsonpath={.status.cheVersion}") - cheIsRunning=$(oc get checluster/eclipse-che -n "${NAMESPACE}" -o "jsonpath={.status.cheClusterRunning}" ) + chePhase=$(oc get checluster/eclipse-che -n "${NAMESPACE}" -o "jsonpath={.status.chePhase}" ) oc get pods -n ${NAMESPACE} - if [ "${cheVersion}" == "${version}" ] && [ "${cheIsRunning}" == "Available" ] + if [[ "${cheVersion}" == "${version}" ]] then - echo "[INFO] Eclipse Che '${version}' version has been succesfully deployed" + echo "[INFO] Eclipse Che '${version}' version has been successfully deployed" break fi sleep 6 @@ -277,7 +345,16 @@ useCustomOperatorImageInCSV() { } getCheClusterCRFromExistedCSV() { - oc get csv $(getCSVName) -n openshift-operators -o yaml | yq -r ".metadata.annotations[\"alm-examples\"] | fromjson | .[] | select(.kind == \"CheCluster\")" + CHE_CLUSTER="" + CHE_CLUSTER_V2=$(oc get csv $(getCSVName) -n openshift-operators -o yaml | yq -r '.metadata.annotations["alm-examples"] | fromjson | .[] | select(.apiVersion == "org.eclipse.che/v2")') + if [[ -n "${CHE_CLUSTER_V2}" ]]; then + CHE_CLUSTER="${CHE_CLUSTER_V2}" + else + CHE_CLUSTER_V1=$(oc get csv $(getCSVName) -n openshift-operators -o yaml | yq -r '.metadata.annotations["alm-examples"] | fromjson | .[] | select(.apiVersion == "org.eclipse.che/v1")') + CHE_CLUSTER="${CHE_CLUSTER_V1}" + fi + + echo "${CHE_CLUSTER}" } getCheVersionFromExistedCSV() { @@ -290,7 +367,7 @@ getCSVName() { while [ $n -le 24 ] do - csvNumber=$(oc get csv -n openshift-operators --no-headers=true | grep eclipse-che-preview-openshift | wc -l) + csvNumber=$(oc get csv -n openshift-operators --no-headers=true | grep ${ECLIPSE_CHE_PACKAGE_NAME} | wc -l) if [[ $csvNumber == 1 ]]; then break return @@ -404,7 +481,7 @@ EOF sleep 10s if [[ ${installPlan} == "Manual" ]]; then - kubectl wait subscription/${name} -n openshift-operators --for=condition=InstallPlanPending --timeout=120s + kubectl wait subscription/"${name}" -n openshift-operators --for=condition=InstallPlanPending --timeout=120s fi } @@ -420,9 +497,8 @@ deployDevWorkspaceOperator() { devWorkspaceChannel="fast" fi - customDevWorkspaceCatalog=$(getDevWorkspaceCustomCatalogSourceName) - createCatalogSource "${customDevWorkspaceCatalog}" ${devWorkspaceCatalogImage} "Red Hat" "DevWorkspace Operator Catalog" - createSubscription "devworkspace-operator" "devworkspace-operator" "${devWorkspaceChannel}" "${customDevWorkspaceCatalog}" "Auto" + createCatalogSource "${DEV_WORKSPACE_CATALOG_SOURCE_NAME}" ${devWorkspaceCatalogImage} "Red Hat" "DevWorkspace Operator Catalog" + createSubscription "devworkspace-operator" "devworkspace-operator" "${devWorkspaceChannel}" "${DEV_WORKSPACE_CATALOG_SOURCE_NAME}" "Auto" waitDevWorkspaceControllerStarted } @@ -497,7 +573,7 @@ forcePullingOlmImages() { echo "[INFO] Pulling image '${image}'" - yq -r "(.spec.template.spec.containers[0].image) = \"${image}\"" "${BASE_DIR}/force-pulling-olm-images-job.yaml" | kubectl apply -f - -n openshift-operators + yq -r "(.spec.template.spec.containers[0].image) = \"${image}\"" "${OPERATOR_REPO}/olm/force-pulling-olm-images-job.yaml" | kubectl apply -f - -n openshift-operators kubectl wait --for=condition=complete --timeout=30s job/force-pulling-olm-images-job -n openshift-operators kubectl delete job/force-pulling-olm-images-job -n openshift-operators @@ -510,3 +586,13 @@ installchectl() { mkdir /tmp/chectl-${version} tar -xvzf /tmp/chectl-${version}.tar.gz -C /tmp/chectl-${version} } + +getBundlePath() { + channel="${1}" + if [ -z "${channel}" ]; then + echo "[ERROR] 'channel' is not specified" + exit 1 + fi + + echo "${OPERATOR_REPO}/bundle/${channel}/${ECLIPSE_CHE_PACKAGE_NAME}" +} diff --git a/.github/bin/minikube/test-helm.sh b/.github/bin/minikube/test-helm.sh new file mode 100755 index 000000000..7f035d662 --- /dev/null +++ b/.github/bin/minikube/test-helm.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +set -e +set -x + +# Get absolute path for root repo directory from github actions context: https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions +export OPERATOR_REPO="${GITHUB_WORKSPACE}" +if [ -z "${OPERATOR_REPO}" ]; then + OPERATOR_REPO=$(dirname "$(dirname "$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")")") +fi + +source "${OPERATOR_REPO}/.github/bin/common.sh" + +# Stop execution on any error +trap "catchFinish" EXIT SIGINT + +runTest() { + deployEclipseCheWithHelm "chectl" "minikube" "${CURRENT_OPERATOR_VERSION_TEMPLATE_PATH}" "true" +} + +initDefaults +initTemplates +runTest diff --git a/.github/bin/minikube/test-operator.sh b/.github/bin/minikube/test-operator.sh index afff033ad..1d0278385 100755 --- a/.github/bin/minikube/test-operator.sh +++ b/.github/bin/minikube/test-operator.sh @@ -20,13 +20,13 @@ if [ -z "${OPERATOR_REPO}" ]; then OPERATOR_REPO=$(dirname "$(dirname "$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")")") fi -source "${OPERATOR_REPO}"/.github/bin/common.sh +source "${OPERATOR_REPO}/.github/bin/common.sh" # Stop execution on any error trap "catchFinish" EXIT SIGINT runTest() { - deployEclipseCheOnWithOperator "chectl" "minikube" ${CURRENT_OPERATOR_VERSION_TEMPLATE_PATH} "true" + deployEclipseCheWithOperator "chectl" "minikube" ${CURRENT_OPERATOR_VERSION_TEMPLATE_PATH} "true" } initDefaults diff --git a/.github/bin/minikube/test-upgrade-from-stable-to-next.sh b/.github/bin/minikube/test-upgrade-from-stable-to-next.sh index 6b7cf9889..afb3bd2ee 100755 --- a/.github/bin/minikube/test-upgrade-from-stable-to-next.sh +++ b/.github/bin/minikube/test-upgrade-from-stable-to-next.sh @@ -20,17 +20,17 @@ if [ -z "${OPERATOR_REPO}" ]; then OPERATOR_REPO=$(dirname "$(dirname "$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")")") fi -source "${OPERATOR_REPO}"/.github/bin/common.sh +source "${OPERATOR_REPO}/.github/bin/common.sh" # Stop execution on any error trap "catchFinish" EXIT SIGINT runTest() { - deployEclipseCheOnWithOperator "/tmp/chectl-${LAST_PACKAGE_VERSION}/chectl/bin/run" "minikube" ${LAST_OPERATOR_VERSION_TEMPLATE_PATH} "false" - updateEclipseChe "chectl" "minikube" ${CURRENT_OPERATOR_VERSION_TEMPLATE_PATH} "true" + deployEclipseCheWithOperator "/tmp/chectl-${LAST_PACKAGE_VERSION}/chectl/bin/run" "minikube" "${LAST_OPERATOR_VERSION_TEMPLATE_PATH}" "false" + updateEclipseChe "chectl" "minikube" "${CURRENT_OPERATOR_VERSION_TEMPLATE_PATH}" "true" } initDefaults initTemplates -installchectl ${LAST_PACKAGE_VERSION} +installchectl "${LAST_PACKAGE_VERSION}" runTest diff --git a/.github/bin/minikube/test-upgrade-from-stable-to-stable.sh b/.github/bin/minikube/test-upgrade-from-stable-to-stable.sh index 38347ea17..1510ca7c9 100755 --- a/.github/bin/minikube/test-upgrade-from-stable-to-stable.sh +++ b/.github/bin/minikube/test-upgrade-from-stable-to-stable.sh @@ -20,13 +20,13 @@ if [ -z "${OPERATOR_REPO}" ]; then OPERATOR_REPO=$(dirname "$(dirname "$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")")") fi -source "${OPERATOR_REPO}"/.github/bin/common.sh +source "${OPERATOR_REPO}/.github/bin/common.sh" # Stop execution on any error trap "catchFinish" EXIT SIGINT runTest() { - deployEclipseCheOnWithOperator "/tmp/chectl-${PREVIOUS_PACKAGE_VERSION}/chectl/bin/run" "minikube" ${PREVIOUS_OPERATOR_VERSION_TEMPLATE_PATH} "false" + deployEclipseCheWithOperator "/tmp/chectl-${PREVIOUS_PACKAGE_VERSION}/chectl/bin/run" "minikube" ${PREVIOUS_OPERATOR_VERSION_TEMPLATE_PATH} "false" updateEclipseChe "chectl" "minikube" ${LAST_OPERATOR_VERSION_TEMPLATE_PATH} "false" } diff --git a/.github/workflows/minikube-test-helm.yaml b/.github/workflows/minikube-test-helm.yaml new file mode 100644 index 000000000..e85c16441 --- /dev/null +++ b/.github/workflows/minikube-test-helm.yaml @@ -0,0 +1,38 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +name: Helm test +on: pull_request +jobs: + helm-on-minikube: + runs-on: ubuntu-20.04 + steps: + - name: Checkout source code + uses: actions/checkout@v2.3.1 + - name: Start minikube cluster + id: run-minikube + uses: che-incubator/setup-minikube-action@next + with: + minikube-version: v1.23.2 + - name: Enable Private registry + run: /bin/bash olm/minikube-registry-addon.sh & + - name: Install yq + run: sudo pip install yq + - name: Install chectl + run: bash <(curl -sL https://www.eclipse.org/che/chectl/) --channel=next + - name: Run tests + run: /bin/bash .github/bin/minikube/test-helm.sh + - uses: actions/upload-artifact@v2 + if: ${{ always() }} + with: + name: artifacts + path: /tmp/artifacts-che diff --git a/.github/workflows/release-community-operator-PRs.yml b/.github/workflows/release-community-operator-PRs.yml index d95ed3ccf..dc1f2c813 100644 --- a/.github/workflows/release-community-operator-PRs.yml +++ b/.github/workflows/release-community-operator-PRs.yml @@ -51,7 +51,7 @@ jobs: set -e OP_SDK_DIR=/opt/operator-sdk mkdir -p $OP_SDK_DIR - make download-operator-sdk OP_SDK_DIR="${OP_SDK_DIR}" + make download-operator-sdk DEST="${OP_SDK_DIR}" export PATH="$PATH:$OP_SDK_DIR" export QUAY_ECLIPSE_CHE_USERNAME=${{ secrets.QUAY_USERNAME }} export QUAY_ECLIPSE_CHE_PASSWORD=${{ secrets.QUAY_PASSWORD }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4a43a778e..1b8605a05 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -88,7 +88,7 @@ jobs: OP_SDK_DIR=/opt/operator-sdk mkdir -p $OP_SDK_DIR - make download-operator-sdk OP_SDK_DIR="${OP_SDK_DIR}" + make download-operator-sdk DEST="${OP_SDK_DIR}" export PATH="$PATH:$OP_SDK_DIR" export QUAY_ECLIPSE_CHE_USERNAME=${{ secrets.QUAY_USERNAME }} diff --git a/.github/workflows/resources-check-main.yml b/.github/workflows/resources-check-main.yml index be2c58054..555423414 100644 --- a/.github/workflows/resources-check-main.yml +++ b/.github/workflows/resources-check-main.yml @@ -28,7 +28,9 @@ jobs: with: go-version: 1.16 - name: Validate operator resources - run: ${GITHUB_WORKSPACE}/.github/bin/check-resources.sh + run: | + go install golang.org/x/tools/cmd/goimports@latest + ${GITHUB_WORKSPACE}/.github/bin/check-resources.sh bundle-version-validation: runs-on: ubuntu-20.04 steps: diff --git a/.vscode/launch.json b/.vscode/launch.json index 71b5a69b2..f5b79874f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,124 +1,6 @@ { "version": "0.2.0", "configurations": [ - { - "type": "bashdb", - "request": "launch", - "name": "Make release", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/make-release.sh", - "args": [ - "7.30.0", - "--release", - "--check-resources", - "--release-olm-files" - ] - }, - { - "type": "bashdb", - "request": "launch", - "name": "Bash-Debug (release-olm-files.sh)", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/olm/release-olm-files.sh", - "args": [ - "7.30.0" - ] - }, - { - "type": "bashdb", - "request": "launch", - "name": "Bash-Debug (testCatalog.sh) openshift next", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/olm/testCatalog.sh", - "args": [ - "-p", - "openshift", - "-c", - "next", - "-i", - "quay.io/eclipse/eclipse-che-openshift-opm-catalog:next", - "-n", - "eclipse-che", - ] - }, - { - "type": "bashdb", - "request": "launch", - "name": "Bash-Debug (testCatalog.sh) openshift stable", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/olm/testCatalog.sh", - "args": [ - "-p", - "openshift", - "-c", - "stable", - "-i", - "quay.io/eclipse/eclipse-che-openshift-opm-catalog:test", - "-n", - "eclipse-che", - ] - }, - { - "type": "bashdb", - "request": "launch", - "name": "Bash-Debug (testUpdate.sh) openshift stable", - "cwd": "${workspaceFolder}/olm", - "program": "${workspaceFolder}/olm/testUpdate.sh", - "args": [ - "-p", - "openshift", - "-c", - "stable", - "-i", - "quay.io/eclipse/eclipse-che-openshift-opm-catalog:test", - "-n", - "eclipse-che" - ] - }, - { - "type": "bashdb", - "request": "launch", - "name": "Bash-Debug (testUpdate.sh) openshift next", - "cwd": "${workspaceFolder}/olm", - "program": "${workspaceFolder}/olm/testUpdate.sh", - "args": [ - "-p", - "openshift", - "-c", - "next", - "-i", - "quay.io/eclipse/eclipse-che-openshift-opm-catalog:next", - "-n", - "eclipse-che" - ] - }, - { - "type": "bashdb", - "request": "launch", - "name": "Bash-Debug (buildCatalog.sh) Openshift platform", - "cwd": "${workspaceFolder}/olm", - "program": "./buildCatalog.sh", - "args": [ - "-p", - "openshift", - "-c", - "next", - "-i", - "quay.io/eclipse/eclipse-che-openshift-opm-catalog:next" - ] - }, - { - "type": "bashdb", - "request": "launch", - "name": "Bash-Debug (simplest configuration)", - "program": "${file}", - }, - { - "name": "Launch Main *.go File", - "type": "go", - "request": "launch", - "program": "${file}", - }, { "useApiV1": false, "dlvLoadConfig": { @@ -133,20 +15,26 @@ "request": "launch", "mode": "auto", "program": "${workspaceFolder}/main.go", - "env": { - "CHE_FLAVOR": "che", - "CONSOLE_LINK_NAME": "che", - "CONSOLE_LINK_DISPLAY_NAME": "Eclipse Che", - "CONSOLE_LINK_SECTION": "Red Hat Applications", - "CONSOLE_LINK_IMAGE": "/dashboard/assets/branding/loader.svg", - "MAX_CONCURRENT_RECONCILES": 10, - }, - "envFile": "/tmp/che-operator-debug.env", + "envFile": "/tmp/che-operator-dev/vscode.env", "cwd": "${workspaceFolder}", "args": [ "--defaults-path", "config/manager/manager.yaml" ] + }, + { + "type": "bashdb", + "request": "launch", + "name": "Bash-Debug", + "cwd": "${workspaceFolder}", + "program": "${file}", + "args": [] + }, + { + "name": "Launch Main *.go File", + "type": "go", + "request": "launch", + "program": "${file}", } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..8438ddbd6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cSpell.words": [] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 18224a200..4ae404033 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -77,8 +77,8 @@ "group": "build" }, { - "label": "Update resources", - "command": "make update-resources -s", + "label": "Update all", + "command": "make update-dev-resources", "type": "shell", "args": [], "problemMatcher": [ diff --git a/Dockerfile b/Dockerfile index 5d9e617cf..f9e813482 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ FROM registry.access.redhat.com/ubi8/go-toolset:1.17.7-13 as builder ENV GOPATH=/go/ ARG DEV_WORKSPACE_CONTROLLER_VERSION="v0.14.1" ARG DEV_HEADER_REWRITE_TRAEFIK_PLUGIN="main" -ARG TESTS="true" +ARG SKIP_TESTS="false" USER root # upstream, download zips for every build @@ -47,7 +47,7 @@ COPY pkg/ pkg/ # build operator RUN export ARCH="$(uname -m)" && if [[ ${ARCH} == "x86_64" ]]; then export ARCH="amd64"; elif [[ ${ARCH} == "aarch64" ]]; then export ARCH="arm64"; fi && \ - if [[ ${TESTS} == "true" ]]; then export MOCK_API=true && go test -mod=vendor -v ./...; fi && \ + if [[ ${SKIP_TESTS} == "false" ]]; then export MOCK_API=true && go test -mod=vendor -v ./...; fi && \ CGO_ENABLED=0 GOOS=linux GOARCH=${ARCH} GO111MODULE=on go build -mod=vendor -a -o che-operator main.go # https://access.redhat.com/containers/?tab=tags#/registry.access.redhat.com/ubi8-minimal diff --git a/Makefile b/Makefile index ade41be1e..5ad441b26 100644 --- a/Makefile +++ b/Makefile @@ -18,55 +18,76 @@ # - use environment variables to overwrite this value (e.g export VERSION=0.0.2) VERSION ?= 1.0.2 +ifeq (,$(shell which kubectl)$(shell which oc)) + $(error oc or kubectl is required to proceed) +endif + +ifneq (,$(shell which kubectl)) + K8S_CLI := kubectl +else + K8S_CLI := oc +endif + +# Detect image tool +ifneq (,$(shell which docker)) + IMAGE_TOOL := docker +else + IMAGE_TOOL := podman +endif ifndef VERBOSE -MAKEFLAGS += --silent + MAKEFLAGS += --silent endif +ifeq ($(shell $(K8S_CLI) api-resources --api-group='route.openshift.io' 2>&1 | grep -o routes),routes) + PLATFORM := openshift +else + PLATFORM := kubernetes +endif + +# go-get-tool will 'go get' any package $2 and install it to $1. +PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST)))) +define go-get-tool +@[ -f $(1) ] || { \ +set -e ;\ +TMP_DIR=$$(mktemp -d) ;\ +cd $$TMP_DIR ;\ +go mod init tmp ;\ +echo "[INFO] Downloading $(2)" ;\ +GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\ +rm -rf $$TMP_DIR ;\ +} +endef + mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) mkfile_dir := $(dir $(mkfile_path)) -OPERATOR_SDK_BINARY ?= operator-sdk - -# IMAGE_TAG_BASE defines the quay.io namespace and part of the image name for remote images. -# This variable is used to construct full image tags for bundle and catalog images. -# -# For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both -# quay.io/eclipse/che-operator-bundle:$VERSION and quay.io/eclipse/che-operator-catalog:$VERSION. -IMAGE_TAG_BASE ?= quay.io/eclipse/che-operator - -# BUNDLE_IMG defines the image:tag used for the bundle. -# You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=/:) -BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION) - -# Image URL to use all building/pushing image targets +# Default Eclipse Che operator image IMG ?= quay.io/eclipse/che-operator:next -# Produce CRDs that work back to Kubernetes 1.11 (no version conversion) -CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false" -OPERATOR_YAML="config/manager/manager.yaml" +CRD_OPTIONS ?= "crd:crdVersions=v1" +CONFIG_MANAGER="config/manager/manager.yaml" + +INTERNAL_TMP_DIR=/tmp/che-operator-dev +BASH_ENV_FILE=$(INTERNAL_TMP_DIR)/bash.env +VSCODE_ENV_FILE=$(INTERNAL_TMP_DIR)/vscode.env + +DEPLOYMENT_DIR=$(PROJECT_DIR)/deploy/deployment -ENV_FILE="/tmp/che-operator-debug.env" ECLIPSE_CHE_NAMESPACE="eclipse-che" +ECLIPSE_CHE_PACKAGE_NAME="eclipse-che-preview-openshift" -CRD_FOLDER="config/crd/bases" - -ECLIPSE_CHE_CR=config/samples/org.eclipse.che_v1_checluster.yaml - -# legacy crd file names -ECLIPSE_CHE_CRD_V1="$(CRD_FOLDER)/org_v1_che_crd.yaml" - -# default crd names used operator-sdk from the box -ECLIPSE_CHE_CRD="$(CRD_FOLDER)/org.eclipse.che_checlusters.yaml" +CHECLUSTER_CR_PATH="$(PROJECT_DIR)/config/samples/org_v2_checluster.yaml" +CHECLUSTER_CRD_PATH="$(PROJECT_DIR)/config/crd/bases/org.eclipse.che_checlusters.yaml" DEV_WORKSPACE_CONTROLLER_VERSION="v0.14.1" DEV_HEADER_REWRITE_TRAEFIK_PLUGIN="main" # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) -GOBIN=$(shell go env GOPATH)/bin + GOBIN=$(shell go env GOPATH)/bin else -GOBIN=$(shell go env GOBIN) + GOBIN=$(shell go env GOBIN) endif # Setting SHELL to bash allows bash commands to be executed by recipes. @@ -90,242 +111,30 @@ all: build # http://linuxcommand.org/lc3_adv_awk.php help: ## Display this help. - @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-25s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) ##@ Development -download-operator-sdk: - ARCH=$$(case "$$(uname -m)" in - x86_64) echo -n amd64 ;; - aarch64) echo -n arm64 ;; - *) echo -n $$(uname -m) - esac) - OS=$$(uname | awk '{print tolower($$0)}') +update-dev-resources: SHELL := /bin/bash +update-dev-resources: validate-requirements ## Update all resources + # Update ubi8 image + ubiMinimal8Version=$$(skopeo --override-os linux inspect docker://registry.access.redhat.com/ubi8-minimal:latest | jq -r '.Labels.version') + ubiMinimal8Release=$$(skopeo --override-os linux inspect docker://registry.access.redhat.com/ubi8-minimal:latest | jq -r '.Labels.release') + UBI8_MINIMAL_IMAGE="registry.access.redhat.com/ubi8-minimal:$${ubiMinimal8Version}-$${ubiMinimal8Release}" + skopeo --override-os linux inspect docker://$${UBI8_MINIMAL_IMAGE} > /dev/null + echo "[INFO] UBI8 image $${UBI8_MINIMAL_IMAGE}" - OPERATOR_SDK_VERSION=$$(sed -r 's|operator-sdk:\s*(.*)|\1|' REQUIREMENTS) + # Dockerfile + sed -i 's|registry.access.redhat.com/ubi8-minimal:[^\s]* |'$${UBI8_MINIMAL_IMAGE}' |g' $(PROJECT_DIR)/Dockerfile - echo "[INFO] ARCH: $$ARCH, OS: $$OS. operator-sdk version: $$OPERATOR_SDK_VERSION" - - if [ -z $(OP_SDK_DIR) ]; then - OP_SDK_PATH="operator-sdk" - else - OP_SDK_PATH="$(OP_SDK_DIR)/operator-sdk" - fi - - echo "[INFO] Downloading operator-sdk..." - - OPERATOR_SDK_DL_URL=https://github.com/operator-framework/operator-sdk/releases/download/$${OPERATOR_SDK_VERSION} - curl -sSLo $${OP_SDK_PATH} $${OPERATOR_SDK_DL_URL}/operator-sdk_$${OS}_$${ARCH} - - echo "[INFO] operator-sdk will downloaded to: $${OP_SDK_PATH}" - echo "[INFO] Set up executable permissions to binary." - chmod +x $${OP_SDK_PATH} - echo "[INFO] operator-sdk is ready." - -manifests: controller-gen add-license-download ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. - # Generate CRDs v1 - $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases - mv "$(ECLIPSE_CHE_CRD)" "$(ECLIPSE_CHE_CRD_V1)" - - # remove yaml delimitier, which makes OLM catalog source image broken. - sed -i.bak '/---/d' "$(ECLIPSE_CHE_CRD_V1)" - rm -rf "$(ECLIPSE_CHE_CRD_V1).bak" - - # remove v1alphav2 version from crd files - yq -rYi "del(.spec.versions[1])" "$(ECLIPSE_CHE_CRD_V1)" - - $(MAKE) add-license $$(find ./config/crd -not -path "./vendor/*" -name "*.yaml") - -generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. - $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." - -compile: - binary="$(BINARY)" - if [ -z "$${binary}" ]; then - binary="/tmp/che-operator/che-operator" - fi - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 GO111MODULE=on go build -mod=vendor -a -o "$${binary}" main.go - echo "che-operator binary compiled to $${binary}" - -fmt: add-license-download ## Run go fmt against code. - ifneq ($(shell command -v goimports 2> /dev/null),) - find . -not -path "./vendor/*" -name "*.go" -exec goimports -w {} \; - else - @echo "WARN: goimports is not installed -- formatting using go fmt instead." - @echo " Please install goimports to ensure file imports are consistent." - go fmt -x ./... - endif - - FILES_TO_CHECK_LICENSE=$$(find . \ - -not -path "./mocks/*" \ - -not -path "./vendor/*" \ - -not -path "./testbin/*" \ - -not -path "./bundle/stable/*" \ - -not -path "./config/manager/controller_manager_config.yaml" \ - \( -name '*.sh' -o -name "*.go" -o -name "*.yaml" -o -name "*.yml" \)) - - for f in $${FILES_TO_CHECK_LICENSE} - do - $(MAKE) add-license $${f} - done - - -vet: ## Run go vet against code. - go vet ./... - -ENVTEST_ASSETS_DIR=$(shell pwd)/testbin -test: prepare-templates ## Run tests. - export MOCK_API=true; go test -mod=vendor ./... -coverprofile cover.out - -##@ Build - -build: generate fmt vet ## Build manager binary. - go build -o bin/manager main.go - -run: manifests generate fmt vet ## Run a controller from your host. - go run ./main.go - -IMAGE_TOOL=docker - -docker-build: ## Build docker image with the manager. - ${IMAGE_TOOL} build -t ${IMG} . - -docker-push: ## Push docker image with the manager. - ${IMAGE_TOOL} push ${IMG} - -##@ Deployment - -install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd | kubectl apply -f - - -uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd | kubectl delete -f - - -deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. - cd config/manager || true && $(KUSTOMIZE) edit set image quay.io/eclipse/che-operator:next=${IMG} && cd ../.. - $(KUSTOMIZE) build config/default | kubectl apply -f - - - echo "[INFO] Start printing logs..." - oc wait --for=condition=ready pod -l app.kubernetes.io/component=che-operator -n ${ECLIPSE_CHE_NAMESPACE} --timeout=60s - oc logs $$(oc get pods -o json -n ${ECLIPSE_CHE_NAMESPACE} | jq -r '.items[] | select(.metadata.name | test("che-operator-")).metadata.name') -n ${ECLIPSE_CHE_NAMESPACE} --all-containers -f - -undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/default | kubectl delete -f - - -prepare-templates: - # Download Dev Workspace operator templates - echo "[INFO] Downloading Dev Workspace operator templates ..." - rm -f /tmp/devworkspace-operator.zip - rm -rf /tmp/devfile-devworkspace-operator-* - rm -rf /tmp/devworkspace-operator/ - mkdir -p /tmp/devworkspace-operator/templates - - curl -sL https://api.github.com/repos/devfile/devworkspace-operator/zipball/${DEV_WORKSPACE_CONTROLLER_VERSION} > /tmp/devworkspace-operator.zip - - unzip -q /tmp/devworkspace-operator.zip '*/deploy/deployment/*' -d /tmp - cp -rf /tmp/devfile-devworkspace-operator*/deploy/* /tmp/devworkspace-operator/templates - echo "[INFO] Downloading Dev Workspace operator templates completed." - - echo "[INFO] Downloading Gateway plugin resources ..." - rm -f /tmp/asset-header-rewrite-traefik-plugin.zip - rm -rf /tmp/header-rewrite-traefik-plugin - rm -rf /tmp/*-header-rewrite-traefik-plugin-*/ - curl -sL https://api.github.com/repos/che-incubator/header-rewrite-traefik-plugin/zipball/${DEV_HEADER_REWRITE_TRAEFIK_PLUGIN} > /tmp/asset-header-rewrite-traefik-plugin.zip - - unzip -q /tmp/asset-header-rewrite-traefik-plugin.zip -d /tmp - mkdir -p /tmp/header-rewrite-traefik-plugin - mv /tmp/*-header-rewrite-traefik-plugin-*/headerRewrite.go /tmp/*-header-rewrite-traefik-plugin-*/.traefik.yml /tmp/header-rewrite-traefik-plugin - echo "[INFO] Downloading Gateway plugin resources completed." - -create-namespace: - set +e - kubectl create namespace ${ECLIPSE_CHE_NAMESPACE} || true - set -e - -apply-crd: - kubectl apply -f ${ECLIPSE_CHE_CRD_V1} - -.PHONY: init-cr -init-cr: - if [ "$$(oc get checluster -n ${ECLIPSE_CHE_NAMESPACE} eclipse-che || false )" ]; then - echo "Che Cluster already exists. Using it." - else - echo "Che Cluster is not found. Creating a new one from $(ECLIPSE_CHE_CR)" - # before applying resources on K8s check if ingress domain corresponds to the current cluster - # no OpenShift ingress domain is ignored, so skip it - if [ "$$(oc api-resources --api-group='route.openshift.io' 2>&1 | grep -o routes)" != "routes" ]; then - export CLUSTER_API_URL=$$(oc whoami --show-server=true) || true; - export CLUSTER_DOMAIN=$$(echo $${CLUSTER_API_URL} | sed -E 's/https:\/\/(.*):.*/\1/g') - export CHE_CLUSTER_DOMAIN=$$(yq -r .spec.k8s.ingressDomain $(ECLIPSE_CHE_CR)) - export CHE_CLUSTER_DOMAIN=$${CHE_CLUSTER_DOMAIN%".nip.io"} - if [ $${CLUSTER_DOMAIN} != $${CHE_CLUSTER_DOMAIN} ];then - echo "[WARN] Your cluster address is $${CLUSTER_DOMAIN} but CheCluster has $${CHE_CLUSTER_DOMAIN} configured" - echo "[WARN] Make sure that .spec.k8s.ingressDomain in $${ECLIPSE_CHE_CR} has the right value and rerun" - echo "[WARN] Press y to continue anyway. [y/n] ? " && read ans && [ $${ans:-N} = y ] || exit 1; - fi - fi - kubectl apply -f ${ECLIPSE_CHE_CR} -n ${ECLIPSE_CHE_NAMESPACE} - fi - -create-env-file: prepare-templates - rm -rf "${ENV_FILE}" - touch "${ENV_FILE}" - CLUSTER_API_URL=$$(oc whoami --show-server=true) || true; - if [ -n $${CLUSTER_API_URL} ]; then - echo "CLUSTER_API_URL='$${CLUSTER_API_URL}'" >> "${ENV_FILE}" - echo "[INFO] Set up cluster api url: $${CLUSTER_API_URL}" - fi; - echo "WATCH_NAMESPACE='${ECLIPSE_CHE_NAMESPACE}'" >> "${ENV_FILE}" - -create-full-env-file: create-env-file - cat ./$(OPERATOR_YAML) | \ - yq -r '.spec.template.spec.containers[0].env[] | select(.name == "WATCH_NAMESPACE" | not) | "export \(.name)=\"\(.value)\""' \ - >> ${ENV_FILE} - echo "[INFO] Env file: ${ENV_FILE}" - source ${ENV_FILE} ; env | grep CHE_VERSION - -debug: generate manifests kustomize prepare-templates create-namespace apply-crd init-cr create-env-file - echo "[WARN] Make sure that your CR contains valid ingress domain!" - # dlv has an issue with 'Ctrl-C' termination, that's why we're doing trick with detach. - dlv debug --listen=:2345 --headless=true --api-version=2 ./main.go -- & - OPERATOR_SDK_PID=$! - echo "[INFO] Use 'make uninstall' to remove Che installation after debug" - wait $$OPERATOR_SDK_PID - -CONTROLLER_GEN = $(shell pwd)/bin/controller-gen -controller-gen: ## Download controller-gen locally if necessary. - $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.4.1) - -KUSTOMIZE = $(shell pwd)/bin/kustomize -kustomize: ## Download kustomize locally if necessary. - $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7) - -ADD_LICENSE = $(shell pwd)/bin/addlicense -add-license-download: ## Download addlicense locally if necessary. - $(call go-get-tool,$(ADD_LICENSE),github.com/google/addlicense@99ebc9c9db7bceb8623073e894533b978d7b7c8a) - -add-license: - # Get all argument and remove make goal("add-license") to get only list files - FILES=$$(echo $(filter-out $@,$(MAKECMDGOALS))) - $(ADD_LICENSE) -f hack/license-header.txt $${FILES} - -# go-get-tool will 'go get' any package $2 and install it to $1. -PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST)))) -define go-get-tool -@[ -f $(1) ] || { \ -set -e ;\ -TMP_DIR=$$(mktemp -d) ;\ -cd $$TMP_DIR ;\ -go mod init tmp ;\ -echo "[INFO] Downloading $(2)" ;\ -GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\ -rm -rf $$TMP_DIR ;\ -} -endef - -update-roles: - echo "[INFO] Updating roles with DW roles" + $(MAKE) update-rbac + $(MAKE) bundle CHANNEL=next + $(MAKE) gen-deployment + $(MAKE) update-helmcharts CHANNEL=next + $(MAKE) fmt +update-rbac: SHELL := /bin/bash +update-rbac: CLUSTER_ROLES=( https://raw.githubusercontent.com/devfile/devworkspace-operator/${DEV_WORKSPACE_CONTROLLER_VERSION}/deploy/deployment/openshift/objects/devworkspace-controller-view-workspaces.ClusterRole.yaml https://raw.githubusercontent.com/devfile/devworkspace-operator/${DEV_WORKSPACE_CONTROLLER_VERSION}/deploy/deployment/openshift/objects/devworkspace-controller-edit-workspaces.ClusterRole.yaml @@ -369,419 +178,546 @@ update-roles: done <<< "$$CONTENT" done + echo "[INFO] Updated config/rbac/role.yaml" + echo "[INFO] Updated config/rbac/cluster_role.yam" + +update-helmcharts: SHELL := /bin/bash +update-helmcharts: ## Update Helm Charts + [[ -z "$(CHANNEL)" ]] && { echo [ERROR] CHANNEL not defined; exit 1; } + + HELM_DIR=$(PROJECT_DIR)/helmcharts/$(CHANNEL) + HELMCHARTS_TEMPLATES=$${HELM_DIR}/templates + HELMCHARTS_CRDS=$${HELM_DIR}/crds + + rm -rf $${HELMCHARTS_TEMPLATES} $${HELMCHARTS_CRDS} + mkdir -p $${HELMCHARTS_TEMPLATES} $${HELMCHARTS_CRDS} + + rsync -a --exclude='checlusters.org.eclipse.che.CustomResourceDefinition.yaml' $(DEPLOYMENT_DIR)/kubernetes/objects/ $${HELMCHARTS_TEMPLATES} + cp $(DEPLOYMENT_DIR)/kubernetes/org_v2_checluster.yaml $${HELMCHARTS_TEMPLATES} + cp $(DEPLOYMENT_DIR)/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml $${HELMCHARTS_CRDS} + + # Remove namespace since its creation is mentioned is README.md + rm $${HELMCHARTS_TEMPLATES}/eclipse-che.Namespace.yaml + + if [ $(CHANNEL) == "stable" ]; then + chartYaml=$${HELM_DIR}/Chart.yaml + + CRDS_SAMPLES_FILES=( + $${HELMCHARTS_TEMPLATES}/org_v2_checluster.yaml + ) + + CRDS_SAMPLES="" + for CRD_SAMPLE in "$${CRDS_SAMPLES_FILES[@]}"; do + CRD_SAMPLE=$$(cat $${CRD_SAMPLE} | yq -rY ". | (.metadata.namespace = \"$(ECLIPSE_CHE_NAMESPACE)\") | [.]") + CRDS_SAMPLES=$${CRDS_SAMPLES}$${CRD_SAMPLE}$$'\n' + done + + yq -rYi --arg examples "$${CRDS_SAMPLES}" ".annotations.\"artifacthub.io/crdsExamples\" = \$$examples" $${chartYaml} + rm -rf $${HELMCHARTS_TEMPLATES}/org_v2_checluster.yaml + else + yq -riY '.spec.networking.tlsSecretName = "che-tls"' $${HELMCHARTS_TEMPLATES}/org_v2_checluster.yaml + yq -riY '.spec.networking.domain = "{{ .Values.networking.domain }}"' $${HELMCHARTS_TEMPLATES}/org_v2_checluster.yaml + yq -riY '.spec.networking.auth.oAuthSecret = "{{ .Values.networking.auth.oAuthSecret }}"' $${HELMCHARTS_TEMPLATES}/org_v2_checluster.yaml + yq -riY '.spec.networking.auth.oAuthClientName = "{{ .Values.networking.auth.oAuthClientName }}"' $${HELMCHARTS_TEMPLATES}/org_v2_checluster.yaml + yq -riY '.spec.networking.auth.identityProviderURL = "{{ .Values.networking.auth.identityProviderURL }}"' $${HELMCHARTS_TEMPLATES}/org_v2_checluster.yaml + fi + + echo "[INFO] HelmCharts updated $${HELM_DIR}" + +gen-deployment: SHELL := /bin/bash +gen-deployment: manifests download-kustomize _kustomize-operator-image ## Generate Eclipse Che k8s deployment resources + rm -rf $(DEPLOYMENT_DIR) + for TARGET_PLATFORM in kubernetes openshift; do + PLATFORM_DIR=$(DEPLOYMENT_DIR)/$${TARGET_PLATFORM} + OBJECTS_DIR=$${PLATFORM_DIR}/objects + + mkdir -p $${OBJECTS_DIR} + + COMBINED_FILENAME=$${PLATFORM_DIR}/combined.yaml + $(KUSTOMIZE) build config/$${TARGET_PLATFORM} | cat > $${COMBINED_FILENAME} - + + # Split the giant files output by kustomize per-object + csplit -s -f "temp" --suppress-matched "$${COMBINED_FILENAME}" '/^---$$/' '{*}' + for file in temp??; do + name_kind=$$(yq -r '"\(.metadata.name).\(.kind)"' "$${file}") + mv "$${file}" "$${OBJECTS_DIR}/$${name_kind}.yaml" + done + cp $(PROJECT_DIR)/config/samples/org_v2_checluster.yaml $${PLATFORM_DIR} + + echo "[INFO] Deployments resources generated into $${PLATFORM_DIR}" + done + +gen-chectl-tmpl: SHELL := /bin/bash +gen-chectl-tmpl: ## Generate Eclipse Che k8s deployment resources used by chectl + [[ -z "$(TARGET)" ]] && { echo [ERROR] TARGET not defined; exit 1; } + [[ -z "$(SOURCE)" ]] && src=$(PROJECT_DIR) || src=$(SOURCE) + + dst=$(TARGET)/che-operator && rm -rf $${dst} + + if [[ -d "$${src}/deploy/deployment" ]]; then + # CheCluster API v2 + src="$${src}/deploy/deployment" + + for TARGET_PLATFORM in kubernetes openshift; do + mkdir -p "$${dst}/$${TARGET_PLATFORM}/crds" + + cp $${src}/$${TARGET_PLATFORM}/objects/che-operator.Deployment.yaml $${dst}/$${TARGET_PLATFORM}/operator.yaml + + cp $${src}/$${TARGET_PLATFORM}/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml $${dst}/$${TARGET_PLATFORM}/crds/org.eclipse.che_checlusters.yaml + cp $${src}/$${TARGET_PLATFORM}/org_v2_checluster.yaml $${dst}/$${TARGET_PLATFORM}/crds/org_checluster_cr.yaml + + cp $${src}/$${TARGET_PLATFORM}/objects/che-operator.ServiceAccount.yaml $${dst}/$${TARGET_PLATFORM}/service_account.yaml + cp $${src}/$${TARGET_PLATFORM}/objects/che-operator.ClusterRoleBinding.yaml $${dst}/$${TARGET_PLATFORM}/cluster_rolebinding.yaml + cp $${src}/$${TARGET_PLATFORM}/objects/che-operator.ClusterRole.yaml $${dst}/$${TARGET_PLATFORM}/cluster_role.yaml + cp $${src}/$${TARGET_PLATFORM}/objects/che-operator.RoleBinding.yaml $${dst}/$${TARGET_PLATFORM}/role_binding.yaml + cp $${src}/$${TARGET_PLATFORM}/objects/che-operator.Role.yaml $${dst}/$${TARGET_PLATFORM}/role.yaml + + cp $${src}/$${TARGET_PLATFORM}/objects/che-operator-service.Service.yaml $${dst}/$${TARGET_PLATFORM}/webhook-service.yaml + + if [[ $${TARGET_PLATFORM} == "kubernetes" ]]; then + cp $${src}/$${TARGET_PLATFORM}/objects/che-operator-serving-cert.Certificate.yaml $${dst}/$${TARGET_PLATFORM}/serving-cert.yaml + cp $${src}/$${TARGET_PLATFORM}/objects/che-operator-selfsigned-issuer.Issuer.yaml $${dst}/$${TARGET_PLATFORM}/selfsigned-issuer.yaml + fi + done + else + # CheCluster API v1 + mkdir -p $${dst}/crds + + cp -f $${src}/config/manager/manager.yaml $${dst}/operator.yaml + + cp -f $${src}/config/crd/bases/org_v1_che_crd.yaml $${dst}/crds + cp -f $${src}/config/samples/org.eclipse.che_v1_checluster.yaml $${dst}/crds/org_v1_che_cr.yaml + + cp -f $${src}/config/rbac/role.yaml $${dst} + cp -f $${src}/config/rbac/role_binding.yaml $${dst} + cp -f $${src}/config/rbac/cluster_role.yaml $${dst} + cp -f $${src}/config/rbac/cluster_rolebinding.yaml $${dst} + cp -f $${src}/config/rbac/service_account.yaml $${dst} + fi + + echo "[INFO] Generated chectl templates into $${dst}" + +build: generate ## Build Eclipse Che operator binary + go build -o bin/manager main.go + +run: SHELL := /bin/bash +run: generate manifests download-kustomize genenerate-env download-devworkspace-resources ## Run Eclipse Che operator + echo "[INFO] Running on $(PLATFORM)" + [[ $(PLATFORM) == "kubernetes" ]] && $(MAKE) install-certmgr + + $(KUSTOMIZE) build config/$(PLATFORM) | $(K8S_CLI) apply -f - + $(MAKE) wait-pod-running COMPONENT=che-operator NAMESPACE=$(ECLIPSE_CHE_NAMESPACE) + + $(K8S_CLI) scale deploy che-operator -n $(ECLIPSE_CHE_NAMESPACE) --replicas=0 + $(MAKE) store_tls_cert + $(MAKE) create-checluster-cr + + source $(BASH_ENV_FILE) + + go run ./main.go + +debug: SHELL := /bin/bash +debug: generate manifests download-kustomize genenerate-env download-devworkspace-resources ## Run and debug Eclipse Che operator + echo "[INFO] Running on $(PLATFORM)" + [[ $(PLATFORM) == "kubernetes" ]] && $(MAKE) install-certmgr + + $(KUSTOMIZE) build config/$(PLATFORM) | $(K8S_CLI) apply -f - + $(MAKE) wait-pod-running COMPONENT=che-operator NAMESPACE=$(ECLIPSE_CHE_NAMESPACE) + + $(K8S_CLI) scale deploy che-operator -n $(ECLIPSE_CHE_NAMESPACE) --replicas=0 + $(MAKE) store_tls_cert + $(MAKE) create-checluster-cr + + source $(BASH_ENV_FILE) + + # dlv has an issue with 'Ctrl-C' termination, that's why we're doing trick with detach. + dlv debug --listen=:2345 --headless=true --api-version=2 ./main.go -- & + DLV_PID=$! + wait $${DLV_PID} + +docker-build: ## Build Eclipse Che operator image + if [ "$(SKIP_TESTS)" = true ]; then + ${IMAGE_TOOL} build -t ${IMG} --build-arg SKIP_TESTS=true . + else + ${IMAGE_TOOL} build -t ${IMG} . + fi + +docker-push: ## Push Eclipse Che operator image to a registry + ${IMAGE_TOOL} push ${IMG} + +manifests: download-controller-gen download-addlicense ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. + $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases + + # remove yaml delimitier, which makes OLM catalog source image broken. + sed -i '/---/d' "$(CHECLUSTER_CRD_PATH)" + + $(MAKE) license $$(find ./config/crd -not -path "./vendor/*" -name "*.yaml") + +generate: download-controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. + $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + +fmt: download-addlicense ## Run go fmt against code. + ifneq ($(shell command -v goimports 2> /dev/null),) + find . -not -path "./vendor/*" -name "*.go" -exec goimports -w {} \; + else + @echo "WARN: goimports is not installed -- formatting using go fmt instead." + @echo " Please install goimports to ensure file imports are consistent." + go fmt -x ./... + endif + + FILES_TO_CHECK_LICENSE=$$(find . \ + -not -path "./mocks/*" \ + -not -path "./vendor/*" \ + -not -path "./testbin/*" \ + -not -path "./bundle/stable/*" \ + -not -path "./config/manager/controller_manager_config.yaml" \ + \( -name '*.sh' -o -name "*.go" -o -name "*.yaml" -o -name "*.yml" \)) + + $(MAKE) license $${FILES_TO_CHECK_LICENSE} + +vet: ## Run go vet against code. + go vet ./... + +ENVTEST_ASSETS_DIR=$(shell pwd)/testbin +test: download-devworkspace-resources ## Run tests. + export MOCK_API=true; go test -mod=vendor ./... -coverprofile cover.out + +##@ Development utilities + +license: ## Add license to the files + FILES=$$(echo $(filter-out $@,$(MAKECMDGOALS))) + $(ADD_LICENSE) -f hack/license-header.txt $${FILES} + +genenerate-env: ## Generates environment files to use by bash and vscode + mkdir -p $(INTERNAL_TMP_DIR) + cat $(CONFIG_MANAGER) \ + | yq -r \ + '.spec.template.spec.containers[] + | select(.name=="che-operator") + | .env[] + | select(has("value")) + | "export \(.name)=\(.value)"' \ + > $(BASH_ENV_FILE) + echo "export WATCH_NAMESPACE=$(ECLIPSE_CHE_NAMESPACE)" >> $(BASH_ENV_FILE) + echo "[INFO] Created $(BASH_ENV_FILE)" + + cat $(CONFIG_MANAGER) \ + | yq -r \ + '.spec.template.spec.containers[] + | select(.name=="che-operator") + | .env[] + | select(has("value")) + | "\(.name)=\(.value)"' \ + > $(VSCODE_ENV_FILE) + echo "WATCH_NAMESPACE=$(ECLIPSE_CHE_NAMESPACE)" >> $(VSCODE_ENV_FILE) + echo "[INFO] Created $(VSCODE_ENV_FILE)" + + cat $(BASH_ENV_FILE) + +install-certmgr: SHELL := /bin/bash +install-certmgr: ## Install Cert Manager v1.7.1 + oc apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.7.1/cert-manager.yaml + $(MAKE) wait-pod-running COMPONENT=controller NAMESPACE=cert-manager + $(MAKE) wait-pod-running COMPONENT=cainjector NAMESPACE=cert-manager + $(MAKE) wait-pod-running COMPONENT=webhook NAMESPACE=cert-manager + +download-devworkspace-resources: ## Downloads Dev Workspace resources + DEVWORKSPACE_RESOURCES=/tmp/devworkspace-operator/templates + GATEWAY_RESOURCES=/tmp/header-rewrite-traefik-plugin + + rm -rf /tmp/devworkspace-operator.zip /tmp/devfile-devworkspace-operator-* $${DEVWORKSPACE_RESOURCES} + mkdir -p $${DEVWORKSPACE_RESOURCES} + curl -sL https://api.github.com/repos/devfile/devworkspace-operator/zipball/${DEV_WORKSPACE_CONTROLLER_VERSION} > /tmp/devworkspace-operator.zip + unzip -q /tmp/devworkspace-operator.zip '*/deploy/deployment/*' -d /tmp + cp -rf /tmp/devfile-devworkspace-operator*/deploy/* $${DEVWORKSPACE_RESOURCES} + + echo "[INFO] DevWorkspace resources downloaded into $${DEVWORKSPACE_RESOURCES}" + + rm -rf /tmp/asset-header-rewrite-traefik-plugin.zip /tmp/*-header-rewrite-traefik-plugin-*/ $${GATEWAY_RESOURCES} + mkdir -p $${GATEWAY_RESOURCES} + curl -sL https://api.github.com/repos/che-incubator/header-rewrite-traefik-plugin/zipball/${DEV_HEADER_REWRITE_TRAEFIK_PLUGIN} > /tmp/asset-header-rewrite-traefik-plugin.zip + unzip -q /tmp/asset-header-rewrite-traefik-plugin.zip -d /tmp + mv /tmp/*-header-rewrite-traefik-plugin-*/headerRewrite.go /tmp/*-header-rewrite-traefik-plugin-*/.traefik.yml $${GATEWAY_RESOURCES} + + echo "[INFO] Gateway resources downloaded into $${GATEWAY_RESOURCES}" + +setup-checluster: create-namespace create-checluster-crd create-checluster-cr ## Setup CheCluster (creates namespace, CRD and CheCluster CR) + +create-namespace: ## Creates eclipse-che namespace + $(K8S_CLI) create namespace ${ECLIPSE_CHE_NAMESPACE} || true + +create-checluster-crd: SHELL := /bin/bash +create-checluster-crd: ## Creates CheCluster Custom Resource Definition + if [[ $(PLATFORM) == "kubernetes" ]]; then + $(MAKE) install-certmgr + $(K8S_CLI) apply -f $(DEPLOYMENT_DIR)/$(PLATFORM)/objects/che-operator-selfsigned-issuer.Issuer.yaml + $(K8S_CLI) apply -f $(DEPLOYMENT_DIR)/$(PLATFORM)/objects/che-operator-serving-cert.Certificate.yaml + fi + $(K8S_CLI) apply -f $(DEPLOYMENT_DIR)/$(PLATFORM)/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml + +create-checluster-cr: SHELL := /bin/bash +create-checluster-cr: ## Creates CheCluster Custom Resource V2 + if [[ "$$($(K8S_CLI) get checluster eclipse-che -n $(ECLIPSE_CHE_NAMESPACE) || false )" ]]; then + echo "[INFO] CheCluster already exists." + else + CHECLUSTER_CR_2_APPLY=/tmp/checluster_cr.yaml + cp $(CHECLUSTER_CR_PATH) $${CHECLUSTER_CR_2_APPLY} + + # Update networking.domain field with an actual value + if [[ $(PLATFORM) == "kubernetes" ]]; then + # kubectl does not have `whoami` command + CLUSTER_API_URL=$$(oc whoami --show-server=true) || true; + CLUSTER_DOMAIN=$$(echo $${CLUSTER_API_URL} | sed -E 's/https:\/\/(.*):.*/\1/g') + yq -riY '.spec.networking.domain = "'$${CLUSTER_DOMAIN}'.nip.io"' $${CHECLUSTER_CR_2_APPLY} + fi + $(K8S_CLI) apply -f $${CHECLUSTER_CR_2_APPLY} -n $(ECLIPSE_CHE_NAMESPACE) + fi + +wait-pod-running: SHELL := /bin/bash +wait-pod-running: ## Wait until pod is up and running + [[ -z "$(COMPONENT)" ]] && { echo [ERROR] COMPONENT not defined; exit 1; } + [[ -z "$(NAMESPACE)" ]] && { echo [ERROR] NAMESPACE not defined; exit 1; } + + while [ $$(oc get pod -l app.kubernetes.io/component=$(COMPONENT) -n $(NAMESPACE) -o go-template='{{len .items}}') -eq 0 ]; do + sleep 10s + done + oc wait --for=condition=ready pod -l app.kubernetes.io/component=$(COMPONENT) -n $(NAMESPACE) --timeout=120s + +store_tls_cert: ## Store `che-operator-webhook-server-cert` secret locally + mkdir -p /tmp/k8s-webhook-server/serving-certs/ + $(K8S_CLI) get secret che-operator-webhook-server-cert -n $(ECLIPSE_CHE_NAMESPACE) -o json | jq -r '.data["tls.crt"]' | base64 -d > /tmp/k8s-webhook-server/serving-certs/tls.crt + $(K8S_CLI) get secret che-operator-webhook-server-cert -n $(ECLIPSE_CHE_NAMESPACE) -o json | jq -r '.data["tls.key"]' | base64 -d > /tmp/k8s-webhook-server/serving-certs/tls.key + +##@ Deployment +install: SHELL := /bin/bash +install: manifests download-kustomize _kustomize-operator-image ## Install Eclipse Che + echo "[INFO] Running on $(PLATFORM)" + [[ $(PLATFORM) == "kubernetes" ]] && $(MAKE) install-certmgr + + $(KUSTOMIZE) build config/$(PLATFORM) | $(K8S_CLI) apply -f - + $(MAKE) wait-pod-running COMPONENT=che-operator NAMESPACE=${ECLIPSE_CHE_NAMESPACE} + $(MAKE) create-checluster-cr + + # Printing logs + echo "[INFO] Waiting for Eclipse Che" + oc logs $$(oc get pods -o json -n ${ECLIPSE_CHE_NAMESPACE} | jq -r '.items[] | select(.metadata.name | test("che-operator-")).metadata.name') -n ${ECLIPSE_CHE_NAMESPACE} --all-containers -f + +uninstall: ## Uninstall Eclipse Che + $(K8S_CLI) patch checluster eclipse-che -n ${ECLIPSE_CHE_NAMESPACE} --type json -p='[{"op": "remove", "path": "/metadata/finalizers"}]' + $(K8S_CLI) delete checluster eclipse-che -n ${ECLIPSE_CHE_NAMESPACE} + $(KUSTOMIZE) build config/$(PLATFORM) | $(K8S_CLI) delete -f - + .PHONY: bundle -bundle: generate manifests kustomize ## Generate bundle manifests and metadata, then validate generated files. - if [ -z "$(channel)" ]; then - echo "[ERROR] 'channel' is not specified." - exit 1 - fi - - if [ -z "$(NO_INCREMENT)" ]; then - $(MAKE) increment-next-version - fi - +bundle: SHELL := /bin/bash +bundle: generate manifests download-kustomize download-operator-sdk ## Generate OLM bundle echo "[INFO] Updating OperatorHub bundle" - BUNDLE_PATH=$$($(MAKE) getBundlePath channel="$${channel}" -s) - NEW_CSV=$${BUNDLE_PATH}/manifests/che-operator.clusterserviceversion.yaml - newNextBundleVersion=$$(yq -r ".spec.version" "$${NEW_CSV}") - echo "[INFO] Creation new next bundle version: $${newNextBundleVersion}" + [[ -z "$(CHANNEL)" ]] && { echo [ERROR] CHANNEL not defined; exit 1; } + [[ "$(INCREMENT_BUNDLE_VERSION)" == false ]] || $(MAKE) _increment-bundle-version - createdAtOld=$$(yq -r ".metadata.annotations.createdAt" "$${NEW_CSV}") + BUNDLE_PATH=$$($(MAKE) bundle-path) + CSV_PATH=$$($(MAKE) csv-path) + NEXT_BUNDLE_VERSION=$$($(MAKE) bundle-version) + NEXT_BUNDLE_CREATION_DATE=$$(yq -r ".metadata.annotations.createdAt" "$${CSV_PATH}") - BUNDLE_PACKAGE=$$($(MAKE) getPackageName) - BUNDLE_DIR="bundle/"$${channel}"/$${BUNDLE_PACKAGE}" - GENERATED_CSV_NAME=$${BUNDLE_PACKAGE}.clusterserviceversion.yaml - DESIRED_CSV_NAME=che-operator.clusterserviceversion.yaml - GENERATED_CRD_NAME=org.eclipse.che_checlusters.yaml - DESIRED_CRD_NAME=org_v1_che_crd.yaml + # Build default clusterserviceversion file + $(OPERATOR_SDK) generate kustomize manifests - $(OPERATOR_SDK_BINARY) generate kustomize manifests -q - cd config/manager && $(KUSTOMIZE) edit set image quay.io/eclipse/che-operator:next=$(IMG) && cd ../.. - $(KUSTOMIZE) build config/platforms/openshift | \ - $(OPERATOR_SDK_BINARY) generate bundle \ + $(KUSTOMIZE) build config/openshift/olm | \ + $(OPERATOR_SDK) generate bundle \ --quiet \ --overwrite \ - --version $${newNextBundleVersion} \ - --package $${BUNDLE_PACKAGE} \ - --output-dir $${BUNDLE_DIR} \ - --channels $(channel) \ - --default-channel $(channel) + --version $${NEXT_BUNDLE_VERSION} \ + --package $(ECLIPSE_CHE_PACKAGE_NAME) \ + --output-dir $${BUNDLE_PATH} \ + --channels $(CHANNEL) \ + --default-channel $(CHANNEL) - # Copy bundle.Dockerfile to the bundle dir and patch paths - mv bundle.Dockerfile $${BUNDLE_DIR} - sed -i 's|bundle/next/eclipse-che-preview-openshift/||' $${BUNDLE_DIR}/bundle.Dockerfile + # Remove service from the bundle since OLM create that itself + rm $${BUNDLE_PATH}/manifests/che-operator-service_v1_service.yaml - # Add specific labels - printf "\nLABEL com.redhat.openshift.versions=\"v4.8\"" >> $${BUNDLE_DIR}/bundle.Dockerfile + # Rename clusterserviceversion file + mv $${BUNDLE_PATH}/manifests/$(ECLIPSE_CHE_PACKAGE_NAME).clusterserviceversion.yaml $${CSV_PATH} - cd $${BUNDLE_DIR}/manifests; - mv $${GENERATED_CSV_NAME} $${DESIRED_CSV_NAME} - mv $${GENERATED_CRD_NAME} $${DESIRED_CRD_NAME} - cd $(mkfile_dir) - - $(OPERATOR_SDK_BINARY) bundle validate ./$${BUNDLE_DIR} - - containerImage=$$(sed -n 's|^ *image: *\([^ ]*/che-operator:[^ ]*\) *|\1|p' $${NEW_CSV}) - echo "[INFO] Updating new package version fields:" - echo "[INFO] - containerImage => $${containerImage}" - sed -e "s|containerImage:.*$$|containerImage: $${containerImage}|" "$${NEW_CSV}" > "$${NEW_CSV}.new" - mv "$${NEW_CSV}.new" "$${NEW_CSV}" - - if [ "$(NO_DATE_UPDATE)" = true ]; then - echo "[INFO] - createdAt => $${createdAtOld}" - sed -e "s/createdAt:.*$$/createdAt: \"$${createdAtOld}\"/" "$${NEW_CSV}" > "$${NEW_CSV}.new" - mv "$${NEW_CSV}.new" "$${NEW_CSV}" + # Rollback creation date if version is not incremented + if [[ "$(INCREMENT_BUNDLE_VERSION)" == false ]]; then + sed -i "s/createdAt:.*$$/createdAt: \"$${NEXT_BUNDLE_CREATION_DATE}\"/" "$${CSV_PATH}" fi - CRD="$${BUNDLE_PATH}/manifests/org_v1_che_crd.yaml" - yq -riY '.spec.preserveUnknownFields = false' $${CRD} - - if [ -n "$(TAG)" ]; then - echo "[INFO] Set tags in next OLM files" - sed -ri "s/(.*:\s?)$(RELEASE)([^-])?$$/\1$(TAG)\2/" "$${NEW_CSV}" - fi - - # Remove roles for openshift bundle - YAML_CONTENT=$$(cat "$${NEW_CSV}") - clusterPermLength=$$(echo "$${YAML_CONTENT}" | yq -r ".spec.install.spec.clusterPermissions[0].rules | length") - i=0 - while [ "$${i}" -lt "$${clusterPermLength}" ]; do - apiGroupLength=$$(echo "$${YAML_CONTENT}" | yq -r '.spec.install.spec.clusterPermissions[0].rules['$${i}'].apiGroups | length') - if [ "$${apiGroupLength}" -gt 0 ]; then - j=0 - while [ "$${j}" -lt "$${apiGroupLength}" ]; do - apiGroup=$$(echo "$${YAML_CONTENT}" | yq -r '.spec.install.spec.clusterPermissions[0].rules['$${i}'].apiGroups['$${j}']') - case $${apiGroup} in cert-manager.io) - YAML_CONTENT=$$(echo "$${YAML_CONTENT}" | yq -rY 'del(.spec.install.spec.clusterPermissions[0].rules['$${i}'])' ) - j=$$((j-1)) - i=$$((i-1)) - break - ;; - esac; - j=$$((i+1)) - done - fi - i=$$((i+1)) - done - echo "$${YAML_CONTENT}" > "$${NEW_CSV}" - - # Removes che-tls-secret-creator - index=0 - while [ $${index} -le 30 ] - do - if [ $$(cat $${NEW_CSV} | yq -r '.spec.install.spec.deployments[0].spec.template.spec.containers[0].env['$${index}'].name') = "RELATED_IMAGE_che_tls_secrets_creation_job" ]; then - yq -rYSi 'del(.spec.install.spec.deployments[0].spec.template.spec.containers[0].env['$${index}'])' $${NEW_CSV} - break - fi - index=$$((index+1)) - done - - # Fix CSV - echo "[INFO] Fix CSV" - fixedSample=$$(yq -r ".metadata.annotations[\"alm-examples\"] | \ - fromjson | \ - del( .[] | select(.kind == \"CheCluster\") | .spec.k8s)" $${NEW_CSV} | sed -r 's/"/\\"/g') - - yq -riY ".metadata.annotations[\"alm-examples\"] = \"$${fixedSample}\"" $${NEW_CSV} - - # set `app.kubernetes.io/managed-by` label - yq -riSY '(.spec.install.spec.deployments[0].spec.template.metadata.labels."app.kubernetes.io/managed-by") = "olm"' "$${NEW_CSV}" - - # set Pod Security Context Posture - yq -riSY '(.spec.install.spec.deployments[0].spec.template.spec."hostIPC") = false' "$${NEW_CSV}" - yq -riSY '(.spec.install.spec.deployments[0].spec.template.spec."hostNetwork") = false' "$${NEW_CSV}" - yq -riSY '(.spec.install.spec.deployments[0].spec.template.spec."hostPID") = false' "$${NEW_CSV}" - yq -riSY '(.spec.install.spec.deployments[0].spec.template.spec.containers[0].securityContext."allowPrivilegeEscalation") = false' "$${NEW_CSV}" - yq -riSY '(.spec.install.spec.deployments[0].spec.template.spec.containers[0].securityContext."runAsNonRoot") = true' "$${NEW_CSV}" + # Copy bundle.Dockerfile to the bundle dir + # Update paths (since it is created in the root of the project) and labels + mv bundle.Dockerfile $${BUNDLE_PATH} + sed -i 's|$(PROJECT_DIR)/bundle/$(CHANNEL)/eclipse-che-preview-openshift/||' $${BUNDLE_PATH}/bundle.Dockerfile + printf "\nLABEL com.redhat.openshift.versions=\"v4.8\"" >> $${BUNDLE_PATH}/bundle.Dockerfile + # Update annotations.yaml correspondingly to bundle.Dockerfile printf "\n com.redhat.openshift.versions: \"v4.8\"" >> $${BUNDLE_PATH}/metadata/annotations.yaml # Base cluster service version file has got correctly sorted CRDs. # They are sorted with help of annotation markers in the api type files ("api/v1" folder). # Example such annotation: +operator-sdk:csv:customresourcedefinitions:order=0 - # Let's copy this sorted CRDs to the bundle cluster service version file. - BASE_CSV="config/manifests/bases/che-operator.clusterserviceversion.yaml" - CRD_API=$$(yq -c '.spec.customresourcedefinitions.owned' $${BASE_CSV}) - yq -riSY ".spec.customresourcedefinitions.owned = $$CRD_API" "$${NEW_CSV}" - yq -riSY "del(.spec.customresourcedefinitions.owned[] | select(.version == \"v2alpha1\"))" "$${NEW_CSV}" + # Copy this sorted CRDs to the bundle clusterserviceversion file. + CRDS_OWNED=$$(yq '.spec.customresourcedefinitions.owned' "$(PROJECT_DIR)/config/manifests/bases/che-operator.clusterserviceversion.yaml") + yq -riSY ".spec.customresourcedefinitions.owned = $${CRDS_OWNED}" "$${CSV_PATH}" - # Format code. - yq -rY "." "$${NEW_CSV}" > "$${NEW_CSV}.old" - mv "$${NEW_CSV}.old" "$${NEW_CSV}" + # Kustomize won't set default values + # Update deployment explicitly + yq -riSY '(.spec.install.spec.deployments[0].spec.template.spec."hostIPC") = false' "$${CSV_PATH}" + yq -riSY '(.spec.install.spec.deployments[0].spec.template.spec."hostNetwork") = false' "$${CSV_PATH}" + yq -riSY '(.spec.install.spec.deployments[0].spec.template.spec."hostPID") = false' "$${CSV_PATH}" + yq -riSY '(.spec.install.spec.deployments[0].spec.template.spec.containers[0].securityContext."allowPrivilegeEscalation") = false' "$${CSV_PATH}" + yq -riSY '(.spec.install.spec.deployments[0].spec.template.spec.containers[0].securityContext."runAsNonRoot") = true' "$${CSV_PATH}" - $(MAKE) add-license $$(find $${BUNDLE_PATH} -name "*.yaml") - $(MAKE) add-license $${BASE_CSV} + # Fix examples by removing some special characters + FIXED_ALM_EXAMPLES=$$(yq -r '.metadata.annotations["alm-examples"]' $${CSV_PATH} | sed -r 's/"/\\"/g') + yq -riY ".metadata.annotations[\"alm-examples\"] = \"$${FIXED_ALM_EXAMPLES}\"" $${CSV_PATH} -getPackageName: - echo "eclipse-che-preview-openshift" + # Update image + yq -riY '.metadata.annotations.containerImage = "'$(IMG)'"' $${CSV_PATH} + yq -riY '.spec.install.spec.deployments[0].spec.template.spec.containers[0].image = "'$(IMG)'"' $${CSV_PATH} -getBundlePath: - if [ -z "$(channel)" ]; then - echo "[ERROR] 'channel' is not specified" - exit 1 - fi - PACKAGE_NAME=$$($(MAKE) getPackageName) - echo "$(PROJECT_DIR)/bundle/$(channel)/$${PACKAGE_NAME}" + # Format file + yq -riY "." "$${BUNDLE_PATH}/manifests/org.eclipse.che_checlusters.yaml" -increment-next-version: - if [ -z "$(channel)" ]; then - echo "[ERROR] 'channel' is not specified" - exit 1 - fi + $(MAKE) license $$(find $${BUNDLE_PATH} -name "*.yaml") - BUNDLE_PATH=$$($(MAKE) getBundlePath channel="$(channel)" -s) - OPM_BUNDLE_MANIFESTS_DIR="$${BUNDLE_PATH}/manifests" - CSV="$${OPM_BUNDLE_MANIFESTS_DIR}/che-operator.clusterserviceversion.yaml" - - currentNextVersion=$$(yq -r ".spec.version" "$${CSV}") - echo "[INFO] Current next version: $${currentNextVersion}" - - incrementPart=$$($(MAKE) get-next-version-increment nextVersion="$${currentNextVersion}" -s) - - PACKAGE_NAME=$$($(MAKE) getPackageName) - - CLUSTER_SERVICE_VERSION=$$($(MAKE) get-current-stable-version) - STABLE_PACKAGE_VERSION=$$(echo "$${CLUSTER_SERVICE_VERSION}" | sed -e "s/$${PACKAGE_NAME}.v//") - echo "[INFO] Current stable package version: $${STABLE_PACKAGE_VERSION}" - - # Parse stable version parts - majorAndMinor=$${STABLE_PACKAGE_VERSION%.*} - STABLE_MINOR_VERSION=$${majorAndMinor#*.} - STABLE_MAJOR_VERSION=$${majorAndMinor%.*} - - STABLE_MINOR_VERSION=$$(($$STABLE_MINOR_VERSION+1)) - echo "$${STABLE_MINOR_VERSION}" - - incrementPart=$$((incrementPart+1)) - newVersion="$${STABLE_MAJOR_VERSION}.$${STABLE_MINOR_VERSION}.0-$${incrementPart}.$(channel)" - - echo "[INFO] Set up next version: $${newVersion}" - yq -rY "(.spec.version) = \"$${newVersion}\" | (.metadata.name) = \"$${PACKAGE_NAME}.v$${newVersion}\"" "$${CSV}" > "$${CSV}.old" - mv "$${CSV}.old" "$${CSV}" - -get-current-stable-version: - STABLE_BUNDLE_PATH=$$($(MAKE) getBundlePath channel="stable" -s) - LAST_STABLE_CSV="$${STABLE_BUNDLE_PATH}/manifests/che-operator.clusterserviceversion.yaml" - - lastStableVersion=$$(yq -r ".spec.version" "$${LAST_STABLE_CSV}") - echo "$${lastStableVersion}" - -get-next-version-increment: - if [ -z $(nextVersion) ]; then - echo "[ERROR] Provide next version to parse" - exit 1 - fi - - versionWithoutNext="$${nextVersion%.next*}" - version="$${versionWithoutNext%-*}" - incrementPart="$${versionWithoutNext#*-}" - echo "$${incrementPart}" - -update-resources: SHELL := /bin/bash -update-resources: check-requirements update-resource-images update-roles - $(MAKE) bundle channel=next - $(MAKE) update-helmcharts HELM_FOLDER=next - -update-helmcharts: SHELL := /bin/bash -update-helmcharts: add-license-download check-requirements - helmFolder=$(HELM_FOLDER) - if [ -z "$${helmFolder}" ]; then - helmFolder="next" - fi - HELMCHARTS_TEMPLATES="helmcharts/$${helmFolder}/templates" - HELMCHARTS_CRDS="helmcharts/$${helmFolder}/crds" - - echo "[INFO] Update Helm templates $${HELMCHARTS_TEMPLATES}" - cp config/manager/manager.yaml $${HELMCHARTS_TEMPLATES} - cp config/rbac/cluster_role.yaml $${HELMCHARTS_TEMPLATES} - cp config/rbac/cluster_rolebinding.yaml $${HELMCHARTS_TEMPLATES} - cp config/rbac/service_account.yaml $${HELMCHARTS_TEMPLATES} - cp config/rbac/role.yaml $${HELMCHARTS_TEMPLATES} - cp config/rbac/role_binding.yaml $${HELMCHARTS_TEMPLATES} - cp config/samples/org.eclipse.che_v1_checluster.yaml $${HELMCHARTS_TEMPLATES} - - echo "[INFO] Update helm CRDs $${HELMCHARTS_CRDS}" - cp config/crd/bases/org_v1_che_crd.yaml $${HELMCHARTS_CRDS} - - yq -riY ".metadata.namespace = \"$(ECLIPSE_CHE_NAMESPACE)\"" $${HELMCHARTS_TEMPLATES}/manager.yaml - yq -riY ".metadata.namespace = \"$(ECLIPSE_CHE_NAMESPACE)\"" $${HELMCHARTS_TEMPLATES}/service_account.yaml - yq -riY ".metadata.namespace = \"$(ECLIPSE_CHE_NAMESPACE)\"" $${HELMCHARTS_TEMPLATES}/role.yaml - yq -riY ".metadata.namespace = \"$(ECLIPSE_CHE_NAMESPACE)\"" $${HELMCHARTS_TEMPLATES}/role_binding.yaml - yq -riY ".subjects[0].namespace = \"$(ECLIPSE_CHE_NAMESPACE)\"" $${HELMCHARTS_TEMPLATES}/cluster_rolebinding.yaml - - if [ $${helmFolder} == "stable" ]; then - chartYaml="helmcharts/$${helmFolder}/Chart.yaml" - - EXAMPLE_FILES=( - $${HELMCHARTS_TEMPLATES}/org.eclipse.che_v1_checluster.yaml - ) - - CRDS="" - for exampleFile in "$${EXAMPLE_FILES[@]}"; do - example=$$(cat $${exampleFile} | yq -rY ". | (.metadata.namespace = \"$(ECLIPSE_CHE_NAMESPACE)\") | [.]") - CRDS=$${CRDS}$${example}$$'\n' - done - - yq -rYi --arg examples "$${CRDS}" ".annotations.\"artifacthub.io/crdsExamples\" = \$$examples" $${chartYaml} - rm -rf $${HELMCHARTS_TEMPLATES}/org.eclipse.che_v1_checluster.yaml - else - # Set references to values - yq -riY ".metadata.namespace = \"$(ECLIPSE_CHE_NAMESPACE)\"" $${HELMCHARTS_TEMPLATES}/org.eclipse.che_v1_checluster.yaml - yq -riY ".spec.k8s.ingressDomain |= \"{{ .Values.k8s.ingressDomain }}\"" $${HELMCHARTS_TEMPLATES}/org.eclipse.che_v1_checluster.yaml - fi - - $(MAKE) add-license $$(find ./helmcharts -name "*.yaml") - -check-requirements: - . olm/check-yq.sh - - DOCKER=$$(command -v docker || true) - if [[ ! -x $$DOCKER ]]; then - echo "[ERROR] "docker" is not installed." - exit 1 - fi - - SKOPEO=$$(command -v skopeo || true) - if [[ ! -x $$SKOPEO ]]; then - echo "[ERROR] "scopeo" is not installed." - exit 1 - fi - - OPERATOR_SDK_BINARY=$(OPERATOR_SDK_BINARY) - if [ -z "$${OPERATOR_SDK_BINARY}" ]; then - OPERATOR_SDK_BINARY=$$(command -v operator-sdk) - if [[ ! -x "$${OPERATOR_SDK_BINARY}" ]]; then - echo "[ERROR] operator-sdk is not installed." - exit 1 - fi - fi - - operatorVersion=$$($${OPERATOR_SDK_BINARY} version) - REQUIRED_OPERATOR_SDK=$$(yq -r ".\"operator-sdk\"" "REQUIREMENTS") - case "$$operatorVersion" in - *$$REQUIRED_OPERATOR_SDK*) ;; - *) echo "[ERROR] operator-sdk $${REQUIRED_OPERATOR_SDK} is required"; exit 1 ;; - esac - -update-deployment-yaml-images: add-license-download - if [ -z $(UBI8_MINIMAL_IMAGE) ]; then - echo "[ERROR] Define required arguments: `UBI8_MINIMAL_IMAGE`" - exit 1 - fi - yq -riY "( .spec.template.spec.containers[] | select(.name == \"che-operator\").env[] | select(.name == \"RELATED_IMAGE_pvc_jobs\") | .value ) = \"$(UBI8_MINIMAL_IMAGE)\"" $(OPERATOR_YAML) - - $(MAKE) add-license $(OPERATOR_YAML) - -update-dockerfile-image: - if [ -z $(UBI8_MINIMAL_IMAGE) ]; then - echo "[ERROR] Define `UBI8_MINIMAL_IMAGE` argument" - fi - DOCKERFILE="Dockerfile" - sed -i 's|registry.access.redhat.com/ubi8-minimal:[^\s]* |'${UBI8_MINIMAL_IMAGE}' |g' $${DOCKERFILE} - -update-resource-images: - # Detect newer images - echo "[INFO] Check update some base images..." - ubiMinimal8Version=$$(skopeo --override-os linux inspect docker://registry.access.redhat.com/ubi8-minimal:latest | jq -r '.Labels.version') - ubiMinimal8Release=$$(skopeo --override-os linux inspect docker://registry.access.redhat.com/ubi8-minimal:latest | jq -r '.Labels.release') - UBI8_MINIMAL_IMAGE="registry.access.redhat.com/ubi8-minimal:$${ubiMinimal8Version}-$${ubiMinimal8Release}" - skopeo --override-os linux inspect docker://$${UBI8_MINIMAL_IMAGE} > /dev/null - - echo "[INFO] UBI base image : $${UBI8_MINIMAL_IMAGE}" - - # Update operator deployment images. - $(MAKE) update-deployment-yaml-images \ - UBI8_MINIMAL_IMAGE="$${UBI8_MINIMAL_IMAGE}" \ - - # Update che-operator Dockerfile - $(MAKE) update-dockerfile-image UBI8_MINIMAL_IMAGE="$${UBI8_MINIMAL_IMAGE}" + $(OPERATOR_SDK) bundle validate $${BUNDLE_PATH} .PHONY: bundle-build -bundle-build: ## Build the bundle image. - if [ -z "$(channel)" ]; then - echo "[ERROR] 'channel' is not specified" - exit 1 - fi +bundle-build: SHELL := /bin/bash +bundle-build: ## Build a bundle image + [[ -z "$(CHANNEL)" ]] && { echo [ERROR] CHANNEL not defined; exit 1; } + [[ -z "$(BUNDLE_IMG)" ]] && { echo [ERROR] BUNDLE_IMG not defined; exit 1; } - BUNDLE_PACKAGE=$$($(MAKE) getPackageName) - BUNDLE_DIR="bundle/$(channel)/$${BUNDLE_PACKAGE}" - cd $${BUNDLE_DIR} - docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . - cd ../../.. + BUNDLE_DIR="$(PROJECT_DIR)/bundle/$(CHANNEL)/$(ECLIPSE_CHE_PACKAGE_NAME)" + pushd $${BUNDLE_DIR} + $(IMAGE_TOOL) build -f bundle.Dockerfile -t $(BUNDLE_IMG) . + popd .PHONY: bundle-push -bundle-push: ## Push the bundle image. +bundle-push: SHELL := /bin/bash +bundle-push: ## Push a bundle image + [[ -z "$(BUNDLE_IMG)" ]] && { echo [ERROR] BUNDLE_IMG not defined; exit 1; } $(MAKE) docker-push IMG=$(BUNDLE_IMG) -.PHONY: opm -OPM = ./bin/opm -opm: ## Download opm locally if necessary. -ifeq (,$(wildcard $(OPM))) -ifeq (,$(shell which opm 2>/dev/null)) - @{ \ - set -e ;\ - mkdir -p $(dir $(OPM)) ;\ - OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ - curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.15.2/$${OS}-$${ARCH}-opm ;\ - chmod +x $(OPM) ;\ - } -else -OPM = $(shell which opm) -endif -endif - -# A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=quay.io/eclipse/operator-bundle:v0.1.0,quay.io/eclipse/operator-bundle:v0.2.0). -# These images MUST exist in a registry and be pull-able. -BUNDLE_IMGS ?= $(BUNDLE_IMG) - -# The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=quay.io/eclipse/operator-catalog:v0.2.0). -CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION) - -# Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image. -ifneq ($(origin CATALOG_BASE_IMG), undefined) -FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) -endif - # Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'. # This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see: # https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator .PHONY: catalog-build -catalog-build: opm ## Build a catalog image. +catalog-build: SHELL := /bin/bash +catalog-build: download-opm ## Build a catalog image + [[ -z "$(BUNDLE_IMG)" ]] && { echo [ERROR] BUNDLE_IMG not defined; exit 1; } + [[ -z "$(CATALOG_IMG)" ]] && { echo [ERROR] CATALOG_IMG not defined; exit 1; } + $(OPM) index add \ --build-tool $(IMAGE_TOOL) \ - --bundles $(BUNDLE_IMGS) \ + --bundles $(BUNDLE_IMG) \ --tag $(CATALOG_IMG) \ --pull-tool $(IMAGE_TOOL) \ --binary-image=quay.io/operator-framework/upstream-opm-builder:v1.15.2 \ --mode semver $(FROM_INDEX_OPT) -# Push the catalog image. .PHONY: catalog-push -catalog-push: ## Push a catalog image. +catalog-push: SHELL := /bin/bash +catalog-push: ## Push a catalog image + [[ -z "$(CATALOG_IMG)" ]] && { echo [ERROR] CATALOG_IMG not defined; exit 1; } $(MAKE) docker-push IMG=$(CATALOG_IMG) -chectl-templ: SHELL := /bin/bash -chectl-templ: - if [ -z "$(TARGET)" ]; then - echo "[ERROR] Specify templates target location, using argument `TARGET`" - exit 1 - fi - if [ -z "$(SRC)" ]; then - SRC=$$(pwd) - else - SRC=$(SRC) - fi +##@ Utilities - mkdir -p $(TARGET) +bundle-path: SHELL := /bin/bash +bundle-path: ## Prints path to a bundle directory for a given channel + [[ -z "$(CHANNEL)" ]] && { echo [ERROR] CHANNEL not defined; exit 1; } + echo "$(PROJECT_DIR)/bundle/$(CHANNEL)/$(ECLIPSE_CHE_PACKAGE_NAME)" - cp -f "$${SRC}/config/manager/manager.yaml" "$(TARGET)/operator.yaml" - cp -rf "$${SRC}/config/crd/bases/" "$(TARGET)/crds/" - cp -f "$${SRC}/config/rbac/role.yaml" "$(TARGET)/" - cp -f "$${SRC}/config/rbac/role_binding.yaml" "$(TARGET)/" - cp -f "$${SRC}/config/rbac/cluster_role.yaml" "$(TARGET)/" - cp -f "$${SRC}/config/rbac/cluster_rolebinding.yaml" "$(TARGET)/" - cp -f "$${SRC}/config/rbac/service_account.yaml" "$(TARGET)/" - cp -f "$${SRC}/$(ECLIPSE_CHE_CR)" "$(TARGET)/crds/org_v1_che_cr.yaml" +csv-path: SHELL := /bin/bash +csv-path: ## Prints path to a clusterserviceversion file for a given channel + [[ -z "$(CHANNEL)" ]] && { echo [ERROR] CHANNEL not defined; exit 1; } + BUNDLE_PATH=$$($(MAKE) bundle-path) + echo "$${BUNDLE_PATH}/manifests/che-operator.clusterserviceversion.yaml" - echo "[INFO] chectl template folder is ready: ${TARGET}" +bundle-version: SHELL := /bin/bash +bundle-version: ## Prints a bundle version for a given channel + [[ -z "$(CHANNEL)" ]] && { echo [ERROR] CHANNEL not defined; exit 1; } + CSV_PATH=$$($(MAKE) csv-path) + echo $$(yq -r ".spec.version" "$${CSV_PATH}") + +OPM ?= $(shell pwd)/bin/opm +download-opm: ## Download opm tool + command -v $(OPM) >/dev/null 2>&1 && exit + + OS=$(shell go env GOOS) + ARCH=$(shell go env GOARCH) + OPM_VERSION=$$(yq -r '.opm' $(PROJECT_DIR)/REQUIREMENTS) + + echo "[INFO] Downloading opm version: $$OPM_VERSION" + + curl -SLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/$${OPM_VERSION}/$${OS}-$${ARCH}-opm + chmod +x $(OPM) + +CONTROLLER_GEN = $(shell pwd)/bin/controller-gen +download-controller-gen: ## Download controller-gen tool + $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.4.1) + +KUSTOMIZE = $(shell pwd)/bin/kustomize +download-kustomize: ## Download kustomize tool + $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7) + +ADD_LICENSE = $(shell pwd)/bin/addlicense +download-addlicense: ## Download addlicense tool + $(call go-get-tool,$(ADD_LICENSE),github.com/google/addlicense@99ebc9c9db7bceb8623073e894533b978d7b7c8a) + +OPERATOR_SDK ?= $(shell pwd)/bin/operator-sdk +download-operator-sdk: SHELL := /bin/bash +download-operator-sdk: ## Downloads operator sdk tool + command -v $(OPERATOR_SDK) >/dev/null 2>&1 && exit + + OS=$(shell go env GOOS) + ARCH=$(shell go env GOARCH) + OPERATOR_SDK_VERSION=$$(yq -r '."operator-sdk"' $(PROJECT_DIR)/REQUIREMENTS) + + [[ -z "$(DEST)" ]] && dest=$(OPERATOR_SDK) || dest=$(DEST)/$(OPERATOR_SDK) + echo "[INFO] Downloading operator-sdk version $$OPERATOR_SDK_VERSION into $${dest}" + + curl -SLo $${dest} https://github.com/operator-framework/operator-sdk/releases/download/$${OPERATOR_SDK_VERSION}/operator-sdk_$${OS}_$${ARCH} + chmod +x $${dest} + +validate-requirements: SHELL := /bin/bash +validate-requirements: ## Check if all required packages are installed + command -v yq >/dev/null 2>&1 || { echo "[ERROR] yq is not installed. See https://github.com/kislyuk/yq"; exit 1; } + command -v skopeo >/dev/null 2>&1 || { echo "[ERROR] skopeo is not installed."; exit 1; } + +# Set a new operator image for kustomize +_kustomize-operator-image: + cd config/manager + $(KUSTOMIZE) edit set image quay.io/eclipse/che-operator:next=$(IMG) + cd ../.. + +# Set a new version for the next channel +_increment-bundle-version: SHELL := /bin/bash +_increment-bundle-version: + echo "[INFO] Increment bundle version for the next channel" + + STABLE_BUNDLE_VERSION=$$($(MAKE) bundle-version CHANNEL=stable) + echo "[INFO] Current stable version: $${STABLE_BUNDLE_VERSION}" + + # Parse stable bundle version + STABLE_BUNDLE_MAJOR_AND_MINOR_VERSION=$${STABLE_BUNDLE_VERSION%.*} + STABLE_BUNDLE_MINOR_VERSION=$${STABLE_BUNDLE_MAJOR_AND_MINOR_VERSION#*.} + STABLE_BUNDLE_MAJOR_VERSION=$${STABLE_BUNDLE_MAJOR_AND_MINOR_VERSION%.*} + + NEXT_BUNDLE_VERSION=$$($(MAKE) bundle-version CHANNEL=next) + echo "[INFO] Current next version: $${NEXT_BUNDLE_VERSION}" + + # Parse next bundle version + NEXT_BUNDLE_VERSION_STIPPED_NEXT="$${NEXT_BUNDLE_VERSION%.next*}" + NEXT_BUNDLE_VERSION_INCRIMENT_PART="$${NEXT_BUNDLE_VERSION_STIPPED_NEXT#*-}" + + # Set a new next bundle version + NEW_NEXT_BUNDLE_VERSION="$${STABLE_BUNDLE_MAJOR_VERSION}.$$(($$STABLE_BUNDLE_MINOR_VERSION+1)).0-$$(($$NEXT_BUNDLE_VERSION_INCRIMENT_PART+1)).next" + + # Update csv + NEXT_CSV_PATH=$$($(MAKE) csv-path CHANNEL=next) + yq -riY "(.spec.version) = \"$${NEW_NEXT_BUNDLE_VERSION}\" | (.metadata.name) = \"$(ECLIPSE_CHE_PACKAGE_NAME).v$${NEW_NEXT_BUNDLE_VERSION}\"" $${NEXT_CSV_PATH} + + echo "[INFO] New next version: $${NEW_NEXT_BUNDLE_VERSION}" \ No newline at end of file diff --git a/PROJECT b/PROJECT index 0d58c0f9e..c1ee02aee 100644 --- a/PROJECT +++ b/PROJECT @@ -22,6 +22,9 @@ resources: domain: eclipse.che group: org kind: CheCluster - path: github.com/eclipse-che/che-operator/api/v2alpha1 - version: v2alpha1 + path: github.com/eclipse-che/che-operator/api/v2 + version: v2 + webhooks: + conversion: true + webhookVersion: v1 version: "3" diff --git a/README.md b/README.md index e4d77a43d..75e0bda3d 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,7 @@ Che operator is implemented using [operator framework](https://github.com/operat ## CheCluster custom resource -Che operator deploys Eclipse Che using configuration stored in the Kubernetes custom resource(CR). CR object structure defined in the code using `api/v1/checluster_types.go` file. Field name defined using the serialization tag `json`, for example `json:"openShiftoAuth"`. Che operator default CR sample is stored in the `config/samples/org.eclipse.che_v1_checluster.yaml`. This file should be directly modified if you want to apply new fields with default values, or in case of changing default values for existing fields. -Also, you can apply in the field comments Openshift UI annotations: to display some -interactive information about these fields on the Openshift UI. +Che operator deploys Eclipse Che using configuration stored in the Kubernetes custom resource(CR). CR object structure defined in the code using `api/v2/checluster_types.go` file. Field name defined using the serialization tag `json`. Che operator default CR sample is stored in the `config/samples/org_v2_checluster.yaml`. This file should be directly modified if you want to apply new fields with default values, or in case of changing default values for existing fields. Also, you can apply in the field comments Openshift UI annotations: to display some interactive information about these fields on the Openshift UI. For example: ```go @@ -60,7 +58,7 @@ che-operator MAKE file provides ability to install che-operator(VSCode task `Dep ```bash $ make deploy IMG=\"${IMAGE_REGISTRY_HOST}/${IMAGE_REGISTRY_USER_NAME}/che-operator:next\" -$ kubectl apply -f config/samples/org.eclipse.che_v1_checluster.yaml -n +$ kubectl apply -f config/samples/org_v2_checluster.yaml -n ``` Undeploy che-operator(VSCode task `UnDeploy che-operator`): @@ -89,7 +87,7 @@ Where: If you have changed Che operator deployment, roles, cluster roles, CRD or CR then you must use `--templates` flag to point chectl to modified Che operator templates. Use make command to prepare chectl templates folder: ```bash -$ make chectl-templ TARGET=/che-operator +$ make gen-chectl-tmpl TARGET= ``` Execute chectl: @@ -105,7 +103,7 @@ $ chectl server:deploy --installer operator -p --che-operator-image=$ 2. Update OLM files: ```bash -$ make update-resources -s +$ make update-dev-resources ``` 3. Build catalog source and bundle images: @@ -171,7 +169,7 @@ Go client grabs kubeconfig either from InClusterConfig or `~/.kube` locally. Mak Where: * `ECLIPSE-CHE-NAMESPACE` - namespace name to deploy Che operator into, default is `che` -* `CUSTOM_RESOURCE` - path to custom resource yaml, default is `./config/samples/org.eclipse.che_v1_checluster.yaml` +* `CUSTOM_RESOURCE` - path to custom resource yaml, default is `./config/samples/org_v2_checluster.yaml` Use VSCode debug configuration `Che Operator` to attach to the running process. @@ -238,7 +236,7 @@ $ make generate; make manifests ``` This command will update CRD files: - - `config/crd/bases/org_v1_che_crd.yaml` + - `config/crd/bases/org.eclipse.che_checlusters.yaml` CRD beta yamls should be used for back compatibility with Openshift 3. @@ -248,21 +246,21 @@ Sometimes, during development, you need to modify some YAML definitions in the ` - operator deployment `config/manager/manager.yaml` - operator roles/cluster roles permissions. They are defined like role/rolebinding or cluster role/rolebinding yamls in the `config` folder. - operator custom resource CR `config/crd/bases/org_v1_che_cr.yaml`. This file contains the default CheCluster sample. Also this file is the default OLM CheCluster sample. - - Che cluster custom resource definition `api/v1/checluster_types.go`. For example you want to fix some properties description or apply new Che type properties with default values. These changes affect CRD `config/crd/bases/org_v1_che_crd.yaml`. + - Che cluster custom resource definition `api/v1/checluster_types.go`. For example you want to fix some properties description or apply new Che type properties with default values. These changes affect CRD `config/crd/bases/org.eclipse.che_checlusters.yaml`. - add Openshift ui annotations for Che types properties (`api/v1/checluster_types.go`) to display information or interactive elements on the Openshift user interface. For all these cases it's a necessary to generate a new OLM bundle to make these changes working with OLM. Run the VSCode tasks `Update resources` or use the terminal: ```bash -$ make update-resources -s +$ make update-dev-resources ``` Every changes will be included to the `bundle` folder and will override all previous changes. OLM bundle changes should be committed to the pull request. -To update a bundle without version incrementation and time update you can use env variables `NO_DATE_UPDATE` and `NO_INCREMENT`. For example, during development you need to update bundle a lot of times with changed che-operator deployment or role, rolebinding and etc, but you don't want to increment the bundle version and time creation, when all desired changes were completed: +To update a bundle without version incrementation and time update: ```bash -$ make update-resources NO_DATE_UPDATE="true" NO_INCREMENT="true" -s +$ make update-dev-resources INCREMENT_BUNDLE_VERSION=false ``` ### Che operator PR checks diff --git a/REQUIREMENTS b/REQUIREMENTS index 51502beb1..ac94aba93 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -1 +1,2 @@ operator-sdk: v1.9.2 +opm: v1.15.1 diff --git a/api/checluster_conversion_from_test.go b/api/checluster_conversion_from_test.go new file mode 100644 index 000000000..7b288dd82 --- /dev/null +++ b/api/checluster_conversion_from_test.go @@ -0,0 +1,463 @@ +// +// Copyright (c) 2019-2022 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package org + +import ( + "testing" + + "k8s.io/apimachinery/pkg/api/resource" + + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + chev1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/utils/pointer" +) + +func TestConvertFromEmptyCheCluster(t *testing.T) { + checlusterv1 := &chev1.CheCluster{} + checlusterv2 := &chev2.CheCluster{} + + err := checlusterv1.ConvertFrom(checlusterv2) + assert.Nil(t, err) +} + +func TestConvertFromIngressOnK8s(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.Kubernetes) + + checlusterv2 := &chev2.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eclipse-che", + Namespace: "eclipse-che", + }, + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + Labels: map[string]string{"a": "b", "c": "d"}, + Annotations: map[string]string{"a": "b", "c": "d", "kubernetes.io/ingress.class": "nginx"}, + Domain: "Domain", + Hostname: "Hostname", + TlsSecretName: "tlsSecret", + }, + }, + } + + checlusterv1 := &chev1.CheCluster{} + err := checlusterv1.ConvertFrom(checlusterv2) + assert.Nil(t, err) + + assert.Equal(t, map[string]string{"a": "b", "c": "d"}, checlusterv1.Spec.Server.CheServerIngress.Annotations) + assert.Equal(t, "Domain", checlusterv1.Spec.K8s.IngressDomain) + assert.Equal(t, "nginx", checlusterv1.Spec.K8s.IngressClass) + assert.Equal(t, "Hostname", checlusterv1.Spec.Server.CheHost) + assert.Equal(t, "a=b,c=d", checlusterv1.Spec.Server.CheServerIngress.Labels) + assert.Equal(t, "tlsSecret", checlusterv1.Spec.K8s.TlsSecretName) +} + +func TestConvertFromIngressOnOpenShift(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + + checlusterv2 := &chev2.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eclipse-che", + Namespace: "eclipse-che", + }, + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + Labels: map[string]string{"a": "b", "c": "d"}, + Annotations: map[string]string{"a": "b", "c": "d"}, + Domain: "Domain", + Hostname: "Hostname", + TlsSecretName: "tlsSecret", + }, + }, + } + + checlusterv1 := &chev1.CheCluster{} + err := checlusterv1.ConvertFrom(checlusterv2) + assert.Nil(t, err) + + assert.Equal(t, map[string]string{"a": "b", "c": "d"}, checlusterv1.Spec.Server.CheServerRoute.Annotations) + assert.Equal(t, "Domain", checlusterv1.Spec.Server.CheServerRoute.Domain) + assert.Equal(t, "Hostname", checlusterv1.Spec.Server.CheHost) + assert.Equal(t, "a=b,c=d", checlusterv1.Spec.Server.CheServerRoute.Labels) + assert.Equal(t, "tlsSecret", checlusterv1.Spec.Server.CheHostTLSSecret) +} + +func TestConvertFrom(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.Kubernetes) + + checlusterv2 := &chev2.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eclipse-che", + Namespace: "eclipse-che", + }, + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + Metrics: chev2.ServerMetrics{ + Enable: true, + }, + Database: chev2.Database{ + ExternalDb: true, + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Image: "DatabaseImage", + ImagePullPolicy: corev1.PullAlways, + Resources: chev2.ResourceRequirements{ + Requests: chev2.ResourceList{ + Memory: resource.MustParse("128Mi"), + Cpu: resource.MustParse("1"), + }, + Limits: chev2.ResourceList{ + Memory: resource.MustParse("228Mi"), + Cpu: resource.MustParse("2"), + }, + }, + }, + }, + SecurityContext: chev2.PodSecurityContext{ + RunAsUser: pointer.Int64Ptr(64), + FsGroup: pointer.Int64Ptr(65), + }, + }, + PostgresHostName: "PostgresHostName", + PostgresPort: "PostgresPort", + PostgresDb: "PostgresDb", + CredentialsSecretName: "DatabaseCredentialsSecretName", + Pvc: chev2.PVC{ + ClaimSize: "DatabaseClaimSize", + StorageClass: "DatabaseStorageClass", + }, + }, + PluginRegistry: chev2.PluginRegistry{ + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Image: "PluginRegistryImage", + ImagePullPolicy: corev1.PullAlways, + Resources: chev2.ResourceRequirements{ + Requests: chev2.ResourceList{ + Memory: resource.MustParse("128Mi"), + Cpu: resource.MustParse("1"), + }, + Limits: chev2.ResourceList{ + Memory: resource.MustParse("228Mi"), + Cpu: resource.MustParse("2"), + }, + }, + }, + }, + SecurityContext: chev2.PodSecurityContext{ + RunAsUser: pointer.Int64Ptr(64), + FsGroup: pointer.Int64Ptr(65), + }, + }, + DisableInternalRegistry: true, + ExternalPluginRegistries: []chev2.ExternalPluginRegistry{ + { + Url: "ExternalPluginRegistries_1", + }, + { + Url: "ExternalPluginRegistries_2", + }, + }, + }, + DevfileRegistry: chev2.DevfileRegistry{ + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Image: "DevfileRegistryImage", + ImagePullPolicy: corev1.PullAlways, + Resources: chev2.ResourceRequirements{ + Requests: chev2.ResourceList{ + Memory: resource.MustParse("128Mi"), + Cpu: resource.MustParse("1"), + }, + Limits: chev2.ResourceList{ + Memory: resource.MustParse("228Mi"), + Cpu: resource.MustParse("2"), + }, + }, + }, + }, + SecurityContext: chev2.PodSecurityContext{ + RunAsUser: pointer.Int64Ptr(64), + FsGroup: pointer.Int64Ptr(65), + }, + }, + DisableInternalRegistry: true, + ExternalDevfileRegistries: []chev2.ExternalDevfileRegistry{ + { + Url: "ExternalDevfileRegistries", + }, + }, + }, + Dashboard: chev2.Dashboard{ + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Image: "DashboardImage", + ImagePullPolicy: corev1.PullAlways, + Resources: chev2.ResourceRequirements{ + Requests: chev2.ResourceList{ + Memory: resource.MustParse("128Mi"), + Cpu: resource.MustParse("1"), + }, + Limits: chev2.ResourceList{ + Memory: resource.MustParse("228Mi"), + Cpu: resource.MustParse("2"), + }, + }, + }, + }, + SecurityContext: chev2.PodSecurityContext{ + RunAsUser: pointer.Int64Ptr(64), + FsGroup: pointer.Int64Ptr(65), + }, + }, + HeaderMessage: chev2.DashboardHeaderMessage{ + Show: true, + Text: "DashboardWarning", + }, + }, + ImagePuller: chev2.ImagePuller{ + Enable: true, + }, + CheServer: chev2.CheServer{ + ExtraProperties: map[string]string{"a": "b", "c": "d"}, + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Image: "ServerImage:ServerTag", + ImagePullPolicy: corev1.PullAlways, + Resources: chev2.ResourceRequirements{ + Requests: chev2.ResourceList{ + Memory: resource.MustParse("128Mi"), + Cpu: resource.MustParse("1"), + }, + Limits: chev2.ResourceList{ + Memory: resource.MustParse("228Mi"), + Cpu: resource.MustParse("2"), + }, + }, + }, + }, + SecurityContext: chev2.PodSecurityContext{ + RunAsUser: pointer.Int64Ptr(64), + FsGroup: pointer.Int64Ptr(65), + }, + }, + LogLevel: "LogLevel", + Debug: pointer.BoolPtr(true), + ClusterRoles: []string{"ClusterRoles_1", "ClusterRoles_2"}, + Proxy: chev2.Proxy{ + Url: "ProxyUrl", + Port: "ProxyPort", + NonProxyHosts: []string{"NonProxyHosts_1", "NonProxyHosts_2"}, + CredentialsSecretName: "ProxyCredentialsSecretName", + }, + }, + DevWorkspace: chev2.DevWorkspace{ + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Image: "DevWorkspaceImage", + }, + }, + }, + RunningLimit: "RunningLimit", + }, + }, + Networking: chev2.CheClusterSpecNetworking{ + Auth: chev2.Auth{ + IdentityProviderURL: "IdentityProviderURL", + OAuthClientName: "OAuthClientName", + OAuthSecret: "OAuthSecret", + Gateway: chev2.Gateway{ + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: "gateway", + Image: "GatewayImage", + }, + { + Name: "configbump", + Image: "ConfigSidecarImage", + }, + { + Name: "oauth-proxy", + Image: "AuthenticationSidecarImage", + }, + { + Name: "kube-rbac-proxy", + Image: "AuthorizationSidecarImage", + }, + }, + }, + ConfigLabels: map[string]string{"a": "b", "c": "d"}, + }, + }, + }, + DevEnvironments: chev2.CheClusterDevEnvironments{ + DefaultNamespace: chev2.DefaultNamespace{ + Template: "WorkspaceNamespaceName", + }, + TrustedCerts: chev2.TrustedCerts{ + GitTrustedCertsConfigMapName: "che-git-self-signed-cert", + }, + Storage: chev2.WorkspaceStorage{ + Pvc: chev2.PVC{ + ClaimSize: "StorageClaimSize", + StorageClass: "StorageClass", + }, + PvcStrategy: "PvcStrategy", + }, + DefaultPlugins: []chev2.WorkspaceDefaultPlugins{ + { + Editor: "Editor", + Plugins: []string{"Plugins_1", "Plugins_2"}, + }, + }, + NodeSelector: map[string]string{"a": "b", "c": "d"}, + Tolerations: []corev1.Toleration{{ + Key: "Key", + Operator: "Operator", + Value: "Value", + Effect: "Effect", + }}, + }, + ContainerRegistry: chev2.CheClusterContainerRegistry{ + Hostname: "AirGapContainerRegistryHostname", + Organization: "AirGapContainerRegistryOrganization", + }, + }, + Status: chev2.CheClusterStatus{ + CheVersion: "CheVersion", + CheURL: "CheURL", + DevfileRegistryURL: "DevfileRegistryURL", + PluginRegistryURL: "PluginRegistryURL", + ChePhase: "Active", + Message: "Message", + Reason: "Reason", + PostgresVersion: "PostgresVersion", + }, + } + + checlusterv1 := &chev1.CheCluster{} + err := checlusterv1.ConvertFrom(checlusterv2) + assert.Nil(t, err) + + assert.Equal(t, checlusterv1.ObjectMeta.Name, "eclipse-che") + assert.Equal(t, checlusterv1.ObjectMeta.Namespace, "eclipse-che") + + assert.Equal(t, checlusterv1.Status.CheClusterRunning, "Available") + assert.Equal(t, checlusterv1.Status.CheURL, "CheURL") + assert.Equal(t, checlusterv1.Status.CheVersion, "CheVersion") + assert.Equal(t, checlusterv1.Status.DevfileRegistryURL, "DevfileRegistryURL") + assert.Equal(t, checlusterv1.Status.Message, "Message") + assert.Equal(t, checlusterv1.Status.PluginRegistryURL, "PluginRegistryURL") + assert.Equal(t, checlusterv1.Status.Reason, "Reason") + assert.Equal(t, checlusterv1.Status.GitServerTLSCertificateConfigMapName, "che-git-self-signed-cert") + + assert.Equal(t, checlusterv1.Spec.Auth.GatewayAuthenticationSidecarImage, "AuthenticationSidecarImage") + assert.Equal(t, checlusterv1.Spec.Auth.GatewayAuthorizationSidecarImage, "AuthorizationSidecarImage") + assert.Equal(t, checlusterv1.Spec.Auth.IdentityProviderURL, "IdentityProviderURL") + assert.Equal(t, checlusterv1.Spec.Auth.OAuthClientName, "OAuthClientName") + assert.Equal(t, checlusterv1.Spec.Auth.OAuthSecret, "OAuthSecret") + + assert.Equal(t, checlusterv1.Spec.Database.ChePostgresContainerResources.Limits.Cpu, "2") + assert.Equal(t, checlusterv1.Spec.Database.ChePostgresContainerResources.Limits.Memory, "228Mi") + assert.Equal(t, checlusterv1.Spec.Database.ChePostgresContainerResources.Requests.Cpu, "1") + assert.Equal(t, checlusterv1.Spec.Database.ChePostgresContainerResources.Requests.Memory, "128Mi") + assert.Equal(t, checlusterv1.Spec.Database.ChePostgresDb, "PostgresDb") + assert.Equal(t, checlusterv1.Spec.Database.ChePostgresHostName, "PostgresHostName") + assert.Equal(t, checlusterv1.Spec.Database.ChePostgresPort, "PostgresPort") + assert.Equal(t, checlusterv1.Spec.Database.ChePostgresSecret, "DatabaseCredentialsSecretName") + assert.Equal(t, checlusterv1.Spec.Database.ExternalDb, true) + assert.Equal(t, checlusterv1.Spec.Database.PostgresImage, "DatabaseImage") + assert.Equal(t, checlusterv1.Spec.Database.PostgresImagePullPolicy, corev1.PullAlways) + assert.Equal(t, checlusterv1.Spec.Database.PostgresVersion, "PostgresVersion") + assert.Equal(t, checlusterv1.Spec.Database.PvcClaimSize, "DatabaseClaimSize") + + assert.Equal(t, checlusterv1.Spec.DevWorkspace.ControllerImage, "DevWorkspaceImage") + assert.Equal(t, checlusterv1.Spec.DevWorkspace.RunningLimit, "RunningLimit") + assert.True(t, checlusterv1.Spec.DevWorkspace.Enable) + + assert.Equal(t, checlusterv1.Spec.Dashboard.Warning, "DashboardWarning") + + assert.Equal(t, checlusterv1.Spec.ImagePuller.Enable, true) + assert.Equal(t, checlusterv1.Spec.Metrics.Enable, true) + + assert.Equal(t, checlusterv1.Spec.Server.AirGapContainerRegistryHostname, "AirGapContainerRegistryHostname") + assert.Equal(t, checlusterv1.Spec.Server.AirGapContainerRegistryOrganization, "AirGapContainerRegistryOrganization") + assert.Equal(t, checlusterv1.Spec.Server.CheClusterRoles, "ClusterRoles_1,ClusterRoles_2") + assert.Equal(t, checlusterv1.Spec.Server.CheDebug, "true") + assert.Equal(t, checlusterv1.Spec.Server.CheImage, "ServerImage") + assert.Equal(t, checlusterv1.Spec.Server.CheImagePullPolicy, corev1.PullPolicy("Always")) + assert.Equal(t, checlusterv1.Spec.Server.CheImageTag, "ServerTag") + assert.Equal(t, checlusterv1.Spec.Server.CheLogLevel, "LogLevel") + assert.Equal(t, checlusterv1.Spec.Server.CustomCheProperties, map[string]string{"a": "b", "c": "d"}) + assert.Equal(t, checlusterv1.Spec.Server.DashboardCpuLimit, "2") + assert.Equal(t, checlusterv1.Spec.Server.DashboardCpuRequest, "1") + assert.Equal(t, checlusterv1.Spec.Server.DashboardImage, "DashboardImage") + assert.Equal(t, checlusterv1.Spec.Server.DashboardImagePullPolicy, "Always") + assert.Equal(t, checlusterv1.Spec.Server.DashboardMemoryLimit, "228Mi") + assert.Equal(t, checlusterv1.Spec.Server.DashboardMemoryRequest, "128Mi") + assert.Equal(t, checlusterv1.Spec.Server.DevfileRegistryCpuLimit, "2") + assert.Equal(t, checlusterv1.Spec.Server.DevfileRegistryCpuRequest, "1") + assert.Equal(t, checlusterv1.Spec.Server.DevfileRegistryImage, "DevfileRegistryImage") + assert.Equal(t, checlusterv1.Spec.Server.DevfileRegistryMemoryLimit, "228Mi") + assert.Equal(t, checlusterv1.Spec.Server.DevfileRegistryMemoryRequest, "128Mi") + assert.Equal(t, checlusterv1.Spec.Server.DevfileRegistryPullPolicy, corev1.PullPolicy("Always")) + assert.Equal(t, checlusterv1.Spec.Server.ExternalDevfileRegistries, []chev1.ExternalDevfileRegistries{{Url: "ExternalDevfileRegistries"}}) + assert.Equal(t, checlusterv1.Spec.Server.ExternalDevfileRegistry, true) + assert.Equal(t, checlusterv1.Spec.Server.ExternalPluginRegistry, true) + assert.Equal(t, checlusterv1.Spec.Server.GitSelfSignedCert, true) + assert.Equal(t, checlusterv1.Spec.Server.NonProxyHosts, "NonProxyHosts_1|NonProxyHosts_2") + assert.Equal(t, checlusterv1.Spec.Server.PluginRegistryCpuLimit, "2") + assert.Equal(t, checlusterv1.Spec.Server.PluginRegistryCpuRequest, "1") + assert.Equal(t, checlusterv1.Spec.Server.PluginRegistryImage, "PluginRegistryImage") + assert.Equal(t, checlusterv1.Spec.Server.PluginRegistryMemoryLimit, "228Mi") + assert.Equal(t, checlusterv1.Spec.Server.PluginRegistryMemoryRequest, "128Mi") + assert.Equal(t, checlusterv1.Spec.Server.PluginRegistryPullPolicy, corev1.PullPolicy("Always")) + assert.Equal(t, checlusterv1.Spec.Server.PluginRegistryUrl, "ExternalPluginRegistries_1") + assert.Equal(t, checlusterv1.Spec.Server.ProxyPort, "ProxyPort") + assert.Equal(t, checlusterv1.Spec.Server.ProxySecret, "ProxyCredentialsSecretName") + assert.Equal(t, checlusterv1.Spec.Server.ProxyURL, "ProxyUrl") + assert.Equal(t, checlusterv1.Spec.Server.ServerCpuLimit, "2") + assert.Equal(t, checlusterv1.Spec.Server.ServerCpuRequest, "1") + assert.Equal(t, checlusterv1.Spec.Server.ServerMemoryLimit, "228Mi") + assert.Equal(t, checlusterv1.Spec.Server.ServerMemoryRequest, "128Mi") + assert.Equal(t, checlusterv1.Spec.Server.SingleHostGatewayConfigMapLabels, labels.Set{"a": "b", "c": "d"}) + assert.Equal(t, checlusterv1.Spec.Server.SingleHostGatewayConfigSidecarImage, "ConfigSidecarImage") + assert.Equal(t, checlusterv1.Spec.Server.SingleHostGatewayImage, "GatewayImage") + assert.Equal(t, checlusterv1.Spec.Server.WorkspaceNamespaceDefault, "WorkspaceNamespaceName") + assert.Equal(t, checlusterv1.Spec.Server.WorkspacePodNodeSelector, map[string]string{"a": "b", "c": "d"}) + assert.Equal(t, checlusterv1.Spec.Server.WorkspacePodTolerations, []corev1.Toleration{ + { + Key: "Key", + Operator: "Operator", + Value: "Value", + Effect: "Effect", + }, + }) + assert.Equal(t, checlusterv1.Spec.Server.WorkspacesDefaultPlugins, []chev1.WorkspacesDefaultPlugins{{Editor: "Editor", Plugins: []string{"Plugins_1", "Plugins_2"}}}) + + assert.Equal(t, checlusterv1.Spec.Storage.PostgresPVCStorageClassName, "DatabaseStorageClass") + assert.Equal(t, checlusterv1.Spec.Storage.PvcClaimSize, "StorageClaimSize") + assert.Equal(t, checlusterv1.Spec.Storage.PvcStrategy, "PvcStrategy") + assert.Equal(t, checlusterv1.Spec.Storage.WorkspacePVCStorageClassName, "StorageClass") +} diff --git a/api/checluster_conversion_to_test.go b/api/checluster_conversion_to_test.go new file mode 100644 index 000000000..f42b90c63 --- /dev/null +++ b/api/checluster_conversion_to_test.go @@ -0,0 +1,412 @@ +// +// Copyright (c) 2019-2022 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package org + +import ( + "testing" + + "k8s.io/apimachinery/pkg/api/resource" + + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + chev1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestConvertToEmptyCheCluster(t *testing.T) { + checlusterv1 := &chev1.CheCluster{} + checlusterv2 := &chev2.CheCluster{} + + err := checlusterv1.ConvertTo(checlusterv2) + assert.Nil(t, err) +} + +func TestConvertToIngressOnOpenShift(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + + checlusterv1 := &chev1.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eclipse-che", + Namespace: "eclipse-che", + }, + Spec: chev1.CheClusterSpec{ + Server: chev1.CheClusterSpecServer{ + CheHost: "CheHost", + CheHostTLSSecret: "CheHostTLSSecret", + CheServerIngress: chev1.IngressCustomSettings{ + Labels: "a=b,c=d", + }, + CheServerRoute: chev1.RouteCustomSettings{ + Labels: "a=b,c=d", + Annotations: map[string]string{"a": "b", "c": "d"}, + Domain: "CheServerRoute.Domain", + }, + }, + }, + } + + checlusterv2 := &chev2.CheCluster{} + err := checlusterv1.ConvertTo(checlusterv2) + assert.Nil(t, err) + + assert.Equal(t, map[string]string{"a": "b", "c": "d"}, checlusterv2.Spec.Networking.Annotations) + assert.Equal(t, "CheServerRoute.Domain", checlusterv2.Spec.Networking.Domain) + assert.Equal(t, "CheHost", checlusterv2.Spec.Networking.Hostname) + assert.Equal(t, map[string]string{"a": "b", "c": "d"}, checlusterv2.Spec.Networking.Labels) + assert.Equal(t, "CheHostTLSSecret", checlusterv2.Spec.Networking.TlsSecretName) +} + +func TestConvertToIngressOnK8s(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.Kubernetes) + + checlusterv1 := &chev1.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eclipse-che", + Namespace: "eclipse-che", + }, + Spec: chev1.CheClusterSpec{ + Server: chev1.CheClusterSpecServer{ + CheHost: "CheHost", + CheServerIngress: chev1.IngressCustomSettings{ + Labels: "a=b,c=d", + Annotations: map[string]string{"a": "b", "c": "d"}, + }, + }, + K8s: chev1.CheClusterSpecK8SOnly{ + IngressDomain: "k8s.IngressDomain", + IngressClass: "k8s.IngressClass", + TlsSecretName: "k8s.TlsSecretName", + }, + }, + } + + checlusterv2 := &chev2.CheCluster{} + err := checlusterv1.ConvertTo(checlusterv2) + assert.Nil(t, err) + + assert.Equal(t, map[string]string{"a": "b", "c": "d", "kubernetes.io/ingress.class": "k8s.IngressClass"}, checlusterv2.Spec.Networking.Annotations) + assert.Equal(t, "k8s.IngressDomain", checlusterv2.Spec.Networking.Domain) + assert.Equal(t, "CheHost", checlusterv2.Spec.Networking.Hostname) + assert.Equal(t, map[string]string{"a": "b", "c": "d"}, checlusterv2.Spec.Networking.Labels) + assert.Equal(t, "k8s.TlsSecretName", checlusterv2.Spec.Networking.TlsSecretName) +} + +func TestConvertToIngressOnK8sWithCheHostTLSSecret(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.Kubernetes) + + checlusterv1 := &chev1.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eclipse-che", + Namespace: "eclipse-che", + }, + Spec: chev1.CheClusterSpec{ + Server: chev1.CheClusterSpecServer{ + CheHost: "CheHost", + CheHostTLSSecret: "CheHostTLSSecret", + CheServerIngress: chev1.IngressCustomSettings{ + Labels: "a=b,c=d", + Annotations: map[string]string{"a": "b", "c": "d"}, + }, + }, + K8s: chev1.CheClusterSpecK8SOnly{ + IngressDomain: "k8s.IngressDomain", + IngressClass: "k8s.IngressClass", + TlsSecretName: "k8s.TlsSecretName", + }, + }, + } + + checlusterv2 := &chev2.CheCluster{} + err := checlusterv1.ConvertTo(checlusterv2) + assert.Nil(t, err) + + assert.Equal(t, map[string]string{"a": "b", "c": "d", "kubernetes.io/ingress.class": "k8s.IngressClass"}, checlusterv2.Spec.Networking.Annotations) + assert.Equal(t, "k8s.IngressDomain", checlusterv2.Spec.Networking.Domain) + assert.Equal(t, "CheHost", checlusterv2.Spec.Networking.Hostname) + assert.Equal(t, map[string]string{"a": "b", "c": "d"}, checlusterv2.Spec.Networking.Labels) + assert.Equal(t, "CheHostTLSSecret", checlusterv2.Spec.Networking.TlsSecretName) +} + +func TestConvertTo(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.Kubernetes) + + checlusterv1 := &chev1.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eclipse-che", + Namespace: "eclipse-che", + }, + Spec: chev1.CheClusterSpec{ + Server: chev1.CheClusterSpecServer{ + AirGapContainerRegistryHostname: "AirGapContainerRegistryHostname", + AirGapContainerRegistryOrganization: "AirGapContainerRegistryOrganization", + CheImage: "CheImage", + CheImageTag: "CheImageTag", + CheImagePullPolicy: "Always", + CheLogLevel: "CheLogLevel", + CheDebug: "true", + CheClusterRoles: "CheClusterRoles_1,CheClusterRoles_2", + CheWorkspaceClusterRole: "CheWorkspaceClusterRole", + WorkspaceNamespaceDefault: "WorkspaceNamespaceDefault", + ServerTrustStoreConfigMapName: "ServerTrustStoreConfigMapName", + GitSelfSignedCert: true, + DashboardImage: "DashboardImage", + DashboardImagePullPolicy: "Always", + DashboardMemoryLimit: "200Mi", + DashboardMemoryRequest: "100Mi", + DashboardCpuLimit: "2", + DashboardCpuRequest: "1", + DevfileRegistryImage: "DevfileRegistryImage", + DevfileRegistryPullPolicy: "Always", + DevfileRegistryMemoryLimit: "200Mi", + DevfileRegistryMemoryRequest: "100Mi", + DevfileRegistryCpuLimit: "2", + DevfileRegistryCpuRequest: "1", + ExternalDevfileRegistry: true, + ExternalDevfileRegistries: []chev1.ExternalDevfileRegistries{ + { + Url: "ExternalDevfileRegistries_1", + }, + { + Url: "ExternalDevfileRegistries_2", + }, + }, + PluginRegistryUrl: "PluginRegistryUrl", + PluginRegistryImage: "PluginRegistryImage", + PluginRegistryPullPolicy: "Always", + PluginRegistryMemoryLimit: "200Mi", + PluginRegistryMemoryRequest: "100Mi", + PluginRegistryCpuLimit: "2", + PluginRegistryCpuRequest: "1", + ExternalPluginRegistry: true, + CustomCheProperties: map[string]string{"a": "b", "c": "d"}, + ProxyURL: "ProxyURL", + ProxyPort: "ProxyPort", + ProxySecret: "ProxySecret", + NonProxyHosts: "NonProxyHosts_1|NonProxyHosts_2", + ServerMemoryRequest: "100Mi", + ServerMemoryLimit: "200Mi", + ServerCpuLimit: "2", + ServerCpuRequest: "1", + SingleHostGatewayImage: "SingleHostGatewayImage", + SingleHostGatewayConfigSidecarImage: "SingleHostGatewayConfigSidecarImage", + SingleHostGatewayConfigMapLabels: map[string]string{"a": "b", "c": "d"}, + WorkspacesDefaultPlugins: []chev1.WorkspacesDefaultPlugins{ + { + Editor: "Editor", + Plugins: []string{"Plugin_1,Plugin_2"}, + }, + }, + WorkspacePodNodeSelector: map[string]string{"a": "b", "c": "d"}, + WorkspacePodTolerations: []corev1.Toleration{ + { + Key: "Key", + Operator: "Operator", + Value: "Value", + Effect: "Effect", + }, + }, + }, + Database: chev1.CheClusterSpecDB{ + ExternalDb: true, + ChePostgresHostName: "ChePostgresHostName", + ChePostgresPort: "ChePostgresPort", + ChePostgresDb: "ChePostgresDb", + ChePostgresSecret: "ChePostgresSecret", + PostgresImage: "PostgresImage", + PostgresVersion: "PostgresVersion", + PostgresImagePullPolicy: "Always", + PvcClaimSize: "DatabasePvcClaimSize", + ChePostgresContainerResources: chev1.ResourcesCustomSettings{ + Requests: chev1.Resources{ + Memory: "100Mi", + Cpu: "1", + }, + Limits: chev1.Resources{ + Memory: "200Mi", + Cpu: "2", + }, + }, + }, + Auth: chev1.CheClusterSpecAuth{ + IdentityProviderURL: "IdentityProviderURL", + OAuthClientName: "OAuthClientName", + OAuthSecret: "OAuthSecret", + GatewayAuthenticationSidecarImage: "GatewayAuthenticationSidecarImage", + GatewayAuthorizationSidecarImage: "GatewayAuthorizationSidecarImage", + }, + Storage: chev1.CheClusterSpecStorage{ + PvcStrategy: "PvcStrategy", + PvcClaimSize: "WorkspacePvcClaimSize", + PostgresPVCStorageClassName: "PostgresPVCStorageClassName", + WorkspacePVCStorageClassName: "WorkspacePVCStorageClassName", + }, + Metrics: chev1.CheClusterSpecMetrics{ + Enable: true, + }, + K8s: chev1.CheClusterSpecK8SOnly{ + SecurityContextFsGroup: "64", + SecurityContextRunAsUser: "65", + }, + ImagePuller: chev1.CheClusterSpecImagePuller{ + Enable: true, + }, + DevWorkspace: chev1.CheClusterSpecDevWorkspace{ + Enable: true, + ControllerImage: "ControllerImage", + RunningLimit: "RunningLimit", + }, + Dashboard: chev1.CheClusterSpecDashboard{ + Warning: "DashboardWarning", + }, + }, + Status: chev1.CheClusterStatus{ + CheClusterRunning: "Available", + CheVersion: "CheVersion", + CheURL: "CheURL", + Message: "Message", + Reason: "Reason", + HelpLink: "HelpLink", + DevfileRegistryURL: "DevfileRegistryURL", + PluginRegistryURL: "PluginRegistryURL", + }, + } + + checlusterv2 := &chev2.CheCluster{} + err := checlusterv1.ConvertTo(checlusterv2) + assert.Nil(t, err) + + assert.Equal(t, checlusterv2.ObjectMeta.Name, "eclipse-che") + assert.Equal(t, checlusterv2.ObjectMeta.Namespace, "eclipse-che") + + assert.Equal(t, checlusterv2.Spec.Networking.Auth.Gateway.Deployment.Containers[0].Name, constants.GatewayContainerName) + assert.Equal(t, checlusterv2.Spec.Networking.Auth.Gateway.Deployment.Containers[0].Image, "SingleHostGatewayImage") + assert.Equal(t, checlusterv2.Spec.Networking.Auth.Gateway.Deployment.Containers[1].Name, constants.GatewayConfigSideCarContainerName) + assert.Equal(t, checlusterv2.Spec.Networking.Auth.Gateway.Deployment.Containers[1].Image, "SingleHostGatewayConfigSidecarImage") + assert.Equal(t, checlusterv2.Spec.Networking.Auth.Gateway.Deployment.Containers[2].Name, constants.GatewayAuthenticationContainerName) + assert.Equal(t, checlusterv2.Spec.Networking.Auth.Gateway.Deployment.Containers[2].Image, "GatewayAuthenticationSidecarImage") + assert.Equal(t, checlusterv2.Spec.Networking.Auth.Gateway.Deployment.Containers[3].Name, constants.GatewayAuthorizationContainerName) + assert.Equal(t, checlusterv2.Spec.Networking.Auth.Gateway.Deployment.Containers[3].Image, "GatewayAuthorizationSidecarImage") + assert.Equal(t, checlusterv2.Spec.Networking.Auth.Gateway.ConfigLabels, map[string]string{"a": "b", "c": "d"}) + assert.Equal(t, checlusterv2.Spec.Networking.Auth.IdentityProviderURL, "IdentityProviderURL") + assert.Equal(t, checlusterv2.Spec.Networking.Auth.OAuthClientName, "OAuthClientName") + assert.Equal(t, checlusterv2.Spec.Networking.Auth.OAuthSecret, "OAuthSecret") + + assert.Equal(t, checlusterv2.Spec.ContainerRegistry.Hostname, "AirGapContainerRegistryHostname") + assert.Equal(t, checlusterv2.Spec.ContainerRegistry.Organization, "AirGapContainerRegistryOrganization") + assert.Equal(t, checlusterv2.Spec.Components.CheServer.ClusterRoles, []string{"CheClusterRoles_1", "CheClusterRoles_2"}) + assert.Equal(t, checlusterv2.Spec.Components.CheServer.ExtraProperties, map[string]string{"a": "b", "c": "d"}) + assert.True(t, *checlusterv2.Spec.Components.CheServer.Debug) + assert.Equal(t, checlusterv2.Spec.Components.CheServer.Deployment.Containers[0].Image, "CheImage:CheImageTag") + assert.Equal(t, checlusterv2.Spec.Components.CheServer.Deployment.Containers[0].Name, defaults.GetCheFlavor()) + assert.Equal(t, checlusterv2.Spec.Components.CheServer.Deployment.Containers[0].ImagePullPolicy, corev1.PullPolicy("Always")) + assert.Equal(t, checlusterv2.Spec.Components.CheServer.Deployment.Containers[0].Resources.Limits.Cpu, resource.MustParse("2")) + assert.Equal(t, checlusterv2.Spec.Components.CheServer.Deployment.Containers[0].Resources.Limits.Memory, resource.MustParse("200Mi")) + assert.Equal(t, checlusterv2.Spec.Components.CheServer.Deployment.Containers[0].Resources.Requests.Cpu, resource.MustParse("1")) + assert.Equal(t, checlusterv2.Spec.Components.CheServer.Deployment.Containers[0].Resources.Requests.Memory, resource.MustParse("100Mi")) + assert.Equal(t, *checlusterv2.Spec.Components.CheServer.Deployment.SecurityContext.FsGroup, int64(64)) + assert.Equal(t, *checlusterv2.Spec.Components.CheServer.Deployment.SecurityContext.RunAsUser, int64(65)) + assert.Equal(t, checlusterv2.Spec.DevEnvironments.TrustedCerts.GitTrustedCertsConfigMapName, "che-git-self-signed-cert") + assert.Equal(t, checlusterv2.Spec.Components.CheServer.LogLevel, "CheLogLevel") + assert.Equal(t, checlusterv2.Spec.Components.CheServer.Proxy.CredentialsSecretName, "ProxySecret") + assert.Equal(t, checlusterv2.Spec.Components.CheServer.Proxy.NonProxyHosts, []string{"NonProxyHosts_1", "NonProxyHosts_2"}) + assert.Equal(t, checlusterv2.Spec.Components.CheServer.Proxy.Port, "ProxyPort") + assert.Equal(t, checlusterv2.Spec.Components.CheServer.Proxy.Url, "ProxyURL") + assert.Equal(t, checlusterv2.Spec.DevEnvironments.DefaultNamespace.Template, "WorkspaceNamespaceDefault") + assert.Equal(t, checlusterv2.Spec.DevEnvironments.NodeSelector, map[string]string{"a": "b", "c": "d"}) + assert.Equal(t, checlusterv2.Spec.DevEnvironments.Tolerations, []corev1.Toleration{{ + Key: "Key", + Operator: "Operator", + Value: "Value", + Effect: "Effect", + }}) + assert.Equal(t, checlusterv2.Spec.DevEnvironments.DefaultPlugins, []chev2.WorkspaceDefaultPlugins{{ + Editor: "Editor", + Plugins: []string{"Plugin_1,Plugin_2"}, + }}) + + assert.Equal(t, checlusterv2.Spec.Components.Dashboard.Deployment.Containers[0].Name, defaults.GetCheFlavor()+"-dashboard") + assert.Equal(t, checlusterv2.Spec.Components.Dashboard.Deployment.Containers[0].Image, "DashboardImage") + assert.Equal(t, checlusterv2.Spec.Components.Dashboard.Deployment.Containers[0].ImagePullPolicy, corev1.PullPolicy("Always")) + assert.Equal(t, checlusterv2.Spec.Components.Dashboard.Deployment.Containers[0].Resources.Limits.Cpu, resource.MustParse("2")) + assert.Equal(t, checlusterv2.Spec.Components.Dashboard.Deployment.Containers[0].Resources.Limits.Memory, resource.MustParse("200Mi")) + assert.Equal(t, checlusterv2.Spec.Components.Dashboard.Deployment.Containers[0].Resources.Requests.Cpu, resource.MustParse("1")) + assert.Equal(t, checlusterv2.Spec.Components.Dashboard.Deployment.Containers[0].Resources.Requests.Memory, resource.MustParse("100Mi")) + assert.Equal(t, *checlusterv2.Spec.Components.Dashboard.Deployment.SecurityContext.FsGroup, int64(64)) + assert.Equal(t, *checlusterv2.Spec.Components.Dashboard.Deployment.SecurityContext.RunAsUser, int64(65)) + assert.Equal(t, checlusterv2.Spec.Components.Dashboard.HeaderMessage.Text, "DashboardWarning") + assert.True(t, checlusterv2.Spec.Components.Dashboard.HeaderMessage.Show) + + assert.Equal(t, checlusterv2.Spec.Components.Database.CredentialsSecretName, "ChePostgresSecret") + assert.Equal(t, checlusterv2.Spec.Components.Database.Deployment.Containers[0].Name, constants.PostgresName) + assert.Equal(t, checlusterv2.Spec.Components.Database.Deployment.Containers[0].Image, "PostgresImage") + assert.Equal(t, checlusterv2.Spec.Components.Database.Deployment.Containers[0].ImagePullPolicy, corev1.PullPolicy("Always")) + assert.Equal(t, checlusterv2.Spec.Components.Database.Deployment.Containers[0].Resources.Limits.Cpu, resource.MustParse("2")) + assert.Equal(t, checlusterv2.Spec.Components.Database.Deployment.Containers[0].Resources.Limits.Memory, resource.MustParse("200Mi")) + assert.Equal(t, checlusterv2.Spec.Components.Database.Deployment.Containers[0].Resources.Requests.Cpu, resource.MustParse("1")) + assert.Equal(t, checlusterv2.Spec.Components.Database.Deployment.Containers[0].Resources.Requests.Memory, resource.MustParse("100Mi")) + assert.Equal(t, checlusterv2.Spec.Components.Database.ExternalDb, true) + assert.Equal(t, checlusterv2.Spec.Components.Database.PostgresDb, "ChePostgresDb") + assert.Equal(t, checlusterv2.Spec.Components.Database.PostgresHostName, "ChePostgresHostName") + assert.Equal(t, checlusterv2.Spec.Components.Database.PostgresPort, "ChePostgresPort") + assert.Equal(t, checlusterv2.Spec.Components.Database.Pvc.ClaimSize, "DatabasePvcClaimSize") + assert.Equal(t, checlusterv2.Spec.Components.Database.Pvc.StorageClass, "PostgresPVCStorageClassName") + + assert.Equal(t, checlusterv2.Spec.Components.DevWorkspace.Deployment.Containers[0].Image, "ControllerImage") + assert.Equal(t, checlusterv2.Spec.Components.DevWorkspace.RunningLimit, "RunningLimit") + + assert.Equal(t, checlusterv2.Spec.Components.ImagePuller.Enable, true) + assert.Equal(t, checlusterv2.Spec.Components.Metrics.Enable, true) + + assert.Equal(t, checlusterv2.Spec.Components.DevfileRegistry.Deployment.Containers[0].Name, constants.DevfileRegistryName) + assert.Equal(t, checlusterv2.Spec.Components.DevfileRegistry.Deployment.Containers[0].Image, "DevfileRegistryImage") + assert.Equal(t, checlusterv2.Spec.Components.DevfileRegistry.Deployment.Containers[0].ImagePullPolicy, corev1.PullPolicy("Always")) + assert.Equal(t, checlusterv2.Spec.Components.DevfileRegistry.Deployment.Containers[0].Resources.Limits.Cpu, resource.MustParse("2")) + assert.Equal(t, checlusterv2.Spec.Components.DevfileRegistry.Deployment.Containers[0].Resources.Limits.Memory, resource.MustParse("200Mi")) + assert.Equal(t, checlusterv2.Spec.Components.DevfileRegistry.Deployment.Containers[0].Resources.Requests.Cpu, resource.MustParse("1")) + assert.Equal(t, checlusterv2.Spec.Components.DevfileRegistry.Deployment.Containers[0].Resources.Requests.Memory, resource.MustParse("100Mi")) + assert.Equal(t, checlusterv2.Spec.Components.DevfileRegistry.DisableInternalRegistry, true) + assert.Equal(t, checlusterv2.Spec.Components.DevfileRegistry.ExternalDevfileRegistries, []chev2.ExternalDevfileRegistry{ + { + Url: "ExternalDevfileRegistries_1", + }, + { + Url: "ExternalDevfileRegistries_2", + }}) + + assert.Equal(t, checlusterv2.Spec.Components.PluginRegistry.Deployment.Containers[0].Name, constants.PluginRegistryName) + assert.Equal(t, checlusterv2.Spec.Components.PluginRegistry.Deployment.Containers[0].Image, "PluginRegistryImage") + assert.Equal(t, checlusterv2.Spec.Components.PluginRegistry.Deployment.Containers[0].ImagePullPolicy, corev1.PullPolicy("Always")) + assert.Equal(t, checlusterv2.Spec.Components.PluginRegistry.Deployment.Containers[0].Resources.Limits.Cpu, resource.MustParse("2")) + assert.Equal(t, checlusterv2.Spec.Components.PluginRegistry.Deployment.Containers[0].Resources.Limits.Memory, resource.MustParse("200Mi")) + assert.Equal(t, checlusterv2.Spec.Components.PluginRegistry.Deployment.Containers[0].Resources.Requests.Cpu, resource.MustParse("1")) + assert.Equal(t, checlusterv2.Spec.Components.PluginRegistry.Deployment.Containers[0].Resources.Requests.Memory, resource.MustParse("100Mi")) + assert.Equal(t, checlusterv2.Spec.Components.PluginRegistry.DisableInternalRegistry, true) + assert.Equal(t, checlusterv2.Spec.Components.PluginRegistry.ExternalPluginRegistries, []chev2.ExternalPluginRegistry{{Url: "PluginRegistryUrl"}}) + + assert.Equal(t, checlusterv2.Spec.DevEnvironments.Storage.Pvc.ClaimSize, "WorkspacePvcClaimSize") + assert.Equal(t, checlusterv2.Spec.DevEnvironments.Storage.Pvc.StorageClass, "WorkspacePVCStorageClassName") + assert.Equal(t, checlusterv2.Spec.DevEnvironments.Storage.PvcStrategy, "PvcStrategy") + + assert.Equal(t, checlusterv2.Status.CheURL, "CheURL") + assert.Equal(t, checlusterv2.Status.CheVersion, "CheVersion") + assert.Equal(t, checlusterv2.Status.DevfileRegistryURL, "DevfileRegistryURL") + assert.Equal(t, checlusterv2.Status.Message, "Message") + assert.Equal(t, checlusterv2.Status.ChePhase, chev2.CheClusterPhase("Active")) + assert.Equal(t, checlusterv2.Status.PluginRegistryURL, "PluginRegistryURL") + assert.Equal(t, checlusterv2.Status.Reason, "Reason") + assert.Equal(t, checlusterv2.Status.PostgresVersion, "PostgresVersion") +} diff --git a/api/checluster_round_conversion_test.go b/api/checluster_round_conversion_test.go new file mode 100644 index 000000000..ee8e3ab00 --- /dev/null +++ b/api/checluster_round_conversion_test.go @@ -0,0 +1,541 @@ +// +// Copyright (c) 2019-2022 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package org + +import ( + "context" + "testing" + + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/constants" + k8shelper "github.com/eclipse-che/che-operator/pkg/common/k8s-helper" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/pointer" + + chev1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/stretchr/testify/assert" +) + +func TestRoundConvertEmptyCheClusterV2(t *testing.T) { + f := func() { + checlusterv2Orignal := &chev2.CheCluster{} + + checlusterv1 := &chev1.CheCluster{} + checlusterv2 := &chev2.CheCluster{} + + err := checlusterv1.ConvertFrom(checlusterv2Orignal) + assert.Nil(t, err) + + err = checlusterv1.ConvertTo(checlusterv2) + assert.Nil(t, err) + + assert.Equal(t, checlusterv2Orignal, checlusterv2) + } + + onKubernetes(f) + onOpenShift(f) +} + +func TestRoundConvertEmptyCheClusterV1(t *testing.T) { + f := func() { + checlusterv1Orignal := &chev1.CheCluster{ + Spec: chev1.CheClusterSpec{ + DevWorkspace: chev1.CheClusterSpecDevWorkspace{ + Enable: true, + }, + }, + } + + checlusterv1 := &chev1.CheCluster{} + checlusterv2 := &chev2.CheCluster{} + + err := checlusterv1Orignal.ConvertTo(checlusterv2) + assert.Nil(t, err) + + err = checlusterv1.ConvertFrom(checlusterv2) + assert.Nil(t, err) + + assert.Equal(t, checlusterv1Orignal, checlusterv1) + } + + onKubernetes(f) + onOpenShift(f) +} + +func TestRoundConvertCheClusterV2(t *testing.T) { + f := func() { + checlusterv2Original := &chev2.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eclipse-che", + Namespace: "eclipse-che", + }, + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + Metrics: chev2.ServerMetrics{ + Enable: true, + }, + Database: chev2.Database{ + ExternalDb: true, + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: "postgres", + Image: "DatabaseImage", + ImagePullPolicy: corev1.PullAlways, + Resources: chev2.ResourceRequirements{ + Requests: chev2.ResourceList{ + Memory: resource.MustParse("148Mi"), + Cpu: resource.MustParse("1"), + }, + Limits: chev2.ResourceList{ + Memory: resource.MustParse("228Mi"), + Cpu: resource.MustParse("2"), + }, + }, + }, + }, + }, + PostgresHostName: "PostgresHostName", + PostgresPort: "PostgresPort", + PostgresDb: "PostgresDb", + CredentialsSecretName: "DatabaseCredentialsSecretName", + Pvc: chev2.PVC{ + ClaimSize: "DatabaseClaimSize", + StorageClass: "DatabaseStorageClass", + }, + }, + PluginRegistry: chev2.PluginRegistry{ + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: "plugin-registry", + Image: "PluginRegistryImage", + ImagePullPolicy: corev1.PullAlways, + Resources: chev2.ResourceRequirements{ + Requests: chev2.ResourceList{ + Memory: resource.MustParse("148Mi"), + Cpu: resource.MustParse("1"), + }, + Limits: chev2.ResourceList{ + Memory: resource.MustParse("228Mi"), + Cpu: resource.MustParse("2"), + }, + }, + }, + }, + }, + DisableInternalRegistry: true, + ExternalPluginRegistries: []chev2.ExternalPluginRegistry{ + { + Url: "ExternalPluginRegistries_1", + }, + }, + }, + DevfileRegistry: chev2.DevfileRegistry{ + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: "devfile-registry", + Image: "DevfileRegistryImage", + ImagePullPolicy: corev1.PullAlways, + Resources: chev2.ResourceRequirements{ + Requests: chev2.ResourceList{ + Memory: resource.MustParse("148Mi"), + Cpu: resource.MustParse("1"), + }, + Limits: chev2.ResourceList{ + Memory: resource.MustParse("228Mi"), + Cpu: resource.MustParse("2"), + }, + }, + }, + }, + }, + DisableInternalRegistry: true, + ExternalDevfileRegistries: []chev2.ExternalDevfileRegistry{ + { + Url: "ExternalDevfileRegistries", + }, + }, + }, + Dashboard: chev2.Dashboard{ + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: defaults.GetCheFlavor() + "-dashboard", + Image: "DashboardImage", + ImagePullPolicy: corev1.PullAlways, + Resources: chev2.ResourceRequirements{ + Requests: chev2.ResourceList{ + Memory: resource.MustParse("148Mi"), + Cpu: resource.MustParse("1"), + }, + Limits: chev2.ResourceList{ + Memory: resource.MustParse("228Mi"), + Cpu: resource.MustParse("2"), + }, + }, + }, + }, + SecurityContext: chev2.PodSecurityContext{ + RunAsUser: pointer.Int64Ptr(64), + FsGroup: pointer.Int64Ptr(65), + }, + }, + HeaderMessage: chev2.DashboardHeaderMessage{ + Show: true, + Text: "DashboardWarning", + }, + }, + ImagePuller: chev2.ImagePuller{ + Enable: true, + }, + CheServer: chev2.CheServer{ + ExtraProperties: map[string]string{"a": "b", "c": "d"}, + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: defaults.GetCheFlavor(), + Image: "ServerImage:ServerTag", + ImagePullPolicy: corev1.PullAlways, + Resources: chev2.ResourceRequirements{ + Requests: chev2.ResourceList{ + Memory: resource.MustParse("148Mi"), + Cpu: resource.MustParse("1"), + }, + Limits: chev2.ResourceList{ + Memory: resource.MustParse("228Mi"), + Cpu: resource.MustParse("2"), + }, + }, + }, + }, + SecurityContext: chev2.PodSecurityContext{ + RunAsUser: pointer.Int64Ptr(64), + FsGroup: pointer.Int64Ptr(65), + }, + }, + LogLevel: "LogLevel", + Debug: pointer.BoolPtr(true), + ClusterRoles: []string{"ClusterRoles_1", "ClusterRoles_2"}, + Proxy: chev2.Proxy{ + Url: "ProxyUrl", + Port: "ProxyPort", + NonProxyHosts: []string{"NonProxyHosts_1", "NonProxyHosts_2"}, + CredentialsSecretName: "ProxyCredentialsSecretName", + }, + }, + DevWorkspace: chev2.DevWorkspace{ + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: "devworkspace-controller", + Image: "DevWorkspaceImage", + }, + }, + }, + RunningLimit: "RunningLimit", + }, + }, + Networking: chev2.CheClusterSpecNetworking{ + TlsSecretName: "che-tls", + Domain: "domain", + Hostname: "hostname", + Labels: map[string]string{ + "label": "value", + }, + Annotations: map[string]string{ + "kubernetes.io/ingress.class": "nginx", + }, + Auth: chev2.Auth{ + IdentityProviderURL: "IdentityProviderURL", + OAuthClientName: "OAuthClientName", + OAuthSecret: "OAuthSecret", + Gateway: chev2.Gateway{ + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: "gateway", + Image: "GatewayImage", + }, + { + Name: "configbump", + Image: "ConfigSidecarImage", + }, + { + Name: "oauth-proxy", + Image: "AuthenticationSidecarImage", + }, + { + Name: "kube-rbac-proxy", + Image: "AuthorizationSidecarImage", + }, + }, + }, + ConfigLabels: map[string]string{"a": "b", "c": "d"}, + }, + }, + }, + DevEnvironments: chev2.CheClusterDevEnvironments{ + DefaultNamespace: chev2.DefaultNamespace{ + Template: "WorkspaceNamespaceName", + }, + TrustedCerts: chev2.TrustedCerts{ + GitTrustedCertsConfigMapName: "che-git-self-signed-cert", + }, + Storage: chev2.WorkspaceStorage{ + Pvc: chev2.PVC{ + ClaimSize: "StorageClaimSize", + StorageClass: "StorageClass", + }, + PvcStrategy: "PvcStrategy", + }, + DefaultPlugins: []chev2.WorkspaceDefaultPlugins{ + { + Editor: "Editor", + Plugins: []string{"Plugins_1", "Plugins_2"}, + }, + }, + NodeSelector: map[string]string{"a": "b", "c": "d"}, + Tolerations: []corev1.Toleration{{ + Key: "Key", + Operator: "Operator", + Value: "Value", + Effect: "Effect", + }}, + }, + ContainerRegistry: chev2.CheClusterContainerRegistry{ + Hostname: "AirGapContainerRegistryHostname", + Organization: "AirGapContainerRegistryOrganization", + }, + }, + Status: chev2.CheClusterStatus{ + CheVersion: "CheVersion", + CheURL: "CheURL", + DevfileRegistryURL: "DevfileRegistryURL", + PluginRegistryURL: "PluginRegistryURL", + ChePhase: "Active", + Message: "Message", + Reason: "Reason", + PostgresVersion: "PostgresVersion", + }, + } + + checlusterv1 := &chev1.CheCluster{} + checlusterv2 := &chev2.CheCluster{} + + err := checlusterv1.ConvertFrom(checlusterv2Original) + assert.Nil(t, err) + + err = checlusterv1.ConvertTo(checlusterv2) + assert.Nil(t, err) + + assert.Equal(t, checlusterv2Original, checlusterv2) + } + onKubernetes(f) + onOpenShift(f) +} + +func TestRoundConvertCheClusterV1(t *testing.T) { + f := func() { + truststoreConfigMap := &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: constants.DefaultServerTrustStoreConfigMapName, + Namespace: "eclipse-che", + }, + } + + k8sHelper := k8shelper.New() + _, err := k8sHelper.GetClientset().CoreV1().ConfigMaps("eclipse-che").Create(context.TODO(), truststoreConfigMap, metav1.CreateOptions{}) + + checlusterv1Orignal := &chev1.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eclipse-che", + Namespace: "eclipse-che", + }, + Spec: chev1.CheClusterSpec{ + Server: chev1.CheClusterSpecServer{ + AirGapContainerRegistryHostname: "AirGapContainerRegistryHostname", + AirGapContainerRegistryOrganization: "AirGapContainerRegistryOrganization", + CheImage: "CheImage", + CheImageTag: "CheImageTag", + CheImagePullPolicy: "Always", + CheLogLevel: "CheLogLevel", + CheDebug: "true", + CheClusterRoles: "CheClusterRoles_1,CheClusterRoles_2", + WorkspaceNamespaceDefault: "WorkspaceNamespaceDefault", + ServerTrustStoreConfigMapName: constants.DefaultServerTrustStoreConfigMapName, + GitSelfSignedCert: true, + DashboardImage: "DashboardImage", + DashboardImagePullPolicy: "Always", + DashboardMemoryLimit: "200Mi", + DashboardMemoryRequest: "100Mi", + DashboardCpuLimit: "2", + DashboardCpuRequest: "1", + DevfileRegistryImage: "DevfileRegistryImage", + DevfileRegistryPullPolicy: "Always", + DevfileRegistryMemoryLimit: "200Mi", + DevfileRegistryMemoryRequest: "100Mi", + DevfileRegistryCpuLimit: "2", + DevfileRegistryCpuRequest: "1", + ExternalDevfileRegistry: true, + ExternalDevfileRegistries: []chev1.ExternalDevfileRegistries{ + { + Url: "ExternalDevfileRegistries_1", + }, + { + Url: "ExternalDevfileRegistries_2", + }, + }, + PluginRegistryUrl: "PluginRegistryUrl", + PluginRegistryImage: "PluginRegistryImage", + PluginRegistryPullPolicy: "Always", + PluginRegistryMemoryLimit: "200Mi", + PluginRegistryMemoryRequest: "100Mi", + PluginRegistryCpuLimit: "2", + PluginRegistryCpuRequest: "1", + ExternalPluginRegistry: true, + CustomCheProperties: map[string]string{"a": "b", "c": "d"}, + ProxyURL: "ProxyURL", + ProxyPort: "ProxyPort", + ProxySecret: "ProxySecret", + NonProxyHosts: "NonProxyHosts_1|NonProxyHosts_2", + ServerMemoryRequest: "100Mi", + ServerMemoryLimit: "200Mi", + ServerCpuLimit: "2", + ServerCpuRequest: "1", + SingleHostGatewayImage: "SingleHostGatewayImage", + SingleHostGatewayConfigSidecarImage: "SingleHostGatewayConfigSidecarImage", + SingleHostGatewayConfigMapLabels: map[string]string{"a": "b", "c": "d"}, + WorkspacesDefaultPlugins: []chev1.WorkspacesDefaultPlugins{ + { + Editor: "Editor", + Plugins: []string{"Plugin_1,Plugin_2"}, + }, + }, + WorkspacePodNodeSelector: map[string]string{"a": "b", "c": "d"}, + WorkspacePodTolerations: []corev1.Toleration{ + { + Key: "Key", + Operator: "Operator", + Value: "Value", + Effect: "Effect", + }, + }, + }, + Database: chev1.CheClusterSpecDB{ + ExternalDb: true, + ChePostgresHostName: "ChePostgresHostName", + ChePostgresPort: "ChePostgresPort", + ChePostgresDb: "ChePostgresDb", + ChePostgresSecret: "ChePostgresSecret", + PostgresImage: "PostgresImage", + PostgresVersion: "PostgresVersion", + PostgresImagePullPolicy: "Always", + PvcClaimSize: "DatabasePvcClaimSize", + ChePostgresContainerResources: chev1.ResourcesCustomSettings{ + Requests: chev1.Resources{ + Memory: "100Mi", + Cpu: "1", + }, + Limits: chev1.Resources{ + Memory: "200Mi", + Cpu: "2", + }, + }, + }, + Auth: chev1.CheClusterSpecAuth{ + IdentityProviderURL: "IdentityProviderURL", + OAuthClientName: "OAuthClientName", + OAuthSecret: "OAuthSecret", + GatewayAuthenticationSidecarImage: "GatewayAuthenticationSidecarImage", + GatewayAuthorizationSidecarImage: "GatewayAuthorizationSidecarImage", + }, + Storage: chev1.CheClusterSpecStorage{ + PvcStrategy: "PvcStrategy", + PvcClaimSize: "WorkspacePvcClaimSize", + PostgresPVCStorageClassName: "PostgresPVCStorageClassName", + WorkspacePVCStorageClassName: "WorkspacePVCStorageClassName", + }, + Metrics: chev1.CheClusterSpecMetrics{ + Enable: true, + }, + K8s: chev1.CheClusterSpecK8SOnly{ + SecurityContextFsGroup: "64", + SecurityContextRunAsUser: "65", + }, + ImagePuller: chev1.CheClusterSpecImagePuller{ + Enable: true, + }, + DevWorkspace: chev1.CheClusterSpecDevWorkspace{ + Enable: true, + ControllerImage: "ControllerImage", + RunningLimit: "RunningLimit", + }, + Dashboard: chev1.CheClusterSpecDashboard{ + Warning: "DashboardWarning", + }, + }, + Status: chev1.CheClusterStatus{ + CheClusterRunning: "Available", + CheVersion: "CheVersion", + CheURL: "CheURL", + Message: "Message", + Reason: "Reason", + DevfileRegistryURL: "DevfileRegistryURL", + PluginRegistryURL: "PluginRegistryURL", + GitServerTLSCertificateConfigMapName: "che-git-self-signed-cert", + DevworkspaceStatus: chev1.LegacyDevworkspaceStatus{ + GatewayHost: "CheURL", + Message: "Message", + Reason: "Reason", + Phase: chev1.ClusterPhaseActive, + GatewayPhase: chev1.GatewayPhaseEstablished, + WorkspaceBaseDomain: "Domain", + }, + }, + } + + checlusterv1 := &chev1.CheCluster{} + checlusterv2 := &chev2.CheCluster{} + + err = checlusterv1Orignal.ConvertTo(checlusterv2) + assert.Nil(t, err) + + err = checlusterv1.ConvertFrom(checlusterv2) + assert.Nil(t, err) + + assert.Equal(t, checlusterv1Orignal, checlusterv1) + } + + onKubernetes(f) + onOpenShift(f) +} + +func onKubernetes(f func()) { + infrastructure.InitializeForTesting(infrastructure.Kubernetes) + f() +} + +func onOpenShift(f func()) { + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + f() +} diff --git a/api/conversion.go b/api/conversion.go deleted file mode 100644 index ac572adc7..000000000 --- a/api/conversion.go +++ /dev/null @@ -1,289 +0,0 @@ -// -// Copyright (c) 2019-2021 Red Hat, Inc. -// This program and the accompanying materials are made -// available under the terms of the Eclipse Public License 2.0 -// which is available at https://www.eclipse.org/legal/epl-2.0/ -// -// SPDX-License-Identifier: EPL-2.0 -// -// Contributors: -// Red Hat, Inc. - initial API and implementation -// - -package org - -import ( - "encoding/json" - "strings" - - v1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/api/v2alpha1" - "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" - corev1 "k8s.io/api/core/v1" - "k8s.io/utils/pointer" - "sigs.k8s.io/yaml" -) - -const ( - v1StorageAnnotation = "che.eclipse.org/cheClusterV1Spec" - v2alpha1StorageAnnotation = "che.eclipse.org/cheClusterV2alpha1Spec" - routeDomainSuffixPropertyKey = "CHE_INFRA_OPENSHIFT_ROUTE_HOST_DOMAIN__SUFFIX" - defaultV2alpha1IngressClass = "nginx" - defaultV1IngressClass = "nginx" -) - -func AsV1(v2 *v2alpha1.CheCluster) *v1.CheCluster { - ret := &v1.CheCluster{} - V2alpha1ToV1(v2, ret) - return ret -} - -func AsV2alpha1(v1 *v1.CheCluster) *v2alpha1.CheCluster { - ret := &v2alpha1.CheCluster{} - V1ToV2alpha1(v1, ret) - return ret -} - -func V1ToV2alpha1(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) error { - v2Data := v1.Annotations[v2alpha1StorageAnnotation] - v2Spec := v2alpha1.CheClusterSpec{} - if v2Data != "" { - err := yaml.Unmarshal([]byte(v2Data), &v2Spec) - if err != nil { - return err - } - } - - v2.ObjectMeta = *v1.ObjectMeta.DeepCopy() - v2.Spec = v2Spec - - v1Spec, err := yaml.Marshal(v1.Spec) - if err != nil { - return err - } - if v2.Annotations == nil { - v2.Annotations = map[string]string{} - } - v2.Annotations[v1StorageAnnotation] = string(v1Spec) - - v1ToV2alpha1_Enabled(v1, v2) - v1ToV2alpha1_Host(v1, v2) - v1ToV2alpha1_GatewayEnabled(v1, v2) - v1ToV2alpha1_GatewayImage(v1, v2) - v1ToV2alpha1_GatewayConfigurerImage(v1, v2) - v1ToV2alpha1_GatewayTlsSecretName(v1, v2) - v1toV2alpha1_GatewayConfigLabels(v1, v2) - v1ToV2alpha1_WorkspaceDomainEndpointsBaseDomain(v1, v2) - v1ToV2alpha1_WorkspaceDomainEndpointsTlsSecretName(v1, v2) - v1ToV2alpha1_WorkspacePodNodeSelector(v1, v2) - if err := v1ToV2alpha1_WorkspacePodTolerations(v1, v2); err != nil { - return err - } - v1ToV2alpha1_K8sIngressAnnotations(v1, v2) - - // we don't need to store the serialized v2 on a v2 object - delete(v2.Annotations, v2alpha1StorageAnnotation) - - return nil -} - -func V2alpha1ToV1(v2 *v2alpha1.CheCluster, v1Obj *v1.CheCluster) error { - v1Data := v2.Annotations[v1StorageAnnotation] - v1Spec := v1.CheClusterSpec{} - if v1Data != "" { - err := yaml.Unmarshal([]byte(v1Data), &v1Spec) - if err != nil { - return err - } - } - - v1Obj.ObjectMeta = *v2.ObjectMeta.DeepCopy() - v1Obj.Spec = v1Spec - v1Obj.Status = v1.CheClusterStatus{} - - v2Spec, err := yaml.Marshal(v2.Spec) - if err != nil { - return err - } - if v1Obj.Annotations == nil { - v1Obj.Annotations = map[string]string{} - } - v1Obj.Annotations[v2alpha1StorageAnnotation] = string(v2Spec) - - v2alpha1ToV1_Host(v1Obj, v2) - v2alpha1ToV1_GatewayImage(v1Obj, v2) - v2alpha1ToV1_GatewayConfigurerImage(v1Obj, v2) - v2alpha1ToV1_GatewayTlsSecretName(v1Obj, v2) - v2alpha1ToV1_GatewayConfigLabels(v1Obj, v2) - v2alpha1ToV1_WorkspaceDomainEndpointsBaseDomain(v1Obj, v2) - v2alpha1ToV1_WorkspaceDomainEndpointsTlsSecretName(v1Obj, v2) - v2alpha1ToV1_WorkspacePodNodeSelector(v1Obj, v2) - v2alpha1ToV1_WorkspacePodTolerations(v1Obj, v2) - v2alpha1ToV1_K8sIngressAnnotations(v1Obj, v2) - - // we don't need to store the serialized v1 on a v1 object - delete(v1Obj.Annotations, v1StorageAnnotation) - - return nil -} - -func v1ToV2alpha1_Enabled(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - v2.Spec.Enabled = pointer.BoolPtr(true) -} - -func v1ToV2alpha1_Host(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - v2.Spec.Gateway.Host = v1.Spec.Server.CheHost -} - -func v1ToV2alpha1_WorkspaceDomainEndpointsBaseDomain(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - if util.IsOpenShift { - v2.Spec.Workspaces.DomainEndpoints.BaseDomain = v1.Spec.Server.CustomCheProperties[routeDomainSuffixPropertyKey] - } else { - v2.Spec.Workspaces.DomainEndpoints.BaseDomain = v1.Spec.K8s.IngressDomain - } -} - -func v1ToV2alpha1_WorkspaceDomainEndpointsTlsSecretName(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - // Che server always uses the default cluster certificate for subdomain workspace endpoints on OpenShift, and the K8s.TlsSecretName on K8s. - // Because we're dealing with endpoints, let's try to use the secret on Kubernetes and nothing (e.g. the default cluster cert on OpenShift) - // which is in line with the logic of the Che server. - if !util.IsOpenShift { - v2.Spec.Workspaces.DomainEndpoints.TlsSecretName = v1.Spec.K8s.TlsSecretName - } -} - -func v1ToV2alpha1_WorkspacePodNodeSelector(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - selector := v1.Spec.Server.WorkspacePodNodeSelector - if len(selector) == 0 { - prop := v1.Spec.Server.CustomCheProperties["CHE_WORKSPACE_POD_NODE__SELECTOR"] - if prop != "" { - selector = map[string]string{} - kvs := strings.Split(prop, ",") - for _, pair := range kvs { - kv := strings.Split(pair, "=") - if len(kv) == 2 { - selector[kv[0]] = kv[1] - } - } - } - } - v2.Spec.Workspaces.PodNodeSelector = selector -} - -func v1ToV2alpha1_WorkspacePodTolerations(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) error { - tolerations := v1.Spec.Server.WorkspacePodTolerations - - if len(tolerations) == 0 { - prop := v1.Spec.Server.CustomCheProperties["CHE_WORKSPACE_POD_TOLERATIONS__JSON"] - if prop != "" { - tols := []corev1.Toleration{} - if err := json.Unmarshal([]byte(prop), &tols); err != nil { - return err - } - tolerations = tols - } - } - - v2.Spec.Workspaces.PodTolerations = tolerations - return nil -} - -func v1ToV2alpha1_GatewayEnabled(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - // On Kubernetes, we can have single-host realized using ingresses (that use the same host but different paths). - // This is actually not supported on DWCO where we always use the gateway for that. So here, we actually just - // ignore the Spec.K8s.SingleHostExposureType, but we need to be aware of it when converting back. - // Note that default-host is actually not supported on v2, but it is similar enough to default host that we - // treat it as such for v2. The difference between default-host and single-host is that the default-host uses - // the cluster domain itself as the base domain whereas single-host uses a configured domain. In v2 we always - // need a domain configured. - v2.Spec.Gateway.Enabled = pointer.BoolPtr(true) -} - -func v1ToV2alpha1_GatewayImage(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - v2.Spec.Gateway.Image = util.GetValue(v1.Spec.Server.SingleHostGatewayImage, deploy.DefaultSingleHostGatewayImage(v1)) -} - -func v1ToV2alpha1_GatewayConfigurerImage(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - v2.Spec.Gateway.ConfigurerImage = v1.Spec.Server.SingleHostGatewayConfigSidecarImage -} - -func v1ToV2alpha1_GatewayTlsSecretName(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - // v1.Spec.Server.CheHostTLSSecret is used specifically for Che Host - i.e. the host under which che server is deployed. - // In DW we would only used that for subpath endpoints but wouldn't know what TLS to use for subdomain endpoints. - - v2.Spec.Gateway.TlsSecretName = v1.Spec.Server.CheHostTLSSecret -} - -func v1toV2alpha1_GatewayConfigLabels(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - v2.Spec.Gateway.ConfigLabels = v1.Spec.Server.SingleHostGatewayConfigMapLabels -} - -func v1ToV2alpha1_K8sIngressAnnotations(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - // The only property in v1 spec that boils down to the ingress annotations is the K8s.IngressClass - if v1.Spec.K8s.IngressClass != "" && v1.Spec.K8s.IngressClass != defaultV2alpha1IngressClass { - if v2.Spec.K8s.IngressAnnotations == nil { - v2.Spec.K8s.IngressAnnotations = map[string]string{} - } - v2.Spec.K8s.IngressAnnotations["kubernetes.io/ingress.class"] = v1.Spec.K8s.IngressClass - } -} - -func v2alpha1ToV1_Host(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - v1.Spec.Server.CheHost = v2.Spec.Gateway.Host -} - -func v2alpha1ToV1_WorkspaceDomainEndpointsBaseDomain(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - if util.IsOpenShift { - if v1.Spec.Server.CustomCheProperties == nil { - v1.Spec.Server.CustomCheProperties = map[string]string{} - } - if len(v2.Spec.Workspaces.DomainEndpoints.BaseDomain) > 0 { - v1.Spec.Server.CustomCheProperties[routeDomainSuffixPropertyKey] = v2.Spec.Workspaces.DomainEndpoints.BaseDomain - } - } else { - v1.Spec.K8s.IngressDomain = v2.Spec.Workspaces.DomainEndpoints.BaseDomain - } -} - -func v2alpha1ToV1_WorkspaceDomainEndpointsTlsSecretName(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - // see the comments in the v1 to v2alpha1 conversion method - if !util.IsOpenShift { - v1.Spec.K8s.TlsSecretName = v2.Spec.Workspaces.DomainEndpoints.TlsSecretName - } -} - -func v2alpha1ToV1_WorkspacePodNodeSelector(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - v1.Spec.Server.WorkspacePodNodeSelector = v2.Spec.Workspaces.PodNodeSelector -} - -func v2alpha1ToV1_WorkspacePodTolerations(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - v1.Spec.Server.WorkspacePodTolerations = v2.Spec.Workspaces.PodTolerations -} - -func v2alpha1ToV1_GatewayImage(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - v1.Spec.Server.SingleHostGatewayImage = v2.Spec.Gateway.Image -} - -func v2alpha1ToV1_GatewayConfigurerImage(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - v1.Spec.Server.SingleHostGatewayConfigSidecarImage = v2.Spec.Gateway.ConfigurerImage -} - -func v2alpha1ToV1_GatewayTlsSecretName(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - // see the comments in the v1 to v2alpha1 conversion method - v1.Spec.Server.CheHostTLSSecret = v2.Spec.Gateway.TlsSecretName -} - -func v2alpha1ToV1_GatewayConfigLabels(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - v1.Spec.Server.SingleHostGatewayConfigMapLabels = v2.Spec.Gateway.ConfigLabels -} - -func v2alpha1ToV1_K8sIngressAnnotations(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - ingressClass := v2.Spec.K8s.IngressAnnotations["kubernetes.io/ingress.class"] - if ingressClass == "" { - ingressClass = defaultV2alpha1IngressClass - } - if v1.Spec.K8s.IngressClass != "" || ingressClass != defaultV1IngressClass { - v1.Spec.K8s.IngressClass = ingressClass - } -} diff --git a/api/conversion_test.go b/api/conversion_test.go deleted file mode 100644 index f4db68e5d..000000000 --- a/api/conversion_test.go +++ /dev/null @@ -1,643 +0,0 @@ -// -// Copyright (c) 2019-2021 Red Hat, Inc. -// This program and the accompanying materials are made -// available under the terms of the Eclipse Public License 2.0 -// which is available at https://www.eclipse.org/legal/epl-2.0/ -// -// SPDX-License-Identifier: EPL-2.0 -// -// Contributors: -// Red Hat, Inc. - initial API and implementation -// - -package org - -import ( - "encoding/json" - "reflect" - "testing" - - corev1 "k8s.io/api/core/v1" - - "github.com/stretchr/testify/assert" - - "github.com/che-incubator/kubernetes-image-puller-operator/api/v1alpha1" - v1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/api/v2alpha1" - "github.com/eclipse-che/che-operator/pkg/util" - "github.com/google/go-cmp/cmp" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/utils/pointer" - "sigs.k8s.io/yaml" -) - -func TestV1ToV2alpha1(t *testing.T) { - tolerations := []corev1.Toleration{ - { - Key: "a", - Operator: corev1.TolerationOpEqual, - Value: "b", - }, - } - - tolBytes, err := json.Marshal(tolerations) - assert.NoError(t, err) - - tolerationStr := string(tolBytes) - - v1Obj := v1.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: "che-cluster", - Annotations: map[string]string{ - "anno1": "annoValue1", - "anno2": "annoValue2", - }, - }, - Spec: v1.CheClusterSpec{ - Auth: v1.CheClusterSpecAuth{ - IdentityProviderURL: "kachny", - }, - Database: v1.CheClusterSpecDB{ - ExternalDb: true, - PostgresImage: "postgres:the-best-version", - }, - DevWorkspace: v1.CheClusterSpecDevWorkspace{}, - ImagePuller: v1.CheClusterSpecImagePuller{ - Spec: v1alpha1.KubernetesImagePullerSpec{ - ConfigMapName: "pulled-kachna", - }, - }, - K8s: v1.CheClusterSpecK8SOnly{ - IngressDomain: "ingressDomain", - IngressClass: "traefik", - TlsSecretName: "k8sSecret", - }, - Metrics: v1.CheClusterSpecMetrics{ - Enable: true, - }, - Server: v1.CheClusterSpecServer{ - CheHost: "cheHost", - CheImage: "teh-che-severe", - SingleHostGatewayImage: "single-host-image-of-the-year", - CheHostTLSSecret: "cheSecret", - SingleHostGatewayConfigMapLabels: labels.Set{ - "a": "b", - "c": "d", - }, - CustomCheProperties: map[string]string{ - "CHE_INFRA_OPENSHIFT_ROUTE_HOST_DOMAIN__SUFFIX": "routeDomain", - "CHE_WORKSPACE_POD_TOLERATIONS__JSON": tolerationStr, - "CHE_WORKSPACE_POD_NODE__SELECTOR": "a=b,c=d", - }, - }, - Storage: v1.CheClusterSpecStorage{ - PvcStrategy: "common", - }, - }, - } - - t.Run("origInAnnos", func(t *testing.T) { - v2 := &v2alpha1.CheCluster{} - err := V1ToV2alpha1(&v1Obj, v2) - if err != nil { - t.Error(err) - } - - anno1 := v2.Annotations["anno1"] - anno2 := v2.Annotations["anno2"] - storedV1 := v2.Annotations[v1StorageAnnotation] - - if anno1 != "annoValue1" { - t.Errorf("anno1 not copied") - } - - if anno2 != "annoValue2" { - t.Errorf("anno2 not copied") - } - - if storedV1 == "" { - t.Errorf("v2 should contain v1 data in annnotation") - } - - restoredV1Spec := v1.CheClusterSpec{} - if err = yaml.Unmarshal([]byte(storedV1), &restoredV1Spec); err != nil { - t.Error(err) - } - - if !reflect.DeepEqual(&v1Obj.Spec, &restoredV1Spec) { - t.Errorf("The spec should be restored verbatim from the annotations, but there's a diff %s", cmp.Diff(&v1Obj.Spec, &restoredV1Spec)) - } - }) - - t.Run("Enabled", func(t *testing.T) { - v2 := &v2alpha1.CheCluster{} - err := V1ToV2alpha1(&v1Obj, v2) - if err != nil { - t.Error(err) - } - - if !*v2.Spec.Enabled { - t.Errorf("Unexpected v2.Spec.Enabled: %t", *v2.Spec.Enabled) - } - }) - - t.Run("Host-k8s", func(t *testing.T) { - v2 := &v2alpha1.CheCluster{} - err := V1ToV2alpha1(&v1Obj, v2) - if err != nil { - t.Error(err) - } - - if v2.Spec.Gateway.Host != "cheHost" { - t.Errorf("Unexpected v2.Spec.Host: %s", v2.Spec.Gateway.Host) - } - }) - - t.Run("WorkspaceDomainEndpointsBaseDomain-k8s", func(t *testing.T) { - onFakeKubernetes(func() { - v2 := &v2alpha1.CheCluster{} - err := V1ToV2alpha1(&v1Obj, v2) - if err != nil { - t.Error(err) - } - - if v2.Spec.Workspaces.DomainEndpoints.BaseDomain != "ingressDomain" { - t.Errorf("Unexpected v2.Spec.Workspaces.DomainEndpoints.BaseDomain: %s", v2.Spec.Workspaces.DomainEndpoints.BaseDomain) - } - }) - }) - - t.Run("WorkspaceDomainEndpointsBaseDomain-opensfhit", func(t *testing.T) { - onFakeOpenShift(func() { - v2 := &v2alpha1.CheCluster{} - err := V1ToV2alpha1(&v1Obj, v2) - if err != nil { - t.Error(err) - } - - if v2.Spec.Workspaces.DomainEndpoints.BaseDomain != "routeDomain" { - t.Errorf("Unexpected v2.Spec.Workspaces.DomainEndpoints.BaseDomain: %s", v2.Spec.Workspaces.DomainEndpoints.BaseDomain) - } - }) - }) - - t.Run("WorkspaceDomainEndpointsTlsSecretName_k8s", func(t *testing.T) { - onFakeKubernetes(func() { - v2 := &v2alpha1.CheCluster{} - err := V1ToV2alpha1(&v1Obj, v2) - if err != nil { - t.Error(err) - } - - if v2.Spec.Workspaces.DomainEndpoints.TlsSecretName != "k8sSecret" { - t.Errorf("Unexpected TlsSecretName") - } - }) - }) - - t.Run("WorkspaceDomainEndpointsTlsSecretName_OpenShift", func(t *testing.T) { - onFakeOpenShift(func() { - v2 := &v2alpha1.CheCluster{} - err := V1ToV2alpha1(&v1Obj, v2) - if err != nil { - t.Error(err) - } - - if v2.Spec.Workspaces.DomainEndpoints.TlsSecretName != "" { - t.Errorf("Unexpected TlsSecretName") - } - }) - }) - - t.Run("GatewayEnabled", func(t *testing.T) { - onFakeOpenShift(func() { - v2 := &v2alpha1.CheCluster{} - err := V1ToV2alpha1(&v1Obj, v2) - if err != nil { - t.Error(err) - } - - if v2.Spec.Gateway.Enabled == nil { - t.Logf("The gateway.enabled attribute should be set explicitly after the conversion.") - t.FailNow() - } - - if !*v2.Spec.Gateway.Enabled { - t.Errorf("The default for OpenShift is single") - } - }) - }) - - t.Run("GatewayImage", func(t *testing.T) { - v2 := &v2alpha1.CheCluster{} - err := V1ToV2alpha1(&v1Obj, v2) - if err != nil { - t.Error(err) - } - - if v2.Spec.Gateway.Image != "single-host-image-of-the-year" { - t.Errorf("Unexpected gateway image") - } - }) - - t.Run("GatewayTlsSecretName", func(t *testing.T) { - v2 := &v2alpha1.CheCluster{} - err := V1ToV2alpha1(&v1Obj, v2) - if err != nil { - t.Error(err) - } - - if v2.Spec.Gateway.TlsSecretName != "cheSecret" { - t.Errorf("Unexpected TlsSecretName") - } - }) - - t.Run("GatewayConfigLabels", func(t *testing.T) { - v2 := &v2alpha1.CheCluster{} - err := V1ToV2alpha1(&v1Obj, v2) - if err != nil { - t.Error(err) - } - - if !reflect.DeepEqual(v2.Spec.Gateway.ConfigLabels, v1Obj.Spec.Server.SingleHostGatewayConfigMapLabels) { - t.Errorf("Unexpected Spec.Gateway.ConfigLabels: %v", cmp.Diff(v1Obj.Spec.Server.SingleHostGatewayConfigMapLabels, v2.Spec.Gateway.ConfigLabels)) - } - }) - - t.Run("WorkspacePodSelector", func(t *testing.T) { - v2 := &v2alpha1.CheCluster{} - assert.NoError(t, V1ToV2alpha1(&v1Obj, v2)) - assert.Equal(t, map[string]string{"a": "b", "c": "d"}, v2.Spec.Workspaces.PodNodeSelector) - }) - - t.Run("WorkspacePodTolerations", func(t *testing.T) { - v2 := &v2alpha1.CheCluster{} - assert.NoError(t, V1ToV2alpha1(&v1Obj, v2)) - assert.Equal(t, tolerations, v2.Spec.Workspaces.PodTolerations) - }) -} - -func TestV2alpha1ToV1(t *testing.T) { - v2Obj := v2alpha1.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: "che-cluster", - Annotations: map[string]string{ - "anno1": "annoValue1", - "anno2": "annoValue2", - }, - }, - Spec: v2alpha1.CheClusterSpec{ - Enabled: pointer.BoolPtr(true), - Workspaces: v2alpha1.Workspaces{ - DomainEndpoints: v2alpha1.DomainEndpoints{ - BaseDomain: "baseDomain", - TlsSecretName: "workspaceSecret", - }, - PodNodeSelector: map[string]string{"a": "b", "c": "d"}, - PodTolerations: []corev1.Toleration{ - { - Key: "a", - Operator: corev1.TolerationOpEqual, - Value: "b", - }, - }, - }, - Gateway: v2alpha1.CheGatewaySpec{ - Host: "v2Host", - Enabled: pointer.BoolPtr(true), - Image: "gateway-image", - ConfigurerImage: "configurer-image", - TlsSecretName: "superSecret", - ConfigLabels: labels.Set{ - "a": "b", - }, - }, - K8s: v2alpha1.CheClusterSpecK8s{ - IngressAnnotations: map[string]string{ - "kubernetes.io/ingress.class": "some-other-ingress", - "a": "b", - }, - }, - }, - } - - t.Run("origInAnnos", func(t *testing.T) { - v1 := &v1.CheCluster{} - err := V2alpha1ToV1(&v2Obj, v1) - if err != nil { - t.Error(err) - } - - anno1 := v1.Annotations["anno1"] - anno2 := v1.Annotations["anno2"] - storedV2 := v1.Annotations[v2alpha1StorageAnnotation] - - if anno1 != "annoValue1" { - t.Errorf("anno1 not copied") - } - - if anno2 != "annoValue2" { - t.Errorf("anno2 not copied") - } - - if storedV2 == "" { - t.Errorf("v1 should contain v2 data in annnotation") - } - - restoredV2Spec := v2alpha1.CheClusterSpec{} - yaml.Unmarshal([]byte(storedV2), &restoredV2Spec) - - if !reflect.DeepEqual(&v2Obj.Spec, &restoredV2Spec) { - t.Errorf("The spec should be restored verbatim from the annotations, but there's a diff %s", cmp.Diff(&v2Obj.Spec, &restoredV2Spec)) - } - }) - - t.Run("Host", func(t *testing.T) { - v1 := &v1.CheCluster{} - err := V2alpha1ToV1(&v2Obj, v1) - if err != nil { - t.Error(err) - } - - if v1.Spec.Server.CheHost != "v2Host" { - t.Errorf("Unexpected v1.Spec.Server.CheHost: %s", v1.Spec.Server.CheHost) - } - }) - - t.Run("WorkspaceDomainEndpointsBaseDomain-k8s", func(t *testing.T) { - onFakeKubernetes(func() { - v1 := &v1.CheCluster{} - err := V2alpha1ToV1(&v2Obj, v1) - if err != nil { - t.Error(err) - } - - if v1.Spec.K8s.IngressDomain != "baseDomain" { - t.Errorf("Unexpected v1.Spec.K8s.IngressDomain: %s", v1.Spec.K8s.IngressDomain) - } - }) - }) - - t.Run("WorkspaceDomainEndpointsBaseDomain-openshift", func(t *testing.T) { - onFakeOpenShift(func() { - v1 := &v1.CheCluster{} - err := V2alpha1ToV1(&v2Obj, v1) - if err != nil { - t.Error(err) - } - - if v1.Spec.Server.CustomCheProperties[routeDomainSuffixPropertyKey] != "baseDomain" { - t.Errorf("Unexpected v1.Spec.Server.CustomCheProperties[%s]: %s", routeDomainSuffixPropertyKey, v1.Spec.Server.CustomCheProperties[routeDomainSuffixPropertyKey]) - } - }) - }) - - t.Run("WorkspaceDomainEndpointsBaseDomain-openshift-should-not-be-set-empty-value", func(t *testing.T) { - onFakeOpenShift(func() { - v1 := &v1.CheCluster{} - v2apha := v2Obj.DeepCopy() - v2apha.Spec.Workspaces.DomainEndpoints.BaseDomain = "" - err := V2alpha1ToV1(v2apha, v1) - if err != nil { - t.Error(err) - } - - if _, ok := v1.Spec.Server.CustomCheProperties[routeDomainSuffixPropertyKey]; ok { - t.Errorf("Unexpected value. We shouldn't set key with empty value for %s custom Che property", routeDomainSuffixPropertyKey) - } - }) - }) - - t.Run("WorkspaceDomainEndpointsTlsSecretName_k8s", func(t *testing.T) { - onFakeKubernetes(func() { - v1 := &v1.CheCluster{} - err := V2alpha1ToV1(&v2Obj, v1) - if err != nil { - t.Error(err) - } - - if v1.Spec.K8s.TlsSecretName != "workspaceSecret" { - t.Errorf("Unexpected TlsSecretName: %s", v1.Spec.K8s.TlsSecretName) - } - }) - }) - - t.Run("WorkspaceDomainEndpointsTlsSecretName_OpenShift", func(t *testing.T) { - onFakeOpenShift(func() { - v1 := &v1.CheCluster{} - err := V2alpha1ToV1(&v2Obj, v1) - if err != nil { - t.Error(err) - } - - if v1.Spec.K8s.TlsSecretName != "" { - t.Errorf("Unexpected TlsSecretName") - } - }) - }) - - t.Run("GatewayEnabled", func(t *testing.T) { - onFakeOpenShift(func() { - v1 := &v1.CheCluster{} - err := V2alpha1ToV1(&v2Obj, v1) - if err != nil { - t.Error(err) - } - }) - }) - - t.Run("GatewayImage", func(t *testing.T) { - v1 := &v1.CheCluster{} - err := V2alpha1ToV1(&v2Obj, v1) - if err != nil { - t.Error(err) - } - - if v1.Spec.Server.SingleHostGatewayImage != "gateway-image" { - t.Errorf("Unexpected gateway image") - } - }) - - t.Run("GatewayTlsSecretName", func(t *testing.T) { - v1 := &v1.CheCluster{} - err := V2alpha1ToV1(&v2Obj, v1) - if err != nil { - t.Error(err) - } - - if v1.Spec.Server.CheHostTLSSecret != "superSecret" { - t.Errorf("Unexpected TlsSecretName: %s", v1.Spec.Server.CheHostTLSSecret) - } - }) - - t.Run("GatewayConfigLabels", func(t *testing.T) { - v1 := &v1.CheCluster{} - err := V2alpha1ToV1(&v2Obj, v1) - if err != nil { - t.Error(err) - } - - if !reflect.DeepEqual(v1.Spec.Server.SingleHostGatewayConfigMapLabels, v2Obj.Spec.Gateway.ConfigLabels) { - t.Errorf("Unexpected SingleHostGatewayConfigMapLabels: %s", v1.Spec.Server.SingleHostGatewayConfigMapLabels) - } - }) - - t.Run("WorkspacePodNodeSelector", func(t *testing.T) { - v1 := &v1.CheCluster{} - assert.NoError(t, V2alpha1ToV1(&v2Obj, v1)) - assert.Equal(t, map[string]string{"a": "b", "c": "d"}, v1.Spec.Server.WorkspacePodNodeSelector) - }) - - t.Run("WorkspacePodTolerations", func(t *testing.T) { - v1 := &v1.CheCluster{} - assert.NoError(t, V2alpha1ToV1(&v2Obj, v1)) - assert.Equal(t, []corev1.Toleration{{ - Key: "a", - Operator: corev1.TolerationOpEqual, - Value: "b", - }}, v1.Spec.Server.WorkspacePodTolerations) - - }) -} - -func TestFullCircleV1(t *testing.T) { - v1Obj := v1.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: "che-cluster", - Annotations: map[string]string{ - "anno1": "annoValue1", - "anno2": "annoValue2", - }, - }, - Spec: v1.CheClusterSpec{ - Auth: v1.CheClusterSpecAuth{ - IdentityProviderURL: "kachny", - }, - Database: v1.CheClusterSpecDB{ - ExternalDb: true, - PostgresImage: "postgres:the-best-version", - }, - DevWorkspace: v1.CheClusterSpecDevWorkspace{}, - ImagePuller: v1.CheClusterSpecImagePuller{ - Spec: v1alpha1.KubernetesImagePullerSpec{ - ConfigMapName: "pulled-kachna", - }, - }, - K8s: v1.CheClusterSpecK8SOnly{ - IngressDomain: "ingressDomain", - IngressClass: "traefik", - TlsSecretName: "k8sSecret", - }, - Metrics: v1.CheClusterSpecMetrics{ - Enable: true, - }, - Server: v1.CheClusterSpecServer{ - CheHost: "cheHost", - CheImage: "teh-che-severe", - SingleHostGatewayImage: "single-host-image-of-the-year", - CheHostTLSSecret: "cheSecret", - SingleHostGatewayConfigMapLabels: labels.Set{ - "a": "b", - }, - CustomCheProperties: map[string]string{ - "CHE_INFRA_OPENSHIFT_ROUTE_HOST_DOMAIN__SUFFIX": "routeDomain", - }, - }, - Storage: v1.CheClusterSpecStorage{ - PvcStrategy: "common", - }, - }, - } - - v2Obj := v2alpha1.CheCluster{} - assert.NoError(t, V1ToV2alpha1(&v1Obj, &v2Obj)) - - convertedV1 := v1.CheCluster{} - assert.NoError(t, V2alpha1ToV1(&v2Obj, &convertedV1)) - - assert.Empty(t, convertedV1.Annotations[v1StorageAnnotation]) - assert.NotEmpty(t, convertedV1.Annotations[v2alpha1StorageAnnotation]) - - // remove v2 content annotation on the convertedV1 so that it doesn't interfere with the equality. - delete(convertedV1.Annotations, v2alpha1StorageAnnotation) - - assert.Equal(t, &v1Obj, &convertedV1) -} - -func TestFullCircleV2(t *testing.T) { - v2Obj := v2alpha1.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: "che-cluster", - Annotations: map[string]string{ - "anno1": "annoValue1", - "anno2": "annoValue2", - }, - }, - Spec: v2alpha1.CheClusterSpec{ - Enabled: pointer.BoolPtr(true), - Workspaces: v2alpha1.Workspaces{ - DomainEndpoints: v2alpha1.DomainEndpoints{ - BaseDomain: "baseDomain", - TlsSecretName: "workspaceSecret", - }, - }, - Gateway: v2alpha1.CheGatewaySpec{ - Host: "v2Host", - Enabled: pointer.BoolPtr(true), - Image: "gateway-image", - ConfigurerImage: "configurer-image", - TlsSecretName: "superSecret", - ConfigLabels: labels.Set{ - "a": "b", - }, - }, - K8s: v2alpha1.CheClusterSpecK8s{ - IngressAnnotations: map[string]string{ - "kubernetes.io/ingress.class": "some-other-ingress", - "a": "b", - }, - }, - }, - } - - v1Obj := v1.CheCluster{} - assert.NoError(t, V2alpha1ToV1(&v2Obj, &v1Obj)) - - convertedV2 := v2alpha1.CheCluster{} - assert.NoError(t, V1ToV2alpha1(&v1Obj, &convertedV2)) - - assert.Empty(t, convertedV2.Annotations[v2alpha1StorageAnnotation]) - assert.NotEmpty(t, convertedV2.Annotations[v1StorageAnnotation]) - - // remove v1 content annotation on the convertedV1 so that it doesn't interfere with the equality. - delete(convertedV2.Annotations, v1StorageAnnotation) - - assert.Equal(t, &v2Obj, &convertedV2) -} - -func onFakeOpenShift(f func()) { - origOpenshift := util.IsOpenShift - origOpenshift4 := util.IsOpenShift4 - - util.IsOpenShift = true - util.IsOpenShift4 = true - - f() - - util.IsOpenShift = origOpenshift - util.IsOpenShift4 = origOpenshift4 -} - -func onFakeKubernetes(f func()) { - origOpenshift := util.IsOpenShift - origOpenshift4 := util.IsOpenShift4 - - util.IsOpenShift = false - util.IsOpenShift4 = false - - f() - - util.IsOpenShift = origOpenshift - util.IsOpenShift4 = origOpenshift4 -} diff --git a/api/init_test.go b/api/init_test.go new file mode 100644 index 000000000..074585ae1 --- /dev/null +++ b/api/init_test.go @@ -0,0 +1,25 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// +package org + +import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" +) + +func init() { + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../config/manager/manager.yaml") +} diff --git a/api/v1/checluster_conversion_from.go b/api/v1/checluster_conversion_from.go new file mode 100644 index 000000000..4c24783b4 --- /dev/null +++ b/api/v1/checluster_conversion_from.go @@ -0,0 +1,356 @@ +// +// Copyright (c) 2019-2022 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package v1 + +import ( + "context" + "strconv" + "strings" + + "github.com/eclipse-che/che-operator/pkg/common/utils" + + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/constants" + k8shelper "github.com/eclipse-che/che-operator/pkg/common/k8s-helper" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +func (dst *CheCluster) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*chev2.CheCluster) + dst.ObjectMeta = src.ObjectMeta + + if err := dst.convertFrom_Server(src); err != nil { + return err + } + + if err := dst.convertFrom_K8s(src); err != nil { + return err + } + + if err := dst.convertFrom_Auth(src); err != nil { + return err + } + + if err := dst.convertFrom_Database(src); err != nil { + return err + } + + if err := dst.convertFrom_DevWorkspace(src); err != nil { + return err + } + + if err := dst.convertFrom_Dashboard(src); err != nil { + return err + } + + if err := dst.convertFrom_Metrics(src); err != nil { + return err + } + + if err := dst.convertFrom_ImagePuller(src); err != nil { + return err + } + + if err := dst.convertFrom_Storage(src); err != nil { + return err + } + + if err := dst.convertFrom_Status(src); err != nil { + return err + } + + return nil +} + +func (dst *CheCluster) convertFrom_Server(src *chev2.CheCluster) error { + dst.Spec.Server.AirGapContainerRegistryHostname = src.Spec.ContainerRegistry.Hostname + dst.Spec.Server.AirGapContainerRegistryOrganization = src.Spec.ContainerRegistry.Organization + dst.Spec.Server.CheClusterRoles = strings.Join(src.Spec.Components.CheServer.ClusterRoles, ",") + dst.Spec.Server.CustomCheProperties = utils.CloneMap(src.Spec.Components.CheServer.ExtraProperties) + if src.Spec.Components.CheServer.Debug != nil { + dst.Spec.Server.CheDebug = strconv.FormatBool(*src.Spec.Components.CheServer.Debug) + } + dst.Spec.Server.CheLogLevel = src.Spec.Components.CheServer.LogLevel + dst.Spec.Server.ProxyURL = src.Spec.Components.CheServer.Proxy.Url + dst.Spec.Server.ProxyPort = src.Spec.Components.CheServer.Proxy.Port + dst.Spec.Server.NonProxyHosts = strings.Join(src.Spec.Components.CheServer.Proxy.NonProxyHosts, "|") + dst.Spec.Server.ProxySecret = src.Spec.Components.CheServer.Proxy.CredentialsSecretName + dst.Spec.Server.WorkspaceNamespaceDefault = src.Spec.DevEnvironments.DefaultNamespace.Template + dst.Spec.Server.WorkspacePodNodeSelector = utils.CloneMap(src.Spec.DevEnvironments.NodeSelector) + + for _, v := range src.Spec.DevEnvironments.Tolerations { + dst.Spec.Server.WorkspacePodTolerations = append(dst.Spec.Server.WorkspacePodTolerations, v) + } + + for _, p := range src.Spec.DevEnvironments.DefaultPlugins { + dst.Spec.Server.WorkspacesDefaultPlugins = append(dst.Spec.Server.WorkspacesDefaultPlugins, + WorkspacesDefaultPlugins{ + Editor: p.Editor, + Plugins: p.Plugins, + }) + } + + if len(src.Spec.Components.CheServer.Deployment.Containers) != 0 { + cheServerImageAndTag := strings.Split(src.Spec.Components.CheServer.Deployment.Containers[0].Image, ":") + dst.Spec.Server.CheImage = strings.Join(cheServerImageAndTag[0:len(cheServerImageAndTag)-1], ":") + dst.Spec.Server.CheImageTag = cheServerImageAndTag[len(cheServerImageAndTag)-1] + dst.Spec.Server.CheImagePullPolicy = src.Spec.Components.CheServer.Deployment.Containers[0].ImagePullPolicy + dst.Spec.Server.ServerMemoryRequest = src.Spec.Components.CheServer.Deployment.Containers[0].Resources.Requests.Memory.String() + dst.Spec.Server.ServerCpuRequest = src.Spec.Components.CheServer.Deployment.Containers[0].Resources.Requests.Cpu.String() + dst.Spec.Server.ServerMemoryLimit = src.Spec.Components.CheServer.Deployment.Containers[0].Resources.Limits.Memory.String() + dst.Spec.Server.ServerCpuLimit = src.Spec.Components.CheServer.Deployment.Containers[0].Resources.Limits.Cpu.String() + } + + if infrastructure.IsOpenShift() { + dst.Spec.Server.CheHost = src.Spec.Networking.Hostname + dst.Spec.Server.CheServerRoute.Labels = utils.FormatLabels(src.Spec.Networking.Labels) + dst.Spec.Server.CheServerRoute.Annotations = utils.CloneMap(src.Spec.Networking.Annotations) + dst.Spec.Server.CheServerRoute.Domain = src.Spec.Networking.Domain + dst.Spec.Server.CheHostTLSSecret = src.Spec.Networking.TlsSecretName + } else { + dst.Spec.Server.CheHost = src.Spec.Networking.Hostname + dst.Spec.Server.CheServerIngress.Labels = utils.FormatLabels(src.Spec.Networking.Labels) + dst.Spec.Server.CheServerIngress.Annotations = utils.CloneMap(src.Spec.Networking.Annotations) + delete(dst.Spec.Server.CheServerIngress.Annotations, "kubernetes.io/ingress.class") + } + + for _, c := range src.Spec.Networking.Auth.Gateway.Deployment.Containers { + switch c.Name { + case constants.GatewayContainerName: + dst.Spec.Server.SingleHostGatewayImage = c.Image + case constants.GatewayConfigSideCarContainerName: + dst.Spec.Server.SingleHostGatewayConfigSidecarImage = c.Image + } + } + + dst.Spec.Server.SingleHostGatewayConfigMapLabels = utils.CloneMap(src.Spec.Networking.Auth.Gateway.ConfigLabels) + + trustStoreConfigMap, err := findTrustStoreConfigMap(src.Namespace) + if err != nil { + return err + } else { + dst.Spec.Server.ServerTrustStoreConfigMapName = trustStoreConfigMap + } + + if src.Spec.DevEnvironments.TrustedCerts.GitTrustedCertsConfigMapName != "" { + dst.Spec.Server.GitSelfSignedCert = true + } + + if err := dst.convertFrom_Server_PluginRegistry(src); err != nil { + return err + } + + if err := dst.convertFrom_Server_DevfileRegistry(src); err != nil { + return err + } + + if err := dst.convertFrom_Server_Dashboard(src); err != nil { + return err + } + + return nil +} + +func (dst *CheCluster) convertFrom_Server_PluginRegistry(src *chev2.CheCluster) error { + dst.Spec.Server.ExternalPluginRegistry = src.Spec.Components.PluginRegistry.DisableInternalRegistry + + if src.Spec.Components.PluginRegistry.DisableInternalRegistry { + if len(src.Spec.Components.PluginRegistry.ExternalPluginRegistries) != 0 { + dst.Spec.Server.PluginRegistryUrl = src.Spec.Components.PluginRegistry.ExternalPluginRegistries[0].Url + } + } + + if len(src.Spec.Components.PluginRegistry.Deployment.Containers) != 0 { + dst.Spec.Server.PluginRegistryImage = src.Spec.Components.PluginRegistry.Deployment.Containers[0].Image + dst.Spec.Server.PluginRegistryPullPolicy = src.Spec.Components.PluginRegistry.Deployment.Containers[0].ImagePullPolicy + dst.Spec.Server.PluginRegistryMemoryRequest = src.Spec.Components.PluginRegistry.Deployment.Containers[0].Resources.Requests.Memory.String() + dst.Spec.Server.PluginRegistryCpuRequest = src.Spec.Components.PluginRegistry.Deployment.Containers[0].Resources.Requests.Cpu.String() + dst.Spec.Server.PluginRegistryMemoryLimit = src.Spec.Components.PluginRegistry.Deployment.Containers[0].Resources.Limits.Memory.String() + dst.Spec.Server.PluginRegistryCpuLimit = src.Spec.Components.PluginRegistry.Deployment.Containers[0].Resources.Limits.Cpu.String() + } + + return nil +} + +func (dst *CheCluster) convertFrom_Server_DevfileRegistry(src *chev2.CheCluster) error { + dst.Spec.Server.ExternalDevfileRegistry = src.Spec.Components.DevfileRegistry.DisableInternalRegistry + + for _, r := range src.Spec.Components.DevfileRegistry.ExternalDevfileRegistries { + dst.Spec.Server.ExternalDevfileRegistries = append(dst.Spec.Server.ExternalDevfileRegistries, + ExternalDevfileRegistries{ + Url: r.Url, + }) + } + + if len(src.Spec.Components.DevfileRegistry.Deployment.Containers) != 0 { + dst.Spec.Server.DevfileRegistryImage = src.Spec.Components.DevfileRegistry.Deployment.Containers[0].Image + dst.Spec.Server.DevfileRegistryPullPolicy = src.Spec.Components.DevfileRegistry.Deployment.Containers[0].ImagePullPolicy + dst.Spec.Server.DevfileRegistryMemoryRequest = src.Spec.Components.DevfileRegistry.Deployment.Containers[0].Resources.Requests.Memory.String() + dst.Spec.Server.DevfileRegistryCpuRequest = src.Spec.Components.DevfileRegistry.Deployment.Containers[0].Resources.Requests.Cpu.String() + dst.Spec.Server.DevfileRegistryMemoryLimit = src.Spec.Components.DevfileRegistry.Deployment.Containers[0].Resources.Limits.Memory.String() + dst.Spec.Server.DevfileRegistryCpuLimit = src.Spec.Components.DevfileRegistry.Deployment.Containers[0].Resources.Limits.Cpu.String() + } + + return nil +} + +func (dst *CheCluster) convertFrom_Server_Dashboard(src *chev2.CheCluster) error { + if len(src.Spec.Components.Dashboard.Deployment.Containers) != 0 { + dst.Spec.Server.DashboardImage = src.Spec.Components.Dashboard.Deployment.Containers[0].Image + dst.Spec.Server.DashboardImagePullPolicy = string(src.Spec.Components.Dashboard.Deployment.Containers[0].ImagePullPolicy) + dst.Spec.Server.DashboardMemoryRequest = src.Spec.Components.Dashboard.Deployment.Containers[0].Resources.Requests.Memory.String() + dst.Spec.Server.DashboardCpuRequest = src.Spec.Components.Dashboard.Deployment.Containers[0].Resources.Requests.Cpu.String() + dst.Spec.Server.DashboardMemoryLimit = src.Spec.Components.Dashboard.Deployment.Containers[0].Resources.Limits.Memory.String() + dst.Spec.Server.DashboardCpuLimit = src.Spec.Components.Dashboard.Deployment.Containers[0].Resources.Limits.Cpu.String() + } + + return nil +} + +func (dst *CheCluster) convertFrom_K8s(src *chev2.CheCluster) error { + if src.Spec.Components.CheServer.Deployment.SecurityContext.RunAsUser != nil { + dst.Spec.K8s.SecurityContextRunAsUser = strconv.FormatInt(*src.Spec.Components.CheServer.Deployment.SecurityContext.RunAsUser, 10) + } + + if src.Spec.Components.CheServer.Deployment.SecurityContext.FsGroup != nil { + dst.Spec.K8s.SecurityContextFsGroup = strconv.FormatInt(*src.Spec.Components.CheServer.Deployment.SecurityContext.FsGroup, 10) + } + + if !infrastructure.IsOpenShift() { + dst.Spec.K8s.IngressDomain = src.Spec.Networking.Domain + dst.Spec.K8s.TlsSecretName = src.Spec.Networking.TlsSecretName + dst.Spec.K8s.IngressClass = src.Spec.Networking.Annotations["kubernetes.io/ingress.class"] + } + + return nil +} + +func (dst *CheCluster) convertFrom_Auth(src *chev2.CheCluster) error { + dst.Spec.Auth.IdentityProviderURL = src.Spec.Networking.Auth.IdentityProviderURL + dst.Spec.Auth.OAuthClientName = src.Spec.Networking.Auth.OAuthClientName + dst.Spec.Auth.OAuthSecret = src.Spec.Networking.Auth.OAuthSecret + + for _, c := range src.Spec.Networking.Auth.Gateway.Deployment.Containers { + switch c.Name { + case constants.GatewayAuthenticationContainerName: + dst.Spec.Auth.GatewayAuthenticationSidecarImage = c.Image + case constants.GatewayAuthorizationContainerName: + dst.Spec.Auth.GatewayAuthorizationSidecarImage = c.Image + } + } + + return nil +} + +func (dst *CheCluster) convertFrom_Database(src *chev2.CheCluster) error { + dst.Spec.Database.ExternalDb = src.Spec.Components.Database.ExternalDb + dst.Spec.Database.ChePostgresDb = src.Spec.Components.Database.PostgresDb + dst.Spec.Database.ChePostgresHostName = src.Spec.Components.Database.PostgresHostName + dst.Spec.Database.ChePostgresPort = src.Spec.Components.Database.PostgresPort + dst.Spec.Database.PostgresVersion = src.Status.PostgresVersion + dst.Spec.Database.PvcClaimSize = src.Spec.Components.Database.Pvc.ClaimSize + dst.Spec.Database.ChePostgresSecret = src.Spec.Components.Database.CredentialsSecretName + + if len(src.Spec.Components.Database.Deployment.Containers) != 0 { + dst.Spec.Database.PostgresImage = src.Spec.Components.Database.Deployment.Containers[0].Image + dst.Spec.Database.PostgresImagePullPolicy = src.Spec.Components.Database.Deployment.Containers[0].ImagePullPolicy + dst.Spec.Database.ChePostgresContainerResources.Requests.Memory = src.Spec.Components.Database.Deployment.Containers[0].Resources.Requests.Memory.String() + dst.Spec.Database.ChePostgresContainerResources.Requests.Cpu = src.Spec.Components.Database.Deployment.Containers[0].Resources.Requests.Cpu.String() + dst.Spec.Database.ChePostgresContainerResources.Limits.Memory = src.Spec.Components.Database.Deployment.Containers[0].Resources.Limits.Memory.String() + dst.Spec.Database.ChePostgresContainerResources.Limits.Cpu = src.Spec.Components.Database.Deployment.Containers[0].Resources.Limits.Cpu.String() + } + + return nil +} + +func (dst *CheCluster) convertFrom_DevWorkspace(src *chev2.CheCluster) error { + if len(src.Spec.Components.DevWorkspace.Deployment.Containers) != 0 { + dst.Spec.DevWorkspace.ControllerImage = src.Spec.Components.DevWorkspace.Deployment.Containers[0].Image + } + dst.Spec.DevWorkspace.RunningLimit = src.Spec.Components.DevWorkspace.RunningLimit + dst.Spec.DevWorkspace.Enable = true + return nil +} + +func (dst *CheCluster) convertFrom_ImagePuller(src *chev2.CheCluster) error { + dst.Spec.ImagePuller.Enable = src.Spec.Components.ImagePuller.Enable + dst.Spec.ImagePuller.Spec = src.Spec.Components.ImagePuller.Spec + + return nil +} + +func (dst *CheCluster) convertFrom_Metrics(src *chev2.CheCluster) error { + dst.Spec.Metrics.Enable = src.Spec.Components.Metrics.Enable + + return nil +} + +func (dst *CheCluster) convertFrom_Dashboard(src *chev2.CheCluster) error { + dst.Spec.Dashboard.Warning = src.Spec.Components.Dashboard.HeaderMessage.Text + + return nil +} + +func (dst *CheCluster) convertFrom_Status(src *chev2.CheCluster) error { + dst.Status.CheURL = src.Status.CheURL + dst.Status.CheVersion = src.Status.CheVersion + dst.Status.DevfileRegistryURL = src.Status.DevfileRegistryURL + dst.Status.PluginRegistryURL = src.Status.PluginRegistryURL + dst.Status.Message = src.Status.Message + dst.Status.Reason = src.Status.Reason + dst.Status.DevworkspaceStatus.GatewayPhase = GatewayPhase(src.Status.GatewayPhase) + dst.Status.DevworkspaceStatus.GatewayHost = src.GetCheHost() + dst.Status.DevworkspaceStatus.WorkspaceBaseDomain = src.Status.WorkspaceBaseDomain + dst.Status.DevworkspaceStatus.Message = src.Status.Message + dst.Status.DevworkspaceStatus.Phase = ClusterPhase(src.Status.ChePhase) + dst.Status.DevworkspaceStatus.Reason = src.Status.Reason + + switch src.Status.ChePhase { + case chev2.ClusterPhaseActive: + dst.Status.CheClusterRunning = "Available" + case chev2.ClusterPhaseInactive: + dst.Status.CheClusterRunning = "Unavailable" + case chev2.RollingUpdate: + dst.Status.CheClusterRunning = "Available, Rolling Update in Progress" + } + + if src.Spec.DevEnvironments.TrustedCerts.GitTrustedCertsConfigMapName != "" { + dst.Status.GitServerTLSCertificateConfigMapName = src.Spec.DevEnvironments.TrustedCerts.GitTrustedCertsConfigMapName + } + + return nil +} + +func (dst *CheCluster) convertFrom_Storage(src *chev2.CheCluster) error { + dst.Spec.Storage.PostgresPVCStorageClassName = src.Spec.Components.Database.Pvc.StorageClass + dst.Spec.Storage.PvcClaimSize = src.Spec.DevEnvironments.Storage.Pvc.ClaimSize + dst.Spec.Storage.WorkspacePVCStorageClassName = src.Spec.DevEnvironments.Storage.Pvc.StorageClass + dst.Spec.Storage.PvcStrategy = src.Spec.DevEnvironments.Storage.PvcStrategy + + return nil +} + +// Finds TrustStore ConfigMap. +func findTrustStoreConfigMap(namespace string) (string, error) { + k8sHelper := k8shelper.New() + + _, err := k8sHelper.GetClientset().CoreV1().ConfigMaps(namespace).Get(context.TODO(), constants.DefaultServerTrustStoreConfigMapName, metav1.GetOptions{}) + if err == nil { + // TrustStore ConfigMap with a default name exists + return constants.DefaultServerTrustStoreConfigMapName, nil + } + + return "", nil +} diff --git a/api/v1/checluster_conversion_to.go b/api/v1/checluster_conversion_to.go new file mode 100644 index 000000000..197b59c8c --- /dev/null +++ b/api/v1/checluster_conversion_to.go @@ -0,0 +1,634 @@ +// +// Copyright (c) 2019-2022 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package v1 + +import ( + "context" + "fmt" + "reflect" + "strconv" + "strings" + + "k8s.io/apimachinery/pkg/api/resource" + + "github.com/eclipse-che/che-operator/pkg/common/utils" + ctrl "sigs.k8s.io/controller-runtime" + + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/constants" + k8shelper "github.com/eclipse-che/che-operator/pkg/common/k8s-helper" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/utils/pointer" + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +var ( + logger = ctrl.Log.WithName("conversion") +) + +func (src *CheCluster) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*chev2.CheCluster) + dst.ObjectMeta = src.ObjectMeta + + if err := src.convertTo_Components(dst); err != nil { + return err + } + + if err := src.convertTo_Networking(dst); err != nil { + return err + } + + if err := src.convertTo_DevEnvironments(dst); err != nil { + return err + } + + if err := src.convertTo_ContainerRegistry(dst); err != nil { + return err + } + + if err := src.convertTo_Status(dst); err != nil { + return err + } + + return nil +} + +func (src *CheCluster) convertTo_Status(dst *chev2.CheCluster) error { + dst.Status.PostgresVersion = src.Spec.Database.PostgresVersion + dst.Status.CheURL = src.Status.CheURL + dst.Status.CheVersion = src.Status.CheVersion + dst.Status.DevfileRegistryURL = src.Status.DevfileRegistryURL + dst.Status.PluginRegistryURL = src.Status.PluginRegistryURL + dst.Status.Message = src.Status.Message + dst.Status.Reason = src.Status.Reason + dst.Status.GatewayPhase = chev2.GatewayPhase(src.Status.DevworkspaceStatus.GatewayPhase) + dst.Status.WorkspaceBaseDomain = src.Status.DevworkspaceStatus.WorkspaceBaseDomain + + switch src.Status.CheClusterRunning { + case "Available": + dst.Status.ChePhase = chev2.ClusterPhaseActive + case "Unavailable": + dst.Status.ChePhase = chev2.ClusterPhaseInactive + case "Available, Rolling Update in Progress": + dst.Status.ChePhase = chev2.RollingUpdate + } + + return nil +} + +func (src *CheCluster) convertTo_ContainerRegistry(dst *chev2.CheCluster) error { + dst.Spec.ContainerRegistry.Hostname = src.Spec.Server.AirGapContainerRegistryHostname + dst.Spec.ContainerRegistry.Organization = src.Spec.Server.AirGapContainerRegistryOrganization + + return nil +} + +func (src *CheCluster) convertTo_DevEnvironments(dst *chev2.CheCluster) error { + if src.Spec.Server.GitSelfSignedCert { + if src.Status.GitServerTLSCertificateConfigMapName != "" { + dst.Spec.DevEnvironments.TrustedCerts.GitTrustedCertsConfigMapName = src.Status.GitServerTLSCertificateConfigMapName + } else { + dst.Spec.DevEnvironments.TrustedCerts.GitTrustedCertsConfigMapName = constants.DefaultGitSelfSignedCertsConfigMapName + } + } + + dst.Spec.DevEnvironments.DefaultNamespace.Template = src.Spec.Server.WorkspaceNamespaceDefault + dst.Spec.DevEnvironments.NodeSelector = utils.CloneMap(src.Spec.Server.WorkspacePodNodeSelector) + + for _, v := range src.Spec.Server.WorkspacePodTolerations { + dst.Spec.DevEnvironments.Tolerations = append(dst.Spec.DevEnvironments.Tolerations, v) + } + + for _, p := range src.Spec.Server.WorkspacesDefaultPlugins { + dst.Spec.DevEnvironments.DefaultPlugins = append(dst.Spec.DevEnvironments.DefaultPlugins, + chev2.WorkspaceDefaultPlugins{ + Editor: p.Editor, + Plugins: p.Plugins, + }) + } + + if err := src.convertTo_Workspaces_Storage(dst); err != nil { + return err + } + + return nil +} + +func (src *CheCluster) convertTo_Workspaces_Storage(dst *chev2.CheCluster) error { + dst.Spec.DevEnvironments.Storage.Pvc = chev2.PVC{ + ClaimSize: src.Spec.Storage.PvcClaimSize, + StorageClass: src.Spec.Storage.WorkspacePVCStorageClassName, + } + dst.Spec.DevEnvironments.Storage.PvcStrategy = src.Spec.Storage.PvcStrategy + + return nil +} + +func (src *CheCluster) convertTo_Networking(dst *chev2.CheCluster) error { + if infrastructure.IsOpenShift() { + dst.Spec.Networking = chev2.CheClusterSpecNetworking{ + Labels: utils.ParseMap(src.Spec.Server.CheServerRoute.Labels), + Annotations: utils.CloneMap(src.Spec.Server.CheServerRoute.Annotations), + Hostname: src.Spec.Server.CheHost, + Domain: src.Spec.Server.CheServerRoute.Domain, + TlsSecretName: src.Spec.Server.CheHostTLSSecret, + } + } else { + dst.Spec.Networking = chev2.CheClusterSpecNetworking{ + Labels: utils.ParseMap(src.Spec.Server.CheServerIngress.Labels), + Domain: src.Spec.K8s.IngressDomain, + Hostname: src.Spec.Server.CheHost, + } + + if src.Spec.Server.CheHostTLSSecret != "" { + dst.Spec.Networking.TlsSecretName = src.Spec.Server.CheHostTLSSecret + } else { + dst.Spec.Networking.TlsSecretName = src.Spec.K8s.TlsSecretName + } + + if src.Spec.K8s.IngressClass != "" { + dst.Spec.Networking.Annotations = map[string]string{"kubernetes.io/ingress.class": src.Spec.K8s.IngressClass} + } + + if len(dst.Spec.Networking.Annotations) > 0 || len(src.Spec.Server.CheServerIngress.Annotations) > 0 { + dst.Spec.Networking.Annotations = labels.Merge(dst.Spec.Networking.Annotations, src.Spec.Server.CheServerIngress.Annotations) + } + } + + if err := src.convertTo_Networking_Auth(dst); err != nil { + return err + } + + return nil +} + +func (src *CheCluster) convertTo_Networking_Auth(dst *chev2.CheCluster) error { + dst.Spec.Networking.Auth.IdentityProviderURL = src.Spec.Auth.IdentityProviderURL + dst.Spec.Networking.Auth.OAuthClientName = src.Spec.Auth.OAuthClientName + dst.Spec.Networking.Auth.OAuthSecret = src.Spec.Auth.OAuthSecret + + if err := src.convertTo_Networking_Auth_Gateway(dst); err != nil { + return err + } + + return nil +} + +func (src *CheCluster) convertTo_Networking_Auth_Gateway(dst *chev2.CheCluster) error { + dst.Spec.Networking.Auth.Gateway.ConfigLabels = utils.CloneMap(src.Spec.Server.SingleHostGatewayConfigMapLabels) + + if src.Spec.Server.SingleHostGatewayImage != "" { + dst.Spec.Networking.Auth.Gateway.Deployment.Containers = append( + dst.Spec.Networking.Auth.Gateway.Deployment.Containers, + chev2.Container{ + Name: constants.GatewayContainerName, + Image: src.Spec.Server.SingleHostGatewayImage, + }, + ) + } + + if src.Spec.Server.SingleHostGatewayConfigSidecarImage != "" { + dst.Spec.Networking.Auth.Gateway.Deployment.Containers = append( + dst.Spec.Networking.Auth.Gateway.Deployment.Containers, + chev2.Container{ + Name: constants.GatewayConfigSideCarContainerName, + Image: src.Spec.Server.SingleHostGatewayConfigSidecarImage, + }, + ) + } + + if src.Spec.Auth.GatewayAuthenticationSidecarImage != "" { + dst.Spec.Networking.Auth.Gateway.Deployment.Containers = append( + dst.Spec.Networking.Auth.Gateway.Deployment.Containers, + chev2.Container{ + Name: constants.GatewayAuthenticationContainerName, + Image: src.Spec.Auth.GatewayAuthenticationSidecarImage, + }, + ) + } + + if src.Spec.Auth.GatewayAuthorizationSidecarImage != "" { + dst.Spec.Networking.Auth.Gateway.Deployment.Containers = append( + dst.Spec.Networking.Auth.Gateway.Deployment.Containers, + chev2.Container{ + Name: constants.GatewayAuthorizationContainerName, + Image: src.Spec.Auth.GatewayAuthorizationSidecarImage, + }, + ) + } + + return nil +} + +func (src *CheCluster) convertTo_Components(dst *chev2.CheCluster) error { + if err := src.convertTo_Components_Dashboard(dst); err != nil { + return err + } + + if err := src.convertTo_Components_DevfileRegistry(dst); err != nil { + return err + } + + if err := src.convertTo_Components_PluginRegistry(dst); err != nil { + return err + } + + if err := src.convertTo_Components_CheServer(dst); err != nil { + return err + } + + if err := src.convertTo_Components_Database(dst); err != nil { + return err + } + + if err := src.convertTo_Components_Metrics(dst); err != nil { + return err + } + + if err := src.convertTo_Components_ImagePuller(dst); err != nil { + return err + } + + if err := src.convertTo_Components_DevWorkspace(dst); err != nil { + return err + } + + return nil +} + +func (src *CheCluster) convertTo_Components_DevWorkspace(dst *chev2.CheCluster) error { + if src.Spec.DevWorkspace.ControllerImage != "" { + dst.Spec.Components.DevWorkspace.Deployment = chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: constants.DevWorkspaceController, + Image: src.Spec.DevWorkspace.ControllerImage, + }, + }, + } + } + dst.Spec.Components.DevWorkspace.RunningLimit = src.Spec.DevWorkspace.RunningLimit + + return nil +} + +func (src *CheCluster) convertTo_Components_ImagePuller(dst *chev2.CheCluster) error { + dst.Spec.Components.ImagePuller.Enable = src.Spec.ImagePuller.Enable + dst.Spec.Components.ImagePuller.Spec = src.Spec.ImagePuller.Spec + return nil +} + +func (src *CheCluster) convertTo_Components_Metrics(dst *chev2.CheCluster) error { + dst.Spec.Components.Metrics.Enable = src.Spec.Metrics.Enable + return nil +} + +func (src *CheCluster) convertTo_Components_CheServer(dst *chev2.CheCluster) error { + dst.Spec.Components.CheServer.ExtraProperties = utils.CloneMap(src.Spec.Server.CustomCheProperties) + dst.Spec.Components.CheServer.LogLevel = src.Spec.Server.CheLogLevel + if src.Spec.Server.CheClusterRoles != "" { + dst.Spec.Components.CheServer.ClusterRoles = strings.Split(src.Spec.Server.CheClusterRoles, ",") + } + + if src.Spec.Server.CheDebug != "" { + debug, err := strconv.ParseBool(src.Spec.Server.CheDebug) + if err != nil { + return err + } else { + dst.Spec.Components.CheServer.Debug = pointer.BoolPtr(debug) + } + } + + dst.Spec.Components.CheServer.Proxy = chev2.Proxy{ + Url: src.Spec.Server.ProxyURL, + Port: src.Spec.Server.ProxyPort, + CredentialsSecretName: src.Spec.Server.ProxySecret, + } + if src.Spec.Server.NonProxyHosts != "" { + dst.Spec.Components.CheServer.Proxy.NonProxyHosts = strings.Split(src.Spec.Server.NonProxyHosts, "|") + } + + if src.Spec.Server.ProxySecret == "" && src.Spec.Server.ProxyUser != "" && src.Spec.Server.ProxyPassword != "" { + if err := createCredentialsSecret( + src.Spec.Server.ProxyUser, + src.Spec.Server.ProxyPassword, + constants.DefaultProxyCredentialsSecret, + src.ObjectMeta.Namespace); err != nil { + return err + } + + dst.Spec.Components.CheServer.Proxy.CredentialsSecretName = constants.DefaultProxyCredentialsSecret + } + + runAsUser, fsGroup, err := parseSecurityContext(src) + if err != nil { + return err + } + + dst.Spec.Components.CheServer.Deployment = toCheV2Deployment( + defaults.GetCheFlavor(), + map[bool]string{true: src.Spec.Server.CheImage + ":" + src.Spec.Server.CheImageTag, false: ""}[src.Spec.Server.CheImage != ""], + src.Spec.Server.CheImagePullPolicy, + src.Spec.Server.ServerMemoryRequest, + src.Spec.Server.ServerMemoryLimit, + src.Spec.Server.ServerCpuRequest, + src.Spec.Server.ServerCpuLimit, + fsGroup, + runAsUser, + ) + + if src.Spec.Server.ServerTrustStoreConfigMapName != "" { + if err := renameTrustStoreConfigMapToDefault(src.Spec.Server.ServerTrustStoreConfigMapName, src.Namespace); err != nil { + return err + } + } + + return nil +} + +func (src *CheCluster) convertTo_Components_PluginRegistry(dst *chev2.CheCluster) error { + dst.Spec.Components.PluginRegistry.DisableInternalRegistry = src.Spec.Server.ExternalPluginRegistry + + if dst.Spec.Components.PluginRegistry.DisableInternalRegistry { + dst.Spec.Components.PluginRegistry.ExternalPluginRegistries = []chev2.ExternalPluginRegistry{ + { + Url: src.Spec.Server.PluginRegistryUrl, + }, + } + } + + dst.Spec.Components.PluginRegistry.Deployment = toCheV2Deployment( + constants.PluginRegistryName, + src.Spec.Server.PluginRegistryImage, + src.Spec.Server.PluginRegistryPullPolicy, + src.Spec.Server.PluginRegistryMemoryRequest, + src.Spec.Server.PluginRegistryMemoryLimit, + src.Spec.Server.PluginRegistryCpuRequest, + src.Spec.Server.PluginRegistryCpuLimit, + nil, + nil, + ) + + return nil +} + +func (src *CheCluster) convertTo_Components_DevfileRegistry(dst *chev2.CheCluster) error { + dst.Spec.Components.DevfileRegistry.DisableInternalRegistry = src.Spec.Server.ExternalDevfileRegistry + + for _, r := range src.Spec.Server.ExternalDevfileRegistries { + dst.Spec.Components.DevfileRegistry.ExternalDevfileRegistries = append(dst.Spec.Components.DevfileRegistry.ExternalDevfileRegistries, + chev2.ExternalDevfileRegistry{ + Url: r.Url, + }) + } + + dst.Spec.Components.DevfileRegistry.Deployment = toCheV2Deployment( + constants.DevfileRegistryName, + src.Spec.Server.DevfileRegistryImage, + src.Spec.Server.DevfileRegistryPullPolicy, + src.Spec.Server.DevfileRegistryMemoryRequest, + src.Spec.Server.DevfileRegistryMemoryLimit, + src.Spec.Server.DevfileRegistryCpuRequest, + src.Spec.Server.DevfileRegistryCpuLimit, + nil, + nil, + ) + + return nil +} + +func (src *CheCluster) convertTo_Components_Database(dst *chev2.CheCluster) error { + dst.Spec.Components.Database.CredentialsSecretName = src.Spec.Database.ChePostgresSecret + + if src.Spec.Database.ChePostgresSecret == "" && src.Spec.Database.ChePostgresUser != "" && src.Spec.Database.ChePostgresPassword != "" { + if err := createCredentialsSecret( + src.Spec.Database.ChePostgresUser, + src.Spec.Database.ChePostgresPassword, + constants.DefaultPostgresCredentialsSecret, + src.ObjectMeta.Namespace); err != nil { + return err + } + dst.Spec.Components.Database.CredentialsSecretName = constants.DefaultPostgresCredentialsSecret + } + + dst.Spec.Components.Database.Deployment = toCheV2Deployment( + constants.PostgresName, + src.Spec.Database.PostgresImage, + src.Spec.Database.PostgresImagePullPolicy, + src.Spec.Database.ChePostgresContainerResources.Requests.Memory, + src.Spec.Database.ChePostgresContainerResources.Limits.Memory, + src.Spec.Database.ChePostgresContainerResources.Requests.Cpu, + src.Spec.Database.ChePostgresContainerResources.Limits.Cpu, + nil, + nil, + ) + + dst.Spec.Components.Database.ExternalDb = src.Spec.Database.ExternalDb + dst.Spec.Components.Database.PostgresDb = src.Spec.Database.ChePostgresDb + dst.Spec.Components.Database.PostgresHostName = src.Spec.Database.ChePostgresHostName + dst.Spec.Components.Database.PostgresPort = src.Spec.Database.ChePostgresPort + dst.Spec.Components.Database.Pvc = chev2.PVC{ + ClaimSize: src.Spec.Database.PvcClaimSize, + StorageClass: src.Spec.Storage.PostgresPVCStorageClassName, + } + + return nil +} + +func (src *CheCluster) convertTo_Components_Dashboard(dst *chev2.CheCluster) error { + runAsUser, fsGroup, err := parseSecurityContext(src) + if err != nil { + return err + } + + dst.Spec.Components.Dashboard.Deployment = toCheV2Deployment( + defaults.GetCheFlavor()+"-dashboard", + src.Spec.Server.DashboardImage, + corev1.PullPolicy(src.Spec.Server.DashboardImagePullPolicy), + src.Spec.Server.DashboardMemoryRequest, + src.Spec.Server.DashboardMemoryLimit, + src.Spec.Server.DashboardCpuRequest, + src.Spec.Server.DashboardCpuLimit, + fsGroup, + runAsUser, + ) + + dst.Spec.Components.Dashboard.HeaderMessage.Text = src.Spec.Dashboard.Warning + dst.Spec.Components.Dashboard.HeaderMessage.Show = src.Spec.Dashboard.Warning != "" + + return nil +} + +func parseSecurityContext(cheClusterV1 *CheCluster) (*int64, *int64, error) { + var runAsUser *int64 = nil + if cheClusterV1.Spec.K8s.SecurityContextRunAsUser != "" { + intValue, err := strconv.ParseInt(cheClusterV1.Spec.K8s.SecurityContextRunAsUser, 10, 64) + if err != nil { + return nil, nil, err + } + + runAsUser = pointer.Int64Ptr(intValue) + } + + var fsGroup *int64 = nil + if cheClusterV1.Spec.K8s.SecurityContextFsGroup != "" { + intValue, err := strconv.ParseInt(cheClusterV1.Spec.K8s.SecurityContextFsGroup, 10, 64) + if err != nil { + return nil, nil, err + } + fsGroup = pointer.Int64Ptr(intValue) + } + + return runAsUser, fsGroup, nil +} + +// Create a secret with a user's credentials +// Username and password are stored in `user` and `password` fields correspondingly. +func createCredentialsSecret(username string, password string, secretName string, namespace string) error { + k8sHelper := k8shelper.New() + + _, err := k8sHelper.GetClientset().CoreV1().Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) + if err == nil { + // Credentials secret already exists, we can't proceed + return fmt.Errorf("secret %s already exists", secretName) + } + + secret := &corev1.Secret{ + ObjectMeta: v1.ObjectMeta{ + Name: secretName, + Namespace: namespace, + Labels: map[string]string{ + "app.kubernetes.io/part-of": "che.eclipse.org", + }, + }, + Data: map[string][]byte{ + "user": []byte(username), + "password": []byte(password), + }, + } + + if _, err := k8sHelper.GetClientset().CoreV1().Secrets(namespace).Create(context.TODO(), secret, metav1.CreateOptions{}); err != nil { + return err + } + + logger.Info("Credentials secret '" + secretName + "' with created.") + return nil +} + +// Convert `server.ServerTrustStoreConfigMapName` field from API V1 to API V2 +// Since we API V2 does not have `server.ServerTrustStoreConfigMapName` field, we need to create +// the same ConfigMap but with a default name to be correctly handled by a controller. +func renameTrustStoreConfigMapToDefault(trustStoreConfigMapName string, namespace string) error { + if trustStoreConfigMapName == constants.DefaultServerTrustStoreConfigMapName { + // Already in default name + return nil + } + + k8sHelper := k8shelper.New() + + _, err := k8sHelper.GetClientset().CoreV1().ConfigMaps(namespace).Get(context.TODO(), constants.DefaultServerTrustStoreConfigMapName, metav1.GetOptions{}) + if err == nil { + // ConfigMap with a default name already exists, we can't proceed + return fmt.Errorf("TrustStore ConfigMap %s already exists", constants.DefaultServerTrustStoreConfigMapName) + } + + existedTrustStoreConfigMap, err := k8sHelper.GetClientset().CoreV1().ConfigMaps(namespace).Get(context.TODO(), trustStoreConfigMapName, metav1.GetOptions{}) + if err != nil { + if errors.IsNotFound(err) { + // ConfigMap not found, nothing to rename + return nil + } + return err + } + + // must have labels + newTrustStoreConfigMapLabels := map[string]string{ + "app.kubernetes.io/part-of": "che.eclipse.org", + "app.kubernetes.io/component": "ca-bundle", + } + + newTrustStoreConfigMap := &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: constants.DefaultServerTrustStoreConfigMapName, + Namespace: namespace, + Labels: labels.Merge(newTrustStoreConfigMapLabels, existedTrustStoreConfigMap.Labels), + }, + Data: existedTrustStoreConfigMap.Data, + } + + // Create TrustStore ConfigMap with a default name + if _, err = k8sHelper.GetClientset().CoreV1().ConfigMaps(namespace).Create(context.TODO(), newTrustStoreConfigMap, metav1.CreateOptions{}); err != nil { + return err + } + + // Delete legacy TrustStore ConfigMap + if err = k8sHelper.GetClientset().CoreV1().ConfigMaps(namespace).Delete(context.TODO(), trustStoreConfigMapName, metav1.DeleteOptions{}); err != nil { + return err + } + + logger.Info("TrustStore ConfigMap '" + constants.DefaultServerTrustStoreConfigMapName + "' created.") + return nil +} + +func toCheV2Deployment( + name string, + image string, + imagePullPolicy corev1.PullPolicy, + memoryRequest string, + memoryLimit string, + cpuRequest string, + cpuLimit string, + fsGroup *int64, + runAsUser *int64) chev2.Deployment { + + deployment := chev2.Deployment{} + + container := chev2.Container{} + if image != "" { + container.Image = image + } + if imagePullPolicy != "" { + container.ImagePullPolicy = imagePullPolicy + } + if memoryRequest != "" { + container.Resources.Requests.Memory = resource.MustParse(memoryRequest) + } + if memoryLimit != "" { + container.Resources.Limits.Memory = resource.MustParse(memoryLimit) + } + if cpuRequest != "" { + container.Resources.Requests.Cpu = resource.MustParse(cpuRequest) + } + if cpuLimit != "" { + container.Resources.Limits.Cpu = resource.MustParse(cpuLimit) + } + if !reflect.DeepEqual(container, chev2.Container{}) { + container.Name = name + deployment.Containers = []chev2.Container{container} + } + + deployment.SecurityContext.RunAsUser = runAsUser + deployment.SecurityContext.FsGroup = fsGroup + + return deployment +} diff --git a/api/v1/checluster_types.go b/api/v1/checluster_types.go index 412550c2a..f86456414 100644 --- a/api/v1/checluster_types.go +++ b/api/v1/checluster_types.go @@ -12,16 +12,12 @@ package v1 -// Important: You must regenerate some generated code after modifying this file. At the root of the project: -// Run `make generate`. It will perform required changes: -// - update `api/v1/zz_generatedxxx` files; -// - update `config/crd/bases/org_v1_checluster_crd.yaml` file; +// Important: Don't modify this file. import ( "strings" chev1alpha1 "github.com/che-incubator/kubernetes-image-puller-operator/api/v1alpha1" - v2alpha1 "github.com/eclipse-che/che-operator/api/v2alpha1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -719,6 +715,50 @@ type ExternalDevfileRegistries struct { Url string `json:"url,omitempty"` } +// GatewayPhase describes the different phases of the Che gateway lifecycle +type GatewayPhase string + +const ( + GatewayPhaseInitializing = "Initializing" + GatewayPhaseEstablished = "Established" + GatewayPhaseInactive = "Inactive" +) + +// ClusterPhase describes the different phases of the Che cluster lifecycle +type ClusterPhase string + +const ( + ClusterPhaseActive = "Active" + ClusterPhaseInactive = "Inactive" + ClusterPhasePendingDeletion = "PendingDeletion" +) + +// LegacyDevworkspaceStatus contains the status of the CheCluster object +// +k8s:openapi-gen=true +type LegacyDevworkspaceStatus struct { + // GatewayPhase specifies the phase in which the gateway deployment currently is. + // If the gateway is disabled, the phase is "Inactive". + GatewayPhase GatewayPhase `json:"gatewayPhase,omitempty"` + + // GatewayHost is the resolved host of the ingress/route. This is equal to the Host in the spec + // on Kubernetes but contains the actual host name of the route if Host is unspecified on OpenShift. + GatewayHost string `json:"gatewayHost,omitempty"` + + // Phase is the phase in which the Che cluster as a whole finds itself in. + Phase ClusterPhase `json:"phase,omitempty"` + + // A brief CamelCase message indicating details about why the Che cluster is in this state. + Reason string `json:"reason,omitempty"` + + // Message contains further human-readable info for why the Che cluster is in the phase it currently is. + Message string `json:"message,omitempty"` + + // The resolved workspace base domain. This is either the copy of the explicitly defined property of the + // same name in the spec or, if it is undefined in the spec and we're running on OpenShift, the automatically + // resolved basedomain for routes. + WorkspaceBaseDomain string `json:"workspaceBaseDomain,omitempty"` +} + // CheClusterStatus defines the observed state of Che installation type CheClusterStatus struct { // OpenShift OAuth secret in `openshift-config` namespace that contains user credentials for HTPasswd identity provider. @@ -738,6 +778,12 @@ type CheClusterStatus struct { // Indicates whether an Identity Provider instance, Keycloak or RH-SSO, has been configured to integrate with the GitHub OAuth. // +optional GitHubOAuthProvisioned bool `json:"gitHubOAuthProvisioned"` + // The ConfigMap containing certificates to propagate to the Che components and to provide particular configuration for Git. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=status + // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Git certificates" + // +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors="urn:alm:descriptor:text" + GitServerTLSCertificateConfigMapName string `json:"gitServerTLSCertificateConfigMapName"` // Status of a Che installation. Can be `Available`, `Unavailable`, or `Available, Rolling Update in Progress`. // +optional // +operator-sdk:csv:customresourcedefinitions:type=status @@ -794,7 +840,7 @@ type CheClusterStatus struct { HelpLink string `json:"helpLink,omitempty"` // The status of the Devworkspace subsystem // +optional - DevworkspaceStatus v2alpha1.CheClusterStatusV2Alpha1 `json:"devworkspaceStatus,omitempty"` + DevworkspaceStatus LegacyDevworkspaceStatus `json:"devworkspaceStatus,omitempty"` } // The `CheCluster` custom resource allows defining and managing a Che server installation @@ -803,9 +849,8 @@ type CheClusterStatus struct { // +kubebuilder:subresource:status // +k8s:openapi-gen=true // +operator-sdk:csv:customresourcedefinitions:displayName="Eclipse Che instance Specification" -// +operator-sdk:csv:customresourcedefinitions:order=0 +// +operator-sdk:csv:customresourcedefinitions:order=1 // +operator-sdk:csv:customresourcedefinitions:resources={{Ingress,v1},{Route,v1},{ConfigMap,v1},{Service,v1},{Secret,v1},{Deployment,apps/v1},{Role,v1},{RoleBinding,v1},{ClusterRole,v1},{ClusterRoleBinding,v1}} -// +kubebuilder:storageversion type CheCluster struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 5d8eae6c0..1b67695dc 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -366,6 +366,21 @@ func (in *IngressCustomSettings) DeepCopy() *IngressCustomSettings { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LegacyDevworkspaceStatus) DeepCopyInto(out *LegacyDevworkspaceStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LegacyDevworkspaceStatus. +func (in *LegacyDevworkspaceStatus) DeepCopy() *LegacyDevworkspaceStatus { + if in == nil { + return nil + } + out := new(LegacyDevworkspaceStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Resources) DeepCopyInto(out *Resources) { *out = *in diff --git a/api/v1/common.go b/api/v2/checluster_conversion.go similarity index 65% rename from api/v1/common.go rename to api/v2/checluster_conversion.go index 0b16fdac1..f220eede9 100644 --- a/api/v1/common.go +++ b/api/v2/checluster_conversion.go @@ -1,5 +1,5 @@ // -// Copyright (c) 2019-2021 Red Hat, Inc. +// Copyright (c) 2019-2022 Red Hat, Inc. // This program and the accompanying materials are made // available under the terms of the Eclipse Public License 2.0 // which is available at https://www.eclipse.org/legal/epl-2.0/ @@ -10,10 +10,6 @@ // Red Hat, Inc. - initial API and implementation // -package v1 +package v2 -const ( - STATE_IN_PROGRESS = "InProgress" - STATE_SUCCEEDED = "Succeeded" - STATE_FAILED = "Failed" -) +func (*CheCluster) Hub() {} diff --git a/api/v2/checluster_types.go b/api/v2/checluster_types.go new file mode 100644 index 000000000..23450fd82 --- /dev/null +++ b/api/v2/checluster_types.go @@ -0,0 +1,633 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package v2 + +// Important: Run `make generate` at the root directory of the project +// to regenerate `api/v2/zz_generatedxxx` code after modifying this file. + +import ( + "os" + "strings" + + "k8s.io/apimachinery/pkg/api/resource" + + imagepullerv1alpha1 "github.com/che-incubator/kubernetes-image-puller-operator/api/v1alpha1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +k8s:openapi-gen=true +// Desired configuration of Eclipse Che installation. +type CheClusterSpec struct { + // Development environment default configuration options. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,order=1 + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Development environments" + DevEnvironments CheClusterDevEnvironments `json:"devEnvironments"` + // Che components configuration. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,order=2 + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Components" + Components CheClusterComponents `json:"components"` + // Networking, Che authentication and TLS configuration. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,order=3 + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Networking" + Networking CheClusterSpecNetworking `json:"networking,omitempty"` + // Configuration of an alternative registry that stores Che images. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,order=4 + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Container registry" + ContainerRegistry CheClusterContainerRegistry `json:"containerRegistry"` +} + +// Development environment configuration. +// +k8s:openapi-gen=true +type CheClusterDevEnvironments struct { + // Workspaces persistent storage. + // +optional + Storage WorkspaceStorage `json:"storage"` + // Default plug-ins applied to Dev Workspaces. + // +optional + DefaultPlugins []WorkspaceDefaultPlugins `json:"defaultPlugins,omitempty"` + // The node selector limits the nodes that can run the workspace pods. + // +optional + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + // The pod tolerations of the workspace pods limit where the workspace pods can run. + // +optional + Tolerations []corev1.Toleration `json:"tolerations,omitempty"` + // User's default namespace. + // +optional + DefaultNamespace DefaultNamespace `json:"defaultNamespace,omitempty"` + // Trusted certificate settings. + // +optional + TrustedCerts TrustedCerts `json:"trustedCerts,omitempty"` +} + +// Che components configuration. +// +k8s:openapi-gen=true +type CheClusterComponents struct { + // DevWorkspace operator configuration. + // +optional + DevWorkspace DevWorkspace `json:"devWorkspace"` + // General configuration settings related to the Che server. + // +optional + CheServer CheServer `json:"cheServer"` + // Configuration settings related to the plug-in registry used by the Che installation. + // +optional + PluginRegistry PluginRegistry `json:"pluginRegistry"` + // Configuration settings related to the Devfile registry used by the Che installation. + // +optional + DevfileRegistry DevfileRegistry `json:"devfileRegistry"` + // Configuration settings related to the database used by the Che installation. + // +optional + Database Database `json:"database"` + // Configuration settings related to the Dashboard used by the Che installation. + // +optional + Dashboard Dashboard `json:"dashboard"` + // Kubernetes Image Puller configuration. + // +optional + ImagePuller ImagePuller `json:"imagePuller"` + // Che server metrics configuration. + // +optional + Metrics ServerMetrics `json:"metrics"` +} + +// Configuration settings related to the Networking used by the Che installation. +// +k8s:openapi-gen=true +type CheClusterSpecNetworking struct { + // Defines labels which will be set to an ingress (a route for OpenShift platform). + // +optional + Labels map[string]string `json:"labels,omitempty"` + // Defines annotations which will be set to an ingress (a route for OpenShift platform). + // The defaults for kubernetes platforms are: + // kubernetes.io/ingress.class: "nginx" + // nginx.ingress.kubernetes.io/proxy-read-timeout: "3600", + // nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600", + // nginx.ingress.kubernetes.io/ssl-redirect: "true" + // +optional + Annotations map[string]string `json:"annotations,omitempty"` + // For an OpenShift cluster, the Operator uses the domain to generate a hostname for a route. + // The generated hostname follows this pattern: che-.. The is the namespace where the CheCluster CRD is created. + // In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. + // For Kubernetes cluster it contains a global ingress domain. This MUST be explicitly specified: there are no defaults. + // +optional + Domain string `json:"domain,omitempty"` + // The public hostname of the installed Che server. + // +optional + Hostname string `json:"hostname,omitempty"` + // The name of the secret used to set up Ingress TLS termination. + // If the field is an empty string, the default cluster certificate is used. + // The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + // +optional + TlsSecretName string `json:"tlsSecretName,omitempty"` + // Authentication settings. + // +optional + Auth Auth `json:"auth"` +} + +// Container registry configuration. +// +k8s:openapi-gen=true +type CheClusterContainerRegistry struct { + // An optional hostname or URL of an alternative container registry to pull images from. + // This value overrides the container registry hostname defined in all the default container images involved in a Che deployment. + // This is particularly useful for installing Che in a restricted environment. + // +optional + Hostname string `json:"hostname,omitempty"` + // An optional repository name of an alternative registry to pull images from. + // This value overrides the container registry organization defined in all the default container images involved in a Che deployment. + // This is particularly useful for installing Eclipse Che in a restricted environment. + // +optional + Organization string `json:"organization,omitempty"` +} + +// +k8s:openapi-gen=true +// General configuration settings related to the Che server. +type CheServer struct { + // Deployment override options. + // +optional + Deployment Deployment `json:"deployment,omitempty"` + // The log level for the Che server: `INFO` or `DEBUG`. + // +optional + // +kubebuilder:default:="INFO" + LogLevel string `json:"logLevel,omitempty"` + // Enables the debug mode for Che server. + // +optional + Debug *bool `json:"debug,omitempty"` + // ClusterRoles assigned to Che ServiceAccount. + // The defaults roles are: + // - `-cheworkspaces-namespaces-clusterrole` + // - `-cheworkspaces-clusterrole` + // - `-cheworkspaces-devworkspace-clusterrole` + // where the is the namespace where the CheCluster CRD is created. + // Each role must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + // The Che Operator must already have all permissions in these ClusterRoles to grant them. + // +optional + ClusterRoles []string `json:"clusterRoles,omitempty"` + // Proxy server settings for Kubernetes cluster. No additional configuration is required for OpenShift cluster. + // By specifying these settings for the OpenShift cluster, you override the OpenShift proxy configuration. + // +optional + Proxy Proxy `json:"proxy"` + // A map of additional environment variables applied in the generated `che` ConfigMap to be used by the Che server, + // in addition to the values already generated from other fields of the `CheCluster` custom resource (CR). + // If the `extraProperties` fields contains a property normally generated in `che` ConfigMap from other CR fields, + // the value defined in the `extraProperties` is used instead. + // +optional + ExtraProperties map[string]string `json:"extraProperties,omitempty"` +} + +// Configuration settings related to the Dashaboard used by the Che installation. +// +k8s:openapi-gen=true +type Dashboard struct { + // Deployment override options. + // +optional + Deployment Deployment `json:"deployment,omitempty"` + // Dashboard header message. + // +optional + HeaderMessage DashboardHeaderMessage `json:"HeaderMessage,omitempty"` +} + +// Configuration settings related to the plug-in registry used by the Che installation. +// +k8s:openapi-gen=true +type PluginRegistry struct { + // Deployment override options. + // +optional + Deployment Deployment `json:"deployment,omitempty"` + // Disables internal plug-in registry. + // +optional + DisableInternalRegistry bool `json:"disableInternalRegistry,omitempty"` + // External plugin registries. + // +optional + ExternalPluginRegistries []ExternalPluginRegistry `json:"externalPluginRegistries,omitempty"` +} + +// Configuration settings related to the devfile Registry used by the Che installation. +// +k8s:openapi-gen=true +type DevfileRegistry struct { + // Deployment override options. + // +optional + Deployment Deployment `json:"deployment,omitempty"` + // Disables internal devfile registry. + // +optional + DisableInternalRegistry bool `json:"disableInternalRegistry,omitempty"` + // External devfile registries serving sample ready-to-use devfiles. + // +optional + ExternalDevfileRegistries []ExternalDevfileRegistry `json:"externalDevfileRegistries,omitempty"` +} + +// Configuration settings related to the database used by the Che installation. +// +k8s:openapi-gen=true +type Database struct { + // Instructs the Operator to deploy a dedicated database. + // By default, a dedicated PostgreSQL database is deployed as part of the Che installation. + // When `externalDb` is set as `true`, no dedicated database is deployed by the + // Operator and you need to provide connection details to the external DB you want to use. + // +optional + ExternalDb bool `json:"externalDb"` + // Deployment override options. + // +optional + Deployment Deployment `json:"deployment,omitempty"` + // PostgreSQL database hostname that the Che server connects to. + // Override this value only when using an external database. See field `externalDb`. + // +kubebuilder:default:="postgres" + // +optional + PostgresHostName string `json:"postgresHostName,omitempty"` + // PostgreSQL Database port the Che server connects to. + // Override this value only when using an external database. See field `externalDb`. + // +optional + // +kubebuilder:default:="5432" + PostgresPort string `json:"postgresPort,omitempty"` + // PostgreSQL database name that the Che server uses to connect to the DB. + // +optional + // +kubebuilder:default:="dbche" + PostgresDb string `json:"postgresDb,omitempty"` + // The secret that contains PostgreSQL `user` and `password` that the Che server uses to connect to the DB. + // The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + // +optional + // +kubebuilder:default:="postgres-credentials" + CredentialsSecretName string `json:"credentialsSecretName,omitempty"` + // PVC settings for PostgreSQL database. + // +optional + Pvc PVC `json:"pvc,omitempty"` +} + +// Che server metrics configuration +type ServerMetrics struct { + // Enables `metrics` for the Che server endpoint. + // +kubebuilder:default:=true + // +optional + Enable bool `json:"enable"` +} + +// Configuration settings for installation and configuration of the Kubernetes Image Puller +// See https://github.com/che-incubator/kubernetes-image-puller-operator +// +k8s:openapi-gen=true +type ImagePuller struct { + // Install and configure the community supported Kubernetes Image Puller Operator. When you set the value to `true` without providing any specs, + // it creates a default Kubernetes Image Puller object managed by the Operator. + // When you set the value to `false`, the Kubernetes Image Puller object is deleted, and the Operator uninstalled, + // regardless of whether a spec is provided. + // If you leave the `spec.images` field empty, a set of recommended workspace-related images is automatically detected and + // pre-pulled after installation. + // Note that while this Operator and its behavior is community-supported, its payload may be commercially-supported + // for pulling commercially-supported images. + Enable bool `json:"enable"` + // A Kubernetes Image Puller spec to configure the image puller in the CheCluster + // +optional + Spec imagepullerv1alpha1.KubernetesImagePullerSpec `json:"spec"` +} + +// Settings for installation and configuration of the DevWorkspace operator +// See https://github.com/devfile/devworkspace-operator +// +k8s:openapi-gen=true +type DevWorkspace struct { + // Deployment override options. + // +optional + Deployment Deployment `json:"deployment,omitempty"` + // The maximum number of running workspaces per user. + // +optional + RunningLimit string `json:"runningLimit,omitempty"` +} + +type DefaultNamespace struct { + // If you don't create user namespaces in advance, this field defines the Kubernetes namespace created when you start your first workspace. + // You can use ``, `` and `` placeholders, such as che-workspace-. + // +optional + Template string `json:"template,omitempty"` +} + +type DashboardHeaderMessage struct { + // Instructs dashboard to show the message. + // +optional + Show bool `json:"show,omitempty"` + // Warning message displayed on the user dashboard. + // +optional + Text string `json:"warning,text"` +} + +type TrustedCerts struct { + // The ConfigMap contains certificates to propagate to the Che components and to provide a particular configuration for Git. + // See the following page: https://www.eclipse.org/che/docs/stable/administration-guide/deploying-che-with-support-for-git-repositories-with-self-signed-certificates/ + // The ConfigMap must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + // +optional + GitTrustedCertsConfigMapName string `json:"gitTrustedCertsConfigMapName,omitempty"` +} + +// Configuration settings related to the workspaces persistent storage. +type WorkspaceStorage struct { + // PVC settings. + // +optional + Pvc PVC `json:"pvc,omitempty"` + // Persistent volume claim strategy for the Che server. + // Only the `common` strategy is supported (all workspaces PVCs in one volume). + // For details, see https://github.com/eclipse/che/issues/21185. + // +optional + // +kubebuilder:default:="common" + PvcStrategy string `json:"pvcStrategy,omitempty"` +} + +type WorkspaceDefaultPlugins struct { + // The editor id to specify default plug-ins for. + Editor string `json:"editor,omitempty"` + // Default plug-in URIs for the specified editor. + Plugins []string `json:"plugins,omitempty"` +} + +// Authentication settings. +type Auth struct { + // Public URL of the Identity Provider server. + IdentityProviderURL string `json:"identityProviderURL,omitempty"` + // Name of the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side. + OAuthClientName string `json:"oAuthClientName,omitempty"` + // Name of the secret set in the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side. + OAuthSecret string `json:"oAuthSecret,omitempty"` + // Gateway settings. + // +optional + Gateway Gateway `json:"gateway,omitempty"` +} + +// Gateway settings. +type Gateway struct { + // Deployment override options. + // Since gateway deployment consists of several containers, they must be distinguished in the configuration by their names: + // - `gateway` + // - `configbump` + // - `oauth-proxy` + // - `kube-rbac-proxy` + // +optional + Deployment Deployment `json:"deployment,omitempty"` + // Gate configuration labels. + // +optional + ConfigLabels map[string]string `json:"configLabels,omitempty"` +} + +// Proxy server configuration. +type Proxy struct { + // URL (protocol+hostname) of the proxy server. + // Use only when a proxy configuration is required. Operator respects OpenShift cluster-wide proxy configuration, + // defining `url` in a custom resource leads to overriding the cluster proxy configuration. + // See the following page: https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyPort` and `nonProxyHosts` fields. + // +optional + Url string `json:"url,omitempty"` + // Proxy server port. + // +optional + Port string `json:"port,omitempty"` + // A list of hosts that can be reached directly, bypassing the proxy. + // Specify wild card domain use the following form `.`, for example: + // - localhost + // - my.host.com + // - 123.42.12.32 + // Use only when a proxy configuration is required. Operator respects OpenShift cluster-wide proxy configuration, + // defining `nonProxyHosts` in a custom resource leads to merging non-proxy hosts lists from the cluster proxy configuration, and the ones defined in the custom resources. + // See the following page: https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyURL` fields. + NonProxyHosts []string `json:"nonProxyHosts,omitempty"` + // The secret name that contains `user` and `password` for a proxy server. + // The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + // +optional + CredentialsSecretName string `json:"credentialsSecretName,omitempty"` +} + +// PersistentVolumeClaim custom settings. +type PVC struct { + // Persistent Volume Claim size. To update the claim size, Storage class that provisions it must support resize. + // +optional + ClaimSize string `json:"claimSize,omitempty"` + // Storage class for the Persistent Volume Claim. When omitted or left blank, a default storage class is used. + // +optional + StorageClass string `json:"storageClass,omitempty"` +} + +// External devfile registries configuration. +type ExternalDevfileRegistry struct { + // The public UR of the devfile registry that serves sample ready-to-use devfiles. + // +optional + Url string `json:"url,omitempty"` +} + +// External plug-in registries configuration. +type ExternalPluginRegistry struct { + // Public URL of the plug-in registry. + // +optional + Url string `json:"url,omitempty"` +} + +// Deployment custom settings. +type Deployment struct { + // A single application container. + // +optional + Containers []Container `json:"container,omitempty"` + // Security options the pod should run with. + // +optional + SecurityContext PodSecurityContext `json:"securityContext,omitempty"` +} + +// Container custom settings. +type Container struct { + // Container name. + // +optional + Name string `json:"name,omitempty"` + // Container image. Omit it or leave it empty to use the default container image provided by the Operator. + // +optional + Image string `json:"image,omitempty"` + // Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + // +optional + ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"` + // Compute resources required by this container. + // +optional + Resources ResourceRequirements `json:"resources,omitempty"` +} + +// Describes the compute resource requirements. +type ResourceRequirements struct { + // Request describes the minimum amount of compute resources required. + // +optional + Requests ResourceList `json:"request,omitempty"` + // Limits describe the maximum amount of compute resources allowed. + // +optional + Limits ResourceList `json:"limits,omitempty"` +} + +// List of resources. +type ResourceList struct { + // Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + // +optional + Memory resource.Quantity `json:"memory,omitempty"` + // CPU, in cores. (500m = .5 cores) + // +optional + Cpu resource.Quantity `json:"cpu,omitempty"` +} + +// PodSecurityContext holds pod-level security attributes and common container settings. +type PodSecurityContext struct { + // The UID to run the entrypoint of the container process. The default value is `1724`. + // +optional + RunAsUser *int64 `json:"runAsUser,omitempty"` + // A special supplemental group that applies to all containers in a pod. The default value is `1724`. + // +optional + FsGroup *int64 `json:"fsGroup,omitempty"` +} + +// GatewayPhase describes the different phases of the Che gateway lifecycle. +type GatewayPhase string + +const ( + GatewayPhaseInitializing = "Initializing" + GatewayPhaseEstablished = "Established" + GatewayPhaseInactive = "Inactive" +) + +// CheClusterPhase describes the different phases of the Che cluster lifecycle. +type CheClusterPhase string + +const ( + ClusterPhaseActive = "Active" + ClusterPhaseInactive = "Inactive" + ClusterPhasePendingDeletion = "PendingDeletion" + RollingUpdate = "RollingUpdate" +) + +// CheClusterStatus defines the observed state of Che installation. +type CheClusterStatus struct { + // Specifies the current phase of the gateway deployment. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=status + // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Gateway phase" + // +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors="urn:alm:descriptor:text" + GatewayPhase GatewayPhase `json:"gatewayPhase,omitempty"` + // Current installed Che version. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=status + // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="displayName: Eclipse Che version" + // +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors="urn:alm:descriptor:text" + CheVersion string `json:"cheVersion"` + // Public URL to the Che server. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=status + // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Eclipse Che URL" + // +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors="urn:alm:descriptor:org.w3:link" + CheURL string `json:"cheURL"` + // Specifies the current phase of the Che deployment. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=status + // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="ChePhase" + // +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors="urn:alm:descriptor:text" + ChePhase CheClusterPhase `json:"chePhase,omitempty"` + // The public URL to the internal devfile registry. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=status + // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Devfile registry URL" + // +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors="urn:alm:descriptor:org.w3:link" + DevfileRegistryURL string `json:"devfileRegistryURL"` + // The public URL to the internal plug-in registry. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=status + // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Plugin registry URL" + // +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors="urn:alm:descriptor:org.w3:link" + PluginRegistryURL string `json:"pluginRegistryURL"` + // A human readable message indicating details about why the Che deployment is in current phase. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=status + // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Message" + // +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors="urn:alm:descriptor:text" + Message string `json:"message,omitempty"` + // A brief CamelCase message indicating details about why the Che deployment is in current phase. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=status + // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Reason" + // +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors="urn:alm:descriptor:text" + Reason string `json:"reason,omitempty"` + // The PostgreSQL version of the image in use. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=status + // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="PostgreSQL version" + // +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors="urn:alm:descriptor:text" + PostgresVersion string `json:"postgresVersion,omitempty"` + // The resolved workspace base domain. This is either the copy of the explicitly defined property of the + // same name in the spec or, if it is undefined in the spec and we're running on OpenShift, the automatically + // resolved basedomain for routes. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=status + // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Workspace base domain" + // +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors="urn:alm:descriptor:text" + WorkspaceBaseDomain string `json:"workspaceBaseDomain,omitempty"` +} + +// The `CheCluster` custom resource allows defining and managing Eclipse Che server installation. +// Based on these settings, the Operator automatically creates and maintains several ConfigMaps: +// `che`, `plugin-registry`, `devfile-registry` that will contain the appropriate environment variables +// of the various components of the installation. These generated ConfigMaps must NOT be updated manually. +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +k8s:openapi-gen=true +// +operator-sdk:csv:customresourcedefinitions:displayName="Eclipse Che instance Specification" +// +operator-sdk:csv:customresourcedefinitions:order=0 +// +operator-sdk:csv:customresourcedefinitions:resources={{Ingress,v1},{Route,v1},{ConfigMap,v1},{Service,v1},{Secret,v1},{Deployment,apps/v1},{Role,v1},{RoleBinding,v1},{ClusterRole,v1},{ClusterRoleBinding,v1}} +// +kubebuilder:storageversion +type CheCluster struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Desired configuration of Eclipse Che installation. + Spec CheClusterSpec `json:"spec,omitempty"` + + // CheClusterStatus defines the observed state of Che installation. + Status CheClusterStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true +// The CheClusterList contains a list of CheClusters. +type CheClusterList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []CheCluster `json:"items"` +} + +func init() { + SchemeBuilder.Register(&CheCluster{}, &CheClusterList{}) +} + +func (c *CheCluster) IsAirGapMode() bool { + return c.Spec.ContainerRegistry.Hostname != "" || c.Spec.ContainerRegistry.Organization != "" +} + +func (c *CheCluster) IsImagePullerSpecEmpty() bool { + return c.Spec.Components.ImagePuller.Spec == (imagepullerv1alpha1.KubernetesImagePullerSpec{}) +} + +func (c *CheCluster) IsImagePullerImagesEmpty() bool { + return len(c.Spec.Components.ImagePuller.Spec.Images) == 0 +} + +func (c *CheCluster) GetCheHost() string { + if c.Status.CheURL != "" { + return strings.TrimPrefix(c.Status.CheURL, "https://") + } + + return c.Spec.Networking.Hostname +} + +func (c *CheCluster) GetDefaultNamespace() string { + if c.Spec.Components.CheServer.ExtraProperties != nil { + k8sDefaultNamespace := c.Spec.Components.CheServer.ExtraProperties["CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT"] + if k8sDefaultNamespace != "" { + return k8sDefaultNamespace + } + } + + if c.Spec.DevEnvironments.DefaultNamespace.Template != "" { + return c.Spec.DevEnvironments.DefaultNamespace.Template + } + + return "-" + os.Getenv("CHE_FLAVOR") +} diff --git a/api/v2/checluster_webhook.go b/api/v2/checluster_webhook.go new file mode 100644 index 000000000..7d25d00db --- /dev/null +++ b/api/v2/checluster_webhook.go @@ -0,0 +1,29 @@ +// +// Copyright (c) 2019-2022 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package v2 + +import ( + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" +) + +// log is for logging in this package. +var checlusterlog = logf.Log.WithName("checluster-resource") + +func (r *CheCluster) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! diff --git a/api/v2alpha1/groupversion_info.go b/api/v2/groupversion_info.go similarity index 59% rename from api/v2alpha1/groupversion_info.go rename to api/v2/groupversion_info.go index 72fad490a..5ded69324 100644 --- a/api/v2alpha1/groupversion_info.go +++ b/api/v2/groupversion_info.go @@ -10,21 +10,10 @@ // Red Hat, Inc. - initial API and implementation // -// NOTE: Boilerplate only. Ignore this file. - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// NOTE THAT THIS IS CURRENTLY INTENTIONALLY NOT PART OF THE GENERATED API -// -// (the generator comments are switched off by using a '\' instead of a '+') -// -// This is so that we can start using this spec in the code before we are -// actually ready to start deploying it in the cluster. -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -// Package v2alpha1 contains API Schema definitions for the org v2alpha1 API group +// Package v2 contains API Schema definitions for the org v2 API group //+kubebuilder:object:generate=true //+groupName=org.eclipse.che -package v2alpha1 +package v2 import ( "k8s.io/apimachinery/pkg/runtime/schema" @@ -33,7 +22,7 @@ import ( var ( // GroupVersion is group version used to register these objects - GroupVersion = schema.GroupVersion{Group: "org.eclipse.che", Version: "v2alpha1"} + GroupVersion = schema.GroupVersion{Group: "org.eclipse.che", Version: "v2"} // SchemeBuilder is used to add go types to the GroupVersionKind scheme SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} diff --git a/api/v2/zz_generated.deepcopy.go b/api/v2/zz_generated.deepcopy.go new file mode 100644 index 000000000..b1b01ed44 --- /dev/null +++ b/api/v2/zz_generated.deepcopy.go @@ -0,0 +1,662 @@ +// +build !ignore_autogenerated + +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +// Code generated by controller-gen. DO NOT EDIT. + +package v2 + +import ( + v1 "k8s.io/api/core/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Auth) DeepCopyInto(out *Auth) { + *out = *in + in.Gateway.DeepCopyInto(&out.Gateway) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Auth. +func (in *Auth) DeepCopy() *Auth { + if in == nil { + return nil + } + out := new(Auth) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CheCluster) DeepCopyInto(out *CheCluster) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheCluster. +func (in *CheCluster) DeepCopy() *CheCluster { + if in == nil { + return nil + } + out := new(CheCluster) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CheCluster) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CheClusterComponents) DeepCopyInto(out *CheClusterComponents) { + *out = *in + in.DevWorkspace.DeepCopyInto(&out.DevWorkspace) + in.CheServer.DeepCopyInto(&out.CheServer) + in.PluginRegistry.DeepCopyInto(&out.PluginRegistry) + in.DevfileRegistry.DeepCopyInto(&out.DevfileRegistry) + in.Database.DeepCopyInto(&out.Database) + in.Dashboard.DeepCopyInto(&out.Dashboard) + out.ImagePuller = in.ImagePuller + out.Metrics = in.Metrics +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheClusterComponents. +func (in *CheClusterComponents) DeepCopy() *CheClusterComponents { + if in == nil { + return nil + } + out := new(CheClusterComponents) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CheClusterContainerRegistry) DeepCopyInto(out *CheClusterContainerRegistry) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheClusterContainerRegistry. +func (in *CheClusterContainerRegistry) DeepCopy() *CheClusterContainerRegistry { + if in == nil { + return nil + } + out := new(CheClusterContainerRegistry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CheClusterDevEnvironments) DeepCopyInto(out *CheClusterDevEnvironments) { + *out = *in + out.Storage = in.Storage + if in.DefaultPlugins != nil { + in, out := &in.DefaultPlugins, &out.DefaultPlugins + *out = make([]WorkspaceDefaultPlugins, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + out.DefaultNamespace = in.DefaultNamespace + out.TrustedCerts = in.TrustedCerts +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheClusterDevEnvironments. +func (in *CheClusterDevEnvironments) DeepCopy() *CheClusterDevEnvironments { + if in == nil { + return nil + } + out := new(CheClusterDevEnvironments) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CheClusterList) DeepCopyInto(out *CheClusterList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CheCluster, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheClusterList. +func (in *CheClusterList) DeepCopy() *CheClusterList { + if in == nil { + return nil + } + out := new(CheClusterList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CheClusterList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CheClusterSpec) DeepCopyInto(out *CheClusterSpec) { + *out = *in + in.DevEnvironments.DeepCopyInto(&out.DevEnvironments) + in.Components.DeepCopyInto(&out.Components) + in.Networking.DeepCopyInto(&out.Networking) + out.ContainerRegistry = in.ContainerRegistry +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheClusterSpec. +func (in *CheClusterSpec) DeepCopy() *CheClusterSpec { + if in == nil { + return nil + } + out := new(CheClusterSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CheClusterSpecNetworking) DeepCopyInto(out *CheClusterSpecNetworking) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.Auth.DeepCopyInto(&out.Auth) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheClusterSpecNetworking. +func (in *CheClusterSpecNetworking) DeepCopy() *CheClusterSpecNetworking { + if in == nil { + return nil + } + out := new(CheClusterSpecNetworking) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CheClusterStatus) DeepCopyInto(out *CheClusterStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheClusterStatus. +func (in *CheClusterStatus) DeepCopy() *CheClusterStatus { + if in == nil { + return nil + } + out := new(CheClusterStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CheServer) DeepCopyInto(out *CheServer) { + *out = *in + in.Deployment.DeepCopyInto(&out.Deployment) + if in.Debug != nil { + in, out := &in.Debug, &out.Debug + *out = new(bool) + **out = **in + } + if in.ClusterRoles != nil { + in, out := &in.ClusterRoles, &out.ClusterRoles + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.Proxy.DeepCopyInto(&out.Proxy) + if in.ExtraProperties != nil { + in, out := &in.ExtraProperties, &out.ExtraProperties + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheServer. +func (in *CheServer) DeepCopy() *CheServer { + if in == nil { + return nil + } + out := new(CheServer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Container) DeepCopyInto(out *Container) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Container. +func (in *Container) DeepCopy() *Container { + if in == nil { + return nil + } + out := new(Container) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Dashboard) DeepCopyInto(out *Dashboard) { + *out = *in + in.Deployment.DeepCopyInto(&out.Deployment) + out.HeaderMessage = in.HeaderMessage +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Dashboard. +func (in *Dashboard) DeepCopy() *Dashboard { + if in == nil { + return nil + } + out := new(Dashboard) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DashboardHeaderMessage) DeepCopyInto(out *DashboardHeaderMessage) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardHeaderMessage. +func (in *DashboardHeaderMessage) DeepCopy() *DashboardHeaderMessage { + if in == nil { + return nil + } + out := new(DashboardHeaderMessage) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Database) DeepCopyInto(out *Database) { + *out = *in + in.Deployment.DeepCopyInto(&out.Deployment) + out.Pvc = in.Pvc +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Database. +func (in *Database) DeepCopy() *Database { + if in == nil { + return nil + } + out := new(Database) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DefaultNamespace) DeepCopyInto(out *DefaultNamespace) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DefaultNamespace. +func (in *DefaultNamespace) DeepCopy() *DefaultNamespace { + if in == nil { + return nil + } + out := new(DefaultNamespace) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Deployment) DeepCopyInto(out *Deployment) { + *out = *in + if in.Containers != nil { + in, out := &in.Containers, &out.Containers + *out = make([]Container, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.SecurityContext.DeepCopyInto(&out.SecurityContext) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Deployment. +func (in *Deployment) DeepCopy() *Deployment { + if in == nil { + return nil + } + out := new(Deployment) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DevWorkspace) DeepCopyInto(out *DevWorkspace) { + *out = *in + in.Deployment.DeepCopyInto(&out.Deployment) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevWorkspace. +func (in *DevWorkspace) DeepCopy() *DevWorkspace { + if in == nil { + return nil + } + out := new(DevWorkspace) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DevfileRegistry) DeepCopyInto(out *DevfileRegistry) { + *out = *in + in.Deployment.DeepCopyInto(&out.Deployment) + if in.ExternalDevfileRegistries != nil { + in, out := &in.ExternalDevfileRegistries, &out.ExternalDevfileRegistries + *out = make([]ExternalDevfileRegistry, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevfileRegistry. +func (in *DevfileRegistry) DeepCopy() *DevfileRegistry { + if in == nil { + return nil + } + out := new(DevfileRegistry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExternalDevfileRegistry) DeepCopyInto(out *ExternalDevfileRegistry) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalDevfileRegistry. +func (in *ExternalDevfileRegistry) DeepCopy() *ExternalDevfileRegistry { + if in == nil { + return nil + } + out := new(ExternalDevfileRegistry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExternalPluginRegistry) DeepCopyInto(out *ExternalPluginRegistry) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalPluginRegistry. +func (in *ExternalPluginRegistry) DeepCopy() *ExternalPluginRegistry { + if in == nil { + return nil + } + out := new(ExternalPluginRegistry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Gateway) DeepCopyInto(out *Gateway) { + *out = *in + in.Deployment.DeepCopyInto(&out.Deployment) + if in.ConfigLabels != nil { + in, out := &in.ConfigLabels, &out.ConfigLabels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Gateway. +func (in *Gateway) DeepCopy() *Gateway { + if in == nil { + return nil + } + out := new(Gateway) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImagePuller) DeepCopyInto(out *ImagePuller) { + *out = *in + out.Spec = in.Spec +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImagePuller. +func (in *ImagePuller) DeepCopy() *ImagePuller { + if in == nil { + return nil + } + out := new(ImagePuller) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PVC) DeepCopyInto(out *PVC) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PVC. +func (in *PVC) DeepCopy() *PVC { + if in == nil { + return nil + } + out := new(PVC) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PluginRegistry) DeepCopyInto(out *PluginRegistry) { + *out = *in + in.Deployment.DeepCopyInto(&out.Deployment) + if in.ExternalPluginRegistries != nil { + in, out := &in.ExternalPluginRegistries, &out.ExternalPluginRegistries + *out = make([]ExternalPluginRegistry, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PluginRegistry. +func (in *PluginRegistry) DeepCopy() *PluginRegistry { + if in == nil { + return nil + } + out := new(PluginRegistry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodSecurityContext) DeepCopyInto(out *PodSecurityContext) { + *out = *in + if in.RunAsUser != nil { + in, out := &in.RunAsUser, &out.RunAsUser + *out = new(int64) + **out = **in + } + if in.FsGroup != nil { + in, out := &in.FsGroup, &out.FsGroup + *out = new(int64) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodSecurityContext. +func (in *PodSecurityContext) DeepCopy() *PodSecurityContext { + if in == nil { + return nil + } + out := new(PodSecurityContext) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Proxy) DeepCopyInto(out *Proxy) { + *out = *in + if in.NonProxyHosts != nil { + in, out := &in.NonProxyHosts, &out.NonProxyHosts + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Proxy. +func (in *Proxy) DeepCopy() *Proxy { + if in == nil { + return nil + } + out := new(Proxy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceList) DeepCopyInto(out *ResourceList) { + *out = *in + out.Memory = in.Memory.DeepCopy() + out.Cpu = in.Cpu.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceList. +func (in *ResourceList) DeepCopy() *ResourceList { + if in == nil { + return nil + } + out := new(ResourceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceRequirements) DeepCopyInto(out *ResourceRequirements) { + *out = *in + in.Requests.DeepCopyInto(&out.Requests) + in.Limits.DeepCopyInto(&out.Limits) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceRequirements. +func (in *ResourceRequirements) DeepCopy() *ResourceRequirements { + if in == nil { + return nil + } + out := new(ResourceRequirements) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerMetrics) DeepCopyInto(out *ServerMetrics) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerMetrics. +func (in *ServerMetrics) DeepCopy() *ServerMetrics { + if in == nil { + return nil + } + out := new(ServerMetrics) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TrustedCerts) DeepCopyInto(out *TrustedCerts) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrustedCerts. +func (in *TrustedCerts) DeepCopy() *TrustedCerts { + if in == nil { + return nil + } + out := new(TrustedCerts) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspaceDefaultPlugins) DeepCopyInto(out *WorkspaceDefaultPlugins) { + *out = *in + if in.Plugins != nil { + in, out := &in.Plugins, &out.Plugins + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceDefaultPlugins. +func (in *WorkspaceDefaultPlugins) DeepCopy() *WorkspaceDefaultPlugins { + if in == nil { + return nil + } + out := new(WorkspaceDefaultPlugins) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspaceStorage) DeepCopyInto(out *WorkspaceStorage) { + *out = *in + out.Pvc = in.Pvc +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceStorage. +func (in *WorkspaceStorage) DeepCopy() *WorkspaceStorage { + if in == nil { + return nil + } + out := new(WorkspaceStorage) + in.DeepCopyInto(out) + return out +} diff --git a/api/v2alpha1/checluster_types.go b/api/v2alpha1/checluster_types.go deleted file mode 100644 index 499eed655..000000000 --- a/api/v2alpha1/checluster_types.go +++ /dev/null @@ -1,213 +0,0 @@ -// -// Copyright (c) 2019-2021 Red Hat, Inc. -// This program and the accompanying materials are made -// available under the terms of the Eclipse Public License 2.0 -// which is available at https://www.eclipse.org/legal/epl-2.0/ -// -// SPDX-License-Identifier: EPL-2.0 -// -// Contributors: -// Red Hat, Inc. - initial API and implementation -// - -package v2alpha1 - -import ( - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" -) - -// CheClusterSpec holds the configuration of the Che controller. -// +k8s:openapi-gen=true -type CheClusterSpec struct { - // If false, Che is disabled and does not resolve the devworkspaces with the che routingClass. - Enabled *bool `json:"enabled,omitempty"` - - // Workspaces contains configuration affecting the behavior of workspaces. - Workspaces Workspaces `json:"workspaces"` - - // Gateway contains the configuration of the gateway used for workspace endpoint routing. - Gateway CheGatewaySpec `json:"gateway,omitempty"` - - // K8s contains the configuration specific only to Kubernetes - K8s CheClusterSpecK8s `json:"k8s,omitempty"` -} - -type Workspaces struct { - // Configuration of the workspace endpoints that are exposed on separate domains, as opposed to the subpaths - // of the gateway. - DomainEndpoints DomainEndpoints `json:"domainEndpoints,omitempty"` - - // The node selector that limits the nodes that can run the workspace pods. - PodNodeSelector map[string]string `json:"podNodeSelector,omitempty"` - - // The pod tolerations put on the workspace pods to limit where the workspace pods can run. - PodTolerations []corev1.Toleration `json:"podTolerations,omitempty"` -} - -type DomainEndpoints struct { - // The workspace endpoints that need to be deployed on a subdomain will be deployed on subdomains of this base domain. - // This is mandatory on Kubernetes. On OpenShift, an attempt is made to automatically figure out the base domain of - // the routes. The resolved value of this property is written to the status. - BaseDomain string `json:"baseDomain,omitempty"` - - // Name of a secret that will be used to setup ingress/route TLS certificate for the workspace endpoints. The endpoints - // will be on randomly named subdomains of the `BaseDomain` and therefore the TLS certificate should a wildcard certificate. - // - // When the field is empty string, the endpoints are served over unecrypted HTTP. This might be OK because the workspace endpoints - // generally only expose applications in debugging sessions during development. - // - // The secret is assumed to exist in the same namespace as the CheCluster CR is copied to the namespace of the devworkspace on - // devworkspace start (so that the ingresses on Kubernetes can reference it). - // - // The secret has to be of type "tls". - // - // +optional - TlsSecretName string `json:"tlsSecretName,omitempty"` -} - -type CheGatewaySpec struct { - // Enabled enables or disables routing of the url rewrite supporting devworkspace endpoints - // through a common gateway (the hostname of which is defined by the Host). - // - // Default value is "true" meaning that the gateway is enabled. - // - // If set to true (i.e. the gateway is enabled), endpoints marked using the "urlRewriteSupported" attribute - // are exposed on unique subpaths of the Host, while the rest of the devworkspace endpoints are exposed - // on subdomains of the Host. - // - // If set to false (i.e. the gateway is disabled), all endpoints are deployed on subdomains of - // the Host. - Enabled *bool `json:"enabled,omitempty"` - - // Host is the full host name used to expose devworkspace endpoints that support url rewriting reverse proxy. - // See the gateway.enabled attribute for a more detailed description of where and how are devworkspace endpoints - // exposed in various configurations. - // - // This attribute is mandatory on Kubernetes, optional on OpenShift. - Host string `json:"host,omitempty"` - - // Name of a secret that will be used to setup ingress/route TLS certificate for the gateway host. - // When the field is empty string, the default cluster certificate will be used. - // The secret is assumed to exist in the same namespace as the CheCluster CR. - // - // The secret has to be of type "tls". - // - // +optional - TlsSecretName string `json:"tlsSecretName,omitempty"` - - // Image is the docker image to use for the Che gateway. This is only used if Enabled is true. - // If not defined in the CR, it is taken from - // the `RELATED_IMAGE_gateway` environment variable of the operator deployment/pod. If not defined there, - // it defaults to a hardcoded value. - Image string `json:"image,omitempty"` - - // ConfigurerImage is the docker image to use for the sidecar of the Che gateway that is - // used to configure it. This is only used when Enabled is true. If not defined in the CR, - // it is taken from the `RELATED_IMAGE_gateway_configurer` environment variable of the operator - // deployment/pod. If not defined there, it defaults to a hardcoded value. - ConfigurerImage string `json:"configurerImage,omitempty"` - - // ConfigLabels are labels that are put on the gateway configuration configmaps so that they are picked up - // by the gateway configurer. The default value are labels: app=che,component=che-gateway-config - // +optional - ConfigLabels labels.Set `json:"configLabels,omitempty"` -} - -// CheClusterSpecK8s contains the configuration options specific to Kubernetes only. -type CheClusterSpecK8s struct { - // IngressAnnotations are the annotations to be put on the generated ingresses. This can be used to - // configure the ingress class and the ingress-controller-specific behavior for both the gateway - // and the ingresses created to expose the Devworkspace component endpoints. - // When not specified, this defaults to: - // - // kubernetes.io/ingress.class: "nginx" - // nginx.ingress.kubernetes.io/proxy-read-timeout: "3600", - // nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600", - // nginx.ingress.kubernetes.io/ssl-redirect: "true" - // - // +optional - IngressAnnotations map[string]string `json:"ingressAnnotations,omitempty"` -} - -// GatewayPhase describes the different phases of the Che gateway lifecycle -type GatewayPhase string - -const ( - GatewayPhaseInitializing = "Initializing" - GatewayPhaseEstablished = "Established" - GatewayPhaseInactive = "Inactive" -) - -// ClusterPhase describes the different phases of the Che cluster lifecycle -type ClusterPhase string - -const ( - ClusterPhaseActive = "Active" - ClusterPhaseInactive = "Inactive" - ClusterPhasePendingDeletion = "PendingDeletion" -) - -// CheClusterStatusV2Alpha1 contains the status of the CheCluster object -// +k8s:openapi-gen=true -type CheClusterStatusV2Alpha1 struct { - // GatewayPhase specifies the phase in which the gateway deployment currently is. - // If the gateway is disabled, the phase is "Inactive". - GatewayPhase GatewayPhase `json:"gatewayPhase,omitempty"` - - // GatewayHost is the resolved host of the ingress/route. This is equal to the Host in the spec - // on Kubernetes but contains the actual host name of the route if Host is unspecified on OpenShift. - GatewayHost string `json:"gatewayHost,omitempty"` - - // Phase is the phase in which the Che cluster as a whole finds itself in. - Phase ClusterPhase `json:"phase,omitempty"` - - // A brief CamelCase message indicating details about why the Che cluster is in this state. - Reason string `json:"reason,omitempty"` - - // Message contains further human-readable info for why the Che cluster is in the phase it currently is. - Message string `json:"message,omitempty"` - - // The resolved workspace base domain. This is either the copy of the explicitly defined property of the - // same name in the spec or, if it is undefined in the spec and we're running on OpenShift, the automatically - // resolved basedomain for routes. - WorkspaceBaseDomain string `json:"workspaceBaseDomain,omitempty"` -} - -// CheCluster is the configuration of the CheCluster layer of Devworkspace. -// +k8s:openapi-gen=true -// +kubebuilder:subresource:status -// +kubebuilder:resource:path=checlusters,scope=Namespaced -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +operator-sdk:csv:customresourcedefinitions:displayName="Eclipse Che Cluster" -type CheCluster struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec CheClusterSpec `json:"spec,omitempty"` - Status CheClusterStatusV2Alpha1 `json:"status,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// CheClusterList is the list type for CheCluster -type CheClusterList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []CheCluster `json:"items"` -} - -func init() { - SchemeBuilder.Register(&CheCluster{}, &CheClusterList{}) -} - -// IsEnabled is a utility method checking the `Enabled` property using its optional value or the default. -func (s *CheClusterSpec) IsEnabled() bool { - return s.Enabled == nil || *s.Enabled -} - -// IsEnabled is a utility method checking the `Enabled` property using its optional value or the default. -func (s *CheGatewaySpec) IsEnabled() bool { - return s.Enabled == nil || *s.Enabled -} diff --git a/api/v2alpha1/doc.go b/api/v2alpha1/doc.go deleted file mode 100644 index a3525d2d1..000000000 --- a/api/v2alpha1/doc.go +++ /dev/null @@ -1,16 +0,0 @@ -// -// Copyright (c) 2019-2021 Red Hat, Inc. -// This program and the accompanying materials are made -// available under the terms of the Eclipse Public License 2.0 -// which is available at https://www.eclipse.org/legal/epl-2.0/ -// -// SPDX-License-Identifier: EPL-2.0 -// -// Contributors: -// Red Hat, Inc. - initial API and implementation -// - -// Package v2alpha1 contains API Schema definitions for the org v2alpha1 API group -// +k8s:deepcopy-gen=package,register -// +groupName=org.eclipse.che -package v2alpha1 diff --git a/api/v2alpha1/zz_generated.deepcopy.go b/api/v2alpha1/zz_generated.deepcopy.go deleted file mode 100644 index 4a0ff7cda..000000000 --- a/api/v2alpha1/zz_generated.deepcopy.go +++ /dev/null @@ -1,214 +0,0 @@ -// +build !ignore_autogenerated - -// -// Copyright (c) 2019-2021 Red Hat, Inc. -// This program and the accompanying materials are made -// available under the terms of the Eclipse Public License 2.0 -// which is available at https://www.eclipse.org/legal/epl-2.0/ -// -// SPDX-License-Identifier: EPL-2.0 -// -// Contributors: -// Red Hat, Inc. - initial API and implementation -// - -// Code generated by controller-gen. DO NOT EDIT. - -package v2alpha1 - -import ( - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CheCluster) DeepCopyInto(out *CheCluster) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheCluster. -func (in *CheCluster) DeepCopy() *CheCluster { - if in == nil { - return nil - } - out := new(CheCluster) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *CheCluster) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CheClusterList) DeepCopyInto(out *CheClusterList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]CheCluster, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheClusterList. -func (in *CheClusterList) DeepCopy() *CheClusterList { - if in == nil { - return nil - } - out := new(CheClusterList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *CheClusterList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CheClusterSpec) DeepCopyInto(out *CheClusterSpec) { - *out = *in - if in.Enabled != nil { - in, out := &in.Enabled, &out.Enabled - *out = new(bool) - **out = **in - } - in.Workspaces.DeepCopyInto(&out.Workspaces) - in.Gateway.DeepCopyInto(&out.Gateway) - in.K8s.DeepCopyInto(&out.K8s) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheClusterSpec. -func (in *CheClusterSpec) DeepCopy() *CheClusterSpec { - if in == nil { - return nil - } - out := new(CheClusterSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CheClusterSpecK8s) DeepCopyInto(out *CheClusterSpecK8s) { - *out = *in - if in.IngressAnnotations != nil { - in, out := &in.IngressAnnotations, &out.IngressAnnotations - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheClusterSpecK8s. -func (in *CheClusterSpecK8s) DeepCopy() *CheClusterSpecK8s { - if in == nil { - return nil - } - out := new(CheClusterSpecK8s) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CheClusterStatusV2Alpha1) DeepCopyInto(out *CheClusterStatusV2Alpha1) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheClusterStatusV2Alpha1. -func (in *CheClusterStatusV2Alpha1) DeepCopy() *CheClusterStatusV2Alpha1 { - if in == nil { - return nil - } - out := new(CheClusterStatusV2Alpha1) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CheGatewaySpec) DeepCopyInto(out *CheGatewaySpec) { - *out = *in - if in.Enabled != nil { - in, out := &in.Enabled, &out.Enabled - *out = new(bool) - **out = **in - } - if in.ConfigLabels != nil { - in, out := &in.ConfigLabels, &out.ConfigLabels - *out = make(labels.Set, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheGatewaySpec. -func (in *CheGatewaySpec) DeepCopy() *CheGatewaySpec { - if in == nil { - return nil - } - out := new(CheGatewaySpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DomainEndpoints) DeepCopyInto(out *DomainEndpoints) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainEndpoints. -func (in *DomainEndpoints) DeepCopy() *DomainEndpoints { - if in == nil { - return nil - } - out := new(DomainEndpoints) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Workspaces) DeepCopyInto(out *Workspaces) { - *out = *in - out.DomainEndpoints = in.DomainEndpoints - if in.PodNodeSelector != nil { - in, out := &in.PodNodeSelector, &out.PodNodeSelector - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.PodTolerations != nil { - in, out := &in.PodTolerations, &out.PodTolerations - *out = make([]v1.Toleration, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Workspaces. -func (in *Workspaces) DeepCopy() *Workspaces { - if in == nil { - return nil - } - out := new(Workspaces) - in.DeepCopyInto(out) - return out -} diff --git a/bundle/next/eclipse-che-preview-openshift/manifests/che-operator.clusterserviceversion.yaml b/bundle/next/eclipse-che-preview-openshift/manifests/che-operator.clusterserviceversion.yaml index 7be730fdf..83019e747 100644 --- a/bundle/next/eclipse-che-preview-openshift/manifests/che-operator.clusterserviceversion.yaml +++ b/bundle/next/eclipse-che-preview-openshift/manifests/che-operator.clusterserviceversion.yaml @@ -20,7 +20,8 @@ metadata: "apiVersion": "org.eclipse.che/v1", "kind": "CheCluster", "metadata": { - "name": "eclipse-che" + "name": "eclipse-che", + "namespace": "eclipse-che" }, "spec": { "auth": { @@ -29,34 +30,55 @@ metadata: "oAuthSecret": "" }, "database": { - "chePostgresDb": "", - "chePostgresHostName": "", - "chePostgresPort": "", - "chePostgresSecret": "", "externalDb": false }, - "devWorkspace": { - "enable": true + "k8s": { + "ingressDomain": null, + "tlsSecretName": null }, "metrics": { "enable": true }, "server": { - "cheClusterRoles": "", - "cheWorkspaceClusterRole": "", - "gitSelfSignedCert": false, - "nonProxyHosts": "", - "proxyPort": "", - "proxySecret": "", - "proxyURL": "", "workspaceNamespaceDefault": "-che" }, "storage": { - "postgresPVCStorageClassName": "", - "preCreateSubPaths": true, - "pvcClaimSize": "10Gi", - "pvcStrategy": "common", - "workspacePVCStorageClassName": "" + "pvcStrategy": "common" + } + } + }, + { + "apiVersion": "org.eclipse.che/v2", + "kind": "CheCluster", + "metadata": { + "name": "eclipse-che", + "namespace": "eclipse-che" + }, + "spec": { + "components": { + "database": { + "externalDb": false + }, + "metrics": { + "enable": true + } + }, + "devEnvironments": { + "defaultNamespace": { + "template": "-che" + }, + "storage": { + "pvcStrategy": "common" + } + }, + "networking": { + "auth": { + "identityProviderURL": "", + "oAuthClientName": "", + "oAuthSecret": "" + }, + "domain": "", + "tlsSecretName": "" } } } @@ -75,12 +97,123 @@ metadata: operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 repository: https://github.com/eclipse-che/che-operator support: Eclipse Foundation - name: eclipse-che-preview-openshift.v7.48.0-445.next + name: eclipse-che-preview-openshift.v7.49.0-565.next namespace: placeholder spec: apiservicedefinitions: {} customresourcedefinitions: owned: + - description: 'The `CheCluster` custom resource allows defining and managing + Eclipse Che server installation. Based on these settings, the Operator + automatically creates and maintains several ConfigMaps: `che`, `plugin-registry`, + `devfile-registry` that will contain the appropriate environment variables + of the various components of the installation. These generated ConfigMaps + must NOT be updated manually.' + displayName: Eclipse Che instance Specification + kind: CheCluster + name: checlusters.org.eclipse.che + resources: + - kind: ClusterRole + name: '' + version: v1 + - kind: ClusterRoleBinding + name: '' + version: v1 + - kind: ConfigMap + name: '' + version: v1 + - kind: Deployment + name: '' + version: apps/v1 + - kind: Ingress + name: '' + version: v1 + - kind: Role + name: '' + version: v1 + - kind: RoleBinding + name: '' + version: v1 + - kind: Route + name: '' + version: v1 + - kind: Secret + name: '' + version: v1 + - kind: Service + name: '' + version: v1 + specDescriptors: + - description: Development environment default configuration options. + displayName: Development environments + path: devEnvironments + - description: Che components configuration. + displayName: Components + path: components + - description: Networking, Che authentication and TLS configuration. + displayName: Networking + path: networking + - description: Configuration of an alternative registry that stores Che + images. + displayName: Container registry + path: containerRegistry + statusDescriptors: + - description: Specifies the current phase of the Che deployment. + displayName: ChePhase + path: chePhase + x-descriptors: + - urn:alm:descriptor:text + - description: Public URL to the Che server. + displayName: Eclipse Che URL + path: cheURL + x-descriptors: + - urn:alm:descriptor:org.w3:link + - description: Current installed Che version. + displayName: 'displayName: Eclipse Che version' + path: cheVersion + x-descriptors: + - urn:alm:descriptor:text + - description: The public URL to the internal devfile registry. + displayName: Devfile registry URL + path: devfileRegistryURL + x-descriptors: + - urn:alm:descriptor:org.w3:link + - description: Specifies the current phase of the gateway deployment. + displayName: Gateway phase + path: gatewayPhase + x-descriptors: + - urn:alm:descriptor:text + - description: A human readable message indicating details about why the + Che deployment is in current phase. + displayName: Message + path: message + x-descriptors: + - urn:alm:descriptor:text + - description: The public URL to the internal plug-in registry. + displayName: Plugin registry URL + path: pluginRegistryURL + x-descriptors: + - urn:alm:descriptor:org.w3:link + - description: The PostgreSQL version of the image in use. + displayName: PostgreSQL version + path: postgresVersion + x-descriptors: + - urn:alm:descriptor:text + - description: A brief CamelCase message indicating details about why the + Che deployment is in current phase. + displayName: Reason + path: reason + x-descriptors: + - urn:alm:descriptor:text + - description: The resolved workspace base domain. This is either the copy + of the explicitly defined property of the same name in the spec or, + if it is undefined in the spec and we're running on OpenShift, the automatically + resolved basedomain for routes. + displayName: Workspace base domain + path: workspaceBaseDomain + x-descriptors: + - urn:alm:descriptor:text + version: v2 - description: The `CheCluster` custom resource allows defining and managing a Che server installation displayName: Eclipse Che instance Specification @@ -220,6 +353,12 @@ spec: path: devfileRegistryURL x-descriptors: - urn:alm:descriptor:org.w3:link + - description: The ConfigMap containing certificates to propagate to the + Che components and to provide particular configuration for Git. + displayName: Git certificates + path: gitServerTLSCertificateConfigMapName + x-descriptors: + - urn:alm:descriptor:text - description: A URL that points to some URL where to find help related to the current Operator status. displayName: Help link @@ -540,6 +679,16 @@ spec: - get - list - watch + - apiGroups: + - cert-manager.io + resources: + - issuers + - certificates + verbs: + - create + - get + - list + - update - apiGroups: - "" resources: @@ -971,8 +1120,8 @@ spec: value: quay.io/eclipse/che-plugin-registry:next - name: RELATED_IMAGE_devfile_registry value: quay.io/eclipse/che-devfile-registry:next - - name: RELATED_IMAGE_pvc_jobs - value: registry.access.redhat.com/ubi8-minimal:8.6-751 + - name: RELATED_IMAGE_che_tls_secrets_creation_job + value: quay.io/eclipse/che-tls-secret-creator:alpine-01a4c34 - name: RELATED_IMAGE_postgres value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392 - name: RELATED_IMAGE_postgres_13_3 @@ -1020,6 +1169,9 @@ spec: timeoutSeconds: 5 name: che-operator ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP - containerPort: 60000 name: metrics readinessProbe: @@ -1046,12 +1198,21 @@ spec: privileged: false readOnlyRootFilesystem: false runAsNonRoot: true + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: webhook-tls-certs + readOnly: true hostIPC: false hostNetwork: false hostPID: false restartPolicy: Always serviceAccountName: che-operator terminationGracePeriodSeconds: 20 + volumes: + - name: webhook-tls-certs + secret: + defaultMode: 420 + secretName: che-operator-webhook-server-cert permissions: - rules: - apiGroups: @@ -1216,37 +1377,6 @@ spec: - patch - update - watch - - apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete - - apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete - - apiGroups: - - "" - resources: - - events - verbs: - - create - - patch serviceAccountName: che-operator strategy: deployment installModes: @@ -1273,9 +1403,22 @@ spec: - name: Operator GitHub Repo url: https://github.com/eclipse-che/che-operator maintainers: - - email: dfestal@redhat.com - name: David Festal + - email: abazko@redhat.com + name: Anatolii Bazko maturity: stable provider: name: Eclipse Foundation - version: 7.48.0-445.next + version: 7.49.0-565.next + webhookdefinitions: + - admissionReviewVersions: + - v1 + - v2 + containerPort: 443 + conversionCRDs: + - checlusters.org.eclipse.che + deploymentName: che-operator + generateName: ccheclusters.kb.io + sideEffects: None + targetPort: 9443 + type: ConversionWebhook + webhookPath: /convert diff --git a/bundle/next/eclipse-che-preview-openshift/manifests/manager-config_v1_configmap.yaml b/bundle/next/eclipse-che-preview-openshift/manifests/manager-config_v1_configmap.yaml deleted file mode 100644 index 83e8dc5c7..000000000 --- a/bundle/next/eclipse-che-preview-openshift/manifests/manager-config_v1_configmap.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright (c) 2019-2021 Red Hat, Inc. -# This program and the accompanying materials are made -# available under the terms of the Eclipse Public License 2.0 -# which is available at https://www.eclipse.org/legal/epl-2.0/ -# -# SPDX-License-Identifier: EPL-2.0 -# -# Contributors: -# Red Hat, Inc. - initial API and implementation -# - -apiVersion: v1 -data: - controller_manager_config.yaml: | - apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 - kind: ControllerManagerConfig - health: - healthProbeBindAddress: :6789 - metrics: - bindAddress: 127.0.0.1:60000 - webhook: - port: 9443 - leaderElection: - leaderElect: true - resourceName: e79b08a4.org.eclipse.che -kind: ConfigMap -metadata: - name: manager-config diff --git a/bundle/next/eclipse-che-preview-openshift/manifests/org_v1_che_crd.yaml b/bundle/next/eclipse-che-preview-openshift/manifests/org.eclipse.che_checlusters.yaml similarity index 51% rename from bundle/next/eclipse-che-preview-openshift/manifests/org_v1_che_crd.yaml rename to bundle/next/eclipse-che-preview-openshift/manifests/org.eclipse.che_checlusters.yaml index 7a1e619a6..7f9e7aae3 100644 --- a/bundle/next/eclipse-che-preview-openshift/manifests/org_v1_che_crd.yaml +++ b/bundle/next/eclipse-che-preview-openshift/manifests/org.eclipse.che_checlusters.yaml @@ -20,8 +20,20 @@ metadata: app.kubernetes.io/instance: che app.kubernetes.io/managed-by: olm app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org name: checlusters.org.eclipse.che spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: che-operator-service + namespace: eclipse-che + path: /convert + conversionReviewVersions: + - v1 + - v2 group: org.eclipse.che names: kind: CheCluster @@ -1191,6 +1203,11 @@ spec: description: Indicates whether an Identity Provider instance, Keycloak or RH-SSO, has been configured to integrate with the GitHub OAuth. type: boolean + gitServerTLSCertificateConfigMapName: + description: The ConfigMap containing certificates to propagate + to the Che components and to provide particular configuration + for Git. + type: string helpLink: description: A URL that points to some URL where to find help related to the current Operator status. @@ -1226,10 +1243,1130 @@ spec: type: object type: object served: true + storage: false + subresources: + status: {} + - name: v2 + schema: + openAPIV3Schema: + description: 'The `CheCluster` custom resource allows defining and managing + Eclipse Che server installation. Based on these settings, the Operator + automatically creates and maintains several ConfigMaps: `che`, `plugin-registry`, + `devfile-registry` that will contain the appropriate environment variables + of the various components of the installation. These generated ConfigMaps + must NOT be updated manually.' + 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: Desired configuration of Eclipse Che installation. + properties: + components: + description: Che components configuration. + properties: + cheServer: + description: General configuration settings related to the Che + server. + properties: + clusterRoles: + description: 'ClusterRoles assigned to Che ServiceAccount. + The defaults roles are: - `-cheworkspaces-namespaces-clusterrole` + - `-cheworkspaces-clusterrole` - `-cheworkspaces-devworkspace-clusterrole` + where the is the namespace where the CheCluster + CRD is created. Each role must have a `app.kubernetes.io/part-of=che.eclipse.org` + label. The Che Operator must already have all permissions + in these ClusterRoles to grant them.' + items: + type: string + type: array + debug: + description: Enables the debug mode for Che server. + type: boolean + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave + it empty to use the default container image + provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value + is `Always` for `nightly`, `next` or `latest` + images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this + container. + properties: + limits: + description: Limits describe the maximum amount + of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 + cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi + = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum + amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 + cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi + = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies + to all containers in a pod. The default value + is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the + container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + extraProperties: + additionalProperties: + type: string + description: A map of additional environment variables applied + in the generated `che` ConfigMap to be used by the Che + server, in addition to the values already generated from + other fields of the `CheCluster` custom resource (CR). + If the `extraProperties` fields contains a property normally + generated in `che` ConfigMap from other CR fields, the + value defined in the `extraProperties` is used instead. + type: object + logLevel: + default: INFO + description: 'The log level for the Che server: `INFO` or + `DEBUG`.' + type: string + proxy: + description: Proxy server settings for Kubernetes cluster. + No additional configuration is required for OpenShift + cluster. By specifying these settings for the OpenShift + cluster, you override the OpenShift proxy configuration. + properties: + credentialsSecretName: + description: The secret name that contains `user` and + `password` for a proxy server. The secret must have + a `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + nonProxyHosts: + description: 'A list of hosts that can be reached directly, + bypassing the proxy. Specify wild card domain use + the following form `.`, for example: - + localhost - my.host.com - 123.42.12.32 Use only + when a proxy configuration is required. Operator respects + OpenShift cluster-wide proxy configuration, defining + `nonProxyHosts` in a custom resource leads to merging + non-proxy hosts lists from the cluster proxy configuration, + and the ones defined in the custom resources. See + the following page: https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. + See also the `proxyURL` fields.' + items: + type: string + type: array + port: + description: Proxy server port. + type: string + url: + description: 'URL (protocol+hostname) of the proxy server. + Use only when a proxy configuration is required. Operator + respects OpenShift cluster-wide proxy configuration, + defining `url` in a custom resource leads to overriding + the cluster proxy configuration. See the following + page: https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. + See also the `proxyPort` and `nonProxyHosts` fields.' + type: string + type: object + type: object + dashboard: + description: Configuration settings related to the Dashboard + used by the Che installation. + properties: + HeaderMessage: + description: Dashboard header message. + properties: + show: + description: Instructs dashboard to show the message. + type: boolean + warning: + description: Warning message displayed on the user dashboard. + type: string + type: object + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave + it empty to use the default container image + provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value + is `Always` for `nightly`, `next` or `latest` + images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this + container. + properties: + limits: + description: Limits describe the maximum amount + of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 + cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi + = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum + amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 + cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi + = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies + to all containers in a pod. The default value + is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the + container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + type: object + database: + description: Configuration settings related to the database + used by the Che installation. + properties: + credentialsSecretName: + default: postgres-credentials + description: The secret that contains PostgreSQL `user` + and `password` that the Che server uses to connect to + the DB. The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` + label. + type: string + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave + it empty to use the default container image + provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value + is `Always` for `nightly`, `next` or `latest` + images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this + container. + properties: + limits: + description: Limits describe the maximum amount + of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 + cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi + = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum + amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 + cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi + = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies + to all containers in a pod. The default value + is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the + container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + externalDb: + description: Instructs the Operator to deploy a dedicated + database. By default, a dedicated PostgreSQL database + is deployed as part of the Che installation. When `externalDb` + is set as `true`, no dedicated database is deployed by + the Operator and you need to provide connection details + to the external DB you want to use. + type: boolean + postgresDb: + default: dbche + description: PostgreSQL database name that the Che server + uses to connect to the DB. + type: string + postgresHostName: + default: postgres + description: PostgreSQL database hostname that the Che server + connects to. Override this value only when using an external + database. See field `externalDb`. + type: string + postgresPort: + default: "5432" + description: PostgreSQL Database port the Che server connects + to. Override this value only when using an external database. + See field `externalDb`. + type: string + pvc: + description: PVC settings for PostgreSQL database. + properties: + claimSize: + description: Persistent Volume Claim size. To update + the claim size, Storage class that provisions it must + support resize. + type: string + storageClass: + description: Storage class for the Persistent Volume + Claim. When omitted or left blank, a default storage + class is used. + type: string + type: object + type: object + devWorkspace: + description: DevWorkspace operator configuration. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave + it empty to use the default container image + provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value + is `Always` for `nightly`, `next` or `latest` + images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this + container. + properties: + limits: + description: Limits describe the maximum amount + of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 + cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi + = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum + amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 + cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi + = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies + to all containers in a pod. The default value + is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the + container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + runningLimit: + description: The maximum number of running workspaces per + user. + type: string + type: object + devfileRegistry: + description: Configuration settings related to the Devfile registry + used by the Che installation. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave + it empty to use the default container image + provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value + is `Always` for `nightly`, `next` or `latest` + images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this + container. + properties: + limits: + description: Limits describe the maximum amount + of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 + cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi + = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum + amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 + cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi + = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies + to all containers in a pod. The default value + is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the + container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + disableInternalRegistry: + description: Disables internal devfile registry. + type: boolean + externalDevfileRegistries: + description: External devfile registries serving sample + ready-to-use devfiles. + items: + description: External devfile registries configuration. + properties: + url: + description: The public UR of the devfile registry + that serves sample ready-to-use devfiles. + type: string + type: object + type: array + type: object + imagePuller: + description: Kubernetes Image Puller configuration. + properties: + enable: + description: Install and configure the community supported + Kubernetes Image Puller Operator. When you set the value + to `true` without providing any specs, it creates a default + Kubernetes Image Puller object managed by the Operator. + When you set the value to `false`, the Kubernetes Image + Puller object is deleted, and the Operator uninstalled, + regardless of whether a spec is provided. If you leave + the `spec.images` field empty, a set of recommended workspace-related + images is automatically detected and pre-pulled after + installation. Note that while this Operator and its behavior + is community-supported, its payload may be commercially-supported + for pulling commercially-supported images. + type: boolean + spec: + description: A Kubernetes Image Puller spec to configure + the image puller in the CheCluster + properties: + affinity: + type: string + cachingCPULimit: + type: string + cachingCPURequest: + type: string + cachingIntervalHours: + type: string + cachingMemoryLimit: + type: string + cachingMemoryRequest: + type: string + configMapName: + type: string + daemonsetName: + type: string + deploymentName: + type: string + imagePullSecrets: + type: string + imagePullerImage: + type: string + images: + type: string + nodeSelector: + type: string + type: object + required: + - enable + type: object + metrics: + description: Che server metrics configuration. + properties: + enable: + default: true + description: Enables `metrics` for the Che server endpoint. + type: boolean + type: object + pluginRegistry: + description: Configuration settings related to the plug-in registry + used by the Che installation. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave + it empty to use the default container image + provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value + is `Always` for `nightly`, `next` or `latest` + images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this + container. + properties: + limits: + description: Limits describe the maximum amount + of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 + cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi + = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum + amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 + cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi + = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies + to all containers in a pod. The default value + is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the + container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + disableInternalRegistry: + description: Disables internal plug-in registry. + type: boolean + externalPluginRegistries: + description: External plugin registries. + items: + description: External plug-in registries configuration. + properties: + url: + description: Public URL of the plug-in registry. + type: string + type: object + type: array + type: object + type: object + containerRegistry: + description: Configuration of an alternative registry that stores + Che images. + properties: + hostname: + description: An optional hostname or URL of an alternative container + registry to pull images from. This value overrides the container + registry hostname defined in all the default container images + involved in a Che deployment. This is particularly useful + for installing Che in a restricted environment. + type: string + organization: + description: An optional repository name of an alternative registry + to pull images from. This value overrides the container registry + organization defined in all the default container images involved + in a Che deployment. This is particularly useful for installing + Eclipse Che in a restricted environment. + type: string + type: object + devEnvironments: + description: Development environment default configuration options. + properties: + defaultNamespace: + description: User's default namespace. + properties: + template: + description: If you don't create user namespaces in advance, + this field defines the Kubernetes namespace created when + you start your first workspace. You can use ``, + `` and `` placeholders, such as che-workspace-. + type: string + type: object + defaultPlugins: + description: Default plug-ins applied to Dev Workspaces. + items: + properties: + editor: + description: The editor id to specify default plug-ins + for. + type: string + plugins: + description: Default plug-in URIs for the specified editor. + items: + type: string + type: array + type: object + type: array + nodeSelector: + additionalProperties: + type: string + description: The node selector limits the nodes that can run + the workspace pods. + type: object + storage: + description: Workspaces persistent storage. + properties: + pvc: + description: PVC settings. + properties: + claimSize: + description: Persistent Volume Claim size. To update + the claim size, Storage class that provisions it must + support resize. + type: string + storageClass: + description: Storage class for the Persistent Volume + Claim. When omitted or left blank, a default storage + class is used. + type: string + type: object + pvcStrategy: + default: common + description: Persistent volume claim strategy for the Che + server. Only the `common` strategy is supported (all workspaces + PVCs in one volume). For details, see https://github.com/eclipse/che/issues/21185. + type: string + type: object + tolerations: + description: The pod tolerations of the workspace pods limit + where the workspace pods can run. + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using + the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, + allowed values are NoSchedule, PreferNoSchedule and + NoExecute. + type: string + key: + description: Key is the taint key that the toleration + applies to. Empty means match all taint keys. If the + key is empty, operator must be Exists; this combination + means to match all values and all keys. + type: string + operator: + description: Operator represents a key's relationship + to the value. Valid operators are Exists and Equal. + Defaults to Equal. Exists is equivalent to wildcard + for value, so that a pod can tolerate all taints of + a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of + time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + By default, it is not set, which means tolerate the + taint forever (do not evict). Zero and negative values + will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + trustedCerts: + description: Trusted certificate settings. + properties: + gitTrustedCertsConfigMapName: + description: 'The ConfigMap contains certificates to propagate + to the Che components and to provide a particular configuration + for Git. See the following page: https://www.eclipse.org/che/docs/stable/administration-guide/deploying-che-with-support-for-git-repositories-with-self-signed-certificates/ + The ConfigMap must have a `app.kubernetes.io/part-of=che.eclipse.org` + label.' + type: string + type: object + type: object + networking: + description: Networking, Che authentication and TLS configuration. + properties: + annotations: + additionalProperties: + type: string + description: 'Defines annotations which will be set to an ingress + (a route for OpenShift platform). The defaults for kubernetes + platforms are: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/proxy-read-timeout: "3600", nginx.ingress.kubernetes.io/proxy-connect-timeout: + "3600", nginx.ingress.kubernetes.io/ssl-redirect: "true"' + type: object + auth: + description: Authentication settings. + properties: + gateway: + description: Gateway settings. + properties: + configLabels: + additionalProperties: + type: string + description: Gate configuration labels. + type: object + deployment: + description: 'Deployment override options. Since gateway + deployment consists of several containers, they must + be distinguished in the configuration by their names: + - `gateway` - `configbump` - `oauth-proxy` - `kube-rbac-proxy`' + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave + it empty to use the default container image + provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value + is `Always` for `nightly`, `next` or `latest` + images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by + this container. + properties: + limits: + description: Limits describe the maximum + amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m + = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi + = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum + amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m + = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi + = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run + with. + properties: + fsGroup: + description: A special supplemental group that + applies to all containers in a pod. The default + value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of + the container process. The default value is + `1724`. + format: int64 + type: integer + type: object + type: object + type: object + identityProviderURL: + description: Public URL of the Identity Provider server. + type: string + oAuthClientName: + description: Name of the OpenShift `OAuthClient` resource + used to set up identity federation on the OpenShift side. + type: string + oAuthSecret: + description: Name of the secret set in the OpenShift `OAuthClient` + resource used to set up identity federation on the OpenShift + side. + type: string + type: object + domain: + description: 'For an OpenShift cluster, the Operator uses the + domain to generate a hostname for a route. The generated hostname + follows this pattern: che-.. The + is the namespace where the CheCluster CRD is created. In a + conjunction with labels it creates a route, which is served + by a non-default Ingress controller. For Kubernetes cluster + it contains a global ingress domain. This MUST be explicitly + specified: there are no defaults.' + type: string + hostname: + description: The public hostname of the installed Che server. + type: string + labels: + additionalProperties: + type: string + description: Defines labels which will be set to an ingress + (a route for OpenShift platform). + type: object + tlsSecretName: + description: The name of the secret used to set up Ingress TLS + termination. If the field is an empty string, the default + cluster certificate is used. The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` + label. + type: string + type: object + type: object + status: + description: CheClusterStatus defines the observed state of Che installation. + properties: + chePhase: + description: Specifies the current phase of the Che deployment. + type: string + cheURL: + description: Public URL to the Che server. + type: string + cheVersion: + description: Current installed Che version. + type: string + devfileRegistryURL: + description: The public URL to the internal devfile registry. + type: string + gatewayPhase: + description: Specifies the current phase of the gateway deployment. + type: string + message: + description: A human readable message indicating details about why + the Che deployment is in current phase. + type: string + pluginRegistryURL: + description: The public URL to the internal plug-in registry. + type: string + postgresVersion: + description: The PostgreSQL version of the image in use. + type: string + reason: + description: A brief CamelCase message indicating details about + why the Che deployment is in current phase. + type: string + workspaceBaseDomain: + description: The resolved workspace base domain. This is either + the copy of the explicitly defined property of the same name in + the spec or, if it is undefined in the spec and we're running + on OpenShift, the automatically resolved basedomain for routes. + type: string + type: object + type: object + served: true storage: true subresources: status: {} - preserveUnknownFields: false status: acceptedNames: kind: "" diff --git a/bundle/stable/eclipse-che-preview-openshift/manifests/org.eclipse.che_chebackupserverconfigurations_crd.yaml b/bundle/stable/eclipse-che-preview-openshift/manifests/org.eclipse.che_chebackupserverconfigurations_crd.yaml deleted file mode 100644 index 7d2e7c444..000000000 --- a/bundle/stable/eclipse-che-preview-openshift/manifests/org.eclipse.che_chebackupserverconfigurations_crd.yaml +++ /dev/null @@ -1,142 +0,0 @@ -# -# Copyright (c) 2019-2021 Red Hat, Inc. -# This program and the accompanying materials are made -# available under the terms of the Eclipse Public License 2.0 -# which is available at https://www.eclipse.org/legal/epl-2.0/ -# -# SPDX-License-Identifier: EPL-2.0 -# -# Contributors: -# Red Hat, Inc. - initial API and implementation -# - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.4.1 - creationTimestamp: null - labels: - app.kubernetes.io/instance: che - app.kubernetes.io/managed-by: olm - app.kubernetes.io/name: che - name: chebackupserverconfigurations.org.eclipse.che -spec: - group: org.eclipse.che - names: - kind: CheBackupServerConfiguration - listKind: CheBackupServerConfigurationList - plural: chebackupserverconfigurations - singular: chebackupserverconfiguration - scope: Namespaced - versions: - - name: v1 - schema: - openAPIV3Schema: - description: The `CheBackupServerConfiguration` custom resource allows defining and managing Eclipse Che Backup Server Configurations - 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: CheBackupServerConfigurationSpec defines the desired state of CheBackupServerConfiguration Only one type of backup server is allowed to be configured per CR. - properties: - awss3: - description: Amazon S3 or compatible alternatives. - properties: - awsAccessKeySecretRef: - description: Reference to secret that contains awsAccessKeyId and awsSecretAccessKey keys. - type: string - hostname: - description: Server hostname, defaults to 's3.amazonaws.com'. Might be customized in case of alternative server. - type: string - port: - description: Backup server port. Usually default value is used. Might be customized in case of alternative server. - type: integer - protocol: - description: Protocol to use when connection to the server. Might be customized in case of alternative server. - type: string - repositoryPasswordSecretRef: - description: Holds reference to a secret with restic repository password under 'repo-password' field to encrypt / decrypt its content. - type: string - repositoryPath: - description: Bucket name and repository, e.g. bucket/repo - type: string - required: - - awsAccessKeySecretRef - - repositoryPasswordSecretRef - - repositoryPath - type: object - rest: - description: Rest backup server configuration. - properties: - credentialsSecretRef: - description: Secret that contains username and password fields to login into restic server. Note, each repository is encrypted with own password. See ResticRepoPasswordSecretRef field. - type: string - hostname: - description: Backup server host - type: string - port: - description: Backup server port - type: integer - protocol: - description: Protocol to use when connection to the server Defaults to https. - type: string - repositoryPasswordSecretRef: - description: Holds reference to a secret with restic repository password under 'repo-password' field to encrypt / decrypt its content. - type: string - repositoryPath: - description: Restic repository path - type: string - required: - - hostname - - repositoryPasswordSecretRef - type: object - sftp: - description: Sftp backup server configuration. - properties: - hostname: - description: Backup server host - type: string - port: - description: Backup server port - type: integer - repositoryPasswordSecretRef: - description: Holds reference to a secret with restic repository password under 'repo-password' field to encrypt / decrypt its content. - type: string - repositoryPath: - description: Restic repository path, relative or absolute, e.g. /srv/repo - type: string - sshKeySecretRef: - description: Private ssh key under 'ssh-privatekey' field for passwordless login - type: string - username: - description: User login on the remote server - type: string - required: - - hostname - - repositoryPasswordSecretRef - - repositoryPath - - sshKeySecretRef - - username - type: object - type: object - status: - description: CheBackupServerConfigurationStatus defines the observed state of CheBackupServerConfiguration - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/bundle/stable/eclipse-che-preview-openshift/manifests/org.eclipse.che_chebackupserverconfigurations_crd.yaml.diff b/bundle/stable/eclipse-che-preview-openshift/manifests/org.eclipse.che_chebackupserverconfigurations_crd.yaml.diff deleted file mode 100644 index e69de29bb..000000000 diff --git a/bundle/stable/eclipse-che-preview-openshift/manifests/org.eclipse.che_checlusterbackups_crd.yaml b/bundle/stable/eclipse-che-preview-openshift/manifests/org.eclipse.che_checlusterbackups_crd.yaml deleted file mode 100644 index 3e7377a2d..000000000 --- a/bundle/stable/eclipse-che-preview-openshift/manifests/org.eclipse.che_checlusterbackups_crd.yaml +++ /dev/null @@ -1,85 +0,0 @@ -# -# Copyright (c) 2019-2021 Red Hat, Inc. -# This program and the accompanying materials are made -# available under the terms of the Eclipse Public License 2.0 -# which is available at https://www.eclipse.org/legal/epl-2.0/ -# -# SPDX-License-Identifier: EPL-2.0 -# -# Contributors: -# Red Hat, Inc. - initial API and implementation -# - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.4.1 - creationTimestamp: null - labels: - app.kubernetes.io/instance: che - app.kubernetes.io/managed-by: olm - app.kubernetes.io/name: che - name: checlusterbackups.org.eclipse.che -spec: - group: org.eclipse.che - names: - kind: CheClusterBackup - listKind: CheClusterBackupList - plural: checlusterbackups - singular: checlusterbackup - scope: Namespaced - versions: - - name: v1 - schema: - openAPIV3Schema: - description: The `CheClusterBackup` custom resource allows defining and managing Eclipse Che backup - 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: CheClusterBackupSpec defines the desired state of CheClusterBackup - properties: - backupServerConfigRef: - description: Name of custom resource with a backup server configuration to use for this backup. Note, UseInternalBackupServer field can configure internal backup server automatically. - type: string - useInternalBackupServer: - description: Automatically setup pod with REST backup server and use the server in this configuration. Note, this flag takes precedence and will overwrite existing backup server configuration. - type: boolean - type: object - status: - description: CheClusterBackupStatus defines the observed state of CheClusterBackup - properties: - cheVersion: - description: Version that was backed up - type: string - message: - description: Message explaining the state of the backup or an error message - type: string - snapshotId: - description: Last backup snapshot ID - type: string - stage: - description: Describes backup progress - type: string - state: - description: 'Backup progress state: InProgress, Failed, Succeeded' - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/bundle/stable/eclipse-che-preview-openshift/manifests/org.eclipse.che_checlusterbackups_crd.yaml.diff b/bundle/stable/eclipse-che-preview-openshift/manifests/org.eclipse.che_checlusterbackups_crd.yaml.diff deleted file mode 100644 index e69de29bb..000000000 diff --git a/bundle/stable/eclipse-che-preview-openshift/manifests/org.eclipse.che_checlusterrestores_crd.yaml b/bundle/stable/eclipse-che-preview-openshift/manifests/org.eclipse.che_checlusterrestores_crd.yaml deleted file mode 100644 index 66a541b1a..000000000 --- a/bundle/stable/eclipse-che-preview-openshift/manifests/org.eclipse.che_checlusterrestores_crd.yaml +++ /dev/null @@ -1,79 +0,0 @@ -# -# Copyright (c) 2019-2021 Red Hat, Inc. -# This program and the accompanying materials are made -# available under the terms of the Eclipse Public License 2.0 -# which is available at https://www.eclipse.org/legal/epl-2.0/ -# -# SPDX-License-Identifier: EPL-2.0 -# -# Contributors: -# Red Hat, Inc. - initial API and implementation -# - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.4.1 - creationTimestamp: null - labels: - app.kubernetes.io/instance: che - app.kubernetes.io/managed-by: olm - app.kubernetes.io/name: che - name: checlusterrestores.org.eclipse.che -spec: - group: org.eclipse.che - names: - kind: CheClusterRestore - listKind: CheClusterRestoreList - plural: checlusterrestores - singular: checlusterrestore - scope: Namespaced - versions: - - name: v1 - schema: - openAPIV3Schema: - description: The `CheClusterRestore` custom resource allows defining and managing Eclipse Che restore - 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: CheClusterRestoreSpec defines the desired state of CheClusterRestore - properties: - backupServerConfigRef: - description: Name of custom resource with a backup server configuration to use for this restore. Can be omitted if only one server configuration object exists within the namespace. - type: string - snapshotId: - description: If omitted, latest snapshot will be used. - type: string - type: object - status: - description: CheClusterRestoreStatus defines the observed state of CheClusterRestore - properties: - message: - description: Restore result or error message - type: string - stage: - description: Describes phase of restore progress - type: string - state: - description: 'Restore progress state: InProgress, Failed, Succeeded' - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/bundle/stable/eclipse-che-preview-openshift/manifests/org.eclipse.che_checlusterrestores_crd.yaml.diff b/bundle/stable/eclipse-che-preview-openshift/manifests/org.eclipse.che_checlusterrestores_crd.yaml.diff deleted file mode 100644 index e69de29bb..000000000 diff --git a/config/certmanager/certificate.yaml b/config/certmanager/certificate.yaml new file mode 100644 index 000000000..0460d8d2f --- /dev/null +++ b/config/certmanager/certificate.yaml @@ -0,0 +1,47 @@ +# +# Copyright (c) 2019-2022 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +# The following manifests contain a self-signed issuer CR and a certificate CR. +# More document can be found at https://docs.cert-manager.io +# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes. +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: che-operator-selfsigned-issuer + namespace: eclipse-che + labels: + app.kubernetes.io/name: che + app.kubernetes.io/instance: che + app.kubernetes.io/part-of: che.eclipse.org + app.kubernetes.io/component: che-operator +spec: + selfSigned: {} +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: che-operator-serving-cert # this name should match the one appeared in kustomizeconfig.yaml + namespace: eclipse-che + labels: + app.kubernetes.io/name: che + app.kubernetes.io/instance: che + app.kubernetes.io/part-of: che.eclipse.org + app.kubernetes.io/component: che-operator +spec: + # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize + dnsNames: + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local + issuerRef: + kind: Issuer + name: che-operator-selfsigned-issuer + secretName: che-operator-webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize diff --git a/config/certmanager/kustomization.yaml b/config/certmanager/kustomization.yaml new file mode 100644 index 000000000..d19e8cabb --- /dev/null +++ b/config/certmanager/kustomization.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2019-2022 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +resources: +- certificate.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/config/certmanager/kustomizeconfig.yaml b/config/certmanager/kustomizeconfig.yaml new file mode 100644 index 000000000..2adb1ef3f --- /dev/null +++ b/config/certmanager/kustomizeconfig.yaml @@ -0,0 +1,28 @@ +# +# Copyright (c) 2019-2022 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +# This configuration is for teaching kustomize how to update name ref and var substitution +nameReference: +- kind: Issuer + group: cert-manager.io + fieldSpecs: + - kind: Certificate + group: cert-manager.io + path: spec/issuerRef/name + +varReference: +- kind: Certificate + group: cert-manager.io + path: spec/commonName +- kind: Certificate + group: cert-manager.io + path: spec/dnsNames diff --git a/config/crd/bases/org.eclipse.che_checlusters.yaml b/config/crd/bases/org.eclipse.che_checlusters.yaml new file mode 100644 index 000000000..382561063 --- /dev/null +++ b/config/crd/bases/org.eclipse.che_checlusters.yaml @@ -0,0 +1,2321 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.4.1 + creationTimestamp: null + name: checlusters.org.eclipse.che +spec: + group: org.eclipse.che + names: + kind: CheCluster + listKind: CheClusterList + plural: checlusters + singular: checluster + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: The `CheCluster` custom resource allows defining and managing + a Che server installation + 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: Desired configuration of the Che installation. Based on these + settings, the Operator automatically creates and maintains several + ConfigMaps that will contain the appropriate environment variables the + various components of the Che installation. These generated ConfigMaps + must NOT be updated manually. + properties: + auth: + description: Configuration settings related to the Authentication + used by the Che installation. + properties: + debug: + description: Deprecated. The value of this flag is ignored. Debug + internal identity provider. + type: boolean + externalIdentityProvider: + description: 'Deprecated. The value of this flag is ignored. Instructs + the Operator on whether or not to deploy a dedicated Identity + Provider (Keycloak or RH SSO instance). Instructs the Operator + on whether to deploy a dedicated Identity Provider (Keycloak + or RH-SSO instance). By default, a dedicated Identity Provider + server is deployed as part of the Che installation. When `externalIdentityProvider` + is `true`, no dedicated identity provider will be deployed by + the Operator and you will need to provide details about the + external identity provider you are about to use. See also all + the other fields starting with: `identityProvider`.' + type: boolean + gatewayAuthenticationSidecarImage: + description: Gateway sidecar responsible for authentication when + NativeUserMode is enabled. See link:https://github.com/oauth2-proxy/oauth2-proxy[oauth2-proxy] + or link:https://github.com/openshift/oauth-proxy[openshift/oauth-proxy]. + type: string + gatewayAuthorizationSidecarImage: + description: Gateway sidecar responsible for authorization when + NativeUserMode is enabled. See link:https://github.com/brancz/kube-rbac-proxy[kube-rbac-proxy] + or link:https://github.com/openshift/kube-rbac-proxy[openshift/kube-rbac-proxy] + type: string + gatewayHeaderRewriteSidecarImage: + description: Deprecated. The value of this flag is ignored. Sidecar + functionality is now implemented in Traefik plugin. + type: string + identityProviderAdminUserName: + description: Deprecated. The value of this flag is ignored. Overrides + the name of the Identity Provider administrator user. Defaults + to `admin`. + type: string + identityProviderClientId: + description: Deprecated. The value of this flag is ignored. Name + of a Identity provider, Keycloak or RH-SSO, `client-id` that + is used for Che. Override this when an external Identity Provider + is in use. See the `externalIdentityProvider` field. When omitted + or left blank, it is set to the value of the `flavour` field + suffixed with `-public`. + type: string + identityProviderContainerResources: + description: Deprecated. The value of this flag is ignored. Identity + provider container custom settings. + properties: + limits: + description: Limits describes the maximum amount of compute + resources allowed. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * + 1024 * 1024 * 1024) + type: string + type: object + request: + description: Requests describes the minimum amount of compute + resources required. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * + 1024 * 1024 * 1024) + type: string + type: object + type: object + identityProviderImage: + description: Deprecated. The value of this flag is ignored. Overrides + the container image used in the Identity Provider, Keycloak + or RH-SSO, deployment. This includes the image tag. Omit it + or leave it empty to use the default container image provided + by the Operator. + type: string + identityProviderImagePullPolicy: + description: Deprecated. The value of this flag is ignored. Overrides + the image pull policy used in the Identity Provider, Keycloak + or RH-SSO, deployment. Default value is `Always` for `nightly`, + `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + identityProviderIngress: + description: Deprecated. The value of this flag is ignored. Ingress + custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource + that may be set by external tools to store and retrieve + arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used + to organize and categorize objects by scoping and selecting. + type: string + type: object + identityProviderPassword: + description: Deprecated. The value of this flag is ignored. Overrides + the password of Keycloak administrator user. Override this when + an external Identity Provider is in use. See the `externalIdentityProvider` + field. When omitted or left blank, it is set to an auto-generated + password. + type: string + identityProviderPostgresPassword: + description: Deprecated. The value of this flag is ignored. Password + for a Identity Provider, Keycloak or RH-SSO, to connect to the + database. Override this when an external Identity Provider is + in use. See the `externalIdentityProvider` field. When omitted + or left blank, it is set to an auto-generated password. + type: string + identityProviderPostgresSecret: + description: 'Deprecated. The value of this flag is ignored. The + secret that contains `password` for the Identity Provider, Keycloak + or RH-SSO, to connect to the database. When the secret is defined, + the `identityProviderPostgresPassword` is ignored. When the + value is omitted or left blank, the one of following scenarios + applies: 1. `identityProviderPostgresPassword` is defined, then + it will be used to connect to the database. 2. `identityProviderPostgresPassword` + is not defined, then a new secret with the name `che-identity-postgres-secret` + will be created with an auto-generated value for `password`. + The secret must have `app.kubernetes.io/part-of=che.eclipse.org` + label.' + type: string + identityProviderRealm: + description: Deprecated. The value of this flag is ignored. Name + of a Identity provider, Keycloak or RH-SSO, realm that is used + for Che. Override this when an external Identity Provider is + in use. See the `externalIdentityProvider` field. When omitted + or left blank, it is set to the value of the `flavour` field. + type: string + identityProviderRoute: + description: Deprecated. The value of this flag is ignored. Route + custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource + that may be set by external tools to store and retrieve + arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname + for a route. In a conjunction with labels it creates a route, + which is served by a non-default Ingress controller. The + generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used + to organize and categorize objects by scoping and selecting. + type: string + type: object + identityProviderSecret: + description: 'Deprecated. The value of this flag is ignored. The + secret that contains `user` and `password` for Identity Provider. + When the secret is defined, the `identityProviderAdminUserName` + and `identityProviderPassword` are ignored. When the value is + omitted or left blank, the one of following scenarios applies: + 1. `identityProviderAdminUserName` and `identityProviderPassword` + are defined, then they will be used. 2. `identityProviderAdminUserName` + or `identityProviderPassword` are not defined, then a new secret + with the name `che-identity-secret` will be created with default + value `admin` for `user` and with an auto-generated value for + `password`. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` + label.' + type: string + identityProviderURL: + description: Public URL of the Identity Provider server (Keycloak + / RH-SSO server). Set this ONLY when a use of an external Identity + Provider is needed. See the `externalIdentityProvider` field. + By default, this will be automatically calculated and set by + the Operator. + type: string + initialOpenShiftOAuthUser: + description: Deprecated. The value of this flag is ignored. For + operating with the OpenShift OAuth authentication, create a + new user account since the kubeadmin can not be used. If the + value is true, then a new OpenShift OAuth user will be created + for the HTPasswd identity provider. If the value is false and + the user has already been created, then it will be removed. + If value is an empty, then do nothing. The user's credentials + are stored in the `openshift-oauth-user-credentials` secret + in 'openshift-config' namespace by Operator. Note that this + solution is Openshift 4 platform-specific. + type: boolean + nativeUserMode: + description: Deprecated. The value of this flag is ignored. Enables + native user mode. Currently works only on OpenShift and DevWorkspace + engine. Native User mode uses OpenShift OAuth directly as identity + provider, without Keycloak. + type: boolean + oAuthClientName: + description: Name of the OpenShift `OAuthClient` resource used + to setup identity federation on the OpenShift side. Auto-generated + when left blank. See also the `OpenShiftoAuth` field. + type: string + oAuthSecret: + description: Name of the secret set in the OpenShift `OAuthClient` + resource used to setup identity federation on the OpenShift + side. Auto-generated when left blank. See also the `OAuthClientName` + field. + type: string + openShiftoAuth: + description: 'Deprecated. The value of this flag is ignored. Enables + the integration of the identity provider (Keycloak / RHSSO) + with OpenShift OAuth. Empty value on OpenShift by default. This + will allow users to directly login with their OpenShift user + through the OpenShift login, and have their workspaces created + under personal OpenShift namespaces. WARNING: the `kubeadmin` + user is NOT supported, and logging through it will NOT allow + accessing the Che Dashboard.' + type: boolean + updateAdminPassword: + description: Deprecated. The value of this flag is ignored. Forces + the default `admin` Che user to update password on first login. + Defaults to `false`. + type: boolean + type: object + dashboard: + description: Configuration settings related to the User Dashboard + used by the Che installation. + properties: + warning: + description: Warning message that will be displayed on the User + Dashboard + type: string + type: object + database: + description: Configuration settings related to the database used by + the Che installation. + properties: + chePostgresContainerResources: + description: PostgreSQL container custom settings + properties: + limits: + description: Limits describes the maximum amount of compute + resources allowed. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * + 1024 * 1024 * 1024) + type: string + type: object + request: + description: Requests describes the minimum amount of compute + resources required. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * + 1024 * 1024 * 1024) + type: string + type: object + type: object + chePostgresDb: + description: PostgreSQL database name that the Che server uses + to connect to the DB. Defaults to `dbche`. + type: string + chePostgresHostName: + description: PostgreSQL Database host name that the Che server + uses to connect to. Defaults is `postgres`. Override this value + ONLY when using an external database. See field `externalDb`. + In the default case it will be automatically set by the Operator. + type: string + chePostgresPassword: + description: PostgreSQL password that the Che server uses to connect + to the DB. When omitted or left blank, it will be set to an + automatically generated value. + type: string + chePostgresPort: + description: PostgreSQL Database port that the Che server uses + to connect to. Defaults to 5432. Override this value ONLY when + using an external database. See field `externalDb`. In the default + case it will be automatically set by the Operator. + type: string + chePostgresSecret: + description: 'The secret that contains PostgreSQL`user` and `password` + that the Che server uses to connect to the DB. When the secret + is defined, the `chePostgresUser` and `chePostgresPassword` + are ignored. When the value is omitted or left blank, the one + of following scenarios applies: 1. `chePostgresUser` and `chePostgresPassword` + are defined, then they will be used to connect to the DB. 2. + `chePostgresUser` or `chePostgresPassword` are not defined, + then a new secret with the name `postgres-credentials` will + be created with default value of `pgche` for `user` and with + an auto-generated value for `password`. The secret must have + `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + chePostgresUser: + description: PostgreSQL user that the Che server uses to connect + to the DB. Defaults to `pgche`. + type: string + externalDb: + description: 'Instructs the Operator on whether to deploy a dedicated + database. By default, a dedicated PostgreSQL database is deployed + as part of the Che installation. When `externalDb` is `true`, + no dedicated database will be deployed by the Operator and you + will need to provide connection details to the external DB you + are about to use. See also all the fields starting with: `chePostgres`.' + type: boolean + postgresImage: + description: Overrides the container image used in the PostgreSQL + database deployment. This includes the image tag. Omit it or + leave it empty to use the default container image provided by + the Operator. + type: string + postgresImagePullPolicy: + description: Overrides the image pull policy used in the PostgreSQL + database deployment. Default value is `Always` for `nightly`, + `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + postgresVersion: + description: 'Indicates a PostgreSQL version image to use. Allowed + values are: `9.6` and `13.3`. Migrate your PostgreSQL database + to switch from one version to another.' + type: string + pvcClaimSize: + description: Size of the persistent volume claim for database. + Defaults to `1Gi`. To update pvc storageclass that provisions + it must support resize when Eclipse Che has been already deployed. + type: string + type: object + devWorkspace: + description: DevWorkspace operator configuration + properties: + controllerImage: + description: Overrides the container image used in the DevWorkspace + controller deployment. This includes the image tag. Omit it + or leave it empty to use the default container image provided + by the Operator. + type: string + enable: + description: Deploys the DevWorkspace Operator in the cluster. + Does nothing when a matching version of the Operator is already + installed. Fails when a non-matching version of the Operator + is already installed. + type: boolean + runningLimit: + description: Maximum number of the running workspaces per user. + type: string + required: + - enable + type: object + imagePuller: + description: Kubernetes Image Puller configuration + properties: + enable: + description: Install and configure the Community Supported Kubernetes + Image Puller Operator. When set to `true` and no spec is provided, + it will create a default KubernetesImagePuller object to be + managed by the Operator. When set to `false`, the KubernetesImagePuller + object will be deleted, and the Operator will be uninstalled, + regardless of whether a spec is provided. If the `spec.images` + field is empty, a set of recommended workspace-related images + will be automatically detected and pre-pulled after installation. + Note that while this Operator and its behavior is community-supported, + its payload may be commercially-supported for pulling commercially-supported + images. + type: boolean + spec: + description: A KubernetesImagePullerSpec to configure the image + puller in the CheCluster + properties: + affinity: + type: string + cachingCPULimit: + type: string + cachingCPURequest: + type: string + cachingIntervalHours: + type: string + cachingMemoryLimit: + type: string + cachingMemoryRequest: + type: string + configMapName: + type: string + daemonsetName: + type: string + deploymentName: + type: string + imagePullSecrets: + type: string + imagePullerImage: + type: string + images: + type: string + nodeSelector: + type: string + type: object + required: + - enable + type: object + k8s: + description: Configuration settings specific to Che installations + made on upstream Kubernetes. + properties: + ingressClass: + description: 'Ingress class that will define the which controller + will manage ingresses. Defaults to `nginx`. NB: This drives + the `kubernetes.io/ingress.class` annotation on Che-related + ingresses.' + type: string + ingressDomain: + description: 'Global ingress domain for a Kubernetes cluster. + This MUST be explicitly specified: there are no defaults.' + type: string + ingressStrategy: + description: 'Deprecated. The value of this flag is ignored. Strategy + for ingress creation. Options are: `multi-host` (host is explicitly + provided in ingress), `single-host` (host is provided, path-based + rules) and `default-host` (no host is provided, path-based rules). + Defaults to `multi-host` Deprecated in favor of `serverExposureStrategy` + in the `server` section, which defines this regardless of the + cluster type. When both are defined, the `serverExposureStrategy` + option takes precedence.' + type: string + securityContextFsGroup: + description: The FSGroup in which the Che Pod and workspace Pods + containers runs in. Default value is `1724`. + type: string + securityContextRunAsUser: + description: ID of the user the Che Pod and workspace Pods containers + run as. Default value is `1724`. + type: string + singleHostExposureType: + description: Deprecated. The value of this flag is ignored. When + the serverExposureStrategy is set to `single-host`, the way + the server, registries and workspaces are exposed is further + configured by this property. The possible values are `native`, + which means that the server and workspaces are exposed using + ingresses on K8s or `gateway` where the server and workspaces + are exposed using a custom gateway based on link:https://doc.traefik.io/traefik/[Traefik]. + All the endpoints whether backed by the ingress or gateway `route` + always point to the subpaths on the same domain. Defaults to + `native`. + type: string + tlsSecretName: + description: Name of a secret that will be used to setup ingress + TLS termination when TLS is enabled. When the field is empty + string, the default cluster certificate will be used. See also + the `tlsSupport` field. + type: string + type: object + metrics: + description: Configuration settings related to the metrics collection + used by the Che installation. + properties: + enable: + description: Enables `metrics` the Che server endpoint. Default + to `true`. + type: boolean + type: object + server: + description: General configuration settings related to the Che server, + the plugin and devfile registries + properties: + airGapContainerRegistryHostname: + description: Optional host name, or URL, to an alternate container + registry to pull images from. This value overrides the container + registry host name defined in all the default container images + involved in a Che deployment. This is particularly useful to + install Che in a restricted environment. + type: string + airGapContainerRegistryOrganization: + description: Optional repository name of an alternate container + registry to pull images from. This value overrides the container + registry organization defined in all the default container images + involved in a Che deployment. This is particularly useful to + install Eclipse Che in a restricted environment. + type: string + allowUserDefinedWorkspaceNamespaces: + description: Deprecated. The value of this flag is ignored. Defines + that a user is allowed to specify a Kubernetes namespace, or + an OpenShift project, which differs from the default. It's NOT + RECOMMENDED to set to `true` without OpenShift OAuth configured. + The OpenShift infrastructure also uses this property. + type: boolean + cheClusterRoles: + description: A comma-separated list of ClusterRoles that will + be assigned to Che ServiceAccount. Each role must have `app.kubernetes.io/part-of=che.eclipse.org` + label. Be aware that the Che Operator has to already have all + permissions in these ClusterRoles to grant them. + type: string + cheDebug: + description: Enables the debug mode for Che server. Defaults to + `false`. + type: string + cheFlavor: + description: Deprecated. The value of this flag is ignored. Specifies + a variation of the installation. The options are `che` for + upstream Che installations or `devspaces` for Red Hat OpenShift + Dev Spaces (formerly Red Hat CodeReady Workspaces) installation + type: string + cheHost: + description: Public host name of the installed Che server. When + value is omitted, the value it will be automatically set by + the Operator. See the `cheHostTLSSecret` field. + type: string + cheHostTLSSecret: + description: Name of a secret containing certificates to secure + ingress or route for the custom host name of the installed Che + server. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` + label. See the `cheHost` field. + type: string + cheImage: + description: Overrides the container image used in Che deployment. + This does NOT include the container image tag. Omit it or leave + it empty to use the default container image provided by the + Operator. + type: string + cheImagePullPolicy: + description: Overrides the image pull policy used in Che deployment. + Default value is `Always` for `nightly`, `next` or `latest` + images, and `IfNotPresent` in other cases. + type: string + cheImageTag: + description: Overrides the tag of the container image used in + Che deployment. Omit it or leave it empty to use the default + image tag provided by the Operator. + type: string + cheLogLevel: + description: 'Log level for the Che server: `INFO` or `DEBUG`. + Defaults to `INFO`.' + type: string + cheServerIngress: + description: The Che server ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource + that may be set by external tools to store and retrieve + arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used + to organize and categorize objects by scoping and selecting. + type: string + type: object + cheServerRoute: + description: The Che server route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource + that may be set by external tools to store and retrieve + arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname + for a route. In a conjunction with labels it creates a route, + which is served by a non-default Ingress controller. The + generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used + to organize and categorize objects by scoping and selecting. + type: string + type: object + cheWorkspaceClusterRole: + description: Custom cluster role bound to the user for the Che + workspaces. The role must have `app.kubernetes.io/part-of=che.eclipse.org` + label. The default roles are used when omitted or left blank. + type: string + customCheProperties: + additionalProperties: + type: string + description: Map of additional environment variables that will + be applied in the generated `che` ConfigMap to be used by the + Che server, in addition to the values already generated from + other fields of the `CheCluster` custom resource (CR). When + `customCheProperties` contains a property that would be normally + generated in `che` ConfigMap from other CR fields, the value + defined in the `customCheProperties` is used instead. + type: object + dashboardCpuLimit: + description: Overrides the CPU limit used in the dashboard deployment. + In cores. (500m = .5 cores). Default to 500m. + type: string + dashboardCpuRequest: + description: Overrides the CPU request used in the dashboard deployment. + In cores. (500m = .5 cores). Default to 100m. + type: string + dashboardImage: + description: Overrides the container image used in the dashboard + deployment. This includes the image tag. Omit it or leave it + empty to use the default container image provided by the Operator. + type: string + dashboardImagePullPolicy: + description: Overrides the image pull policy used in the dashboard + deployment. Default value is `Always` for `nightly`, `next` + or `latest` images, and `IfNotPresent` in other cases. + type: string + dashboardIngress: + description: Deprecated. The value of this flag is ignored. Dashboard + ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource + that may be set by external tools to store and retrieve + arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used + to organize and categorize objects by scoping and selecting. + type: string + type: object + dashboardMemoryLimit: + description: Overrides the memory limit used in the dashboard + deployment. Defaults to 256Mi. + type: string + dashboardMemoryRequest: + description: Overrides the memory request used in the dashboard + deployment. Defaults to 16Mi. + type: string + dashboardRoute: + description: Deprecated. The value of this flag is ignored. Dashboard + route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource + that may be set by external tools to store and retrieve + arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname + for a route. In a conjunction with labels it creates a route, + which is served by a non-default Ingress controller. The + generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used + to organize and categorize objects by scoping and selecting. + type: string + type: object + devfileRegistryCpuLimit: + description: Overrides the CPU limit used in the devfile registry + deployment. In cores. (500m = .5 cores). Default to 500m. + type: string + devfileRegistryCpuRequest: + description: Overrides the CPU request used in the devfile registry + deployment. In cores. (500m = .5 cores). Default to 100m. + type: string + devfileRegistryImage: + description: Overrides the container image used in the devfile + registry deployment. This includes the image tag. Omit it or + leave it empty to use the default container image provided by + the Operator. + type: string + devfileRegistryIngress: + description: Deprecated. The value of this flag is ignored. The + devfile registry ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource + that may be set by external tools to store and retrieve + arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used + to organize and categorize objects by scoping and selecting. + type: string + type: object + devfileRegistryMemoryLimit: + description: Overrides the memory limit used in the devfile registry + deployment. Defaults to 256Mi. + type: string + devfileRegistryMemoryRequest: + description: Overrides the memory request used in the devfile + registry deployment. Defaults to 16Mi. + type: string + devfileRegistryPullPolicy: + description: Overrides the image pull policy used in the devfile + registry deployment. Default value is `Always` for `nightly`, + `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + devfileRegistryRoute: + description: Deprecated. The value of this flag is ignored. The + devfile registry route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource + that may be set by external tools to store and retrieve + arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname + for a route. In a conjunction with labels it creates a route, + which is served by a non-default Ingress controller. The + generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used + to organize and categorize objects by scoping and selecting. + type: string + type: object + devfileRegistryUrl: + description: Deprecated in favor of `externalDevfileRegistries` + fields. + type: string + disableInternalClusterSVCNames: + description: Deprecated. The value of this flag is ignored. Disable + internal cluster SVC names usage to communicate between components + to speed up the traffic and avoid proxy issues. + type: boolean + externalDevfileRegistries: + description: External devfile registries, that serves sample, + ready-to-use devfiles. Configure this in addition to a dedicated + devfile registry (when `externalDevfileRegistry` is `false`) + or instead of it (when `externalDevfileRegistry` is `true`) + items: + description: Settings for a configuration of the external devfile + registries. + properties: + url: + description: Public URL of the devfile registry. + type: string + type: object + type: array + externalDevfileRegistry: + description: Instructs the Operator on whether to deploy a dedicated + devfile registry server. By default, a dedicated devfile registry + server is started. When `externalDevfileRegistry` is `true`, + no such dedicated server will be started by the Operator and + configure at least one devfile registry with `externalDevfileRegistries` + field. + type: boolean + externalPluginRegistry: + description: Instructs the Operator on whether to deploy a dedicated + plugin registry server. By default, a dedicated plugin registry + server is started. When `externalPluginRegistry` is `true`, + no such dedicated server will be started by the Operator and + you will have to manually set the `pluginRegistryUrl` field. + type: boolean + gitSelfSignedCert: + description: When enabled, the certificate from `che-git-self-signed-cert` + ConfigMap will be propagated to the Che components and provide + particular configuration for Git. Note, the `che-git-self-signed-cert` + ConfigMap must have `app.kubernetes.io/part-of=che.eclipse.org` + label. + type: boolean + nonProxyHosts: + description: 'List of hosts that will be reached directly, bypassing + the proxy. Specify wild card domain use the following form `.` + and `|` as delimiter, for example: `localhost|.my.host.com|123.42.12.32` + Only use when configuring a proxy is required. Operator respects + OpenShift cluster wide proxy configuration and no additional + configuration is required, but defining `nonProxyHosts` in a + custom resource leads to merging non proxy hosts lists from + the cluster proxy configuration and ones defined in the custom + resources. See the doc https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. + See also the `proxyURL` fields.' + type: string + pluginRegistryCpuLimit: + description: Overrides the CPU limit used in the plugin registry + deployment. In cores. (500m = .5 cores). Default to 500m. + type: string + pluginRegistryCpuRequest: + description: Overrides the CPU request used in the plugin registry + deployment. In cores. (500m = .5 cores). Default to 100m. + type: string + pluginRegistryImage: + description: Overrides the container image used in the plugin + registry deployment. This includes the image tag. Omit it or + leave it empty to use the default container image provided by + the Operator. + type: string + pluginRegistryIngress: + description: Deprecated. The value of this flag is ignored. Plugin + registry ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource + that may be set by external tools to store and retrieve + arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used + to organize and categorize objects by scoping and selecting. + type: string + type: object + pluginRegistryMemoryLimit: + description: Overrides the memory limit used in the plugin registry + deployment. Defaults to 256Mi. + type: string + pluginRegistryMemoryRequest: + description: Overrides the memory request used in the plugin registry + deployment. Defaults to 16Mi. + type: string + pluginRegistryPullPolicy: + description: Overrides the image pull policy used in the plugin + registry deployment. Default value is `Always` for `nightly`, + `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + pluginRegistryRoute: + description: Deprecated. The value of this flag is ignored. Plugin + registry route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource + that may be set by external tools to store and retrieve + arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname + for a route. In a conjunction with labels it creates a route, + which is served by a non-default Ingress controller. The + generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used + to organize and categorize objects by scoping and selecting. + type: string + type: object + pluginRegistryUrl: + description: Public URL of the plugin registry that serves sample + ready-to-use devfiles. Set this ONLY when a use of an external + devfile registry is needed. See the `externalPluginRegistry` + field. By default, this will be automatically calculated by + the Operator. + type: string + proxyPassword: + description: Password of the proxy server. Only use when proxy + configuration is required. See the `proxyURL`, `proxyUser` and + `proxySecret` fields. + type: string + proxyPort: + description: Port of the proxy server. Only use when configuring + a proxy is required. See also the `proxyURL` and `nonProxyHosts` + fields. + type: string + proxySecret: + description: The secret that contains `user` and `password` for + a proxy server. When the secret is defined, the `proxyUser` + and `proxyPassword` are ignored. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` + label. + type: string + proxyURL: + description: URL (protocol+host name) of the proxy server. This + drives the appropriate changes in the `JAVA_OPTS` and `https(s)_proxy` + variables in the Che server and workspaces containers. Only + use when configuring a proxy is required. Operator respects + OpenShift cluster wide proxy configuration and no additional + configuration is required, but defining `proxyUrl` in a custom + resource leads to overrides the cluster proxy configuration + with fields `proxyUrl`, `proxyPort`, `proxyUser` and `proxyPassword` + from the custom resource. See the doc https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. + See also the `proxyPort` and `nonProxyHosts` fields. + type: string + proxyUser: + description: User name of the proxy server. Only use when configuring + a proxy is required. See also the `proxyURL`, `proxyPassword` + and `proxySecret` fields. + type: string + selfSignedCert: + description: Deprecated. The value of this flag is ignored. The + Che Operator will automatically detect whether the router certificate + is self-signed and propagate it to other components, such as + the Che server. + type: boolean + serverCpuLimit: + description: Overrides the CPU limit used in the Che server deployment + In cores. (500m = .5 cores). Default to 1. + type: string + serverCpuRequest: + description: Overrides the CPU request used in the Che server + deployment In cores. (500m = .5 cores). Default to 100m. + type: string + serverExposureStrategy: + description: Deprecated. The value of this flag is ignored. Sets + the server and workspaces exposure type. Possible values are + `multi-host`, `single-host`, `default-host`. Defaults to `multi-host`, + which creates a separate ingress, or OpenShift routes, for every + required endpoint. `single-host` makes Che exposed on a single + host name with workspaces exposed on subpaths. Read the docs + to learn about the limitations of this approach. Also consult + the `singleHostExposureType` property to further configure how + the Operator and the Che server make that happen on Kubernetes. + `default-host` exposes the Che server on the host of the cluster. + Read the docs to learn about the limitations of this approach. + type: string + serverMemoryLimit: + description: Overrides the memory limit used in the Che server + deployment. Defaults to 1Gi. + type: string + serverMemoryRequest: + description: Overrides the memory request used in the Che server + deployment. Defaults to 512Mi. + type: string + serverTrustStoreConfigMapName: + description: Name of the ConfigMap with public certificates to + add to Java trust store of the Che server. This is often required + when adding the OpenShift OAuth provider, which has HTTPS endpoint + signed with self-signed cert. The Che server must be aware of + its CA cert to be able to request it. This is disabled by default. + The Config Map must have `app.kubernetes.io/part-of=che.eclipse.org` + label. + type: string + singleHostGatewayConfigMapLabels: + additionalProperties: + type: string + description: The labels that need to be present in the ConfigMaps + representing the gateway configuration. + type: object + singleHostGatewayConfigSidecarImage: + description: The image used for the gateway sidecar that provides + configuration to the gateway. Omit it or leave it empty to use + the default container image provided by the Operator. + type: string + singleHostGatewayImage: + description: The image used for the gateway in the single host + mode. Omit it or leave it empty to use the default container + image provided by the Operator. + type: string + tlsSupport: + description: Deprecated. Instructs the Operator to deploy Che + in TLS mode. This is enabled by default. Disabling TLS sometimes + cause malfunction of some Che components. + type: boolean + useInternalClusterSVCNames: + description: Deprecated in favor of `disableInternalClusterSVCNames`. + type: boolean + workspaceNamespaceDefault: + description: Defines Kubernetes default namespace in which user's + workspaces are created for a case when a user does not override + it. It's possible to use ``, `` and `` + placeholders, such as che-workspace-. In that case, + a new namespace will be created for each user or workspace. + type: string + workspacePodNodeSelector: + additionalProperties: + type: string + description: The node selector that limits the nodes that can + run the workspace pods. + type: object + workspacePodTolerations: + description: The pod tolerations put on the workspace pods to + limit where the workspace pods can run. + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using + the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, allowed + values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to + the value. Valid operators are Exists and Equal. Defaults + to Equal. Exists is equivalent to wildcard for value, + so that a pod can tolerate all taints of a particular + category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of + time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + By default, it is not set, which means tolerate the taint + forever (do not evict). Zero and negative values will + be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + workspacesDefaultPlugins: + description: Default plug-ins applied to Devworkspaces. + items: + properties: + editor: + description: The editor id to specify default plug-ins for. + type: string + plugins: + description: Default plug-in uris for the specified editor. + items: + type: string + type: array + type: object + type: array + type: object + storage: + description: Configuration settings related to the persistent storage + used by the Che installation. + properties: + postgresPVCStorageClassName: + description: Storage class for the Persistent Volume Claim dedicated + to the PostgreSQL database. When omitted or left blank, a default + storage class is used. + type: string + preCreateSubPaths: + description: Instructs the Che server to start a special Pod to + pre-create a sub-path in the Persistent Volumes. Defaults to + `false`, however it will need to enable it according to the + configuration of your Kubernetes cluster. + type: boolean + pvcClaimSize: + description: Size of the persistent volume claim for workspaces. + Defaults to `10Gi`. + type: string + pvcJobsImage: + description: Overrides the container image used to create sub-paths + in the Persistent Volumes. This includes the image tag. Omit + it or leave it empty to use the default container image provided + by the Operator. See also the `preCreateSubPaths` field. + type: string + pvcStrategy: + description: Persistent volume claim strategy for the Che server. + This Can be:`common` (all workspaces PVCs in one volume), `per-workspace` + (one PVC per workspace for all declared volumes) and `unique` + (one PVC per declared volume). Defaults to `common`. + type: string + workspacePVCStorageClassName: + description: Storage class for the Persistent Volume Claims dedicated + to the Che workspaces. When omitted or left blank, a default + storage class is used. + type: string + type: object + type: object + status: + description: CheClusterStatus defines the observed state of Che installation + properties: + cheClusterRunning: + description: Status of a Che installation. Can be `Available`, `Unavailable`, + or `Available, Rolling Update in Progress`. + type: string + cheURL: + description: Public URL to the Che server. + type: string + cheVersion: + description: Current installed Che version. + type: string + dbProvisioned: + description: Indicates that a PostgreSQL instance has been correctly + provisioned or not. + type: boolean + devfileRegistryURL: + description: Public URL to the devfile registry. + type: string + devworkspaceStatus: + description: The status of the Devworkspace subsystem + properties: + gatewayHost: + description: GatewayHost is the resolved host of the ingress/route. + This is equal to the Host in the spec on Kubernetes but contains + the actual host name of the route if Host is unspecified on + OpenShift. + type: string + gatewayPhase: + description: GatewayPhase specifies the phase in which the gateway + deployment currently is. If the gateway is disabled, the phase + is "Inactive". + type: string + message: + description: Message contains further human-readable info for + why the Che cluster is in the phase it currently is. + type: string + phase: + description: Phase is the phase in which the Che cluster as a + whole finds itself in. + type: string + reason: + description: A brief CamelCase message indicating details about + why the Che cluster is in this state. + type: string + workspaceBaseDomain: + description: The resolved workspace base domain. This is either + the copy of the explicitly defined property of the same name + in the spec or, if it is undefined in the spec and we're running + on OpenShift, the automatically resolved basedomain for routes. + type: string + type: object + gitHubOAuthProvisioned: + description: Indicates whether an Identity Provider instance, Keycloak + or RH-SSO, has been configured to integrate with the GitHub OAuth. + type: boolean + gitServerTLSCertificateConfigMapName: + description: The ConfigMap containing certificates to propagate to + the Che components and to provide particular configuration for Git. + type: string + helpLink: + description: A URL that points to some URL where to find help related + to the current Operator status. + type: string + keycloakProvisioned: + description: Indicates whether an Identity Provider instance, Keycloak + or RH-SSO, has been provisioned with realm, client and user. + type: boolean + keycloakURL: + description: Public URL to the Identity Provider server, Keycloak + or RH-SSO,. + type: string + message: + description: A human readable message indicating details about why + the Pod is in this condition. + type: string + openShiftOAuthUserCredentialsSecret: + description: OpenShift OAuth secret in `openshift-config` namespace + that contains user credentials for HTPasswd identity provider. + type: string + openShiftoAuthProvisioned: + description: Indicates whether an Identity Provider instance, Keycloak + or RH-SSO, has been configured to integrate with the OpenShift OAuth. + type: boolean + pluginRegistryURL: + description: Public URL to the plugin registry. + type: string + reason: + description: A brief CamelCase message indicating details about why + the Pod is in this state. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - name: v2 + schema: + openAPIV3Schema: + description: 'The `CheCluster` custom resource allows defining and managing + Eclipse Che server installation. Based on these settings, the Operator + automatically creates and maintains several ConfigMaps: `che`, `plugin-registry`, + `devfile-registry` that will contain the appropriate environment variables + of the various components of the installation. These generated ConfigMaps + must NOT be updated manually.' + 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: Desired configuration of Eclipse Che installation. + properties: + components: + description: Che components configuration. + properties: + cheServer: + description: General configuration settings related to the Che + server. + properties: + clusterRoles: + description: 'ClusterRoles assigned to Che ServiceAccount. + The defaults roles are: - `-cheworkspaces-namespaces-clusterrole` + - `-cheworkspaces-clusterrole` - `-cheworkspaces-devworkspace-clusterrole` + where the is the namespace where the CheCluster + CRD is created. Each role must have a `app.kubernetes.io/part-of=che.eclipse.org` + label. The Che Operator must already have all permissions + in these ClusterRoles to grant them.' + items: + type: string + type: array + debug: + description: Enables the debug mode for Che server. + type: boolean + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it + empty to use the default container image provided + by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is + `Always` for `nightly`, `next` or `latest` images, + and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this + container. + properties: + limits: + description: Limits describe the maximum amount + of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = + 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount + of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = + 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies + to all containers in a pod. The default value is + `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the + container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + extraProperties: + additionalProperties: + type: string + description: A map of additional environment variables applied + in the generated `che` ConfigMap to be used by the Che server, + in addition to the values already generated from other fields + of the `CheCluster` custom resource (CR). If the `extraProperties` + fields contains a property normally generated in `che` ConfigMap + from other CR fields, the value defined in the `extraProperties` + is used instead. + type: object + logLevel: + default: INFO + description: 'The log level for the Che server: `INFO` or + `DEBUG`.' + type: string + proxy: + description: Proxy server settings for Kubernetes cluster. + No additional configuration is required for OpenShift cluster. + By specifying these settings for the OpenShift cluster, + you override the OpenShift proxy configuration. + properties: + credentialsSecretName: + description: The secret name that contains `user` and + `password` for a proxy server. The secret must have + a `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + nonProxyHosts: + description: 'A list of hosts that can be reached directly, + bypassing the proxy. Specify wild card domain use the + following form `.`, for example: - localhost - + my.host.com - 123.42.12.32 Use only when a proxy + configuration is required. Operator respects OpenShift + cluster-wide proxy configuration, defining `nonProxyHosts` + in a custom resource leads to merging non-proxy hosts + lists from the cluster proxy configuration, and the + ones defined in the custom resources. See the following + page: https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. + See also the `proxyURL` fields.' + items: + type: string + type: array + port: + description: Proxy server port. + type: string + url: + description: 'URL (protocol+hostname) of the proxy server. + Use only when a proxy configuration is required. Operator + respects OpenShift cluster-wide proxy configuration, + defining `url` in a custom resource leads to overriding + the cluster proxy configuration. See the following page: + https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. + See also the `proxyPort` and `nonProxyHosts` fields.' + type: string + type: object + type: object + dashboard: + description: Configuration settings related to the Dashboard used + by the Che installation. + properties: + HeaderMessage: + description: Dashboard header message. + properties: + show: + description: Instructs dashboard to show the message. + type: boolean + warning: + description: Warning message displayed on the user dashboard. + type: string + type: object + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it + empty to use the default container image provided + by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is + `Always` for `nightly`, `next` or `latest` images, + and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this + container. + properties: + limits: + description: Limits describe the maximum amount + of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = + 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount + of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = + 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies + to all containers in a pod. The default value is + `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the + container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + type: object + database: + description: Configuration settings related to the database used + by the Che installation. + properties: + credentialsSecretName: + default: postgres-credentials + description: The secret that contains PostgreSQL `user` and + `password` that the Che server uses to connect to the DB. + The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` + label. + type: string + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it + empty to use the default container image provided + by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is + `Always` for `nightly`, `next` or `latest` images, + and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this + container. + properties: + limits: + description: Limits describe the maximum amount + of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = + 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount + of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = + 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies + to all containers in a pod. The default value is + `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the + container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + externalDb: + description: Instructs the Operator to deploy a dedicated + database. By default, a dedicated PostgreSQL database is + deployed as part of the Che installation. When `externalDb` + is set as `true`, no dedicated database is deployed by the + Operator and you need to provide connection details to the + external DB you want to use. + type: boolean + postgresDb: + default: dbche + description: PostgreSQL database name that the Che server + uses to connect to the DB. + type: string + postgresHostName: + default: postgres + description: PostgreSQL database hostname that the Che server + connects to. Override this value only when using an external + database. See field `externalDb`. + type: string + postgresPort: + default: "5432" + description: PostgreSQL Database port the Che server connects + to. Override this value only when using an external database. + See field `externalDb`. + type: string + pvc: + description: PVC settings for PostgreSQL database. + properties: + claimSize: + description: Persistent Volume Claim size. To update the + claim size, Storage class that provisions it must support + resize. + type: string + storageClass: + description: Storage class for the Persistent Volume Claim. + When omitted or left blank, a default storage class + is used. + type: string + type: object + type: object + devWorkspace: + description: DevWorkspace operator configuration. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it + empty to use the default container image provided + by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is + `Always` for `nightly`, `next` or `latest` images, + and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this + container. + properties: + limits: + description: Limits describe the maximum amount + of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = + 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount + of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = + 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies + to all containers in a pod. The default value is + `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the + container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + runningLimit: + description: The maximum number of running workspaces per + user. + type: string + type: object + devfileRegistry: + description: Configuration settings related to the Devfile registry + used by the Che installation. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it + empty to use the default container image provided + by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is + `Always` for `nightly`, `next` or `latest` images, + and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this + container. + properties: + limits: + description: Limits describe the maximum amount + of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = + 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount + of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = + 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies + to all containers in a pod. The default value is + `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the + container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + disableInternalRegistry: + description: Disables internal devfile registry. + type: boolean + externalDevfileRegistries: + description: External devfile registries serving sample ready-to-use + devfiles. + items: + description: External devfile registries configuration. + properties: + url: + description: The public UR of the devfile registry that + serves sample ready-to-use devfiles. + type: string + type: object + type: array + type: object + imagePuller: + description: Kubernetes Image Puller configuration. + properties: + enable: + description: Install and configure the community supported + Kubernetes Image Puller Operator. When you set the value + to `true` without providing any specs, it creates a default + Kubernetes Image Puller object managed by the Operator. + When you set the value to `false`, the Kubernetes Image + Puller object is deleted, and the Operator uninstalled, + regardless of whether a spec is provided. If you leave the + `spec.images` field empty, a set of recommended workspace-related + images is automatically detected and pre-pulled after installation. + Note that while this Operator and its behavior is community-supported, + its payload may be commercially-supported for pulling commercially-supported + images. + type: boolean + spec: + description: A Kubernetes Image Puller spec to configure + the image puller in the CheCluster + properties: + affinity: + type: string + cachingCPULimit: + type: string + cachingCPURequest: + type: string + cachingIntervalHours: + type: string + cachingMemoryLimit: + type: string + cachingMemoryRequest: + type: string + configMapName: + type: string + daemonsetName: + type: string + deploymentName: + type: string + imagePullSecrets: + type: string + imagePullerImage: + type: string + images: + type: string + nodeSelector: + type: string + type: object + required: + - enable + type: object + metrics: + description: Che server metrics configuration. + properties: + enable: + default: true + description: Enables `metrics` for the Che server endpoint. + type: boolean + type: object + pluginRegistry: + description: Configuration settings related to the plug-in registry + used by the Che installation. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it + empty to use the default container image provided + by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is + `Always` for `nightly`, `next` or `latest` images, + and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this + container. + properties: + limits: + description: Limits describe the maximum amount + of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = + 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount + of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = + 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies + to all containers in a pod. The default value is + `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the + container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + disableInternalRegistry: + description: Disables internal plug-in registry. + type: boolean + externalPluginRegistries: + description: External plugin registries. + items: + description: External plug-in registries configuration. + properties: + url: + description: Public URL of the plug-in registry. + type: string + type: object + type: array + type: object + type: object + containerRegistry: + description: Configuration of an alternative registry that stores + Che images. + properties: + hostname: + description: An optional hostname or URL of an alternative container + registry to pull images from. This value overrides the container + registry hostname defined in all the default container images + involved in a Che deployment. This is particularly useful for + installing Che in a restricted environment. + type: string + organization: + description: An optional repository name of an alternative registry + to pull images from. This value overrides the container registry + organization defined in all the default container images involved + in a Che deployment. This is particularly useful for installing + Eclipse Che in a restricted environment. + type: string + type: object + devEnvironments: + description: Development environment default configuration options. + properties: + defaultNamespace: + description: User's default namespace. + properties: + template: + description: If you don't create user namespaces in advance, + this field defines the Kubernetes namespace created when + you start your first workspace. You can use ``, + `` and `` placeholders, such as che-workspace-. + type: string + type: object + defaultPlugins: + description: Default plug-ins applied to Dev Workspaces. + items: + properties: + editor: + description: The editor id to specify default plug-ins for. + type: string + plugins: + description: Default plug-in URIs for the specified editor. + items: + type: string + type: array + type: object + type: array + nodeSelector: + additionalProperties: + type: string + description: The node selector limits the nodes that can run the + workspace pods. + type: object + storage: + description: Workspaces persistent storage. + properties: + pvc: + description: PVC settings. + properties: + claimSize: + description: Persistent Volume Claim size. To update the + claim size, Storage class that provisions it must support + resize. + type: string + storageClass: + description: Storage class for the Persistent Volume Claim. + When omitted or left blank, a default storage class + is used. + type: string + type: object + pvcStrategy: + default: common + description: Persistent volume claim strategy for the Che + server. Only the `common` strategy is supported (all workspaces + PVCs in one volume). For details, see https://github.com/eclipse/che/issues/21185. + type: string + type: object + tolerations: + description: The pod tolerations of the workspace pods limit where + the workspace pods can run. + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using + the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, allowed + values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to + the value. Valid operators are Exists and Equal. Defaults + to Equal. Exists is equivalent to wildcard for value, + so that a pod can tolerate all taints of a particular + category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of + time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + By default, it is not set, which means tolerate the taint + forever (do not evict). Zero and negative values will + be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + trustedCerts: + description: Trusted certificate settings. + properties: + gitTrustedCertsConfigMapName: + description: 'The ConfigMap contains certificates to propagate + to the Che components and to provide a particular configuration + for Git. See the following page: https://www.eclipse.org/che/docs/stable/administration-guide/deploying-che-with-support-for-git-repositories-with-self-signed-certificates/ + The ConfigMap must have a `app.kubernetes.io/part-of=che.eclipse.org` + label.' + type: string + type: object + type: object + networking: + description: Networking, Che authentication and TLS configuration. + properties: + annotations: + additionalProperties: + type: string + description: 'Defines annotations which will be set to an ingress + (a route for OpenShift platform). The defaults for kubernetes + platforms are: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/proxy-read-timeout: "3600", nginx.ingress.kubernetes.io/proxy-connect-timeout: + "3600", nginx.ingress.kubernetes.io/ssl-redirect: "true"' + type: object + auth: + description: Authentication settings. + properties: + gateway: + description: Gateway settings. + properties: + configLabels: + additionalProperties: + type: string + description: Gate configuration labels. + type: object + deployment: + description: 'Deployment override options. Since gateway + deployment consists of several containers, they must + be distinguished in the configuration by their names: + - `gateway` - `configbump` - `oauth-proxy` - `kube-rbac-proxy`' + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave + it empty to use the default container image + provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value + is `Always` for `nightly`, `next` or `latest` + images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this + container. + properties: + limits: + description: Limits describe the maximum + amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = + .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi + = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum + amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = + .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi + = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that + applies to all containers in a pod. The default + value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of + the container process. The default value is + `1724`. + format: int64 + type: integer + type: object + type: object + type: object + identityProviderURL: + description: Public URL of the Identity Provider server. + type: string + oAuthClientName: + description: Name of the OpenShift `OAuthClient` resource + used to set up identity federation on the OpenShift side. + type: string + oAuthSecret: + description: Name of the secret set in the OpenShift `OAuthClient` + resource used to set up identity federation on the OpenShift + side. + type: string + type: object + domain: + description: 'For an OpenShift cluster, the Operator uses the + domain to generate a hostname for a route. The generated hostname + follows this pattern: che-.. The + is the namespace where the CheCluster CRD is created. In a conjunction + with labels it creates a route, which is served by a non-default + Ingress controller. For Kubernetes cluster it contains a global + ingress domain. This MUST be explicitly specified: there are + no defaults.' + type: string + hostname: + description: The public hostname of the installed Che server. + type: string + labels: + additionalProperties: + type: string + description: Defines labels which will be set to an ingress (a + route for OpenShift platform). + type: object + tlsSecretName: + description: The name of the secret used to set up Ingress TLS + termination. If the field is an empty string, the default cluster + certificate is used. The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` + label. + type: string + type: object + type: object + status: + description: CheClusterStatus defines the observed state of Che installation. + properties: + chePhase: + description: Specifies the current phase of the Che deployment. + type: string + cheURL: + description: Public URL to the Che server. + type: string + cheVersion: + description: Current installed Che version. + type: string + devfileRegistryURL: + description: The public URL to the internal devfile registry. + type: string + gatewayPhase: + description: Specifies the current phase of the gateway deployment. + type: string + message: + description: A human readable message indicating details about why + the Che deployment is in current phase. + type: string + pluginRegistryURL: + description: The public URL to the internal plug-in registry. + type: string + postgresVersion: + description: The PostgreSQL version of the image in use. + type: string + reason: + description: A brief CamelCase message indicating details about why + the Che deployment is in current phase. + type: string + workspaceBaseDomain: + description: The resolved workspace base domain. This is either the + copy of the explicitly defined property of the same name in the + spec or, if it is undefined in the spec and we're running on OpenShift, + the automatically resolved basedomain for routes. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 7b9757af3..406fc311f 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -14,25 +14,12 @@ # since it depends on service name and namespace that are out of this kustomize package. # It should be run by config/default resources: -- bases/org_v1_che_crd.yaml -# - bases/org.eclipse.che_checlusters.yaml -#+kubebuilder:scaffold:crdkustomizeresource +- bases/org.eclipse.che_checlusters.yaml patchesStrategicMerge: -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. -# patches here are for enabling the conversion webhook for each CRD -#- patches/webhook_in_checlusters.yaml -#+kubebuilder:scaffold:crdkustomizewebhookpatch - -# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. -# patches here are for enabling the CA injection for each CRD -#- patches/cainjection_in_checlusters.yaml -#+kubebuilder:scaffold:crdkustomizecainjectionpatch - -# [LABELS] extra labels patches +- patches/webhook_in_checlusters.yaml - patches/extralabels_in_checlusters.yaml -# +kubebuilder:scaffold:crdkustomizeextralabelspatch -# the following config is for teaching kustomize how to do kustomization for CRDs. configurations: - kustomizeconfig.yaml + diff --git a/config/crd/patches/extralabels_in_checlusters.yaml b/config/crd/patches/extralabels_in_checlusters.yaml index a9f464a14..ceb465554 100644 --- a/config/crd/patches/extralabels_in_checlusters.yaml +++ b/config/crd/patches/extralabels_in_checlusters.yaml @@ -17,5 +17,5 @@ metadata: labels: app.kubernetes.io/name: che app.kubernetes.io/instance: che - app.kubernetes.io/managed-by: olm + app.kubernetes.io/part-of: che.eclipse.org name: checlusters.org.eclipse.che diff --git a/config/crd/patches/webhook_in_checlusters.yaml b/config/crd/patches/webhook_in_checlusters.yaml index 990b1da44..cbde2872b 100644 --- a/config/crd/patches/webhook_in_checlusters.yaml +++ b/config/crd/patches/webhook_in_checlusters.yaml @@ -19,8 +19,11 @@ spec: conversion: strategy: Webhook webhook: + conversionReviewVersions: ["v1", "v2"] clientConfig: service: - namespace: system - name: webhook-service + namespace: eclipse-che + name: che-operator-service path: /convert + # caBundle will be filled on creation + # caBundle: Cg== diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml deleted file mode 100644 index b333f2cba..000000000 --- a/config/default/kustomization.yaml +++ /dev/null @@ -1,87 +0,0 @@ -# -# Copyright (c) 2019-2021 Red Hat, Inc. -# This program and the accompanying materials are made -# available under the terms of the Eclipse Public License 2.0 -# which is available at https://www.eclipse.org/legal/epl-2.0/ -# -# SPDX-License-Identifier: EPL-2.0 -# -# Contributors: -# Red Hat, Inc. - initial API and implementation -# - -# Adds namespace to all resources. -namespace: eclipse-che - -# Value of this field is prepended to the -# names of all resources, e.g. a deployment named -# "wordpress" becomes "alices-wordpress". -# Note that it should also match with the prefix (text before '-') of the namespace -# field above. -# namePrefix: che-operator- - -# Labels to add to all resources and selectors. -#commonLabels: -# someName: someValue - -bases: -- ../crd -- ../rbac -- ../manager -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in -# crd/kustomization.yaml -#- ../webhook -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. -#- ../certmanager -# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. -#- ../prometheus - -patchesStrategicMerge: -# Protect the /metrics endpoint by putting it behind auth. -# If you want your che-operator to expose the /metrics -# endpoint w/o any authn/z, please comment the following line. -# Todo "metrics proxy" -# - manager_auth_proxy_patch.yaml - -# Mount the controller config file for loading che-operator configurations -# through a ComponentConfig type -#- manager_config_patch.yaml - -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in -# crd/kustomization.yaml -#- manager_webhook_patch.yaml - -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. -# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. -# 'CERTMANAGER' needs to be enabled to use ca injection -#- webhookcainjection_patch.yaml - -# the following config is for teaching kustomize how to do var substitution -vars: -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. -#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR -# objref: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldref: -# fieldpath: metadata.namespace -#- name: CERTIFICATE_NAME -# objref: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -#- name: SERVICE_NAMESPACE # namespace of the service -# objref: -# kind: Service -# version: v1 -# name: webhook-service -# fieldref: -# fieldpath: metadata.namespace -#- name: SERVICE_NAME -# objref: -# kind: Service -# version: v1 -# name: webhook-service diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml deleted file mode 100644 index e0702ba3d..000000000 --- a/config/default/manager_auth_proxy_patch.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# -# Copyright (c) 2019-2021 Red Hat, Inc. -# This program and the accompanying materials are made -# available under the terms of the Eclipse Public License 2.0 -# which is available at https://www.eclipse.org/legal/epl-2.0/ -# -# SPDX-License-Identifier: EPL-2.0 -# -# Contributors: -# Red Hat, Inc. - initial API and implementation -# - -# This patch inject a sidecar container which is a HTTP proxy for the -# controller che-operator, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: che-operator - namespace: system -spec: - template: - spec: - containers: - - name: kube-rbac-proxy - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 - imagePullPolicy: Always - args: - - "--secure-listen-address=0.0.0.0:8443" - - "--upstream=http://127.0.0.1:60000/" - - "--logtostderr=true" - - "--v=10" - ports: - - containerPort: 8443 - name: https - - name: che-operator - args: - - "--health-probe-bind-address=:6789" - - "--metrics-bind-address=127.0.0.1:60000" - - "--leader-elect" diff --git a/config/kubernetes/kustomization.yaml b/config/kubernetes/kustomization.yaml new file mode 100644 index 000000000..778276b40 --- /dev/null +++ b/config/kubernetes/kustomization.yaml @@ -0,0 +1,51 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +resources: + - ../crd + - ../rbac + - ../manager + - ../namespace + - ../webhook + - ../certmanager + +# Kubernetes platform specific patches +patchesStrategicMerge: + - patches/cainjection_in_checlusters.yaml + +vars: + - name: CERTIFICATE_NAMESPACE # namespace of the certificate CR + objref: + kind: Certificate + group: cert-manager.io + version: v1 + name: che-operator-serving-cert # this name should match the one in certificate.yaml + fieldref: + fieldpath: metadata.namespace + - name: CERTIFICATE_NAME + objref: + kind: Certificate + group: cert-manager.io + version: v1 + name: che-operator-serving-cert # this name should match the one in certificate.yaml + - name: SERVICE_NAMESPACE # namespace of the service + objref: + kind: Service + version: v1 + name: che-operator-service + fieldref: + fieldpath: metadata.namespace + - name: SERVICE_NAME + objref: + kind: Service + version: v1 + name: che-operator-service diff --git a/config/crd/patches/cainjection_in_checlusters.yaml b/config/kubernetes/patches/cainjection_in_checlusters.yaml similarity index 100% rename from config/crd/patches/cainjection_in_checlusters.yaml rename to config/kubernetes/patches/cainjection_in_checlusters.yaml diff --git a/config/manager/controller_manager_config.yaml b/config/manager/controller_manager_config.yaml deleted file mode 100644 index 70dece424..000000000 --- a/config/manager/controller_manager_config.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 -kind: ControllerManagerConfig -health: - healthProbeBindAddress: :6789 -metrics: - bindAddress: 127.0.0.1:60000 -webhook: - port: 9443 -leaderElection: - leaderElect: true - resourceName: e79b08a4.org.eclipse.che diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 91b556004..0a005305f 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -12,17 +12,15 @@ resources: - manager.yaml -- controller-namespace.yaml + +patchesStrategicMerge: +- patches/manager_webhook_patch.yaml generatorOptions: disableNameSuffixHash: true -configMapGenerator: -- files: - - controller_manager_config.yaml - name: manager-config -apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization +apiVersion: kustomize.config.k8s.io/v1beta1 images: - name: quay.io/eclipse/che-operator:next newName: quay.io/eclipse/che-operator diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index ae44c8a15..c7ba70940 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -71,8 +71,6 @@ spec: value: quay.io/eclipse/che-devfile-registry:next - name: RELATED_IMAGE_che_tls_secrets_creation_job value: quay.io/eclipse/che-tls-secret-creator:alpine-01a4c34 - - name: RELATED_IMAGE_pvc_jobs - value: registry.access.redhat.com/ubi8-minimal:8.6-751 - name: RELATED_IMAGE_postgres value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392 - name: RELATED_IMAGE_postgres_13_3 diff --git a/config/default/manager_config_patch.yaml b/config/manager/patches/manager_webhook_patch.yaml similarity index 57% rename from config/default/manager_config_patch.yaml rename to config/manager/patches/manager_webhook_patch.yaml index b68f8fb40..64145782b 100644 --- a/config/default/manager_config_patch.yaml +++ b/config/manager/patches/manager_webhook_patch.yaml @@ -14,20 +14,22 @@ apiVersion: apps/v1 kind: Deployment metadata: name: che-operator - namespace: system + namespace: eclipse-che spec: template: spec: containers: - name: che-operator - imagePullPolicy: Always - args: - - "--config=controller_manager_config.yaml" + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP volumeMounts: - - name: manager-config - mountPath: /controller_manager_config.yaml - subPath: controller_manager_config.yaml + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: webhook-tls-certs + readOnly: true volumes: - - name: manager-config - configMap: - name: manager-config + - name: webhook-tls-certs + secret: + defaultMode: 420 + secretName: che-operator-webhook-server-cert diff --git a/config/manifests/bases/che-operator.clusterserviceversion.yaml b/config/manifests/bases/che-operator.clusterserviceversion.yaml index 4bf15da85..a5398ab3b 100644 --- a/config/manifests/bases/che-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/che-operator.clusterserviceversion.yaml @@ -33,6 +33,115 @@ spec: apiservicedefinitions: {} customresourcedefinitions: owned: + - description: 'The `CheCluster` custom resource allows defining and managing + Eclipse Che server installation. Based on these settings, the Operator automatically + creates and maintains several ConfigMaps: `che`, `plugin-registry`, `devfile-registry` + that will contain the appropriate environment variables of the various components + of the installation. These generated ConfigMaps must NOT be updated manually.' + displayName: Eclipse Che instance Specification + kind: CheCluster + name: checlusters.org.eclipse.che + resources: + - kind: ClusterRole + name: "" + version: v1 + - kind: ClusterRoleBinding + name: "" + version: v1 + - kind: ConfigMap + name: "" + version: v1 + - kind: Deployment + name: "" + version: apps/v1 + - kind: Ingress + name: "" + version: v1 + - kind: Role + name: "" + version: v1 + - kind: RoleBinding + name: "" + version: v1 + - kind: Route + name: "" + version: v1 + - kind: Secret + name: "" + version: v1 + - kind: Service + name: "" + version: v1 + specDescriptors: + - description: Development environment default configuration options. + displayName: Development environments + path: devEnvironments + - description: Che components configuration. + displayName: Components + path: components + - description: Networking, Che authentication and TLS configuration. + displayName: Networking + path: networking + - description: Configuration of an alternative registry that stores Che images. + displayName: Container registry + path: containerRegistry + statusDescriptors: + - description: Specifies the current phase of the Che deployment. + displayName: ChePhase + path: chePhase + x-descriptors: + - urn:alm:descriptor:text + - description: Public URL to the Che server. + displayName: Eclipse Che URL + path: cheURL + x-descriptors: + - urn:alm:descriptor:org.w3:link + - description: Current installed Che version. + displayName: 'displayName: Eclipse Che version' + path: cheVersion + x-descriptors: + - urn:alm:descriptor:text + - description: The public URL to the internal devfile registry. + displayName: Devfile registry URL + path: devfileRegistryURL + x-descriptors: + - urn:alm:descriptor:org.w3:link + - description: Specifies the current phase of the gateway deployment. + displayName: Gateway phase + path: gatewayPhase + x-descriptors: + - urn:alm:descriptor:text + - description: A human readable message indicating details about why the Che + deployment is in current phase. + displayName: Message + path: message + x-descriptors: + - urn:alm:descriptor:text + - description: The public URL to the internal plug-in registry. + displayName: Plugin registry URL + path: pluginRegistryURL + x-descriptors: + - urn:alm:descriptor:org.w3:link + - description: The PostgreSQL version of the image in use. + displayName: PostgreSQL version + path: postgresVersion + x-descriptors: + - urn:alm:descriptor:text + - description: A brief CamelCase message indicating details about why the Che + deployment is in current phase. + displayName: Reason + path: reason + x-descriptors: + - urn:alm:descriptor:text + - description: The resolved workspace base domain. This is either the copy of + the explicitly defined property of the same name in the spec or, if it is + undefined in the spec and we're running on OpenShift, the automatically + resolved basedomain for routes. + displayName: Workspace base domain + path: workspaceBaseDomain + x-descriptors: + - urn:alm:descriptor:text + version: v2 - description: The `CheCluster` custom resource allows defining and managing a Che server installation displayName: Eclipse Che instance Specification @@ -172,6 +281,12 @@ spec: path: devfileRegistryURL x-descriptors: - urn:alm:descriptor:org.w3:link + - description: The ConfigMap containing certificates to propagate to the Che + components and to provide particular configuration for Git. + displayName: Git certificates + path: gitServerTLSCertificateConfigMapName + x-descriptors: + - urn:alm:descriptor:text - description: A URL that points to some URL where to find help related to the current Operator status. displayName: Help link @@ -208,11 +323,30 @@ spec: x-descriptors: - urn:alm:descriptor:text version: v1 - - description: CheCluster is the configuration of the CheCluster layer of Devworkspace. - displayName: Eclipse Che Cluster - kind: CheCluster - name: checlusters.org.eclipse.che - version: v2alpha1 + description: | + A collaborative Kubernetes-native development solution that delivers OpenShift workspaces and in-browser IDE for rapid cloud application development. + This operator installs PostgreSQL, Plugin and Devfile registries, Dashboard, Gateway and the Eclipse Che server, as well as configures all these services. + OpenShift OAuth is used directly for authentication. TLS mode is on. + + ## How to Install + Press the **Install** button, choose the channel and the upgrade strategy, and wait for the **Installed** Operator status. + When the operator is installed, create a new Custom Resource (CR) of Kind CheCluster (click the **Create New** button). + The CR spec contains all defaults. You can start using Eclipse Che when the CR status is set to **Available**, and you see a URL to Eclipse Che. + + ## Defaults + By default, the operator deploys Eclipse Che with: + * 10Gi storage + * Auto-generated passwords + * Bundled PostgreSQL + * Bundled Plugin and Devfile registries + + Use `oc edit checluster/eclipse-che -n eclipse-che` to update Eclipse Che default installation options. + See more in the [Installation guide](https://www.eclipse.org/che/docs/che-7/installation-guide/configuring-the-che-installation/). + + ### Certificates + Operator uses a default router certificate to secure Eclipse Che routes. + Follow the [guide](https://www.eclipse.org/che/docs/che-7/installation-guide/importing-untrusted-tls-certificates/) + to import certificates into Eclipse Che. displayName: Eclipse Che icon: - base64data: iVBORw0KGgoAAAANSUhEUgAAANMAAAD0CAYAAAABrhNXAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QA/wD/AP+gvaeTAAAaNklEQVR42u3de3QU9dkH8O/zm91EQK0U77dqVdTW++1V20KigUSQahLjsSSbtp4eeqqVLHILCcoiyQZEIbF61B6PVQJ6XiOkr6TlYiABr603wHotar1bBUWUYDY787x/JIGoSchmZ+c3M/t8/iS7M8+M5+vs7szz/IiZIYRIntJdgBB+IWESwiYSJiFsImESwiYSJiFsImESwiaBvv5ARLprEwB4ddaJTBQF8w/JsKbQmI0v665JAL3dUqK+7jNJmPTiNWOHWYhNB1AOILPrn+MA369MazaNe+Iz3TWmMwmTB3AEyrwwu4SIbwVwWB+v+hxEt6gg7qLs1rjumtORhMnlePUlF5hk1RFw4QDf8rrFmBLMa12tu/Z0I2FyKV53yVGWyTVgLgGQ8IknoImMQBnlNL+t+1jShYTJZXjlhKFW8KsbQJgNYP8ktxYDcI8yh95E41bt1H1sfidhcpH4mtETCHQHgONs3vTHAEXUMy33UQSW7uP0KwmTC/DqS84xyaol4Bcp3tULiqiMxrY8pfuY/UjCpBG3ZB1sxfgmgK4HYDi1WwI9SnGaTuPXv6v7HPiJhEkDfv7coPX5AdeB+RaADtRURRtAC9UB7Qvo4md26z4nfiBhcljH6qwcRbgDwKm6a+nyATNVGrkt9USQrtAkSJgcwquyT2ZlLWLQON219FofsMEghGls6ybdtXiVhCnFuOnnw62gEQHoOvTz3KM7sAVSy5RS0yln3X91V+M1EqYU4ZasgBWjawGuAnCI7noStAOM+coaUkvjVrXrLsYrJEwp0LHmkksUrFoAp+uuJSnMbzLR1EBua5PuUrxAwmSj7tYIBhfprsVOBDQTU5jyWl7RXYubSZhs0KM1YiaA/XTXkyIdAN+tMmgOZbfu0F2MG0mYksAMMtdkh4h4AYDDddfj0FF3tnrsOOROurrB1F2Nm0iYBolXjT7fVFRHwEW6a9FkkyIK09iWDboLcQsJU4KSbY3wGwKaCNZkyt34ju5adJMwDRA/fdEQa2fmZBAqARygux536Wr1+CY+m6546ivd1Wg7CxKmfUtha4TP8EeAmpuurR4Spn7w46PONi2qJdAo3bV4CROeM1iFKXf907prcfS4JUzfx82XjrDM+M0Ot0b4TWerB8yplLvxfd3FOHLAEqYeJ2NPawTmAviB7np8YheA21QG5lN26ze6i0klCVOXjtVZOUpxHZh+orsWn3qfmWYH8lqW6C4kVdI+TLwq+2Q2+HZmjNddSzogoIUsI0yXrduiuxa7pW2YuOnnw62MwEwwTwEoQ3c96aWr1SMen+qnKbRpF6a901GthQAdqrueNPcFGAvUzkMW09UNMd3FJCutwtSxenS2ItQCdIbuWsS3vMFENwbGtvxddyHJSIsw8ZpRx1hkVIM5pLsW0TcCmsk0ymjculd11zIYvg5TmrRG+E1nq4cK3kxjmr/UXUwifBkmZpD5+OiriHEbQMfqrkcMynYQ5nmp1cN3YepsjUAtgS7WXYuwA7+oGGHK2/CE7kr2WalfwsRrxxxpcWwOgN8BJEuJ+gwBTWThBrqs9T+6a+mL58PEjxRlWAd99gcw5kFaI3yO20D0JxVEFWW3fq27mu9V5+UwdbVG1AE4XnctwlEfMlOF26bQejJMvDbrLJNRS8Bo3bUIfRj8T0NRGY1pfVZ3LYDHwsSrc39o0TdzpDVC7OWeKbSeCFOP1ogIgIO0FCHcrrPVwxxSo2sKrevD1LVqRC2Anzq+c+FFW5m4IjB2Q4PTO3ZtmLj50pFsmrczcLnTJ0V4HzHWESFMua3/cmqfrgsTt2QdZHWgHIwwgEynToTwpTjA96sMqqTs1m2p3plrwiStESJ1uqbQBnEXZbfGU7YXN4SpY1VWllKoBXBmqg5UCACvW4wpwbzW1anYuNYw8d+zjrYCFJXpqMJJBDSRESijnOa37dyuljDxyglDrYyvZkBaI4Q2XVNozaE30bhVO23ZopNhktYI4UIfAxSxYwqtY2HitVnndT0C9DOHT5YQA/GCIiqjsS1PDXYDKQ8Tr/7FERapCKQ1Qrhf5xTaOE2n8evfTfjNqQrT3tYIvgWgA3WfJSEGjtsAWpjoFNqUhKmzNQK1AP1Y92kRIgkfMFPlQFs9bA0TPz7qVLbUIgbydJ8FIezChFbDojDltWzu93V2hElaI4T/dbV6cHAa5a79tNdXJBMmbskKWDG6FszVIBys+3CFcMAOMOYra0jtd1s9Bh2mjrXZlyrmWgCn6T46IRzH/CYTTQ3ktjbt/acEw8RrR53EbFQzuEj38QihGwHNxBSmvJZXEgqT9Xj2bWC+QVaNEKInjoFQpca0zvvuXwJ9vwdT5XlUIXpiC6T+Vyn1597+Gkh0c0KkIwb+YUCV0diWfwBAbx/oJExC9G/AN3MlTEL0qudE2ZYBTZSVMAnxHQQ0Udz4Y6IPwEqYhNiDX1SdU2OfHMy7pU1CCMY2EMLqy0MvGGyQALkyifTWuXKhNfQmyku+nV3CJNISAc2krMk0ZuNrdm1TwiTSzRtMdKORgtXeJUwiXXwBwtzO4ZQtKRlOKWESftc5Ntm0ZtO4Jz5L5Y4kTMK3CLyerMAUumzdFif2J2HyBu58GkwmPg3QW8w01chr/T8ndyr/cVyPX1QKoxTUBcwY9D2QNLELwFyVgdMCeS2OBgmQK5N7MbZBoUrtOPROurrBBABmjDIfH30VgRaC8SPdJboIg2ip6uAZNL71E11F9N0cuDbbNStbp5nOG4n9zMXuMb99BoAhugvWiQnPGSaX0WUbnnF0vwl12kqYHEdAE5kqTOPWvzWQ16f5yiIfMlPFQOfc2U3C5F5vMHhKIHfDqsG8mddmj7Y6B96cpftAHLAbhDvU7o5quuKpr3QVIWFynx43EpNb5W7vaox8K4DDdB9YKhDQRLAmU+7Gd3TXImFyj5TdSOSWrP2tGKYBKIdf1glmvKRIhSl3/UbdpewpScKkH4HXk+Iwjdn4cir345MxbdtBmKd2HLLnF023kDDptZWJKwJjNzQ4udOO1Vk5ilAL4Ke6T0AiZQN8t1LBm2lM85e6i+mNhEmPXQBuS3TJEjvx8+cGre0H/tYLo617DnrUXUt/JEzOcsWNxG8V5OZFF3oZQexmEiaHMPifhoWw0zcSB1zf46NOZVMtZkKu7lrQPRx/5yGL6eqGmO5iBkrClHpabyQmqnOhOqoDcLzze9/3si1u1ltu5EFXe+wGYYHKwCmBvJYlXggSAARyN6xUXx5yCghhAI7dAGVCq2J1jjG2pdSLQeqLXJmSREATWbiBLmv9j+5aksFrxxxpcWwOUru49/vMNNsrV+7+yMc8OzFeUuAyytvwhO5SbD2stVnnmcx1BLrYxq0OahFmN5Mw2cO1NxLtwgwyHx99FTFuA+jYZDZFoEdJGdNoTPN7uo/LThKm5Lj+RqLdeM3YYRZi0wHMBLBfQu8FnjeIwjS25Sndx5GScyNhGhwCmsk0ymjculd116IDrxl1jEVGNZhDA3j5xwBF1DMt91EElu7aU3ZOJEwJe4OJbgykYMaaF3WsHp3d+WgSnfH9v3IMwD39NTX6iYRp4L4AY4HXbiQ6YW+rh7UQoEOBrl80jUAZ5TS/rbs+x86DhGmf4gD/WRmBmyln3XbdxbhZ56NJ7dMtqMeDuevX667H8eOXMPWNgBayjLBTM9aEt/WWG5lO1H0jMa9lie5ChLelc5h6tEa0+OJGotArHcPUeSMR5lTK3fi+7mKEf6RVmJjwnMEqTLnrn9Zdi/CfNHlqnD8C6PfG060XSpBEqvj9ytQ1Yy2udcaaSA++DdOeGWtj9c9YE/4RiUTUlreCpQAe+O7f/BimTQqqzE0z1oQ/FBTXnL9lK2oBvhg+D5PvWyOEHr+8ZsGRgUB8DsC/Qz+/M/ghTGnXGiGcUVS0aEg8s30ywawE6IB9vd7TYdo7Y63V1TPWhPcUhqommPxNHSUwbMabYeqasWZ4ZMaa8I4rJ1afpRTqmGlUou/1Wpg6Z6xZQ2tp3Kp23cUI/ygqivzQysiYw4RBD+j0SJh6zFjL889oKKHfpEn3Bre3bbvOBEUAHJTMtlwfJia0GpYKU27LZt21CH8pLK3J2bZrey2IbFnUwM1hep+ZZgdypTVC2Cu/NDpSMW5niy+3c/FSF4ap54w1aY0Q9rnyN5GDjHiwnC2EOQULwbkpTF0z1gK+m7Em9IpEImrz1mAJxelWTuESpa4Ik99nrAl98kPR0Vu2oo6AM1O9L81h4o8ANdfw+Yw14byC4gVHA2YUjBLAzm9GfdMSprhF2PThwZvf3Tli/NU33vOhjhqEP02YFBkabAvOAMwZAIY4uW/Hw/TCB4fgL8+fgv9+NeRMAM8Vhmoip5/Qfl8kEpErk0gCU35o/lXUxgsB/EhHBY6N+vrgy/3xwPMnY/NHI3r78/NghFcsq5DvTCJhV06sOVcprgPwM6f2ubx+1vc+Oqb8yvR1ewANL5+I1a8fA4v7/Oh6HghPFJZEH1VKTWtYUi6/5ol9KiipPgJAZF+tEU5J2ZXJtAgtbx2FhzediJ3fZCTy1jaAFx4Y6Jj/wAMRuc8kvqeoKJJhZQb/YIFuIeBAHTX0dmVKSZpf/mQEZvztItz77E8SDRIADAVozs54xr/zS6pLAXbklxjhDYWhqglmZsZrDKrVFaS+2Hpl+njnUDy86UQ88+7hthXIQCugwo1Ly+XZvDRW+KvoKWxgMYA83bUAKfzO9E2HgZWvHYfGl49Hh2XvxY6ALMB6saA4uoxVcFpj/XR5ajyN9GiNuA7a74v2L6krEwN44p0jUf/CSOzYnfDHucHYwaD53wwfVrvqT5Oln8nHsrIigRHHZF7LbFUDdLDuer7L1u9M/972A1Su+h/86cnTnAoSABxE4PlDvvh6S35x9HKndiqcdVVx9aUjjs54kZnvdWOQ+pLwZXN72354+KWTsPGdw8H6fhsYSYSVBcXRZgqo8PIHy2UGhA8UldScaIGjFlCku5bBGHCY2k2Fx145Hn995TjE4oPq6rUfIYdN66XC4ujdZjA2568PRHboLkkkLhRaOGwXx6ab4HKkoDXCKfv8zsRMePa9w1D/wkh8tiuhBbcdPhJ8Tsy3qPaT7mxouFrm5nkCU35JNESgBQDs+wnYAb19Z+o3TG9tPxAPPn8yXvt0uO7aE8CvEWHK8vrKNborEX27cmLVBUoZdQBfqLuWwUjop/G7nj4NG946AuzM0+s2olOZsbowFG1SMCc31N8ks8ZdpKi06ijTVDUglPjthnyfYWp960jdtSWFGZebMMYWFkfv6cg0Zj92/0xZBUOj7umopsWzQdhfdz2poP3hwBTLYMLkQMx8vTBUMykSifj9eF2pMFQ1wcz45lUCzwf8GSTA/2HqdiQz37tla8azV5VUXay7mHRRUFJ9Tn5JdCOzegyE43TXk2qufjwjBc63oJ6UVo/Uyi+NjlAmbmbgehrkdFQvSrcwAQAxUGRa1riCkurbpNXDPt3TUdnCXCb8QHc9TkuXj3m9GQbQnJ1mxpudrR4iGYWlNTmftW3fxKBaIP2CBKTnlenbGMcQ6MGCUPQ3RBxevqRyi+6SvKSoZN7JJoxFbPE4X/3OPQgSpm6MbGZ6SVo9Bmb8xJrh+ylrpgmaAsCxJ53dTML0bQqEkOKOy/NLahYE2tsXNzREYrqLcpM901HBCxl0qO563CSdvzP1iYHhBJ5vZma8XFBSPV53PW5RMLE6e8vWjJcI9CAACdJ3yJWpfyMBaioojjYbQFnDsopXdRekwxXXVB1jGKoahJDuWtxMwjQQhBwT2FRYHL1bxdTNDQ3labEQdXdrBEAzAbi4ZcAd5GPewAWZMNnMtN4qLKkuKyp6xMc3I5nyQzVFu7jjVYDmQII0IBKmxI1gUK2ZufW5gonzE15E2O0KimvOLyiZ/yQxPwLgWN31eIl8zBu8s6GsDX5p9fjlNQuODATic9wyHdWLJExJ6mr1uLSwpPqOjoxAtddaPbqnozLMeQAdoLseL5P/A9ljCINmBmLma16aQts1HfX1rkeAJEhJkiuTvY4i0IMFJTV/ZBUta1xS8YzugnqTH1pwKlnmYmbk6q7FTyRMqXE+WXiqoDi61AgGZjQ8MOMT3QUBPaajsnk9KH1aI5wiYUodAiFkxuMFuls9Jk26N7h99+e/NdmqBuCZoY5eI9+ZUm9Y16oeL+eHahwfrlhYWpOzbdf2l7w2HdWL5MrknBOJ+ZGCkuh6Ujwl1a0ehRPnnQTDWMQWX+65AVMeJWFy3iVs0QsFJdX3G0Ga3fCXis/s3PiVv4kcZMSD5QwKg707HdWLJEx6BACaZHWgyK5Wjz2tEXG6lYHDdB9gOpLvTBp1t3rEMzO3FIai4wa7nfxQdPTLWzNe6GqNkCBpIlcmFyDwycz4W0FxtJmVMbmxfuZrA3lfQfGCowEzCkYJQ74Z6SZhchNCDrG5ubA4encbYjetWhbZ2dvLJkyKDA22BWcA5gwAQ3SXLTrJxzz3CTJh8hAK9tLq0dkaEWzL6G6NkCC5SJ+rYBSGahJeIFqkxIsKCMctalOK6wD8THdBIoULRIuUOscCNijFDPkk4WoSJm8gyA8Mrif/pxPCJhImIWwiYRLCJhImIWwiYRLCJhImIWwiYRLCJhImIWwiYRLCJhImIWwiYRLCJhImIWzSd5iIbgcgS1AK8W2xrmx8T59hWlE/axpZ5mkENOiuXghXYDSToc5ZUT9rWm9/7rM5kGjvE/9XFVdfahHVAjhN9/EIocGbAN+4Ymnl37r/obfcDChMAJCVFQmMOCbzWmarWiaDijSxg0HzexvFllSYuu0Z/k64DtJcKPzJAmMZq+C0xvrpn/b2AlvC1K3wV9FT2MBiAHm6j1wIuzDQCqhw49Lyzf2+zs4wdSsMVU1gVrUAfqz7RAgxaIT3mXl249LKJQN5eW+5Sfo+0/L62SuN9tipBA4zsDPZ7QnhsDaA5x5oxEYONEh9SfrK1FNBSfURACIAySLDwu2YgEeVUtMalpS/l/CbU/ExrzdXTqw5V2a8CRd7HozwimUVTw12A46FqWt3lB+afxUxLwTwIyfPlBB9+JiIIqef0H5fJBKxktmQw2HqtHcuNslcbKFLjBj39De/PVFawtRtz4oNhBLIQEXhECI0waSy5Q/NetvO7WoNU7f8UHQ0MeoAnJmSHQgBAITXmWlK49JZq1Ox+ZT8NJ6oxvqKDWecGDuHwb8G8F+n9y98jvA5gcOfvx87PVVB6nPXTl+ZevrW+quQ9VdFUuIA399hZlaufHjatlTvzBUf83qTXxodqRi3M+Nyx3YqfIOBdSAON9ZX/suxfbo1TN0KS2ty2ORaEH7q+M6FB9G/mVDZWD/L8Z47V3xn6s/yJbOaDx424mwi+j3AKb9UC8/6GuC5u4cPO11HkPriqitTTz1aPa4HYCS9QeEHFhjL4hZPf+zhSq0/Xrn+Y15v8kMLTiXLXAxCru5ahEaEf8KyylYsm/2s7lIAj4apW1erRx2A43XXIhz1IYMrGpdW1APkmnWWXf+dqT9drR6nEDgM4Cvd9YiUayPwAqM9dkpna4R7gtQXz1yZevrlNQuODATic6TVw5+I0GQadMNfH5j1H9219MXTH/N6UxiqOo/ZqAP4Yt21CFu8qIDwo0srntBdyL74Lkxdh9Xd6nEbgGN1VyMGg7cRUKXaT7qzoeFqU3c1A6rYn2HqFAotHLaLY9MBmglgP931iAHpIMbddrZGOMXXYep2xTVVxxiGqgYhpLsW0Q9GMytjcmP9zNd0lzKo8tMhTN0KJlZnQ1EtgDN01yL2YtAbivjG5fUVf9ddS1LH4eWfxhO14qHKljNOjJ3d1erxadIbFEkh4AsGlQfa28/wepD6PEa/Xpl66tHqMQVAhu560owFxjIjA1Mb/lLxme5i7JJWH/N6k18aHUkWLQJ4vO5a0gKhhYjDy5dUbtFdit3SPkzdCktrciyL6wj4ie5afOo9Bt+U7FBHN0ur70z9Wb5kVvMhQ0ec1fVo0pe66/GRXQDPPTAQO9nPQepLWl6ZesovjY5QJm6WVo+kMBhLjWBgRsMDMz7RXYwjBywf8/pWWFpzNltWLUCjdNfiMc+xQlnjkopndBfiJAnTAEirx4B9xOBZbmuNcIqEaYCKihYNiWe2TyZwJYADdNfjMrsJfEdHRqD6sftnpm0rjIQpQUWlVUeZpqqRKbSdiNCkYE5uqL/pHd216CZhGqSC4przAa4D4SLdtWjyEiwVXvFQ+UbdhbiFhCkpTPkl0RCBFgA4XHc1DtlO4Hleao1wioTJBmnS6tFBjLtVTN3c0FAu9+F6IWGy0ZW/nneCYRo1DBTprsVWjGYKqPDyB8tf0V2Km0mYUiA/VHMJMS+G91s93mTG1MZlFU26C/ECeZwoBRrrZ63v0erhxaeidzCofPfw/c+QICVHrkw2Gj+xZvh+yprpkVYPC4xlrILTGuunS79XguRjnkOKSuadbMJYBGCc7lp6w0AroMKNS8s3667FqyRMDissrclhy7oDoFN119LlAwZXpusjQHaS70wOW75kVvPBQw8+0wWtHm1drREneWU6qhfJlckhmlo9mIBH2bKmr3ho9ru6z4GfyMc8FygoqT6HQbUE/CKV+yHCC2yhbMWyiqd0H7MfSZhcpDBUNYEtdQcIx9m86Y+JKHL6Ce33RSIRS/dx+pWEyWUmTIoMDbRl3kDg2QD2T3JzMWLc48XpqF4kYXKpZFs9iNAEk8qWPzTrbd3Hki4kTC535cSqC5Qy6gC+cEBvILzOTFMal85arbv2dCNh8oQBtHoQPifmW7Z/0HFXa2skrrvidCRh8pAerR7lADK7/jkO8P0dZmblyoenyWr0GkmYPKhw4ryTYBiL2EKQlTHFq6tG+E1CYRJCJEYeJxLCJhImIWwiYRLCJhImIWwiYRLCJv8P9sXhC7xE4kIAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTktMDQtMTNUMDg6MTY6MDgrMDI6MDCcYZVaAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE5LTA0LTEzVDA4OjE2OjA4KzAyOjAw7Twt5gAAAABJRU5ErkJggg== @@ -245,8 +379,8 @@ spec: - name: Operator GitHub Repo url: https://github.com/eclipse-che/che-operator maintainers: - - email: dfestal@redhat.com - name: David Festal + - email: abazko@redhat.com + name: Anatolii Bazko maturity: stable provider: name: Eclipse Foundation diff --git a/config/manifests/kustomization.yaml b/config/manifests/kustomization.yaml index 6a91f417d..2c689c545 100644 --- a/config/manifests/kustomization.yaml +++ b/config/manifests/kustomization.yaml @@ -14,26 +14,3 @@ # used to generate the 'manifests/' directory in a bundle. resources: - bases/che-operator.clusterserviceversion.yaml -- ../default -- ../samples -- ../scorecard - -# [WEBHOOK] To enable webhooks, uncomment all the sections with [WEBHOOK] prefix. -# Do NOT uncomment sections with prefix [CERTMANAGER], as OLM does not support cert-manager. -# These patches remove the unnecessary "cert" volume and its che-operator container volumeMount. -#patchesJson6902: -#- target: -# group: apps -# version: v1 -# kind: Deployment -# name: che-operator -# namespace: system -# patch: |- -# # Remove the che-operator container's "cert" volumeMount, since OLM will create and mount a set of certs. -# # Update the indices in this path if adding or removing containers/volumeMounts in the che-operator's Deployment. -# - op: remove -# path: /spec/template/spec/containers/1/volumeMounts/0 -# # Remove the "cert" volume, since OLM will create and mount a set of certs. -# # Update the indices in this path if adding or removing volumes in the che-operator's Deployment. -# - op: remove -# path: /spec/template/spec/volumes/0 diff --git a/config/platforms/openshift/kustomization.yaml b/config/namespace/kustomization.yaml similarity index 56% rename from config/platforms/openshift/kustomization.yaml rename to config/namespace/kustomization.yaml index f1ffbf727..39fb41264 100644 --- a/config/platforms/openshift/kustomization.yaml +++ b/config/namespace/kustomization.yaml @@ -10,12 +10,5 @@ # Red Hat, Inc. - initial API and implementation # -bases: - - ../../manifests -patchesJson6902: -- path: che-operator.clusterserviceversion-description-patch.yaml - target: - group: operators.coreos.com - version: v1alpha1 - kind: ClusterServiceVersion - name: eclipse-che-preview-openshift.v0.0.0 +resources: +- namespace.yaml diff --git a/config/manager/controller-namespace.yaml b/config/namespace/namespace.yaml similarity index 92% rename from config/manager/controller-namespace.yaml rename to config/namespace/namespace.yaml index 9117a61f3..9d6210af9 100644 --- a/config/manager/controller-namespace.yaml +++ b/config/namespace/namespace.yaml @@ -13,6 +13,4 @@ apiVersion: v1 kind: Namespace metadata: - labels: - app: che-operator name: eclipse-che diff --git a/config/openshift/kustomization.yaml b/config/openshift/kustomization.yaml new file mode 100644 index 000000000..85315bb75 --- /dev/null +++ b/config/openshift/kustomization.yaml @@ -0,0 +1,22 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +resources: + - ../crd + - ../rbac + - ../manager + - ../namespace + - ../webhook + +patchesStrategicMerge: + - patches/cainjection_in_checlusters.yaml + - patches/service_cert_patch.yaml \ No newline at end of file diff --git a/config/openshift/olm/kustomization.yaml b/config/openshift/olm/kustomization.yaml new file mode 100644 index 000000000..81f8b82ba --- /dev/null +++ b/config/openshift/olm/kustomization.yaml @@ -0,0 +1,40 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +resources: + - ../../manifests + - ../../crd + - ../../rbac + - ../../manager + - ../../webhook + - ../../samples + - ../../scorecard + +patchesStrategicMerge: + - patches/extralabels_in_checlusters.yaml + +patchesJson6902: + - target: + group: apps + version: v1 + kind: Deployment + name: che-operator + patch: |- + - op: replace + path: /spec/template/metadata/labels + value: + app: che-operator + app.kubernetes.io/name: che + app.kubernetes.io/instance: che + app.kubernetes.io/part-of: che.eclipse.org + app.kubernetes.io/component: che-operator + app.kubernetes.io/managed-by: olm \ No newline at end of file diff --git a/config/openshift/olm/patches/extralabels_in_checlusters.yaml b/config/openshift/olm/patches/extralabels_in_checlusters.yaml new file mode 100644 index 000000000..eb10bfb3a --- /dev/null +++ b/config/openshift/olm/patches/extralabels_in_checlusters.yaml @@ -0,0 +1,19 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +# The following patch adds extra labels to CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + labels: + app.kubernetes.io/managed-by: olm + name: checlusters.org.eclipse.che diff --git a/config/openshift/patches/cainjection_in_checlusters.yaml b/config/openshift/patches/cainjection_in_checlusters.yaml new file mode 100644 index 000000000..70032fd82 --- /dev/null +++ b/config/openshift/patches/cainjection_in_checlusters.yaml @@ -0,0 +1,19 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + service.beta.openshift.io/inject-cabundle: "true" + name: checlusters.org.eclipse.che diff --git a/config/openshift/patches/service_cert_patch.yaml b/config/openshift/patches/service_cert_patch.yaml new file mode 100644 index 000000000..f111d2c72 --- /dev/null +++ b/config/openshift/patches/service_cert_patch.yaml @@ -0,0 +1,19 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: v1 +kind: Service +metadata: + name: che-operator-service + namespace: eclipse-che + annotations: + service.beta.openshift.io/serving-cert-secret-name: che-operator-webhook-server-cert diff --git a/config/platforms/openshift/che-operator.clusterserviceversion-description-patch.yaml b/config/platforms/openshift/che-operator.clusterserviceversion-description-patch.yaml deleted file mode 100644 index a2e93fcad..000000000 --- a/config/platforms/openshift/che-operator.clusterserviceversion-description-patch.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# -# Copyright (c) 2019-2021 Red Hat, Inc. -# This program and the accompanying materials are made -# available under the terms of the Eclipse Public License 2.0 -# which is available at https://www.eclipse.org/legal/epl-2.0/ -# -# SPDX-License-Identifier: EPL-2.0 -# -# Contributors: -# Red Hat, Inc. - initial API and implementation -# - -- op: replace - path: /spec/description - value: | - A collaborative Kubernetes-native development solution that delivers OpenShift workspaces and in-browser IDE for rapid cloud application development. - This operator installs PostgreSQL, Plugin and Devfile registries, Dashboard, Gateway and the Eclipse Che server, as well as configures all these services. - OpenShift OAuth is used directly for authentication. TLS mode is on. - - ## How to Install - Press the **Install** button, choose the channel and the upgrade strategy, and wait for the **Installed** Operator status. - When the operator is installed, create a new Custom Resource (CR) of Kind CheCluster (click the **Create New** button). - The CR spec contains all defaults. You can start using Eclipse Che when the CR status is set to **Available**, and you see a URL to Eclipse Che. - - ## Defaults - By default, the operator deploys Eclipse Che with: - * 10Gi storage - * Auto-generated passwords - * Bundled PostgreSQL - * Bundled Plugin and Devfile registries - - Use `oc edit checluster/eclipse-che -n eclipse-che` to update Eclipse Che default installation options. - See more in the [Installation guide](https://www.eclipse.org/che/docs/che-7/installation-guide/configuring-the-che-installation/). - - ### Certificates - Operator uses a default router certificate to secure Eclipse Che routes. - Follow the [guide](https://www.eclipse.org/che/docs/che-7/installation-guide/importing-untrusted-tls-certificates/) - to import certificates into Eclipse Che. - diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 4128c5988..b1e30e338 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -21,13 +21,3 @@ resources: - role_binding.yaml - cluster_role.yaml - cluster_rolebinding.yaml -- leader_election_role.yaml -- leader_election_role_binding.yaml -# Comment the following 4 lines if you want to disable -# the auth proxy (https://github.com/brancz/kube-rbac-proxy) -# which protects your /metrics endpoint. -# Todo "metrics proxy" -# - auth_proxy_service.yaml -# - auth_proxy_role.yaml -# - auth_proxy_role_binding.yaml -# - auth_proxy_client_clusterrole.yaml diff --git a/config/rbac/service_account.yaml b/config/rbac/service_account.yaml index 6ba5efd26..111907af4 100644 --- a/config/rbac/service_account.yaml +++ b/config/rbac/service_account.yaml @@ -14,6 +14,7 @@ apiVersion: v1 kind: ServiceAccount metadata: name: che-operator + namespace: eclipse-che labels: app.kubernetes.io/name: che app.kubernetes.io/instance: che diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 11ff951ae..c35810268 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -10,11 +10,6 @@ # Red Hat, Inc. - initial API and implementation # -## Append samples you want in your CSV to this file as resources ## resources: -- org.eclipse.che_v1_checluster.yaml -# Uncomment it to enable sample for CheCluster with version v2alphav1 -# - org_v2alpha1_checluster.yaml -# Uncomment to enable a devworkspace sample -# - devworkspace_flattened_theia-nodejs.yaml -#+kubebuilder:scaffold:manifestskustomizesamples +- org_v1_checluster.yaml +- org_v2_checluster.yaml diff --git a/config/samples/org.eclipse.che_v1_checluster.yaml b/config/samples/org.eclipse.che_v1_checluster.yaml deleted file mode 100644 index 5ba480da0..000000000 --- a/config/samples/org.eclipse.che_v1_checluster.yaml +++ /dev/null @@ -1,105 +0,0 @@ -# -# Copyright (c) 2019-2021 Red Hat, Inc. -# This program and the accompanying materials are made -# available under the terms of the Eclipse Public License 2.0 -# which is available at https://www.eclipse.org/legal/epl-2.0/ -# -# SPDX-License-Identifier: EPL-2.0 -# -# Contributors: -# Red Hat, Inc. - initial API and implementation -# - -apiVersion: org.eclipse.che/v1 -kind: CheCluster -metadata: - name: eclipse-che -spec: - server: - # A comma-separated list of ClusterRoles that will be assigned to Che ServiceAccount. - # Be aware that the Che Operator has to already have all permissions in these ClusterRoles to grant them. - cheClusterRoles: '' - # Custom cluster role bound to the user for the Che workspaces. - # The default roles are used when omitted or left blank. - cheWorkspaceClusterRole: '' - # When enabled, the certificate from `che-git-self-signed-cert` ConfigMap will be propagated to the Che components and provide particular configuration for Git. - gitSelfSignedCert: false - # URL (protocol+host name) of the proxy server. This drives the appropriate changes in the `JAVA_OPTS` and `https(s)_proxy` variables - # in the Che server and workspaces containers. - # Only use when configuring a proxy is required. Operator respects OpenShift cluster wide proxy configuration - # and no additional configuration is required, but defining `proxyUrl` in a custom resource leads to overrides the cluster proxy configuration - # with fields `proxyUrl`, `proxyPort`, `proxyUser` and `proxyPassword` from the custom resource. - # See the doc https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyPort` and `nonProxyHosts` fields. - proxyURL: '' - # Port of the proxy server. Only use when configuring a proxy is required. See also the `proxyURL` and `nonProxyHosts` fields. - proxyPort: '' - # The secret that contains `user` and `password` for a proxy server. - # The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label. - proxySecret: '' - # List of hosts that will be reached directly, bypassing the proxy. - # Specify wild card domain use the following form `.` and `|` as delimiter, for example: `localhost|.my.host.com|123.42.12.32` - # Only use when configuring a proxy is required. Operator respects OpenShift cluster wide proxy configuration and no additional configuration is required, - # but defining `nonProxyHosts` in a custom resource leads to merging non proxy hosts lists from the cluster proxy configuration and ones defined in the custom resources. - # See the doc https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyURL` fields. - nonProxyHosts: '' - # Defines Kubernetes default namespace in which user's workspaces are created for a case when a user does not override it. - # It's possible to use ``, `` and `` placeholders, such as che-workspace-. - # In that case, a new namespace will be created for each user or workspace. - workspaceNamespaceDefault: "-che" - database: - # Instructs the Operator on whether to deploy a dedicated database. - # By default, a dedicated PostgreSQL database is deployed as part of the Che installation. When `externalDb` is `true`, no dedicated database will be deployed by the - # Operator and you will need to provide connection details to the external DB you are about to use. See also all the fields starting with: `chePostgres`. - externalDb: false - # PostgreSQL Database host name that the Che server uses to connect to. - # Defaults is `postgres`. Override this value ONLY when using an external database. See field `externalDb`. - # In the default case it will be automatically set by the Operator. - chePostgresHostName: '' - # PostgreSQL Database port that the Che server uses to connect to. Defaults to 5432. - # Override this value ONLY when using an external database. See field `externalDb`. In the default case it will be automatically set by the Operator. - chePostgresPort: '' - # The secret that contains PostgreSQL `user` and `password` that the Che server uses to connect to the DB. - # The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label. - chePostgresSecret: '' - # PostgreSQL database name that the Che server uses to connect to the DB. Defaults to `dbche`. - chePostgresDb: '' - storage: - # Persistent volume claim strategy for the Che server. This Can be:`common` (all workspaces PVCs in one volume), - # `per-workspace` (one PVC per workspace for all declared volumes) and `unique` (one PVC per declared volume). - pvcStrategy: 'common' - # Size of the persistent volume claim for workspaces. - pvcClaimSize: '10Gi' - # Instructs the Che server to start a special Pod to pre-create a sub-path in the Persistent Volumes. - preCreateSubPaths: true - # Storage class for the Persistent Volume Claim dedicated to the PostgreSQL database. When omitted or left blank, a default storage class is used. - postgresPVCStorageClassName: '' - # Storage class for the Persistent Volume Claims dedicated to the Che workspaces. When omitted or left blank, a default storage class is used. - workspacePVCStorageClassName: '' - auth: - # Public URL of the Identity Provider server (Keycloak / RH-SSO server). - identityProviderURL: '' - # Name of the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OpenShiftoAuth` field. - oAuthClientName: '' - # Name of the secret set in the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OAuthClientName` field. - oAuthSecret: '' - k8s: - # Global ingress domain for a Kubernetes cluster. - ingressDomain: '192.168.99.101.nip.io' - # Ingress class that will define the which controller will manage ingresses. Defaults to `nginx`. - # NB: This drives the `kubernetes.io/ingress.class` annotation on Che-related ingresses. - ingressClass: '' - # Name of a secret that will be used to setup ingress TLS termination when TLS is enabled. - # When the field is empty string, the default cluster certificate will be used. - tlsSecretName: 'che-tls' - # The FSGroup in which the Che Pod and workspace Pods containers runs in. Default value is `1724`. - securityContextFsGroup: '' - # ID of the user the Che Pod and workspace Pods containers run as. Default value is `1724`. - securityContextRunAsUser: '' - metrics: - # Enables `metrics` the Che server endpoint. - enable: true - devWorkspace: - # Deploys the DevWorkspace Operator in the cluster. - # Does nothing when a matching version of the Operator is already installed. - # Fails when a non-matching version of the Operator is already installed. - enable: true diff --git a/helmcharts/next/templates/org.eclipse.che_v1_checluster.yaml b/config/samples/org_v1_checluster.yaml similarity index 53% rename from helmcharts/next/templates/org.eclipse.che_v1_checluster.yaml rename to config/samples/org_v1_checluster.yaml index e974ea32a..5a12c8994 100644 --- a/helmcharts/next/templates/org.eclipse.che_v1_checluster.yaml +++ b/config/samples/org_v1_checluster.yaml @@ -17,37 +17,17 @@ metadata: namespace: eclipse-che spec: server: - cheClusterRoles: '' - cheWorkspaceClusterRole: '' - gitSelfSignedCert: false - proxyURL: '' - proxyPort: '' - proxySecret: '' - nonProxyHosts: '' workspaceNamespaceDefault: "-che" database: externalDb: false - chePostgresHostName: '' - chePostgresPort: '' - chePostgresSecret: '' - chePostgresDb: '' storage: pvcStrategy: 'common' - pvcClaimSize: '10Gi' - preCreateSubPaths: true - postgresPVCStorageClassName: '' - workspacePVCStorageClassName: '' auth: identityProviderURL: '' oAuthClientName: '' oAuthSecret: '' k8s: - ingressDomain: '{{ .Values.k8s.ingressDomain }}' - ingressClass: '' - tlsSecretName: 'che-tls' - securityContextFsGroup: '' - securityContextRunAsUser: '' + ingressDomain: + tlsSecretName: metrics: enable: true - devWorkspace: - enable: true diff --git a/config/samples/org_v2_checluster.yaml b/config/samples/org_v2_checluster.yaml new file mode 100644 index 000000000..44f6f381c --- /dev/null +++ b/config/samples/org_v2_checluster.yaml @@ -0,0 +1,35 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: org.eclipse.che/v2 +kind: CheCluster +metadata: + name: eclipse-che + namespace: eclipse-che +spec: + devEnvironments: + defaultNamespace: + template: '-che' + storage: + pvcStrategy: 'common' + components: + database: + externalDb: false + metrics: + enable: true + networking: + domain: '' + tlsSecretName: '' + auth: + identityProviderURL: '' + oAuthClientName: '' + oAuthSecret: '' diff --git a/config/scorecard/kustomization.yaml b/config/scorecard/kustomization.yaml index 72835b716..486677dfe 100644 --- a/config/scorecard/kustomization.yaml +++ b/config/scorecard/kustomization.yaml @@ -12,6 +12,7 @@ resources: - bases/config.yaml + patchesJson6902: - path: patches/basic.config.yaml target: @@ -25,4 +26,3 @@ patchesJson6902: version: v1alpha3 kind: Configuration name: config -#+kubebuilder:scaffold:patchesJson6902 diff --git a/config/webhook/kustomization.yaml b/config/webhook/kustomization.yaml new file mode 100644 index 000000000..d65efe454 --- /dev/null +++ b/config/webhook/kustomization.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +resources: +- service.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/config/webhook/kustomizeconfig.yaml b/config/webhook/kustomizeconfig.yaml new file mode 100644 index 000000000..7a1d750bc --- /dev/null +++ b/config/webhook/kustomizeconfig.yaml @@ -0,0 +1,37 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +# the following config is for teaching kustomize where to look at when substituting vars. +# It requires kustomize v2.1.0 or newer to work properly. +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + - kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + +namespace: +- kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true +- kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true + +varReference: +- path: metadata/annotations diff --git a/config/webhook/service.yaml b/config/webhook/service.yaml new file mode 100644 index 000000000..c76feeb39 --- /dev/null +++ b/config/webhook/service.yaml @@ -0,0 +1,28 @@ +# +# Copyright (c) 2019-2022 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: v1 +kind: Service +metadata: + name: che-operator-service + namespace: eclipse-che + labels: + app.kubernetes.io/name: che + app.kubernetes.io/instance: che + app.kubernetes.io/part-of: che.eclipse.org + app.kubernetes.io/component: che-operator +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + app: che-operator diff --git a/controllers/che/checluster_controller.go b/controllers/che/checluster_controller.go index 174badb68..5f2210883 100644 --- a/controllers/che/checluster_controller.go +++ b/controllers/che/checluster_controller.go @@ -15,6 +15,10 @@ package che import ( "context" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/test" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy/consolelink" "github.com/eclipse-che/che-operator/pkg/deploy/dashboard" @@ -30,7 +34,6 @@ import ( "github.com/eclipse-che/che-operator/pkg/deploy/server" "github.com/eclipse-che/che-operator/pkg/deploy/tls" - "github.com/eclipse-che/che-operator/pkg/util" "github.com/go-logr/logr" routev1 "github.com/openshift/api/route/v1" "github.com/sirupsen/logrus" @@ -47,7 +50,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/source" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" networking "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/api/errors" ) @@ -85,7 +88,7 @@ func NewReconciler( reconcileManager := deploy.NewReconcileManager() // order does matter - if !util.IsTestMode() { + if !test.IsTestMode() { reconcileManager.RegisterReconciler(migration.NewMigrator()) reconcileManager.RegisterReconciler(NewCheClusterValidator()) } @@ -102,7 +105,7 @@ func NewReconciler( // resources since che host is used for dashboard deployment and che config map reconcileManager.RegisterReconciler(server.NewCheHostReconciler()) reconcileManager.RegisterReconciler(postgres.NewPostgresReconciler()) - if util.IsOpenShift { + if infrastructure.IsOpenShift() { reconcileManager.RegisterReconciler(identityprovider.NewIdentityProviderReconciler()) } reconcileManager.RegisterReconciler(devfileregistry.NewDevfileRegistryReconciler()) @@ -111,7 +114,7 @@ func NewReconciler( reconcileManager.RegisterReconciler(gateway.NewGatewayReconciler()) reconcileManager.RegisterReconciler(server.NewCheServerReconciler()) - if util.IsOpenShift4 { + if infrastructure.IsOpenShift() { reconcileManager.RegisterReconciler(consolelink.NewConsoleLinkReconciler()) } @@ -129,8 +132,6 @@ func NewReconciler( // SetupWithManager sets up the controller with the Manager. func (r *CheClusterReconciler) SetupWithManager(mgr ctrl.Manager) error { - isOpenShift := util.IsOpenShift - onAllExceptGenericEventsPredicate := predicate.Funcs{ UpdateFunc: func(evt event.UpdateEvent) bool { return true @@ -164,39 +165,39 @@ func (r *CheClusterReconciler) SetupWithManager(mgr ctrl.Manager) error { controllerBuilder := ctrl.NewControllerManagedBy(mgr). // Watch for changes to primary resource CheCluster - Watches(&source.Kind{Type: &orgv1.CheCluster{}}, &handler.EnqueueRequestForObject{}). + Watches(&source.Kind{Type: &chev2.CheCluster{}}, &handler.EnqueueRequestForObject{}). // Watch for changes to secondary resources and requeue the owner CheCluster Watches(&source.Kind{Type: &corev1.Service{}}, &handler.EnqueueRequestForOwner{ IsController: true, - OwnerType: &orgv1.CheCluster{}, + OwnerType: &chev2.CheCluster{}, }). Watches(&source.Kind{Type: &corev1.Secret{}}, &handler.EnqueueRequestForOwner{ IsController: true, - OwnerType: &orgv1.CheCluster{}, + OwnerType: &chev2.CheCluster{}, }). Watches(&source.Kind{Type: &corev1.ConfigMap{}}, &handler.EnqueueRequestForOwner{ IsController: true, - OwnerType: &orgv1.CheCluster{}, + OwnerType: &chev2.CheCluster{}, }). Watches(&source.Kind{Type: &rbacv1.Role{}}, &handler.EnqueueRequestForOwner{ IsController: true, - OwnerType: &orgv1.CheCluster{}, + OwnerType: &chev2.CheCluster{}, }). Watches(&source.Kind{Type: &rbacv1.RoleBinding{}}, &handler.EnqueueRequestForOwner{ IsController: true, - OwnerType: &orgv1.CheCluster{}, + OwnerType: &chev2.CheCluster{}, }). Watches(&source.Kind{Type: &corev1.ServiceAccount{}}, &handler.EnqueueRequestForOwner{ IsController: true, - OwnerType: &orgv1.CheCluster{}, + OwnerType: &chev2.CheCluster{}, }). Watches(&source.Kind{Type: &appsv1.Deployment{}}, &handler.EnqueueRequestForOwner{ IsController: true, - OwnerType: &orgv1.CheCluster{}, + OwnerType: &chev2.CheCluster{}, }). Watches(&source.Kind{Type: &corev1.PersistentVolumeClaim{}}, &handler.EnqueueRequestForOwner{ IsController: true, - OwnerType: &orgv1.CheCluster{}, + OwnerType: &chev2.CheCluster{}, }). Watches(&source.Kind{Type: &corev1.ConfigMap{}}, handler.EnqueueRequestsFromMapFunc(toTrustedBundleConfigMapRequestMapper), @@ -211,24 +212,24 @@ func (r *CheClusterReconciler) SetupWithManager(mgr ctrl.Manager) error { builder.WithPredicates(onAllExceptGenericEventsPredicate), ) - if isOpenShift { + if infrastructure.IsOpenShift() { controllerBuilder = controllerBuilder.Watches(&source.Kind{Type: &routev1.Route{}}, &handler.EnqueueRequestForOwner{ IsController: true, - OwnerType: &orgv1.CheCluster{}, + OwnerType: &chev2.CheCluster{}, }) } else { controllerBuilder = controllerBuilder.Watches(&source.Kind{Type: &networking.Ingress{}}, &handler.EnqueueRequestForOwner{ IsController: true, - OwnerType: &orgv1.CheCluster{}, + OwnerType: &chev2.CheCluster{}, }) } if r.namespace != "" { - controllerBuilder = controllerBuilder.WithEventFilter(util.InNamespaceEventFilter(r.namespace)) + controllerBuilder = controllerBuilder.WithEventFilter(utils.InNamespaceEventFilter(r.namespace)) } return controllerBuilder. - For(&orgv1.CheCluster{}). + For(&chev2.CheCluster{}). Complete(r) } @@ -240,7 +241,7 @@ func (r *CheClusterReconciler) SetupWithManager(mgr ctrl.Manager) error { func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { _ = r.Log.WithValues("checluster", req.NamespacedName) - clusterAPI := deploy.ClusterAPI{ + clusterAPI := chetypes.ClusterAPI{ Client: r.client, NonCachingClient: r.nonCachedClient, DiscoveryClient: r.discoveryClient, @@ -262,7 +263,7 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, err } - deployContext := &deploy.DeployContext{ + deployContext := &chetypes.DeployContext{ ClusterAPI: clusterAPI, CheCluster: checluster, } @@ -292,13 +293,16 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, nil } } else { + deployContext.CheCluster.Status.ChePhase = chev2.ClusterPhasePendingDeletion + _ = deploy.UpdateCheCRStatus(deployContext, "ChePhase", chev2.ClusterPhasePendingDeletion) + done := r.reconcileManager.FinalizeAll(deployContext) return ctrl.Result{Requeue: !done}, nil } } -func (r *CheClusterReconciler) GetCR(request ctrl.Request) (*orgv1.CheCluster, error) { - checluster := &orgv1.CheCluster{} +func (r *CheClusterReconciler) GetCR(request ctrl.Request) (*chev2.CheCluster, error) { + checluster := &chev2.CheCluster{} err := r.client.Get(context.TODO(), request.NamespacedName, checluster) return checluster, err } diff --git a/controllers/che/checluster_validator.go b/controllers/che/checluster_validator.go index d25c840dc..becbb9fd1 100644 --- a/controllers/che/checluster_validator.go +++ b/controllers/che/checluster_validator.go @@ -16,14 +16,14 @@ import ( "fmt" "strings" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) const ( - NamespaceStrategyErrorMessage = "Namespace strategies other than 'per user' is not supported anymore. Using the or placeholder is required in the 'spec.server.workspaceNamespaceDefault' field. The current value is: %s" - AuthenticationModeErrorMessage = "Single user authentication mode is not supported anymore. To backup your data you can commit workspace configuration to an SCM server and use factories to restore it in multi user mode. To switch to multi user authentication mode set 'spec.server.customCheProperties.CHE_MULTIUSER' to 'true' in %s CheCluster custom resource. Switching to multi user authentication mode without backing up your data will cause data loss." + NamespaceStrategyErrorMessage = "Namespace strategies other than 'per user' is not supported anymore. Using the or placeholder is required in the 'spec.server.workspaceNamespaceDefault' field. The current value is: %s" ) // CheClusterValidator checks CheCluster CR configuration. @@ -39,25 +39,21 @@ func NewCheClusterValidator() *CheClusterValidator { return &CheClusterValidator{} } -func (v *CheClusterValidator) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { - if !util.IsOpenShift { - if ctx.CheCluster.Spec.K8s.IngressDomain == "" { - return reconcile.Result{}, false, fmt.Errorf("Required field \"spec.K8s.IngressDomain\" is not set") +func (v *CheClusterValidator) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { + if !infrastructure.IsOpenShift() { + if ctx.CheCluster.Spec.Networking.Domain == "" { + return reconcile.Result{}, false, fmt.Errorf("Required field \"spec.networking.domain\" is not set") } } - workspaceNamespaceDefault := deploy.GetWorkspaceNamespaceDefault(ctx.CheCluster) + workspaceNamespaceDefault := ctx.CheCluster.GetDefaultNamespace() if strings.Index(workspaceNamespaceDefault, "") == -1 && strings.Index(workspaceNamespaceDefault, "") == -1 { return reconcile.Result{}, false, fmt.Errorf(NamespaceStrategyErrorMessage, workspaceNamespaceDefault) } - if !util.IsCheMultiUser(ctx.CheCluster) { - return reconcile.Result{}, false, fmt.Errorf(AuthenticationModeErrorMessage, ctx.CheCluster.Name) - } - return reconcile.Result{}, true, nil } -func (v *CheClusterValidator) Finalize(ctx *deploy.DeployContext) bool { +func (v *CheClusterValidator) Finalize(ctx *chetypes.DeployContext) bool { return true } diff --git a/controllers/che/cheobj_verifier.go b/controllers/che/cheobj_verifier.go index b43e27808..6a7b477b1 100644 --- a/controllers/che/cheobj_verifier.go +++ b/controllers/che/cheobj_verifier.go @@ -13,9 +13,9 @@ package che import ( + "github.com/eclipse-che/che-operator/pkg/common/constants" "github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy/tls" - "github.com/eclipse-che/che-operator/pkg/util" "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" @@ -29,7 +29,7 @@ func IsTrustedBundleConfigMap(cl client.Client, watchNamespace string, obj clien return false, ctrl.Request{} } - checluster, num, _ := util.FindCheClusterCRInNamespace(cl, watchNamespace) + checluster, num, _ := deploy.FindCheClusterCRInNamespace(cl, watchNamespace) if num != 1 { if num > 1 { logrus.Warn("More than one checluster Custom Resource found.") @@ -42,22 +42,16 @@ func IsTrustedBundleConfigMap(cl client.Client, watchNamespace string, obj clien return false, ctrl.Request{} } - // Check if config map is the config map from CR - trustStoreConfigMap := util.GetValue(checluster.Spec.Server.ServerTrustStoreConfigMapName, deploy.DefaultServerTrustStoreConfigMapName) - if trustStoreConfigMap != obj.GetName() { - // No, it is not form CR + // Check for component + if value, exists := obj.GetLabels()[constants.KubernetesComponentLabelKey]; !exists || value != tls.CheCACertsConfigMapLabelValue { + // Labels do not match + return false, ctrl.Request{} + } - // Check for component - if value, exists := obj.GetLabels()[deploy.KubernetesComponentLabelKey]; !exists || value != tls.CheCACertsConfigMapLabelValue { - // Labels do not match - return false, ctrl.Request{} - } - - // Check for part-of - if value, exists := obj.GetLabels()[deploy.KubernetesPartOfLabelKey]; !exists || value != deploy.CheEclipseOrg { - // ignore not matched labels - return false, ctrl.Request{} - } + // Check for part-of + if value, exists := obj.GetLabels()[constants.KubernetesPartOfLabelKey]; !exists || value != constants.CheEclipseOrg { + // ignore not matched labels + return false, ctrl.Request{} } return true, ctrl.Request{ @@ -77,7 +71,7 @@ func IsEclipseCheRelatedObj(cl client.Client, watchNamespace string, obj client. return false, ctrl.Request{} } - checluster, num, _ := util.FindCheClusterCRInNamespace(cl, watchNamespace) + checluster, num, _ := deploy.FindCheClusterCRInNamespace(cl, watchNamespace) if num != 1 { if num > 1 { logrus.Warn("More than one checluster Custom Resource found.") @@ -91,7 +85,7 @@ func IsEclipseCheRelatedObj(cl client.Client, watchNamespace string, obj client. } // Check for part-of label - if value, exists := obj.GetLabels()[deploy.KubernetesPartOfLabelKey]; !exists || value != deploy.CheEclipseOrg { + if value, exists := obj.GetLabels()[constants.KubernetesPartOfLabelKey]; !exists || value != constants.CheEclipseOrg { return false, ctrl.Request{} } diff --git a/controllers/che/cheobj_verifier_test.go b/controllers/che/cheobj_verifier_test.go index b950f6359..b57c331df 100644 --- a/controllers/che/cheobj_verifier_test.go +++ b/controllers/che/cheobj_verifier_test.go @@ -13,11 +13,11 @@ package che import ( - "os" "testing" - orgv1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/pkg/deploy" + chev2 "github.com/eclipse-che/che-operator/api/v2" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -28,7 +28,7 @@ func TestIsTrustedBundleConfigMap(t *testing.T) { type testCase struct { name string initObjects []runtime.Object - checluster *orgv1.CheCluster + checluster *chev2.CheCluster objNamespace string objLabels map[string]string watchNamespace string @@ -45,7 +45,7 @@ func TestIsTrustedBundleConfigMap(t *testing.T) { Labels: map[string]string{ "app.kubernetes.io/part-of": "che.eclipse.org", "app.kubernetes.io/component": "ca-bundle", - "app.kubernetes.io/instance": os.Getenv("CHE_FLAVOR"), + "app.kubernetes.io/instance": defaults.GetCheFlavor(), }, }, } @@ -73,22 +73,6 @@ func TestIsTrustedBundleConfigMap(t *testing.T) { watchNamespace: "eclipse-che", expectedIsEclipseCheObj: false, }, - { - name: "Object in 'eclipse-che' namespace, not ca-bundle component, but trusted configmap", - initObjects: []runtime.Object{}, - checluster: &orgv1.CheCluster{ - ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "eclipse-che"}, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ServerTrustStoreConfigMapName: "test", - }, - }, - }, - objLabels: map[string]string{}, - objNamespace: "eclipse-che", - watchNamespace: "eclipse-che", - expectedIsEclipseCheObj: true, - }, { name: "Object in another namespace than 'eclipse-che'", initObjects: []runtime.Object{}, @@ -100,7 +84,7 @@ func TestIsTrustedBundleConfigMap(t *testing.T) { name: "Object in 'eclipse-che' namespace, several checluster CR", initObjects: []runtime.Object{ // checluster CR in `default` namespace - &orgv1.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "default"}}, + &chev2.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "default"}}, }, objNamespace: "eclipse-che", watchNamespace: "eclipse-che", @@ -131,7 +115,7 @@ func TestIsTrustedBundleConfigMap(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - deployContext := deploy.GetTestDeployContext(testCase.checluster, testCase.initObjects) + deployContext := test.GetDeployContext(testCase.checluster, testCase.initObjects) newTestObject := testObject.DeepCopy() newTestObject.ObjectMeta.Namespace = testCase.objNamespace @@ -168,7 +152,7 @@ func TestIsEclipseCheRelatedObj(t *testing.T) { Name: "test", Labels: map[string]string{ "app.kubernetes.io/part-of": "che.eclipse.org", - "app.kubernetes.io/instance": os.Getenv("CHE_FLAVOR"), + "app.kubernetes.io/instance": defaults.GetCheFlavor(), }, }, } @@ -199,7 +183,7 @@ func TestIsEclipseCheRelatedObj(t *testing.T) { name: "Object in 'eclipse-che' namespace, several checluster CR", initObjects: []runtime.Object{ // checluster CR in `default` namespace - &orgv1.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "default"}}, + &chev2.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "default"}}, }, objNamespace: "eclipse-che", watchNamespace: "eclipse-che", @@ -230,7 +214,7 @@ func TestIsEclipseCheRelatedObj(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - deployContext := deploy.GetTestDeployContext(nil, testCase.initObjects) + deployContext := test.GetDeployContext(nil, testCase.initObjects) testObject.ObjectMeta.Namespace = testCase.objNamespace isEclipseCheObj, req := IsEclipseCheRelatedObj(deployContext.ClusterAPI.NonCachingClient, testCase.watchNamespace, testObject) diff --git a/controllers/che/init_test.go b/controllers/che/init_test.go index 18319b11c..ff94b0ac1 100644 --- a/controllers/che/init_test.go +++ b/controllers/che/init_test.go @@ -12,12 +12,14 @@ package che import ( - "github.com/eclipse-che/che-operator/pkg/deploy" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" ) func init() { - err := deploy.InitTestDefaultsFromDeployment("../../config/manager/manager.yaml") - if err != nil { - panic(err) - } + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../../config/manager/manager.yaml") } diff --git a/controllers/che/permission_checker.go b/controllers/che/permission_checker.go deleted file mode 100644 index e37e6aeef..000000000 --- a/controllers/che/permission_checker.go +++ /dev/null @@ -1,61 +0,0 @@ -// -// Copyright (c) 2019-2021 Red Hat, Inc. -// This program and the accompanying materials are made -// available under the terms of the Eclipse Public License 2.0 -// which is available at https://www.eclipse.org/legal/epl-2.0/ -// -// SPDX-License-Identifier: EPL-2.0 -// -// Contributors: -// Red Hat, Inc. - initial API and implementation -// - -package che - -import ( - "fmt" - - "github.com/eclipse-che/che-operator/pkg/util" - authorizationv1 "k8s.io/api/authorization/v1" - rbac "k8s.io/api/rbac/v1" -) - -type PermissionChecker interface { - GetNotPermittedPolicyRules(policies []rbac.PolicyRule, namespace string) ([]rbac.PolicyRule, error) -} - -type K8sApiPermissionChecker struct { -} - -func (pc *K8sApiPermissionChecker) GetNotPermittedPolicyRules(policies []rbac.PolicyRule, namespace string) ([]rbac.PolicyRule, error) { - var notPermittedPolicyRules []rbac.PolicyRule = []rbac.PolicyRule{} - for _, policy := range policies { - for _, apiGroup := range policy.APIGroups { - for _, verb := range policy.Verbs { - for _, resource := range policy.Resources { - resourceAttribute := &authorizationv1.ResourceAttributes{ - Namespace: namespace, - Verb: verb, - Group: apiGroup, - Resource: resource, - } - ok, err := util.K8sclient.IsResourceOperationPermitted(resourceAttribute) - if err != nil { - return notPermittedPolicyRules, fmt.Errorf("failed to check policy rule: %v", policy) - } - if !ok { - if len(notPermittedPolicyRules) == 0 { - notPermittedPolicyRules = append(notPermittedPolicyRules, policy) - } else { - lastNotPermittedRule := notPermittedPolicyRules[len(notPermittedPolicyRules)-1] - if lastNotPermittedRule.Resources[0] != policy.Resources[0] && lastNotPermittedRule.APIGroups[0] != policy.APIGroups[0] { - notPermittedPolicyRules = append(notPermittedPolicyRules, policy) - } - } - } - } - } - } - } - return notPermittedPolicyRules, nil -} diff --git a/controllers/che/proxy.go b/controllers/che/proxy.go index ff538913d..25325d471 100644 --- a/controllers/che/proxy.go +++ b/controllers/che/proxy.go @@ -12,21 +12,21 @@ package che import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" configv1 "github.com/openshift/api/config/v1" ) -func GetProxyConfiguration(deployContext *deploy.DeployContext) (*deploy.Proxy, error) { - // OpenShift 4.x - if util.IsOpenShift4 { +func GetProxyConfiguration(deployContext *chetypes.DeployContext) (*chetypes.Proxy, error) { + if infrastructure.IsOpenShift() { clusterProxy := &configv1.Proxy{} exists, err := deploy.GetClusterObject(deployContext, "cluster", clusterProxy) if err != nil { return nil, err } - clusterWideProxyConf := &deploy.Proxy{} + clusterWideProxyConf := &chetypes.Proxy{} if exists { clusterWideProxyConf, err = deploy.ReadClusterWideProxyConfiguration(clusterProxy) if err != nil { @@ -34,7 +34,7 @@ func GetProxyConfiguration(deployContext *deploy.DeployContext) (*deploy.Proxy, } } - cheClusterProxyConf, err := deploy.ReadCheClusterProxyConfiguration(deployContext.CheCluster) + cheClusterProxyConf, err := deploy.ReadCheClusterProxyConfiguration(deployContext) if err != nil { return nil, err } @@ -57,7 +57,7 @@ func GetProxyConfiguration(deployContext *deploy.DeployContext) (*deploy.Proxy, } // OpenShift 3.x and k8s - cheClusterProxyConf, err := deploy.ReadCheClusterProxyConfiguration(deployContext.CheCluster) + cheClusterProxyConf, err := deploy.ReadCheClusterProxyConfiguration(deployContext) if err != nil { return nil, err } diff --git a/controllers/che/proxy_test.go b/controllers/che/proxy_test.go index 188c9b339..f60e978eb 100644 --- a/controllers/che/proxy_test.go +++ b/controllers/che/proxy_test.go @@ -16,9 +16,9 @@ import ( "reflect" "testing" - orgv1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" "github.com/google/go-cmp/cmp" configv1 "github.com/openshift/api/config/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -34,52 +34,56 @@ import ( func TestReadProxyConfiguration(t *testing.T) { type testCase struct { name string - openShiftVersion string - cheCluster *orgv1.CheCluster + isOpenShift bool + cheCluster *chev2.CheCluster clusterProxy *configv1.Proxy initObjects []runtime.Object - expectedProxyConf *deploy.Proxy + expectedProxyConf *chetypes.Proxy } testCases := []testCase{ { - name: "Test no proxy configured", - openShiftVersion: "4", + name: "Test no proxy configured", + isOpenShift: true, clusterProxy: &configv1.Proxy{ ObjectMeta: metav1.ObjectMeta{ Name: "cluster", }, }, - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, }, initObjects: []runtime.Object{}, - expectedProxyConf: &deploy.Proxy{}, + expectedProxyConf: &chetypes.Proxy{}, }, { - name: "Test checluster proxy configured, OpenShift 4.x", - openShiftVersion: "4", + name: "Test checluster proxy configured, OpenShift 4.x", + isOpenShift: true, clusterProxy: &configv1.Proxy{ ObjectMeta: metav1.ObjectMeta{ Name: "cluster", }, }, - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ProxyURL: "http://proxy", - ProxyPort: "3128", - NonProxyHosts: "host1", + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + CheServer: chev2.CheServer{ + Proxy: chev2.Proxy{ + Url: "http://proxy", + Port: "3128", + NonProxyHosts: []string{"host1"}, + }, + }, }, }, }, initObjects: []runtime.Object{}, - expectedProxyConf: &deploy.Proxy{ + expectedProxyConf: &chetypes.Proxy{ HttpProxy: "http://proxy:3128", HttpUser: "", HttpPassword: "", @@ -95,8 +99,8 @@ func TestReadProxyConfiguration(t *testing.T) { }, }, { - name: "Test checluster proxy configured, nonProxy merged, OpenShift 4.x", - openShiftVersion: "4", + name: "Test checluster proxy configured, nonProxy merged, OpenShift 4.x", + isOpenShift: true, clusterProxy: &configv1.Proxy{ ObjectMeta: metav1.ObjectMeta{ Name: "cluster", @@ -107,20 +111,24 @@ func TestReadProxyConfiguration(t *testing.T) { NoProxy: "host2", }, }, - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ProxyURL: "http://proxy", - ProxyPort: "3128", - NonProxyHosts: "host1", + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + CheServer: chev2.CheServer{ + Proxy: chev2.Proxy{ + Url: "http://proxy", + Port: "3128", + NonProxyHosts: []string{"host1"}, + }, + }, }, }, }, initObjects: []runtime.Object{}, - expectedProxyConf: &deploy.Proxy{ + expectedProxyConf: &chetypes.Proxy{ HttpProxy: "http://proxy:3128", HttpUser: "", HttpPassword: "", @@ -136,8 +144,8 @@ func TestReadProxyConfiguration(t *testing.T) { }, }, { - name: "Test cluster wide proxy configured, OpenShift 4.x", - openShiftVersion: "4", + name: "Test cluster wide proxy configured, OpenShift 4.x", + isOpenShift: true, clusterProxy: &configv1.Proxy{ ObjectMeta: metav1.ObjectMeta{ Name: "cluster", @@ -153,16 +161,13 @@ func TestReadProxyConfiguration(t *testing.T) { NoProxy: "host1", }, }, - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{}, - }, }, initObjects: []runtime.Object{}, - expectedProxyConf: &deploy.Proxy{ + expectedProxyConf: &chetypes.Proxy{ HttpProxy: "http://proxy:3128", HttpUser: "", HttpPassword: "", @@ -178,8 +183,8 @@ func TestReadProxyConfiguration(t *testing.T) { }, }, { - name: "Test cluster wide proxy is not configured, but cluster wide CA certs added, OpenShift 4.x", - openShiftVersion: "4", + name: "Test cluster wide proxy is not configured, but cluster wide CA certs added, OpenShift 4.x", + isOpenShift: true, clusterProxy: &configv1.Proxy{ ObjectMeta: metav1.ObjectMeta{ Name: "cluster", @@ -190,19 +195,19 @@ func TestReadProxyConfiguration(t *testing.T) { }, }, }, - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, }, initObjects: []runtime.Object{}, - expectedProxyConf: &deploy.Proxy{ + expectedProxyConf: &chetypes.Proxy{ TrustedCAMapName: "additional-cluster-ca-bundle", }, }, { - name: "Test cluster wide proxy configured, nonProxy merged, OpenShift 4.x", - openShiftVersion: "4", + name: "Test cluster wide proxy configured, nonProxy merged, OpenShift 4.x", + isOpenShift: true, clusterProxy: &configv1.Proxy{ ObjectMeta: metav1.ObjectMeta{ Name: "cluster", @@ -213,18 +218,22 @@ func TestReadProxyConfiguration(t *testing.T) { NoProxy: "host1", }, }, - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - NonProxyHosts: "host2", + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + CheServer: chev2.CheServer{ + Proxy: chev2.Proxy{ + NonProxyHosts: []string{"host2"}, + }, + }, }, }, }, initObjects: []runtime.Object{}, - expectedProxyConf: &deploy.Proxy{ + expectedProxyConf: &chetypes.Proxy{ HttpProxy: "http://proxy:3128", HttpUser: "", HttpPassword: "", @@ -240,23 +249,27 @@ func TestReadProxyConfiguration(t *testing.T) { }, }, { - name: "Test checluster proxy configured, OpenShift 3.x", - openShiftVersion: "3", - clusterProxy: &configv1.Proxy{}, - cheCluster: &orgv1.CheCluster{ + name: "Test checluster proxy configured, Kubernetes", + isOpenShift: false, + clusterProxy: &configv1.Proxy{}, + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ProxyURL: "http://proxy", - ProxyPort: "3128", - NonProxyHosts: "host1", + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + CheServer: chev2.CheServer{ + Proxy: chev2.Proxy{ + Url: "http://proxy", + Port: "3128", + NonProxyHosts: []string{"host1"}, + }, + }, }, }, }, initObjects: []runtime.Object{}, - expectedProxyConf: &deploy.Proxy{ + expectedProxyConf: &chetypes.Proxy{ HttpProxy: "http://proxy:3128", HttpUser: "", HttpPassword: "", @@ -272,23 +285,27 @@ func TestReadProxyConfiguration(t *testing.T) { }, }, { - name: "Test checluster proxy configured, OpenShift 3.x and k8s, svc usage", - openShiftVersion: "3", - clusterProxy: &configv1.Proxy{}, - cheCluster: &orgv1.CheCluster{ + name: "Test checluster proxy configured, Kubernetes", + isOpenShift: true, + clusterProxy: &configv1.Proxy{}, + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ProxyURL: "http://proxy", - ProxyPort: "3128", - NonProxyHosts: "host1", + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + CheServer: chev2.CheServer{ + Proxy: chev2.Proxy{ + Url: "http://proxy", + Port: "3128", + NonProxyHosts: []string{"host1"}, + }, + }, }, }, }, initObjects: []runtime.Object{}, - expectedProxyConf: &deploy.Proxy{ + expectedProxyConf: &chetypes.Proxy{ HttpProxy: "http://proxy:3128", HttpUser: "", HttpPassword: "", @@ -308,11 +325,11 @@ func TestReadProxyConfiguration(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true))) - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) testCase.initObjects = append(testCase.initObjects, testCase.clusterProxy, testCase.cheCluster) scheme := scheme.Scheme - orgv1.SchemeBuilder.AddToScheme(scheme) + chev2.SchemeBuilder.AddToScheme(scheme) scheme.AddKnownTypes(configv1.SchemeGroupVersion, &configv1.Proxy{}) cli := fake.NewFakeClientWithScheme(scheme, testCase.initObjects...) @@ -320,12 +337,11 @@ func TestReadProxyConfiguration(t *testing.T) { fakeDiscovery, _ := clientSet.Discovery().(*fakeDiscovery.FakeDiscovery) fakeDiscovery.Fake.Resources = []*metav1.APIResourceList{} - os.Setenv("OPENSHIFT_VERSION", testCase.openShiftVersion) - util.IsOpenShift, util.IsOpenShift4, _ = util.DetectOpenShift() + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) - deployContext := &deploy.DeployContext{ + deployContext := &chetypes.DeployContext{ CheCluster: testCase.cheCluster, - ClusterAPI: deploy.ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme, diff --git a/controllers/devworkspace/controller.go b/controllers/devworkspace/controller.go index b358eef88..78278cafa 100644 --- a/controllers/devworkspace/controller.go +++ b/controllers/devworkspace/controller.go @@ -18,18 +18,14 @@ import ( stdErrors "errors" "fmt" "math/rand" - "reflect" "strings" "sync" "time" - checluster "github.com/eclipse-che/che-operator/api" - checlusterv1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/api/v2alpha1" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + chev2 "github.com/eclipse-che/che-operator/api/v2" "github.com/eclipse-che/che-operator/controllers/devworkspace/defaults" datasync "github.com/eclipse-che/che-operator/controllers/devworkspace/sync" - "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" routev1 "github.com/openshift/api/route/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -45,7 +41,7 @@ import ( var ( log = ctrl.Log.WithName("devworkspace-che") - currentCheInstances = map[client.ObjectKey]v2alpha1.CheCluster{} + currentCheInstances = map[client.ObjectKey]chev2.CheCluster{} cheInstancesAccess = sync.Mutex{} ) @@ -73,11 +69,11 @@ type CheClusterReconciler struct { // // If need be, this method can be replaced by a simply calling client.List to get all the che // managers in the cluster. -func GetCurrentCheClusterInstances() map[client.ObjectKey]v2alpha1.CheCluster { +func GetCurrentCheClusterInstances() map[client.ObjectKey]chev2.CheCluster { cheInstancesAccess.Lock() defer cheInstancesAccess.Unlock() - ret := map[client.ObjectKey]v2alpha1.CheCluster{} + ret := map[client.ObjectKey]chev2.CheCluster{} for k, v := range currentCheInstances { ret[k] = v @@ -92,7 +88,7 @@ func CleanCheClusterInstancesForTest() { cheInstancesAccess.Lock() defer cheInstancesAccess.Unlock() - currentCheInstances = map[client.ObjectKey]v2alpha1.CheCluster{} + currentCheInstances = map[client.ObjectKey]chev2.CheCluster{} } // New returns a new instance of the Che manager reconciler. This is mainly useful for @@ -111,7 +107,7 @@ func (r *CheClusterReconciler) SetupWithManager(mgr ctrl.Manager) error { r.syncer = datasync.New(r.client, r.scheme) bld := ctrl.NewControllerManagedBy(mgr). - For(&checlusterv1.CheCluster{}). + For(&chev2.CheCluster{}). Owns(&corev1.Service{}). Owns(&corev1.ConfigMap{}). Owns(&appsv1.Deployment{}). @@ -119,7 +115,7 @@ func (r *CheClusterReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&corev1.ServiceAccount{}). Owns(&rbac.Role{}). Owns(&rbac.RoleBinding{}) - if util.IsOpenShift { + if infrastructure.IsOpenShift() { bld.Owns(&routev1.Route{}) } else { bld.Owns(&networkingv1.Ingress{}) @@ -138,8 +134,8 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) delete(currentCheInstances, req.NamespacedName) // make sure we've checked we're in a valid state - currentV1 := &checlusterv1.CheCluster{} - err := r.client.Get(ctx, req.NamespacedName, currentV1) + cluster := &chev2.CheCluster{} + err := r.client.Get(ctx, req.NamespacedName, cluster) if err != nil { if errors.IsNotFound(err) { // Ok, our current router disappeared... @@ -149,14 +145,12 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, err } - current := checluster.AsV2alpha1(currentV1) - - if current.GetDeletionTimestamp() != nil { - return ctrl.Result{}, r.finalize(ctx, current, currentV1) + if cluster.GetDeletionTimestamp() != nil { + return ctrl.Result{}, r.finalize(ctx, cluster) } disabledMessage := "" - switch GetDevWorkspaceState(r.scheme, current) { + switch GetDevWorkspaceState(r.scheme, cluster) { case APINotPresentState: disabledMessage = "DevWorkspace CRDs are not installed" case DisabledState: @@ -164,14 +158,14 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) } if disabledMessage != "" { - res, err := r.updateStatus(ctx, current, currentV1, nil, current.Status.GatewayHost, current.Status.WorkspaceBaseDomain, v2alpha1.ClusterPhaseInactive, disabledMessage) + res, err := r.updateStatus(ctx, cluster, nil, cluster.Status.WorkspaceBaseDomain, chev2.ClusterPhaseInactive, disabledMessage) if err != nil { return res, err } return res, nil } - finalizerUpdated, err := r.ensureFinalizer(ctx, current) + finalizerUpdated, err := r.ensureFinalizer(ctx, cluster) if err != nil { log.Info("Failed to set a finalizer", "object", req.String()) return ctrl.Result{}, err @@ -182,10 +176,10 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) } // validate the CR - err = r.validate(current) + err = r.validate(cluster) if err != nil { log.Info("validation errors", "errors", err.Error()) - res, err := r.updateStatus(ctx, current, currentV1, nil, current.Status.GatewayHost, current.Status.WorkspaceBaseDomain, v2alpha1.ClusterPhaseInactive, err.Error()) + res, err := r.updateStatus(ctx, cluster, nil, cluster.Status.WorkspaceBaseDomain, chev2.ClusterPhaseInactive, err.Error()) if err != nil { return res, err } @@ -195,12 +189,10 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) // now, finally, the actual reconciliation var changed bool - var host string // We are no longer in charge of the gateway, leaving the responsibility for managing it on the che-operator. // But we need to detect the hostname on which the gateway is exposed so that the rest of our subsystems work. - host, err = r.detectCheHost(ctx, currentV1) - if err != nil { + if cluster.GetCheHost() == "" { // Wait some time in case the route is not ready yet return ctrl.Result{RequeueAfter: 2 * time.Second}, err } @@ -209,16 +201,22 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) // control of gateway creation changed = false - workspaceBaseDomain := current.Spec.Workspaces.DomainEndpoints.BaseDomain + workspaceBaseDomain := cluster.Spec.Networking.Domain + + // to be compatible with CheCluster API v1 + routeDomain := cluster.Spec.Components.CheServer.ExtraProperties["CHE_INFRA_OPENSHIFT_ROUTE_HOST_DOMAIN__SUFFIX"] + if routeDomain != "" { + workspaceBaseDomain = routeDomain + } if workspaceBaseDomain == "" { - workspaceBaseDomain, err = r.detectOpenShiftRouteBaseDomain(current) + workspaceBaseDomain, err = r.detectOpenShiftRouteBaseDomain(cluster) if err != nil { return ctrl.Result{}, err } if workspaceBaseDomain == "" { - res, err := r.updateStatus(ctx, current, currentV1, nil, current.Status.GatewayHost, current.Status.WorkspaceBaseDomain, v2alpha1.ClusterPhaseInactive, "Could not auto-detect the workspaceBaseDomain. Please set it explicitly in the spec.") + res, err := r.updateStatus(ctx, cluster, nil, cluster.Status.WorkspaceBaseDomain, chev2.ClusterPhaseInactive, "Could not auto-detect the workspaceBaseDomain. Please set it explicitly in the spec.") if err != nil { return res, err } @@ -227,58 +225,42 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) } } - res, err := r.updateStatus(ctx, current, currentV1, &changed, host, workspaceBaseDomain, v2alpha1.ClusterPhaseActive, "") + res, err := r.updateStatus(ctx, cluster, &changed, workspaceBaseDomain, chev2.ClusterPhaseActive, "") if err != nil { return res, err } // everything went fine and the manager exists, put it back in the shared map - currentCheInstances[req.NamespacedName] = *current + currentCheInstances[req.NamespacedName] = *cluster return res, nil } -func (r *CheClusterReconciler) updateStatus(ctx context.Context, cluster *v2alpha1.CheCluster, v1Cluster *checlusterv1.CheCluster, changed *bool, host string, workspaceDomain string, phase v2alpha1.ClusterPhase, phaseMessage string) (ctrl.Result, error) { +func (r *CheClusterReconciler) updateStatus(ctx context.Context, cluster *chev2.CheCluster, changed *bool, workspaceDomain string, phase chev2.CheClusterPhase, phaseMessage string) (ctrl.Result, error) { currentPhase := cluster.Status.GatewayPhase if changed != nil { - if !cluster.Spec.Gateway.IsEnabled() { - cluster.Status.GatewayPhase = v2alpha1.GatewayPhaseInactive - } else if *changed { - cluster.Status.GatewayPhase = v2alpha1.GatewayPhaseInitializing + if *changed { + cluster.Status.GatewayPhase = chev2.GatewayPhaseInitializing } else { - cluster.Status.GatewayPhase = v2alpha1.GatewayPhaseEstablished + cluster.Status.GatewayPhase = chev2.GatewayPhaseEstablished } } - cluster.Status.GatewayHost = host cluster.Status.WorkspaceBaseDomain = workspaceDomain + err := r.client.Status().Update(ctx, cluster) - // set this unconditionally, because the only other value is set using the finalizer - cluster.Status.Phase = phase - cluster.Status.Message = phaseMessage - - var err error - if !reflect.DeepEqual(v1Cluster.Status.DevworkspaceStatus, cluster.Status) { - v1Cluster.Status.DevworkspaceStatus = cluster.Status - err = r.client.Status().Update(ctx, v1Cluster) - } - - requeue := cluster.Spec.IsEnabled() && (currentPhase == v2alpha1.GatewayPhaseInitializing || - cluster.Status.Phase != v2alpha1.ClusterPhaseActive) - + requeue := currentPhase == chev2.GatewayPhaseInitializing return ctrl.Result{Requeue: requeue}, err } -func (r *CheClusterReconciler) validate(cluster *v2alpha1.CheCluster) error { +func (r *CheClusterReconciler) validate(cluster *chev2.CheCluster) error { validationErrors := []string{} - if !util.IsOpenShift { - // The validation error messages must correspond to the storage version of the resource, which is currently - // v1... - if cluster.Spec.Workspaces.DomainEndpoints.BaseDomain == "" { - validationErrors = append(validationErrors, "spec.k8s.ingressDomain must be specified") + if !infrastructure.IsOpenShift() { + if cluster.Spec.Networking.Domain == "" { + validationErrors = append(validationErrors, "spec.networking.domain must be specified") } } @@ -294,7 +276,7 @@ func (r *CheClusterReconciler) validate(cluster *v2alpha1.CheCluster) error { return nil } -func (r *CheClusterReconciler) finalize(ctx context.Context, cluster *v2alpha1.CheCluster, v1Cluster *checlusterv1.CheCluster) (err error) { +func (r *CheClusterReconciler) finalize(ctx context.Context, cluster *chev2.CheCluster) (err error) { err = r.gatewayConfigFinalize(ctx, cluster) if err == nil { @@ -307,19 +289,17 @@ func (r *CheClusterReconciler) finalize(ctx context.Context, cluster *v2alpha1.C cluster.Finalizers = finalizers - err = r.client.Update(ctx, checluster.AsV1(cluster)) + err = r.client.Update(ctx, cluster) } else { - cluster.Status.Phase = v2alpha1.ClusterPhasePendingDeletion + cluster.Status.ChePhase = chev2.ClusterPhasePendingDeletion cluster.Status.Message = fmt.Sprintf("Finalization has failed: %s", err.Error()) - - v1Cluster.Status.DevworkspaceStatus = cluster.Status - err = r.client.Status().Update(ctx, v1Cluster) + err = r.client.Status().Update(ctx, cluster) } return err } -func (r *CheClusterReconciler) ensureFinalizer(ctx context.Context, cluster *v2alpha1.CheCluster) (updated bool, err error) { +func (r *CheClusterReconciler) ensureFinalizer(ctx context.Context, cluster *chev2.CheCluster) (updated bool, err error) { needsUpdate := true if cluster.Finalizers != nil { @@ -335,15 +315,15 @@ func (r *CheClusterReconciler) ensureFinalizer(ctx context.Context, cluster *v2a if needsUpdate { cluster.Finalizers = append(cluster.Finalizers, FinalizerName) - err = r.client.Update(ctx, checluster.AsV1(cluster)) + err = r.client.Update(ctx, cluster) } return needsUpdate, err } // Tries to autodetect the route base domain. -func (r *CheClusterReconciler) detectOpenShiftRouteBaseDomain(cluster *v2alpha1.CheCluster) (string, error) { - if !util.IsOpenShift { +func (r *CheClusterReconciler) detectOpenShiftRouteBaseDomain(cluster *chev2.CheCluster) (string, error) { + if !infrastructure.IsOpenShift() { return "", nil } @@ -381,58 +361,9 @@ func randomSuffix(length int) string { return hex.EncodeToString(arr) } -func (r *CheClusterReconciler) detectCheHost(ctx context.Context, cluster *checlusterv1.CheCluster) (string, error) { - host := strings.TrimPrefix(cluster.Status.CheURL, "https://") - - if host == "" { - expectedLabels := deploy.GetLabels(cluster, deploy.DefaultCheFlavor(cluster)) - lbls := labels.SelectorFromSet(expectedLabels) - - if util.IsOpenShift { - list := routev1.RouteList{} - err := r.client.List(ctx, &list, &client.ListOptions{ - Namespace: cluster.Namespace, - LabelSelector: lbls, - }) - - if err != nil { - return "", err - } - - if len(list.Items) == 0 { - return "", fmt.Errorf("expecting exactly 1 route to match Che gateway labels but found %d", len(list.Items)) - } - - host = list.Items[0].Spec.Host - } else { - list := networkingv1.IngressList{} - err := r.client.List(ctx, &list, &client.ListOptions{ - Namespace: cluster.Namespace, - LabelSelector: lbls, - }) - - if err != nil { - return "", err - } - - if len(list.Items) == 0 { - return "", fmt.Errorf("expecting exactly 1 ingress to match Che gateway labels but found %d", len(list.Items)) - } - - if len(list.Items[0].Spec.Rules) != 1 { - return "", fmt.Errorf("expecting exactly 1 rule on the Che gateway ingress but found %d. This is a bug", len(list.Items[0].Spec.Rules)) - } - - host = list.Items[0].Spec.Rules[0].Host - } - } - - return host, nil -} - // Checks that there are no devworkspace configurations for the gateway (which would mean running devworkspaces). // If there are some, an error is returned. -func (r *CheClusterReconciler) gatewayConfigFinalize(ctx context.Context, cluster *v2alpha1.CheCluster) error { +func (r *CheClusterReconciler) gatewayConfigFinalize(ctx context.Context, cluster *chev2.CheCluster) error { // we need to stop the reconcile if there are devworkspaces handled by it. // we detect that by the presence of the gateway configmaps in the namespace of the manager list := corev1.ConfigMapList{} diff --git a/controllers/devworkspace/controller_test.go b/controllers/devworkspace/controller_test.go index 07613eab9..cbcdd2aa5 100644 --- a/controllers/devworkspace/controller_test.go +++ b/controllers/devworkspace/controller_test.go @@ -20,13 +20,9 @@ import ( dwo "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" "github.com/devfile/devworkspace-operator/pkg/infrastructure" - checluster "github.com/eclipse-che/che-operator/api" - v1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/api/v2alpha1" + chev2 "github.com/eclipse-che/che-operator/api/v2" "github.com/eclipse-che/che-operator/controllers/devworkspace/defaults" "github.com/eclipse-che/che-operator/controllers/devworkspace/sync" - "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" routev1 "github.com/openshift/api/route/v1" appsv1 "k8s.io/api/apps/v1" @@ -34,7 +30,6 @@ import ( networkingv1 "k8s.io/api/networking/v1" "k8s.io/api/node/v1alpha1" rbac "k8s.io/api/rbac/v1" - "k8s.io/utils/pointer" k8sErrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -56,13 +51,15 @@ func createTestScheme() *runtime.Scheme { utilruntime.Must(appsv1.AddToScheme(scheme)) utilruntime.Must(rbac.AddToScheme(scheme)) utilruntime.Must(routev1.AddToScheme(scheme)) - utilruntime.Must(v1.AddToScheme(scheme)) + utilruntime.Must(chev2.AddToScheme(scheme)) utilruntime.Must(dwo.AddToScheme(scheme)) return scheme } func TestNoCustomResourceSharedWhenReconcilingNonExistent(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.Kubernetes) + // clear the map before the test for k := range currentCheInstances { delete(currentCheInstances, k) @@ -89,24 +86,21 @@ func TestNoCustomResourceSharedWhenReconcilingNonExistent(t *testing.T) { } // now add some manager and reconcile a non-existent one - cl.Create(ctx, asV1(&v2alpha1.CheCluster{ + cl.Create(ctx, &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: managerName + "-not-me", Namespace: ns, Finalizers: []string{FinalizerName}, }, - Spec: v2alpha1.CheClusterSpec{ - Gateway: v2alpha1.CheGatewaySpec{ - Host: "over.the.rainbow", - Enabled: pointer.BoolPtr(false), - }, - Workspaces: v2alpha1.Workspaces{ - DomainEndpoints: v2alpha1.DomainEndpoints{ - BaseDomain: "down.on.earth", - }, + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + Hostname: "over.the.rainbow", }, }, - })) + Status: chev2.CheClusterStatus{ + WorkspaceBaseDomain: "down.on.earth", + }, + }) _, err = reconciler.Reconcile(context.TODO(), reconcile.Request{NamespacedName: types.NamespacedName{Name: managerName, Namespace: ns}}) if err != nil { @@ -120,6 +114,8 @@ func TestNoCustomResourceSharedWhenReconcilingNonExistent(t *testing.T) { } func TestAddsCustomResourceToSharedMapOnCreate(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.Kubernetes) + // clear the map before the test for k := range currentCheInstances { delete(currentCheInstances, k) @@ -128,24 +124,19 @@ func TestAddsCustomResourceToSharedMapOnCreate(t *testing.T) { managerName := "che" ns := "default" scheme := createTestScheme() - cl := fake.NewFakeClientWithScheme(scheme, asV1(&v2alpha1.CheCluster{ + cl := fake.NewFakeClientWithScheme(scheme, &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: managerName, Namespace: ns, Finalizers: []string{FinalizerName}, }, - Spec: v2alpha1.CheClusterSpec{ - Gateway: v2alpha1.CheGatewaySpec{ - Host: "over.the.rainbow", - Enabled: pointer.BoolPtr(false), - }, - Workspaces: v2alpha1.Workspaces{ - DomainEndpoints: v2alpha1.DomainEndpoints{ - BaseDomain: "down.on.earth", - }, + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + Hostname: "over.the.rainbow", + Domain: "down.on.earth", }, }, - })) + }) reconciler := CheClusterReconciler{client: cl, scheme: scheme, syncer: sync.New(cl, scheme)} @@ -170,6 +161,8 @@ func TestAddsCustomResourceToSharedMapOnCreate(t *testing.T) { } func TestUpdatesCustomResourceInSharedMapOnUpdate(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.Kubernetes) + // clear the map before the test for k := range currentCheInstances { delete(currentCheInstances, k) @@ -179,24 +172,19 @@ func TestUpdatesCustomResourceInSharedMapOnUpdate(t *testing.T) { ns := "default" scheme := createTestScheme() - cl := fake.NewFakeClientWithScheme(scheme, asV1(&v2alpha1.CheCluster{ + cl := fake.NewFakeClientWithScheme(scheme, &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: managerName, Namespace: ns, Finalizers: []string{FinalizerName}, }, - Spec: v2alpha1.CheClusterSpec{ - Gateway: v2alpha1.CheGatewaySpec{ - Enabled: pointer.BoolPtr(false), - Host: "over.the.rainbow", - }, - Workspaces: v2alpha1.Workspaces{ - DomainEndpoints: v2alpha1.DomainEndpoints{ - BaseDomain: "down.on.earth", - }, + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + Hostname: "over.the.rainbow", + Domain: "down.on.earth", }, }, - })) + }) reconciler := CheClusterReconciler{client: cl, scheme: scheme, syncer: sync.New(cl, scheme)} @@ -219,19 +207,19 @@ func TestUpdatesCustomResourceInSharedMapOnUpdate(t *testing.T) { t.Fatalf("Found a manager that we didn't reconcile. Curious (and buggy). We found %s but should have found %s", mgr.Name, managerName) } - if mgr.Spec.Gateway.Host != "over.the.rainbow" { - t.Fatalf("Unexpected host value: expected: over.the.rainbow, actual: %s", mgr.Spec.Gateway.Host) + if mgr.GetCheHost() != "over.the.rainbow" { + t.Fatalf("Unexpected host value: expected: over.the.rainbow, actual: %s", mgr.GetCheHost()) } // now update the manager and reconcile again. See that the map contains the updated value - mgrInCluster := v1.CheCluster{} + mgrInCluster := chev2.CheCluster{} cl.Get(context.TODO(), client.ObjectKey{Name: managerName, Namespace: ns}, &mgrInCluster) // to be able to update, we need to set the resource version mgr.SetResourceVersion(mgrInCluster.GetResourceVersion()) - mgr.Spec.Gateway.Host = "over.the.shoulder" - err = cl.Update(context.TODO(), asV1(&mgr)) + mgr.Spec.Networking.Hostname = "over.the.shoulder" + err = cl.Update(context.TODO(), &mgr) if err != nil { t.Fatalf("Failed to update. Wat? %s", err) } @@ -247,8 +235,8 @@ func TestUpdatesCustomResourceInSharedMapOnUpdate(t *testing.T) { t.Fatalf("Found a manager that we didn't reconcile. Curious (and buggy). We found %s but should have found %s", mgr.Name, managerName) } - if mgr.Spec.Gateway.Host != "over.the.rainbow" { - t.Fatalf("Unexpected host value: expected: over.the.rainbow, actual: %s", mgr.Spec.Gateway.Host) + if mgr.Spec.Networking.Hostname != "over.the.rainbow" { + t.Fatalf("Unexpected host value: expected: over.the.rainbow, actual: %s", mgr.Spec.Networking.Hostname) } // now reconcile and see that the value in the map is now updated @@ -268,12 +256,14 @@ func TestUpdatesCustomResourceInSharedMapOnUpdate(t *testing.T) { t.Fatalf("Found a manager that we didn't reconcile. Curious (and buggy). We found %s but should have found %s", mgr.Name, managerName) } - if mgr.Spec.Gateway.Host != "over.the.shoulder" { - t.Fatalf("Unexpected host value: expected: over.the.shoulder, actual: %s", mgr.Spec.Gateway.Host) + if mgr.Spec.Networking.Hostname != "over.the.shoulder" { + t.Fatalf("Unexpected host value: expected: over.the.shoulder, actual: %s", mgr.Spec.Networking.Hostname) } } func TestRemovesCustomResourceFromSharedMapOnDelete(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.Kubernetes) + // clear the map before the test for k := range currentCheInstances { delete(currentCheInstances, k) @@ -283,24 +273,19 @@ func TestRemovesCustomResourceFromSharedMapOnDelete(t *testing.T) { ns := "default" scheme := createTestScheme() - cl := fake.NewFakeClientWithScheme(scheme, asV1(&v2alpha1.CheCluster{ + cl := fake.NewFakeClientWithScheme(scheme, &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: managerName, Namespace: ns, Finalizers: []string{FinalizerName}, }, - Spec: v2alpha1.CheClusterSpec{ - Gateway: v2alpha1.CheGatewaySpec{ - Host: "over.the.rainbow", - Enabled: pointer.BoolPtr(false), - }, - Workspaces: v2alpha1.Workspaces{ - DomainEndpoints: v2alpha1.DomainEndpoints{ - BaseDomain: "down.on.earth", - }, + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + Hostname: "over.the.rainbow", + Domain: "down.on.earth", }, }, - })) + }) reconciler := CheClusterReconciler{client: cl, scheme: scheme, syncer: sync.New(cl, scheme)} @@ -323,7 +308,7 @@ func TestRemovesCustomResourceFromSharedMapOnDelete(t *testing.T) { t.Fatalf("Found a manager that we didn't reconcile. Curious (and buggy). We found %s but should have found %s", mgr.Name, managerName) } - cl.Delete(context.TODO(), asV1(&mgr)) + cl.Delete(context.TODO(), &mgr) // now reconcile and see that the value is no longer in the map @@ -340,28 +325,26 @@ func TestRemovesCustomResourceFromSharedMapOnDelete(t *testing.T) { } func TestCustomResourceFinalization(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.Kubernetes) + managerName := "che" ns := "default" scheme := createTestScheme() ctx := context.TODO() cl := fake.NewFakeClientWithScheme(scheme, - asV1(&v2alpha1.CheCluster{ + &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: managerName, Namespace: ns, Finalizers: []string{FinalizerName}, }, - Spec: v2alpha1.CheClusterSpec{ - Gateway: v2alpha1.CheGatewaySpec{ - Host: "over.the.rainbow", - }, - Workspaces: v2alpha1.Workspaces{ - DomainEndpoints: v2alpha1.DomainEndpoints{ - BaseDomain: "down.on.earth", - }, + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + Hostname: "over.the.rainbow", + Domain: "down.on.earth", }, }, - }), + }, &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "ws1", @@ -382,7 +365,7 @@ func TestCustomResourceFinalization(t *testing.T) { } // check that the reconcile loop added the finalizer - manager := v1.CheCluster{} + manager := chev2.CheCluster{} err = cl.Get(ctx, client.ObjectKey{Name: managerName, Namespace: ns}, &manager) if err != nil { t.Fatalf("Failed to obtain the manager from the fake client: %s", err) @@ -407,7 +390,7 @@ func TestCustomResourceFinalization(t *testing.T) { t.Fatalf("Failed to reconcile che manager with error: %s", err) } - manager = v1.CheCluster{} + manager = chev2.CheCluster{} err = cl.Get(ctx, client.ObjectKey{Name: managerName, Namespace: ns}, &manager) if err != nil { t.Fatalf("Failed to obtain the manager from the fake client: %s", err) @@ -417,10 +400,10 @@ func TestCustomResourceFinalization(t *testing.T) { t.Fatalf("There should have been a finalizer on the manager after a failed finalization attempt") } - if manager.Status.DevworkspaceStatus.Phase != v2alpha1.ClusterPhasePendingDeletion { - t.Fatalf("Expected the manager to be in the pending deletion phase but it is: %s", manager.Status.DevworkspaceStatus.Phase) + if manager.Status.ChePhase != chev2.ClusterPhasePendingDeletion { + t.Fatalf("Expected the manager to be in the pending deletion phase but it is: %s", manager.Status.ChePhase) } - if len(manager.Status.DevworkspaceStatus.Message) == 0 { + if len(manager.Status.Message) == 0 { t.Fatalf("Expected an non-empty message about the failed finalization in the manager status") } @@ -440,7 +423,7 @@ func TestCustomResourceFinalization(t *testing.T) { t.Fatalf("Failed to reconcile che manager with error: %s", err) } - manager = v1.CheCluster{} + manager = chev2.CheCluster{} err = cl.Get(ctx, client.ObjectKey{Name: managerName, Namespace: ns}, &manager) if err == nil || !k8sErrors.IsNotFound(err) { t.Fatalf("Failed to obtain the manager from the fake client: %s", err) @@ -461,40 +444,19 @@ func TestExternalGatewayDetection(t *testing.T) { clusterName := "eclipse-che" ns := "default" - v2cluster := &v2alpha1.CheCluster{ + cluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: clusterName, Namespace: ns, }, - Spec: v2alpha1.CheClusterSpec{ - Workspaces: v2alpha1.Workspaces{ - DomainEndpoints: v2alpha1.DomainEndpoints{ - BaseDomain: "down.on.earth", - }, - }, + Status: chev2.CheClusterStatus{ + WorkspaceBaseDomain: "down.on.earth", + CheURL: "https://host", }, } onKubernetes(func() { - v1Cluster := asV1(v2cluster) - - cl := fake.NewFakeClientWithScheme(scheme, - v1Cluster, - &networkingv1.Ingress{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ingress", - Namespace: ns, - Labels: deploy.GetLabels(v1Cluster, "test-che"), - }, - Spec: networkingv1.IngressSpec{ - Rules: []networkingv1.IngressRule{ - { - Host: "ingress.host", - }, - }, - }, - }, - ) + cl := fake.NewFakeClientWithScheme(scheme, cluster) reconciler := CheClusterReconciler{client: cl, scheme: scheme, syncer: sync.New(cl, scheme)} @@ -508,36 +470,18 @@ func TestExternalGatewayDetection(t *testing.T) { t.Fatalf("Failed to reconcile che manager with error: %s", err) } - persisted := v1.CheCluster{} + persisted := chev2.CheCluster{} if err := cl.Get(context.TODO(), types.NamespacedName{Name: clusterName, Namespace: ns}, &persisted); err != nil { t.Fatal(err) } - if persisted.Status.DevworkspaceStatus.Phase != v2alpha1.ClusterPhaseActive { - t.Fatalf("Unexpected cluster state: %v", persisted.Status.DevworkspaceStatus.Phase) - } - - if persisted.Status.DevworkspaceStatus.GatewayHost != "ingress.host" { - t.Fatalf("Unexpected gateway host: %v", persisted.Status.DevworkspaceStatus.GatewayHost) + if persisted.GetCheHost() != "host" { + t.Fatalf("Unexpected gateway host: %v", persisted.GetCheHost()) } }) onOpenShift(func() { - v1Cluster := asV1(v2cluster) - - cl := fake.NewFakeClientWithScheme(scheme, - v1Cluster, - &routev1.Route{ - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Namespace: ns, - Labels: deploy.GetLabels(v1Cluster, "test-che"), - }, - Spec: routev1.RouteSpec{ - Host: "route.host", - }, - }, - ) + cl := fake.NewFakeClientWithScheme(scheme, cluster) reconciler := CheClusterReconciler{client: cl, scheme: scheme, syncer: sync.New(cl, scheme)} @@ -551,49 +495,23 @@ func TestExternalGatewayDetection(t *testing.T) { t.Fatalf("Failed to reconcile che manager with error: %s", err) } - persisted := v1.CheCluster{} + persisted := chev2.CheCluster{} if err := cl.Get(context.TODO(), types.NamespacedName{Name: clusterName, Namespace: ns}, &persisted); err != nil { t.Fatal(err) } - if persisted.Status.DevworkspaceStatus.Phase != v2alpha1.ClusterPhaseActive { - t.Fatalf("Unexpected cluster state: %v", persisted.Status.DevworkspaceStatus.Phase) - } - - if persisted.Status.DevworkspaceStatus.GatewayHost != "route.host" { - t.Fatalf("Unexpected gateway host: %v", persisted.Status.DevworkspaceStatus.GatewayHost) + if persisted.GetCheHost() != "host" { + t.Fatalf("Unexpected gateway host: %v", persisted.GetCheHost()) } }) } -func asV1(v2Obj *v2alpha1.CheCluster) *v1.CheCluster { - v1 := checluster.AsV1(v2Obj) - v1.Status.CheURL = "https://" + v1.Spec.Server.CheHost - return v1 -} - func onKubernetes(f func()) { - isOpenShift := util.IsOpenShift - isOpenShift4 := util.IsOpenShift4 - - util.IsOpenShift = false - util.IsOpenShift4 = false - + infrastructure.InitializeForTesting(infrastructure.Kubernetes) f() - - util.IsOpenShift = isOpenShift - util.IsOpenShift4 = isOpenShift4 } func onOpenShift(f func()) { - isOpenShift := util.IsOpenShift - isOpenShift4 := util.IsOpenShift4 - - util.IsOpenShift = true - util.IsOpenShift4 = true - + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) f() - - util.IsOpenShift = isOpenShift - util.IsOpenShift4 = isOpenShift4 } diff --git a/controllers/devworkspace/defaults/defaults.go b/controllers/devworkspace/defaults/defaults.go index e7e532b80..447a0044c 100644 --- a/controllers/devworkspace/defaults/defaults.go +++ b/controllers/devworkspace/defaults/defaults.go @@ -13,21 +13,13 @@ package defaults import ( - "os" - "runtime" - - "github.com/eclipse-che/che-operator/api/v2alpha1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/constants" "github.com/eclipse-che/che-operator/pkg/deploy" ctrl "sigs.k8s.io/controller-runtime" ) const ( - gatewayImageEnvVarName = "RELATED_IMAGE_gateway" - gatewayConfigurerImageEnvVarName = "RELATED_IMAGE_gateway_configurer" - - defaultGatewayImage = "quay.io/eclipse/che--traefik:v2.5.0-eb30f9f09a65cee1fab5ef9c64cb4ec91b800dc3fdd738d62a9d4334f0114683" - defaultGatewayConfigurerImage = "quay.io/che-incubator/configbump:0.1.4" - configAnnotationPrefix = "che.routing.controller.devfile.io/" ConfigAnnotationCheManagerName = configAnnotationPrefix + "che-name" ConfigAnnotationCheManagerNamespace = configAnnotationPrefix + "che-namespace" @@ -40,13 +32,6 @@ const ( var ( log = ctrl.Log.WithName("defaults") - DefaultIngressAnnotations = map[string]string{ - "kubernetes.io/ingress.class": "nginx", - "nginx.ingress.kubernetes.io/proxy-read-timeout": "3600", - "nginx.ingress.kubernetes.io/proxy-connect-timeout": "3600", - "nginx.ingress.kubernetes.io/ssl-redirect": "true", - } - // If this looks weirdly out of place to you from all other labels, then you're completely right! // These labels are the default ones used by che-operator and Che7. Let's keep the defaults // the same for the ease of translation... @@ -60,7 +45,7 @@ func GetGatewayWorkspaceConfigMapName(workspaceID string) string { return workspaceID + "-route" } -func GetLabelsForComponent(cluster *v2alpha1.CheCluster, component string) map[string]string { +func GetLabelsForComponent(cluster *chev2.CheCluster, component string) map[string]string { return GetLabelsFromNames(cluster.Name, component) } @@ -68,53 +53,27 @@ func GetLabelsFromNames(appName string, component string) map[string]string { return AddStandardLabelsFromNames(appName, component, map[string]string{}) } -func AddStandardLabelsForComponent(cluster *v2alpha1.CheCluster, component string, labels map[string]string) map[string]string { +func AddStandardLabelsForComponent(cluster *chev2.CheCluster, component string, labels map[string]string) map[string]string { return AddStandardLabelsFromNames(cluster.Name, component, labels) } func AddStandardLabelsFromNames(appName string, component string, labels map[string]string) map[string]string { labels["app.kubernetes.io/name"] = appName - labels["app.kubernetes.io/part-of"] = deploy.CheEclipseOrg + labels["app.kubernetes.io/part-of"] = constants.CheEclipseOrg labels["app.kubernetes.io/component"] = component return labels } -func GetGatewayImage() string { - return read(gatewayImageEnvVarName, defaultGatewayImage) -} - -func GetGatewayConfigurerImage() string { - return read(gatewayConfigurerImageEnvVarName, defaultGatewayConfigurerImage) -} - -func GetIngressAnnotations(cluster *v2alpha1.CheCluster) map[string]string { - if len(cluster.Spec.K8s.IngressAnnotations) > 0 { - return cluster.Spec.K8s.IngressAnnotations +func GetIngressAnnotations(cluster *chev2.CheCluster) map[string]string { + if len(cluster.Spec.Networking.Annotations) > 0 { + return cluster.Spec.Networking.Annotations } - return DefaultIngressAnnotations + return deploy.DefaultIngressAnnotations } -func GetGatewayWorkspaceConfigMapLabels(cluster *v2alpha1.CheCluster) map[string]string { - if len(cluster.Spec.Gateway.ConfigLabels) > 0 { - return cluster.Spec.Gateway.ConfigLabels +func GetGatewayWorkspaceConfigMapLabels(cluster *chev2.CheCluster) map[string]string { + if len(cluster.Spec.Networking.Auth.Gateway.ConfigLabels) > 0 { + return cluster.Spec.Networking.Auth.Gateway.ConfigLabels } return defaultGatewayConfigLabels } - -func read(varName string, fallback string) string { - ret := os.Getenv(varName) - - if len(ret) == 0 { - ret = os.Getenv(archDependent(varName)) - if len(ret) == 0 { - log.Info("Failed to read the default value from the environment. Will use the hardcoded default value.", "envvar", varName, "value", fallback) - ret = fallback - } - } - - return ret -} - -func archDependent(envVarName string) string { - return envVarName + "_" + runtime.GOARCH -} diff --git a/controllers/devworkspace/init_test.go b/controllers/devworkspace/init_test.go new file mode 100644 index 000000000..a1c59fd67 --- /dev/null +++ b/controllers/devworkspace/init_test.go @@ -0,0 +1,25 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// +package devworkspace + +import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" +) + +func init() { + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../../config/manager/manager.yaml") +} diff --git a/controllers/devworkspace/solver/che_routing.go b/controllers/devworkspace/solver/che_routing.go index 5f6ecbdd0..dce96c86b 100644 --- a/controllers/devworkspace/solver/che_routing.go +++ b/controllers/devworkspace/solver/che_routing.go @@ -21,7 +21,9 @@ import ( dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" - "github.com/eclipse-che/che-operator/pkg/util" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/eclipse-che/che-operator/pkg/deploy/gateway" "k8s.io/apimachinery/pkg/api/resource" @@ -30,11 +32,11 @@ import ( dwo "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" "github.com/devfile/devworkspace-operator/controllers/controller/devworkspacerouting/solvers" "github.com/devfile/devworkspace-operator/pkg/common" - "github.com/devfile/devworkspace-operator/pkg/constants" - "github.com/eclipse-che/che-operator/api/v2alpha1" - "github.com/eclipse-che/che-operator/controllers/devworkspace/defaults" + dwconstants "github.com/devfile/devworkspace-operator/pkg/constants" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + chev2 "github.com/eclipse-che/che-operator/api/v2" + dwdefaults "github.com/eclipse-che/che-operator/controllers/devworkspace/defaults" "github.com/eclipse-che/che-operator/controllers/devworkspace/sync" - "github.com/eclipse-che/che-operator/pkg/deploy" "github.com/google/go-cmp/cmp/cmpopts" routeV1 "github.com/openshift/api/route/v1" corev1 "k8s.io/api/core/v1" @@ -59,7 +61,7 @@ var ( configMapDiffOpts = cmpopts.IgnoreFields(corev1.ConfigMap{}, "TypeMeta", "ObjectMeta") ) -func (c *CheRoutingSolver) cheSpecObjects(cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting, workspaceMeta solvers.DevWorkspaceMetadata) (solvers.RoutingObjects, error) { +func (c *CheRoutingSolver) cheSpecObjects(cheCluster *chev2.CheCluster, routing *dwo.DevWorkspaceRouting, workspaceMeta solvers.DevWorkspaceMetadata) (solvers.RoutingObjects, error) { objs := solvers.RoutingObjects{} if err := c.provisionServices(&objs, cheCluster, routing, workspaceMeta); err != nil { @@ -77,7 +79,7 @@ func (c *CheRoutingSolver) cheSpecObjects(cheCluster *v2alpha1.CheCluster, routi return objs, nil } -func (c *CheRoutingSolver) provisionServices(objs *solvers.RoutingObjects, cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting, workspaceMeta solvers.DevWorkspaceMetadata) error { +func (c *CheRoutingSolver) provisionServices(objs *solvers.RoutingObjects, cheCluster *chev2.CheCluster, routing *dwo.DevWorkspaceRouting, workspaceMeta solvers.DevWorkspaceMetadata) error { objs.Services = solvers.GetDiscoverableServicesForEndpoints(routing.Spec.Endpoints, workspaceMeta) commonService := &corev1.Service{ @@ -85,8 +87,8 @@ func (c *CheRoutingSolver) provisionServices(objs *solvers.RoutingObjects, cheCl Name: common.ServiceName(routing.Spec.DevWorkspaceId), Namespace: routing.Namespace, Labels: map[string]string{ - constants.DevWorkspaceIDLabel: routing.Spec.DevWorkspaceId, - deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg, + dwconstants.DevWorkspaceIDLabel: routing.Spec.DevWorkspaceId, + constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg, }, }, Spec: corev1.ServiceSpec{ @@ -105,10 +107,10 @@ func (c *CheRoutingSolver) provisionServices(objs *solvers.RoutingObjects, cheCl objs.Services = append(objs.Services, *commonService) annos := map[string]string{} - annos[defaults.ConfigAnnotationCheManagerName] = cheCluster.Name - annos[defaults.ConfigAnnotationCheManagerNamespace] = cheCluster.Namespace + annos[dwdefaults.ConfigAnnotationCheManagerName] = cheCluster.Name + annos[dwdefaults.ConfigAnnotationCheManagerNamespace] = cheCluster.Namespace - additionalLabels := defaults.GetLabelsForComponent(cheCluster, "exposure") + additionalLabels := dwdefaults.GetLabelsForComponent(cheCluster, "exposure") for i := range objs.Services { // need to use a ref otherwise s would be a copy @@ -140,7 +142,7 @@ func (c *CheRoutingSolver) provisionServices(objs *solvers.RoutingObjects, cheCl return nil } -func (c *CheRoutingSolver) provisionRouting(objs *solvers.RoutingObjects, cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting, workspaceMeta solvers.DevWorkspaceMetadata) error { +func (c *CheRoutingSolver) provisionRouting(objs *solvers.RoutingObjects, cheCluster *chev2.CheCluster, routing *dwo.DevWorkspaceRouting, workspaceMeta solvers.DevWorkspaceMetadata) error { // k, now we have to create our own objects for configuring the gateway configMaps, err := c.getGatewayConfigsAndFillRoutingObjects(cheCluster, workspaceMeta.DevWorkspaceId, routing, objs) if err != nil { @@ -159,15 +161,23 @@ func (c *CheRoutingSolver) provisionRouting(objs *solvers.RoutingObjects, cheClu return nil } -func (c *CheRoutingSolver) provisionPodAdditions(objs *solvers.RoutingObjects, cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting) error { +func (c *CheRoutingSolver) provisionPodAdditions(objs *solvers.RoutingObjects, cheCluster *chev2.CheCluster, routing *dwo.DevWorkspaceRouting) error { objs.PodAdditions = &dwo.PodAdditions{ Containers: []corev1.Container{}, Volumes: []corev1.Volume{}, } + + image := defaults.GetGatewayImage(cheCluster) + for _, c := range cheCluster.Spec.Networking.Auth.Gateway.Deployment.Containers { + if c.Name == constants.GatewayContainerName { + image = c.Image + } + } + objs.PodAdditions.Containers = append(objs.PodAdditions.Containers, corev1.Container{ Name: wsGatewayName, - Image: cheCluster.Spec.Gateway.Image, - ImagePullPolicy: corev1.PullPolicy(deploy.DefaultPullPolicyFromDockerImage(cheCluster.Spec.Gateway.Image)), + Image: image, + ImagePullPolicy: corev1.PullPolicy(utils.GetPullPolicyFromDockerImage(image)), VolumeMounts: []corev1.VolumeMount{ { Name: wsGatewayName, @@ -194,7 +204,7 @@ func (c *CheRoutingSolver) provisionPodAdditions(objs *solvers.RoutingObjects, c VolumeSource: corev1.VolumeSource{ ConfigMap: &corev1.ConfigMapVolumeSource{ LocalObjectReference: corev1.LocalObjectReference{ - Name: defaults.GetGatewayWorkspaceConfigMapName(routing.Spec.DevWorkspaceId), + Name: dwdefaults.GetGatewayWorkspaceConfigMapName(routing.Spec.DevWorkspaceId), }, DefaultMode: &defaultMode, }, @@ -204,14 +214,14 @@ func (c *CheRoutingSolver) provisionPodAdditions(objs *solvers.RoutingObjects, c return nil } -func (c *CheRoutingSolver) cheExposedEndpoints(cheCluster *v2alpha1.CheCluster, workspaceID string, componentEndpoints map[string]dwo.EndpointList, routingObj solvers.RoutingObjects) (exposedEndpoints map[string]dwo.ExposedEndpointList, ready bool, err error) { - if cheCluster.Status.GatewayPhase == v2alpha1.GatewayPhaseInitializing { +func (c *CheRoutingSolver) cheExposedEndpoints(cheCluster *chev2.CheCluster, workspaceID string, componentEndpoints map[string]dwo.EndpointList, routingObj solvers.RoutingObjects) (exposedEndpoints map[string]dwo.ExposedEndpointList, ready bool, err error) { + if cheCluster.Status.GatewayPhase == chev2.GatewayPhaseInitializing { return nil, false, nil } exposedEndpoints = map[string]dwo.ExposedEndpointList{} - gatewayHost := cheCluster.Status.GatewayHost + gatewayHost := cheCluster.GetCheHost() for component, endpoints := range componentEndpoints { for _, endpoint := range endpoints { @@ -242,7 +252,7 @@ func (c *CheRoutingSolver) cheExposedEndpoints(cheCluster *v2alpha1.CheCluster, // try to find the endpoint in the ingresses/routes first. If it is there, it is exposed on a subdomain // otherwise it is exposed through the gateway var endpointURL string - if util.IsOpenShift4 { + if infrastructure.IsOpenShift() { route := findRouteForEndpoint(component, endpoint, &routingObj, workspaceID) if route != nil { endpointURL = path.Join(route.Spec.Host, endpoint.Path) @@ -300,13 +310,13 @@ func isSecureScheme(scheme string) bool { return scheme == "https" || scheme == "wss" } -func (c *CheRoutingSolver) getGatewayConfigsAndFillRoutingObjects(cheCluster *v2alpha1.CheCluster, workspaceID string, routing *dwo.DevWorkspaceRouting, objs *solvers.RoutingObjects) ([]corev1.ConfigMap, error) { - restrictedAnno, setRestrictedAnno := routing.Annotations[constants.DevWorkspaceRestrictedAccessAnnotation] +func (c *CheRoutingSolver) getGatewayConfigsAndFillRoutingObjects(cheCluster *chev2.CheCluster, workspaceID string, routing *dwo.DevWorkspaceRouting, objs *solvers.RoutingObjects) ([]corev1.ConfigMap, error) { + restrictedAnno, setRestrictedAnno := routing.Annotations[dwconstants.DevWorkspaceRestrictedAccessAnnotation] - cmLabels := defaults.AddStandardLabelsForComponent(cheCluster, "gateway-config", defaults.GetGatewayWorkspaceConfigMapLabels(cheCluster)) - cmLabels[constants.DevWorkspaceIDLabel] = workspaceID + cmLabels := dwdefaults.AddStandardLabelsForComponent(cheCluster, "gateway-config", dwdefaults.GetGatewayWorkspaceConfigMapLabels(cheCluster)) + cmLabels[dwconstants.DevWorkspaceIDLabel] = workspaceID if setRestrictedAnno { - cmLabels[constants.DevWorkspaceRestrictedAccessAnnotation] = restrictedAnno + cmLabels[dwconstants.DevWorkspaceRestrictedAccessAnnotation] = restrictedAnno } configs := make([]corev1.ConfigMap, 0) @@ -330,8 +340,8 @@ func (c *CheRoutingSolver) getGatewayConfigsAndFillRoutingObjects(cheCluster *v2 return configs, nil } -func (c *CheRoutingSolver) getInfraSpecificExposer(cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting, objs *solvers.RoutingObjects) (func(info *EndpointInfo), error) { - if util.IsOpenShift4 { +func (c *CheRoutingSolver) getInfraSpecificExposer(cheCluster *chev2.CheCluster, routing *dwo.DevWorkspaceRouting, objs *solvers.RoutingObjects) (func(info *EndpointInfo), error) { + if infrastructure.IsOpenShift() { exposer := &RouteExposer{} if err := exposer.initFrom(context.TODO(), c.client, cheCluster, routing); err != nil { return nil, err @@ -342,7 +352,7 @@ func (c *CheRoutingSolver) getInfraSpecificExposer(cheCluster *v2alpha1.CheClust }, nil } else { exposer := &IngressExposer{} - if err := exposer.initFrom(context.TODO(), c.client, cheCluster, routing, defaults.GetIngressAnnotations(cheCluster)); err != nil { + if err := exposer.initFrom(context.TODO(), c.client, cheCluster, routing, dwdefaults.GetIngressAnnotations(cheCluster)); err != nil { return nil, err } return func(info *EndpointInfo) { @@ -362,7 +372,7 @@ func getCommonService(objs *solvers.RoutingObjects, dwId string) *corev1.Service return nil } -func exposeAllEndpoints(cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting, objs *solvers.RoutingObjects, ingressExpose func(*EndpointInfo)) *corev1.ConfigMap { +func exposeAllEndpoints(cheCluster *chev2.CheCluster, routing *dwo.DevWorkspaceRouting, objs *solvers.RoutingObjects, ingressExpose func(*EndpointInfo)) *corev1.ConfigMap { wsRouteConfig := gateway.CreateEmptyTraefikConfig() commonService := getCommonService(objs, routing.Spec.DevWorkspaceId) @@ -415,11 +425,11 @@ func exposeAllEndpoints(cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspa wsConfigMap := &corev1.ConfigMap{ ObjectMeta: v1.ObjectMeta{ - Name: defaults.GetGatewayWorkspaceConfigMapName(routing.Spec.DevWorkspaceId), + Name: dwdefaults.GetGatewayWorkspaceConfigMapName(routing.Spec.DevWorkspaceId), Namespace: routing.Namespace, Labels: map[string]string{ - constants.DevWorkspaceIDLabel: routing.Spec.DevWorkspaceId, - deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg, + dwconstants.DevWorkspaceIDLabel: routing.Spec.DevWorkspaceId, + constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg, }, }, Data: map[string]string{}, @@ -454,7 +464,7 @@ func containPort(service *corev1.Service, port int32) bool { return false } -func provisionMainWorkspaceRoute(cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting, cmLabels map[string]string) (*corev1.ConfigMap, error) { +func provisionMainWorkspaceRoute(cheCluster *chev2.CheCluster, routing *dwo.DevWorkspaceRouting, cmLabels map[string]string) (*corev1.ConfigMap, error) { dwId := routing.Spec.DevWorkspaceId dwNamespace := routing.Namespace @@ -465,7 +475,7 @@ func provisionMainWorkspaceRoute(cheCluster *v2alpha1.CheCluster, routing *dwo.D getServiceURL(wsGatewayPort, dwId, dwNamespace), []string{"/" + dwId}) - if util.IsOpenShift4 { + if infrastructure.IsOpenShift() { // on OpenShift, we need to set authorization header. // This MUST come before Auth, because Auth needs Authorization header to be properly set. cfg.AddAuthHeaderRewrite(dwId) @@ -482,12 +492,12 @@ func provisionMainWorkspaceRoute(cheCluster *v2alpha1.CheCluster, routing *dwo.D } else { return &corev1.ConfigMap{ ObjectMeta: v1.ObjectMeta{ - Name: defaults.GetGatewayWorkspaceConfigMapName(dwId), + Name: dwdefaults.GetGatewayWorkspaceConfigMapName(dwId), Namespace: cheCluster.Namespace, Labels: cmLabels, Annotations: map[string]string{ - defaults.ConfigAnnotationDevWorkspaceRoutingName: routing.Name, - defaults.ConfigAnnotationDevWorkspaceRoutingNamespace: routing.Namespace, + dwdefaults.ConfigAnnotationDevWorkspaceRoutingName: routing.Name, + dwdefaults.ConfigAnnotationDevWorkspaceRoutingNamespace: routing.Namespace, }, }, Data: map[string]string{dwId + ".yml": string(contents)}, @@ -501,7 +511,7 @@ func routeForHealthzEndpoint(cfg *gateway.TraefikConfig, dwId string, endpoints for _, e := range endpoints { if e.Attributes.GetString(string(dwo.TypeEndpointAttribute), nil) == string(dwo.MainEndpointType) { middlewares := []string{dwId + gateway.StripPrefixMiddlewareSuffix} - if util.IsOpenShift4 { + if infrastructure.IsOpenShift() { middlewares = append(middlewares, dwId+gateway.HeaderRewriteMiddlewareSuffix) } routeName, endpointPath := createEndpointPath(&e, componentName) @@ -517,7 +527,7 @@ func routeForHealthzEndpoint(cfg *gateway.TraefikConfig, dwId string, endpoints } } -func addEndpointToTraefikConfig(componentName string, e dwo.Endpoint, cfg *gateway.TraefikConfig, cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting) { +func addEndpointToTraefikConfig(componentName string, e dwo.Endpoint, cfg *gateway.TraefikConfig, cheCluster *chev2.CheCluster, routing *dwo.DevWorkspaceRouting) { routeName, prefix := createEndpointPath(&e, componentName) rulePrefix := fmt.Sprintf("PathPrefix(`%s`)", prefix) @@ -580,8 +590,8 @@ func findIngressForEndpoint(componentName string, endpoint dwo.Endpoint, objs *s for i := range objs.Ingresses { ingress := &objs.Ingresses[i] - if ingress.Annotations[defaults.ConfigAnnotationComponentName] != componentName || - ingress.Annotations[defaults.ConfigAnnotationEndpointName] != endpoint.Name { + if ingress.Annotations[dwdefaults.ConfigAnnotationComponentName] != componentName || + ingress.Annotations[dwdefaults.ConfigAnnotationEndpointName] != endpoint.Name { continue } @@ -610,8 +620,8 @@ func findRouteForEndpoint(componentName string, endpoint dwo.Endpoint, objs *sol for r := range objs.Routes { route := &objs.Routes[r] - if route.Annotations[defaults.ConfigAnnotationComponentName] == componentName && - route.Annotations[defaults.ConfigAnnotationEndpointName] == endpoint.Name && + if route.Annotations[dwdefaults.ConfigAnnotationComponentName] == componentName && + route.Annotations[dwdefaults.ConfigAnnotationEndpointName] == endpoint.Name && route.Spec.To.Kind == "Service" && route.Spec.To.Name == service.Name && route.Spec.Port.TargetPort.IntValue() == endpoint.TargetPort { @@ -622,8 +632,8 @@ func findRouteForEndpoint(componentName string, endpoint dwo.Endpoint, objs *sol return nil } -func (c *CheRoutingSolver) cheRoutingFinalize(cheManager *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting) error { - selector, err := labels.Parse(fmt.Sprintf("%s=%s", constants.DevWorkspaceIDLabel, routing.Spec.DevWorkspaceId)) +func (c *CheRoutingSolver) cheRoutingFinalize(cheManager *chev2.CheCluster, routing *dwo.DevWorkspaceRouting) error { + selector, err := labels.Parse(fmt.Sprintf("%s=%s", dwconstants.DevWorkspaceIDLabel, routing.Spec.DevWorkspaceId)) if err != nil { return err } diff --git a/controllers/devworkspace/solver/che_routing_test.go b/controllers/devworkspace/solver/che_routing_test.go index 09a356121..3dcf1e2b6 100644 --- a/controllers/devworkspace/solver/che_routing_test.go +++ b/controllers/devworkspace/solver/che_routing_test.go @@ -18,22 +18,17 @@ import ( "strings" "testing" - "github.com/eclipse-che/che-operator/pkg/util" - "github.com/stretchr/testify/assert" - "github.com/eclipse-che/che-operator/pkg/deploy/gateway" - dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" dwo "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" "github.com/devfile/devworkspace-operator/controllers/controller/devworkspacerouting/solvers" "github.com/devfile/devworkspace-operator/pkg/constants" "github.com/devfile/devworkspace-operator/pkg/infrastructure" - org "github.com/eclipse-che/che-operator/api" - v1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/api/v2alpha1" + chev2 "github.com/eclipse-che/che-operator/api/v2" controller "github.com/eclipse-che/che-operator/controllers/devworkspace" "github.com/eclipse-che/che-operator/controllers/devworkspace/defaults" + "github.com/eclipse-che/che-operator/pkg/deploy/gateway" routev1 "github.com/openshift/api/route/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -59,15 +54,15 @@ func createTestScheme() *runtime.Scheme { utilruntime.Must(dw.AddToScheme(scheme)) utilruntime.Must(dwo.AddToScheme(scheme)) utilruntime.Must(routev1.AddToScheme(scheme)) - utilruntime.Must(v1.AddToScheme(scheme)) + utilruntime.Must(chev2.AddToScheme(scheme)) return scheme } -func getSpecObjectsForManager(t *testing.T, mgr *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting, additionalInitialObjects ...runtime.Object) (client.Client, solvers.RoutingSolver, solvers.RoutingObjects) { +func getSpecObjectsForManager(t *testing.T, mgr *chev2.CheCluster, routing *dwo.DevWorkspaceRouting, additionalInitialObjects ...runtime.Object) (client.Client, solvers.RoutingSolver, solvers.RoutingObjects) { scheme := createTestScheme() - allObjs := []runtime.Object{asV1(mgr)} + allObjs := []runtime.Object{mgr} for i := range additionalInitialObjects { allObjs = append(allObjs, additionalInitialObjects[i]) } @@ -103,20 +98,16 @@ func getSpecObjectsForManager(t *testing.T, mgr *v2alpha1.CheCluster, routing *d } func getSpecObjects(t *testing.T, routing *dwo.DevWorkspaceRouting) (client.Client, solvers.RoutingSolver, solvers.RoutingObjects) { - return getSpecObjectsForManager(t, &v2alpha1.CheCluster{ + return getSpecObjectsForManager(t, &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "che", Namespace: "ns", Finalizers: []string{controller.FinalizerName}, }, - Spec: v2alpha1.CheClusterSpec{ - Gateway: v2alpha1.CheGatewaySpec{ - Host: "over.the.rainbow", - }, - Workspaces: v2alpha1.Workspaces{ - DomainEndpoints: v2alpha1.DomainEndpoints{ - BaseDomain: "down.on.earth", - }, + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + Domain: "down.on.earth", + Hostname: "over.the.rainbow", }, }, }, routing) @@ -207,8 +198,7 @@ func relocatableDevWorkspaceRouting() *dwo.DevWorkspaceRouting { } func TestCreateRelocatedObjectsK8S(t *testing.T) { - util.IsOpenShift = false - util.IsOpenShift4 = false + infrastructure.InitializeForTesting(infrastructure.Kubernetes) cl, _, objs := getSpecObjects(t, relocatableDevWorkspaceRouting()) t.Run("noIngresses", func(t *testing.T) { @@ -353,8 +343,7 @@ func TestCreateRelocatedObjectsK8S(t *testing.T) { } func TestCreateRelocatedObjectsOpenshift(t *testing.T) { - util.IsOpenShift = true - util.IsOpenShift4 = true + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) cl, _, objs := getSpecObjects(t, relocatableDevWorkspaceRouting()) @@ -520,8 +509,7 @@ func TestUniqueMainEndpoint(t *testing.T) { func TestCreateSubDomainObjects(t *testing.T) { testCommon := func(infra infrastructure.Type) solvers.RoutingObjects { - util.IsOpenShift = infra == infrastructure.OpenShiftv4 - util.IsOpenShift4 = infra == infrastructure.OpenShiftv4 + infrastructure.InitializeForTesting(infra) cl, _, objs := getSpecObjects(t, subdomainDevWorkspaceRouting()) @@ -592,8 +580,7 @@ func TestCreateSubDomainObjects(t *testing.T) { func TestReportRelocatableExposedEndpoints(t *testing.T) { // kubernetes - util.IsOpenShift = false - util.IsOpenShift4 = false + infrastructure.InitializeForTesting(infrastructure.Kubernetes) routing := relocatableDevWorkspaceRouting() _, solver, objs := getSpecObjects(t, routing) @@ -646,8 +633,7 @@ func TestReportRelocatableExposedEndpoints(t *testing.T) { } func TestExposeEndpoints(t *testing.T) { - util.IsOpenShift = false - util.IsOpenShift4 = false + infrastructure.InitializeForTesting(infrastructure.Kubernetes) routing := &dwo.DevWorkspaceRouting{ ObjectMeta: metav1.ObjectMeta{ @@ -757,8 +743,7 @@ func TestExposeEndpoints(t *testing.T) { } func TestReportSubdomainExposedEndpoints(t *testing.T) { - util.IsOpenShift = false - util.IsOpenShift4 = false + infrastructure.InitializeForTesting(infrastructure.Kubernetes) routing := subdomainDevWorkspaceRouting() _, solver, objs := getSpecObjects(t, routing) @@ -810,8 +795,7 @@ func TestReportSubdomainExposedEndpoints(t *testing.T) { } func TestFinalize(t *testing.T) { - util.IsOpenShift = false - util.IsOpenShift4 = false + infrastructure.InitializeForTesting(infrastructure.Kubernetes) routing := relocatableDevWorkspaceRouting() cl, slv, _ := getSpecObjects(t, routing) @@ -832,8 +816,7 @@ func TestFinalize(t *testing.T) { } func TestEndpointsAlwaysOnSecureProtocolsWhenExposedThroughGateway(t *testing.T) { - util.IsOpenShift = false - util.IsOpenShift4 = false + infrastructure.InitializeForTesting(infrastructure.Kubernetes) routing := relocatableDevWorkspaceRouting() _, slv, objs := getSpecObjects(t, routing) @@ -856,26 +839,19 @@ func TestEndpointsAlwaysOnSecureProtocolsWhenExposedThroughGateway(t *testing.T) } func TestUsesIngressAnnotationsForWorkspaceEndpointIngresses(t *testing.T) { - util.IsOpenShift = false - util.IsOpenShift4 = false + infrastructure.InitializeForTesting(infrastructure.Kubernetes) - mgr := &v2alpha1.CheCluster{ + mgr := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "che", Namespace: "ns", Finalizers: []string{controller.FinalizerName}, }, - Spec: v2alpha1.CheClusterSpec{ - Gateway: v2alpha1.CheGatewaySpec{ - Host: "over.the.rainbow", - }, - Workspaces: v2alpha1.Workspaces{ - DomainEndpoints: v2alpha1.DomainEndpoints{ - BaseDomain: "down.on.earth", - }, - }, - K8s: v2alpha1.CheClusterSpecK8s{ - IngressAnnotations: map[string]string{ + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + Hostname: "over.the.rainbow", + Domain: "down.on.earth", + Annotations: map[string]string{ "a": "b", }, }, @@ -900,24 +876,19 @@ func TestUsesIngressAnnotationsForWorkspaceEndpointIngresses(t *testing.T) { } func TestUsesCustomCertificateForWorkspaceEndpointIngresses(t *testing.T) { - util.IsOpenShift = false - util.IsOpenShift4 = false + infrastructure.InitializeForTesting(infrastructure.Kubernetes) - mgr := &v2alpha1.CheCluster{ + mgr := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "che", Namespace: "ns", Finalizers: []string{controller.FinalizerName}, }, - Spec: v2alpha1.CheClusterSpec{ - Gateway: v2alpha1.CheGatewaySpec{ - Host: "beyond.comprehension", - }, - Workspaces: v2alpha1.Workspaces{ - DomainEndpoints: v2alpha1.DomainEndpoints{ - BaseDomain: "almost.trivial", - TlsSecretName: "tlsSecret", - }, + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + TlsSecretName: "tlsSecret", + Hostname: "beyond.comprehension", + Domain: "almost.trivial", }, }, } @@ -981,24 +952,19 @@ func TestUsesCustomCertificateForWorkspaceEndpointIngresses(t *testing.T) { } func TestUsesCustomCertificateForWorkspaceEndpointRoutes(t *testing.T) { - util.IsOpenShift = true - util.IsOpenShift4 = true + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) - mgr := &v2alpha1.CheCluster{ + mgr := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "che", Namespace: "ns", Finalizers: []string{controller.FinalizerName}, }, - Spec: v2alpha1.CheClusterSpec{ - Gateway: v2alpha1.CheGatewaySpec{ - Host: "beyond.comprehension", - }, - Workspaces: v2alpha1.Workspaces{ - DomainEndpoints: v2alpha1.DomainEndpoints{ - BaseDomain: "almost.trivial", - TlsSecretName: "tlsSecret", - }, + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + Hostname: "beyond.comprehension", + TlsSecretName: "tlsSecret", + Domain: "almost.trivial", }, }, } @@ -1044,9 +1010,3 @@ func TestUsesCustomCertificateForWorkspaceEndpointRoutes(t *testing.T) { t.Errorf("Unexpected TLS on the route: %s", route.Spec.TLS) } } - -func asV1(v2Obj *v2alpha1.CheCluster) *v1.CheCluster { - v1 := org.AsV1(v2Obj) - v1.Status.CheURL = "https://" + v1.Spec.Server.CheHost - return v1 -} diff --git a/controllers/devworkspace/solver/endpoint_exposer.go b/controllers/devworkspace/solver/endpoint_exposer.go index ba7d9d6fb..6ee93062a 100644 --- a/controllers/devworkspace/solver/endpoint_exposer.go +++ b/controllers/devworkspace/solver/endpoint_exposer.go @@ -16,12 +16,13 @@ import ( "context" "fmt" + "github.com/eclipse-che/che-operator/pkg/common/utils" + dwo "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" - "github.com/devfile/devworkspace-operator/pkg/constants" - checluster "github.com/eclipse-che/che-operator/api" - "github.com/eclipse-che/che-operator/api/v2alpha1" + dwconstants "github.com/devfile/devworkspace-operator/pkg/constants" + chev2 "github.com/eclipse-che/che-operator/api/v2" "github.com/eclipse-che/che-operator/controllers/devworkspace/defaults" - "github.com/eclipse-che/che-operator/pkg/deploy" + "github.com/eclipse-che/che-operator/pkg/common/constants" routev1 "github.com/openshift/api/route/v1" corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" @@ -65,17 +66,20 @@ func getEndpointExposingObjectName(componentName string, workspaceID string, por return fmt.Sprintf("%s-%s-%d-%s", workspaceID, componentName, port, endpointName) } -func (e *RouteExposer) initFrom(ctx context.Context, cl client.Client, cluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting) error { +func (e *RouteExposer) initFrom(ctx context.Context, cl client.Client, cluster *chev2.CheCluster, routing *dwo.DevWorkspaceRouting) error { e.baseDomain = cluster.Status.WorkspaceBaseDomain e.devWorkspaceID = routing.Spec.DevWorkspaceId + e.labels = cluster.Spec.Networking.Labels - e.labels = map[string]string{} - checlusterV1 := checluster.AsV1(cluster) - deploy.MergeLabels(e.labels, checlusterV1.Spec.Server.CustomCheProperties["CHE_INFRA_OPENSHIFT_ROUTE_LABELS"]) + // to be compatible from CheCluster API v1 configuration + routeLabels := cluster.Spec.Components.CheServer.ExtraProperties["CHE_INFRA_OPENSHIFT_ROUTE_LABELS"] + if routeLabels != "" { + e.labels = utils.ParseMap(routeLabels) + } - if cluster.Spec.Workspaces.DomainEndpoints.TlsSecretName != "" { + if cluster.Spec.Networking.TlsSecretName != "" { secret := &corev1.Secret{} - err := cl.Get(ctx, client.ObjectKey{Name: cluster.Spec.Workspaces.DomainEndpoints.TlsSecretName, Namespace: cluster.Namespace}, secret) + err := cl.Get(ctx, client.ObjectKey{Name: cluster.Spec.Networking.TlsSecretName, Namespace: cluster.Namespace}, secret) if err != nil { return err } @@ -87,12 +91,12 @@ func (e *RouteExposer) initFrom(ctx context.Context, cl client.Client, cluster * return nil } -func (e *IngressExposer) initFrom(ctx context.Context, cl client.Client, cluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting, ingressAnnotations map[string]string) error { +func (e *IngressExposer) initFrom(ctx context.Context, cl client.Client, cluster *chev2.CheCluster, routing *dwo.DevWorkspaceRouting, ingressAnnotations map[string]string) error { e.baseDomain = cluster.Status.WorkspaceBaseDomain e.devWorkspaceID = routing.Spec.DevWorkspaceId e.ingressAnnotations = ingressAnnotations - if cluster.Spec.Workspaces.DomainEndpoints.TlsSecretName != "" { + if cluster.Spec.Networking.TlsSecretName != "" { tlsSecretName := routing.Spec.DevWorkspaceId + "-endpoints" e.tlsSecretName = tlsSecretName @@ -102,7 +106,7 @@ func (e *IngressExposer) initFrom(ctx context.Context, cl client.Client, cluster err := cl.Get(ctx, client.ObjectKey{Name: tlsSecretName, Namespace: routing.Namespace}, secret) if errors.IsNotFound(err) { secret = &corev1.Secret{} - err = cl.Get(ctx, client.ObjectKey{Name: cluster.Spec.Workspaces.DomainEndpoints.TlsSecretName, Namespace: cluster.Namespace}, secret) + err = cl.Get(ctx, client.ObjectKey{Name: cluster.Spec.Networking.TlsSecretName, Namespace: cluster.Namespace}, secret) if err != nil { return err } @@ -114,7 +118,7 @@ func (e *IngressExposer) initFrom(ctx context.Context, cl client.Client, cluster Name: tlsSecretName, Namespace: routing.Namespace, Labels: map[string]string{ - deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg, + constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg, }, OwnerReferences: []metav1.OwnerReference{ { @@ -143,8 +147,8 @@ func (e *RouteExposer) getRouteForService(endpoint *EndpointInfo) routev1.Route labels := labels.Merge( e.labels, map[string]string{ - constants.DevWorkspaceIDLabel: e.devWorkspaceID, - deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg, + dwconstants.DevWorkspaceIDLabel: e.devWorkspaceID, + constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg, }) route := routev1.Route{ ObjectMeta: metav1.ObjectMeta{ @@ -190,8 +194,8 @@ func (e *IngressExposer) getIngressForService(endpoint *EndpointInfo) networking Name: getEndpointExposingObjectName(endpoint.componentName, e.devWorkspaceID, endpoint.port, endpoint.endpointName), Namespace: endpoint.service.Namespace, Labels: map[string]string{ - constants.DevWorkspaceIDLabel: e.devWorkspaceID, - deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg, + dwconstants.DevWorkspaceIDLabel: e.devWorkspaceID, + constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg, }, Annotations: finalizeIngressAnnotations(e.ingressAnnotations, endpoint.componentName, endpoint.endpointName), OwnerReferences: endpoint.service.OwnerReferences, diff --git a/controllers/devworkspace/solver/init_test.go b/controllers/devworkspace/solver/init_test.go new file mode 100644 index 000000000..811ba050d --- /dev/null +++ b/controllers/devworkspace/solver/init_test.go @@ -0,0 +1,25 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// +package solver + +import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" +) + +func init() { + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../../../config/manager/manager.yaml") +} diff --git a/controllers/devworkspace/solver/solver.go b/controllers/devworkspace/solver/solver.go index abb81e451..7c5fc38b2 100644 --- a/controllers/devworkspace/solver/solver.go +++ b/controllers/devworkspace/solver/solver.go @@ -20,7 +20,7 @@ import ( controllerv1alpha1 "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" "github.com/devfile/devworkspace-operator/controllers/controller/devworkspacerouting/solvers" - "github.com/eclipse-che/che-operator/api/v2alpha1" + chev2 "github.com/eclipse-che/che-operator/api/v2" controller "github.com/eclipse-che/che-operator/controllers/devworkspace" "github.com/eclipse-che/che-operator/controllers/devworkspace/defaults" corev1 "k8s.io/api/core/v1" @@ -164,23 +164,23 @@ func isSupported(routingClass controllerv1alpha1.DevWorkspaceRoutingClass) bool return routingClass == "che" } -func cheManagerOfRouting(routing *controllerv1alpha1.DevWorkspaceRouting) (*v2alpha1.CheCluster, error) { +func cheManagerOfRouting(routing *controllerv1alpha1.DevWorkspaceRouting) (*chev2.CheCluster, error) { cheName := routing.Annotations[defaults.ConfigAnnotationCheManagerName] cheNamespace := routing.Annotations[defaults.ConfigAnnotationCheManagerNamespace] return findCheManager(client.ObjectKey{Name: cheName, Namespace: cheNamespace}) } -func findCheManager(cheManagerKey client.ObjectKey) (*v2alpha1.CheCluster, error) { +func findCheManager(cheManagerKey client.ObjectKey) (*chev2.CheCluster, error) { managers := controller.GetCurrentCheClusterInstances() if len(managers) == 0 { // the CheManager has not been reconciled yet, so let's wait a bit - return &v2alpha1.CheCluster{}, &solvers.RoutingNotReady{Retry: 1 * time.Second} + return &chev2.CheCluster{}, &solvers.RoutingNotReady{Retry: 1 * time.Second} } if len(cheManagerKey.Name) == 0 { if len(managers) > 1 { - return &v2alpha1.CheCluster{}, &solvers.RoutingInvalid{Reason: fmt.Sprintf("the routing does not specify any Che manager in its configuration but there are %d Che managers in the cluster", len(managers))} + return &chev2.CheCluster{}, &solvers.RoutingInvalid{Reason: fmt.Sprintf("the routing does not specify any Che manager in its configuration but there are %d Che managers in the cluster", len(managers))} } for _, m := range managers { return &m, nil @@ -194,5 +194,5 @@ func findCheManager(cheManagerKey client.ObjectKey) (*v2alpha1.CheCluster, error logger.Info("Routing requires a non-existing che manager. Retrying in 10 seconds.", "key", cheManagerKey) - return &v2alpha1.CheCluster{}, &solvers.RoutingNotReady{Retry: 10 * time.Second} + return &chev2.CheCluster{}, &solvers.RoutingNotReady{Retry: 10 * time.Second} } diff --git a/controllers/devworkspace/state.go b/controllers/devworkspace/state.go index 30fde125f..7ee9ec016 100644 --- a/controllers/devworkspace/state.go +++ b/controllers/devworkspace/state.go @@ -13,11 +13,9 @@ package devworkspace import ( - "context" "time" - orgv1 "github.com/eclipse-che/che-operator/api/v1" - chev2alpha1 "github.com/eclipse-che/che-operator/api/v2alpha1" + chev2 "github.com/eclipse-che/che-operator/api/v2" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/discovery" "sigs.k8s.io/controller-runtime/pkg/client" @@ -36,15 +34,6 @@ const ( // which we only do, if there is the controller.devfile.io resource group in the cluster // and DevWorkspaces are enabled at least on one CheCluster func ShouldDevWorkspacesBeEnabled(mgr manager.Manager) (bool, error) { - dwEnabled, err := doesCheClusterWithDevWorkspaceEnabledExist(mgr) - if err != nil { - return false, err - } - - if !dwEnabled { - return false, nil - } - // we assume that if the group is there, then we have all the expected CRs there, too. dwApiExists, err := findApiGroup(mgr, "controller.devfile.io") if err != nil { @@ -78,43 +67,16 @@ func NotifyWhenDevWorkspaceEnabled(mgr manager.Manager, stop <-chan struct{}, ca } } -func GetDevWorkspaceState(scheme *runtime.Scheme, cr *chev2alpha1.CheCluster) DevWorkspaceState { +func GetDevWorkspaceState(scheme *runtime.Scheme, cr *chev2.CheCluster) DevWorkspaceState { if !scheme.IsGroupRegistered("controller.devfile.io") { return APINotPresentState } - if !cr.Spec.IsEnabled() { - return DisabledState - } - return EnabledState } var nonCachedClient *client.Client -func doesCheClusterWithDevWorkspaceEnabledExist(mgr manager.Manager) (bool, error) { - if nonCachedClient == nil { - c, err := client.New(mgr.GetConfig(), client.Options{ - Scheme: mgr.GetScheme(), - }) - if err != nil { - return false, err - } - nonCachedClient = &c - } - - cheClusters := &orgv1.CheClusterList{} - err := (*nonCachedClient).List(context.TODO(), cheClusters, &client.ListOptions{}) - if err != nil { - return false, err - } - - // Previous logic was depended on cheCluster.Spec.DevWorkspace.Enable field. - // Since DevWorkspace.Enable is enabled by default, then it is enough to check - // CheCluster existence. - return len(cheClusters.Items) != 0, nil -} - func findApiGroup(mgr manager.Manager, apiGroup string) (bool, error) { cl, err := discovery.NewDiscoveryClientForConfig(mgr.GetConfig()) if err != nil { diff --git a/controllers/devworkspace/sync/init_test.go b/controllers/devworkspace/sync/init_test.go new file mode 100644 index 000000000..7df978266 --- /dev/null +++ b/controllers/devworkspace/sync/init_test.go @@ -0,0 +1,25 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// +package sync + +import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" +) + +func init() { + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../../../config/manager/manager.yaml") +} diff --git a/controllers/usernamespace/controller.go b/controllers/usernamespace/controller.go index 832d93d2d..02762e674 100644 --- a/controllers/usernamespace/controller.go +++ b/controllers/usernamespace/controller.go @@ -16,15 +16,13 @@ import ( "context" "encoding/json" - "github.com/devfile/devworkspace-operator/pkg/infrastructure" - + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" "github.com/eclipse-che/che-operator/pkg/deploy/tls" - "github.com/eclipse-che/che-operator/pkg/util" - "github.com/devfile/devworkspace-operator/pkg/constants" - org "github.com/eclipse-che/che-operator/api" - v1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/api/v2alpha1" + dwconstants "github.com/devfile/devworkspace-operator/pkg/constants" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + chev2 "github.com/eclipse-che/che-operator/api/v2" "github.com/eclipse-che/che-operator/controllers/che" "github.com/eclipse-che/che-operator/controllers/devworkspace" "github.com/eclipse-che/che-operator/controllers/devworkspace/defaults" @@ -74,7 +72,7 @@ func (r *CheUserNamespaceReconciler) SetupWithManager(mgr ctrl.Manager) error { r.namespaceCache.client = r.client var obj client.Object - if util.IsOpenShift4 { + if infrastructure.IsOpenShift() { obj = &projectv1.Project{} } else { obj = &corev1.Namespace{} @@ -85,13 +83,13 @@ func (r *CheUserNamespaceReconciler) SetupWithManager(mgr ctrl.Manager) error { For(obj). Watches(&source.Kind{Type: &corev1.Secret{}}, r.watchRulesForSecrets(ctx)). Watches(&source.Kind{Type: &corev1.ConfigMap{}}, r.watchRulesForConfigMaps(ctx)). - Watches(&source.Kind{Type: &v1.CheCluster{}}, r.triggerAllNamespaces()) + Watches(&source.Kind{Type: &chev2.CheCluster{}}, r.triggerAllNamespaces()) return bld.Complete(r) } func (r *CheUserNamespaceReconciler) watchRulesForSecrets(ctx context.Context) handler.EventHandler { - rules := r.commonRules(ctx, deploy.CheTLSSelfSignedCertificateSecretName) + rules := r.commonRules(ctx, constants.DefaultSelfSignedCertificateSecretName) return handler.EnqueueRequestsFromMapFunc( handler.MapFunc(func(obj client.Object) []reconcile.Request { return asReconcileRequestsForNamespaces(obj, rules) @@ -180,7 +178,7 @@ func (r *CheUserNamespaceReconciler) triggerAllNamespaces() handler.EventHandler } func (r *CheUserNamespaceReconciler) hasCheCluster(ctx context.Context, namespace string) bool { - list := v1.CheClusterList{} + list := chev2.CheClusterList{} if err := r.client.List(ctx, &list, client.InNamespace(namespace)); err != nil { return false } @@ -210,9 +208,9 @@ func (r *CheUserNamespaceReconciler) Reconcile(ctx context.Context, req ctrl.Req } // let's construct the deployContext to be able to use methods from v1 operator - deployContext := &deploy.DeployContext{ - CheCluster: org.AsV1(checluster), - ClusterAPI: deploy.ClusterAPI{ + deployContext := &chetypes.DeployContext{ + CheCluster: checluster, + ClusterAPI: chetypes.ClusterAPI{ Client: r.client, NonCachingClient: r.client, DiscoveryClient: nil, @@ -248,7 +246,7 @@ func (r *CheUserNamespaceReconciler) Reconcile(ctx context.Context, req ctrl.Req return ctrl.Result{}, nil } -func findManagingCheCluster(key types.NamespacedName) *v2alpha1.CheCluster { +func findManagingCheCluster(key types.NamespacedName) *chev2.CheCluster { instances := devworkspace.GetCurrentCheClusterInstances() if len(instances) == 0 { return nil @@ -272,11 +270,10 @@ func findManagingCheCluster(key types.NamespacedName) *v2alpha1.CheCluster { } } -func (r *CheUserNamespaceReconciler) reconcileSelfSignedCert(ctx context.Context, deployContext *deploy.DeployContext, targetNs string, checluster *v2alpha1.CheCluster) error { +func (r *CheUserNamespaceReconciler) reconcileSelfSignedCert(ctx context.Context, deployContext *chetypes.DeployContext, targetNs string, checluster *chev2.CheCluster) error { if err := deleteLegacyObject("server-cert", &corev1.Secret{}, targetNs, checluster, deployContext); err != nil { return err } - targetCertName := prefixedName("server-cert") delSecret := func() error { @@ -285,7 +282,7 @@ func (r *CheUserNamespaceReconciler) reconcileSelfSignedCert(ctx context.Context } cheCert := &corev1.Secret{} - if err := r.client.Get(ctx, client.ObjectKey{Name: deploy.CheTLSSelfSignedCertificateSecretName, Namespace: checluster.Namespace}, cheCert); err != nil { + if err := r.client.Get(ctx, client.ObjectKey{Name: constants.DefaultSelfSignedCertificateSecretName, Namespace: checluster.Namespace}, cheCert); err != nil { if !errors.IsNotFound(err) { return err } @@ -307,12 +304,12 @@ func (r *CheUserNamespaceReconciler) reconcileSelfSignedCert(ctx context.Context Name: targetCertName, Namespace: targetNs, Labels: defaults.AddStandardLabelsForComponent(checluster, userSettingsComponentLabelValue, map[string]string{ - constants.DevWorkspaceMountLabel: "true", - constants.DevWorkspaceWatchSecretLabel: "true", + dwconstants.DevWorkspaceMountLabel: "true", + dwconstants.DevWorkspaceWatchSecretLabel: "true", }), Annotations: map[string]string{ - constants.DevWorkspaceMountAsAnnotation: "file", - constants.DevWorkspaceMountPathAnnotation: "/tmp/che/secret/", + dwconstants.DevWorkspaceMountAsAnnotation: "file", + dwconstants.DevWorkspaceMountPathAnnotation: "/tmp/che/secret/", }, }, Data: map[string][]byte{ @@ -326,11 +323,10 @@ func (r *CheUserNamespaceReconciler) reconcileSelfSignedCert(ctx context.Context return err } -func (r *CheUserNamespaceReconciler) reconcileTrustedCerts(ctx context.Context, deployContext *deploy.DeployContext, targetNs string, checluster *v2alpha1.CheCluster) error { +func (r *CheUserNamespaceReconciler) reconcileTrustedCerts(ctx context.Context, deployContext *chetypes.DeployContext, targetNs string, checluster *chev2.CheCluster) error { if err := deleteLegacyObject("trusted-ca-certs", &corev1.ConfigMap{}, targetNs, checluster, deployContext); err != nil { return err } - targetConfigMapName := prefixedName("trusted-ca-certs") delConfigMap := func() error { @@ -356,12 +352,12 @@ func (r *CheUserNamespaceReconciler) reconcileTrustedCerts(ctx context.Context, Name: targetConfigMapName, Namespace: targetNs, Labels: defaults.AddStandardLabelsForComponent(checluster, userSettingsComponentLabelValue, map[string]string{ - constants.DevWorkspaceMountLabel: "true", - constants.DevWorkspaceWatchConfigMapLabel: "true", + dwconstants.DevWorkspaceMountLabel: "true", + dwconstants.DevWorkspaceWatchConfigMapLabel: "true", }), Annotations: addToFirst(sourceMap.Annotations, map[string]string{ - constants.DevWorkspaceMountAsAnnotation: "file", - constants.DevWorkspaceMountPathAnnotation: "/public-certs", + dwconstants.DevWorkspaceMountAsAnnotation: "file", + dwconstants.DevWorkspaceMountPathAnnotation: "/public-certs", }), }, Data: sourceMap.Data, @@ -382,11 +378,10 @@ func addToFirst(first map[string]string, second map[string]string) map[string]st return first } -func (r *CheUserNamespaceReconciler) reconcileProxySettings(ctx context.Context, targetNs string, checluster *v2alpha1.CheCluster, deployContext *deploy.DeployContext) error { +func (r *CheUserNamespaceReconciler) reconcileProxySettings(ctx context.Context, targetNs string, checluster *chev2.CheCluster, deployContext *chetypes.DeployContext) error { if err := deleteLegacyObject("proxy-settings", &corev1.ConfigMap{}, targetNs, checluster, deployContext); err != nil { return err } - proxyConfig, err := che.GetProxyConfiguration(deployContext) if err != nil { return err @@ -428,11 +423,11 @@ func (r *CheUserNamespaceReconciler) reconcileProxySettings(ctx context.Context, } requiredLabels := defaults.AddStandardLabelsForComponent(checluster, userSettingsComponentLabelValue, map[string]string{ - constants.DevWorkspaceMountLabel: "true", - constants.DevWorkspaceWatchConfigMapLabel: "true", + dwconstants.DevWorkspaceMountLabel: "true", + dwconstants.DevWorkspaceWatchConfigMapLabel: "true", }) requiredAnnos := map[string]string{ - constants.DevWorkspaceMountAsAnnotation: "env", + dwconstants.DevWorkspaceMountAsAnnotation: "env", } cfg = &corev1.ConfigMap{ @@ -453,25 +448,23 @@ func (r *CheUserNamespaceReconciler) reconcileProxySettings(ctx context.Context, return err } -func (r *CheUserNamespaceReconciler) reconcileGitTlsCertificate(ctx context.Context, targetNs string, checluster *v2alpha1.CheCluster, deployContext *deploy.DeployContext) error { +func (r *CheUserNamespaceReconciler) reconcileGitTlsCertificate(ctx context.Context, targetNs string, checluster *chev2.CheCluster, deployContext *chetypes.DeployContext) error { if err := deleteLegacyObject("git-tls-creds", &corev1.ConfigMap{}, targetNs, checluster, deployContext); err != nil { return err } - targetName := prefixedName("git-tls-creds") delConfigMap := func() error { _, err := deploy.Delete(deployContext, client.ObjectKey{Name: targetName, Namespace: targetNs}, &corev1.ConfigMap{}) return err } - clusterv1 := org.AsV1(checluster) - if !clusterv1.Spec.Server.GitSelfSignedCert { + if checluster.Spec.DevEnvironments.TrustedCerts.GitTrustedCertsConfigMapName == "" { return delConfigMap() } gitCert := &corev1.ConfigMap{} - if err := deployContext.ClusterAPI.Client.Get(ctx, client.ObjectKey{Name: deploy.GitSelfSignedCertsConfigMapName, Namespace: checluster.Namespace}, gitCert); err != nil { + if err := deployContext.ClusterAPI.Client.Get(ctx, client.ObjectKey{Name: checluster.Spec.DevEnvironments.TrustedCerts.GitTrustedCertsConfigMapName, Namespace: checluster.Namespace}, gitCert); err != nil { if !errors.IsNotFound(err) { return err } @@ -487,9 +480,9 @@ func (r *CheUserNamespaceReconciler) reconcileGitTlsCertificate(ctx context.Cont Name: targetName, Namespace: targetNs, Labels: defaults.AddStandardLabelsForComponent(checluster, userSettingsComponentLabelValue, map[string]string{ - constants.DevWorkspaceGitTLSLabel: "true", - constants.DevWorkspaceMountLabel: "true", - constants.DevWorkspaceWatchConfigMapLabel: "true", + dwconstants.DevWorkspaceGitTLSLabel: "true", + dwconstants.DevWorkspaceMountLabel: "true", + dwconstants.DevWorkspaceWatchConfigMapLabel: "true", }), }, Data: map[string]string{ @@ -502,7 +495,7 @@ func (r *CheUserNamespaceReconciler) reconcileGitTlsCertificate(ctx context.Cont return err } -func (r *CheUserNamespaceReconciler) reconcileNodeSelectorAndTolerations(ctx context.Context, targetNs string, checluster *v2alpha1.CheCluster, deployContext *deploy.DeployContext) error { +func (r *CheUserNamespaceReconciler) reconcileNodeSelectorAndTolerations(ctx context.Context, targetNs string, checluster *chev2.CheCluster, deployContext *chetypes.DeployContext) error { var ns client.Object if infrastructure.IsOpenShift() { @@ -518,8 +511,8 @@ func (r *CheUserNamespaceReconciler) reconcileNodeSelectorAndTolerations(ctx con nodeSelector := "" tolerations := "" - if len(checluster.Spec.Workspaces.PodNodeSelector) != 0 { - serialized, err := json.Marshal(checluster.Spec.Workspaces.PodNodeSelector) + if len(checluster.Spec.DevEnvironments.NodeSelector) != 0 { + serialized, err := json.Marshal(checluster.Spec.DevEnvironments.NodeSelector) if err != nil { return err } @@ -527,8 +520,8 @@ func (r *CheUserNamespaceReconciler) reconcileNodeSelectorAndTolerations(ctx con nodeSelector = string(serialized) } - if len(checluster.Spec.Workspaces.PodTolerations) != 0 { - serialized, err := json.Marshal(checluster.Spec.Workspaces.PodTolerations) + if len(checluster.Spec.DevEnvironments.Tolerations) != 0 { + serialized, err := json.Marshal(checluster.Spec.DevEnvironments.Tolerations) if err != nil { return err } @@ -564,7 +557,7 @@ func prefixedName(name string) string { // Deletes object with a legacy name to avoid mounting several ones under the same path // See https://github.com/eclipse/che/issues/21385 -func deleteLegacyObject(name string, objectMeta client.Object, targetNs string, checluster *v2alpha1.CheCluster, deployContext *deploy.DeployContext) error { +func deleteLegacyObject(name string, objectMeta client.Object, targetNs string, checluster *chev2.CheCluster, deployContext *chetypes.DeployContext) error { legacyPrefixedName := checluster.Name + "-" + checluster.Namespace + "-" + name key := client.ObjectKey{Name: legacyPrefixedName, Namespace: targetNs} diff --git a/controllers/usernamespace/controller_test.go b/controllers/usernamespace/controller_test.go index 159b57815..e65da3cd9 100644 --- a/controllers/usernamespace/controller_test.go +++ b/controllers/usernamespace/controller_test.go @@ -15,27 +15,25 @@ package usernamespace import ( "context" "encoding/json" - "os" "sync" "testing" - "github.com/devfile/devworkspace-operator/pkg/constants" + dwconstants "github.com/devfile/devworkspace-operator/pkg/constants" "github.com/devfile/devworkspace-operator/pkg/infrastructure" - v1 "github.com/eclipse-che/che-operator/api/v1" + devworkspaceinfra "github.com/devfile/devworkspace-operator/pkg/infrastructure" + chev2 "github.com/eclipse-che/che-operator/api/v2" "github.com/eclipse-che/che-operator/controllers/devworkspace" - "github.com/eclipse-che/che-operator/pkg/deploy" + "github.com/eclipse-che/che-operator/pkg/common/constants" "github.com/eclipse-che/che-operator/pkg/deploy/tls" - "github.com/eclipse-che/che-operator/pkg/util" configv1 "github.com/openshift/api/config/v1" projectv1 "github.com/openshift/api/project/v1" - routev1 "github.com/openshift/api/route/v1" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" - networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/util/workqueue" + "k8s.io/utils/pointer" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/event" @@ -44,7 +42,7 @@ import ( func setupCheCluster(t *testing.T, ctx context.Context, cl client.Client, scheme *runtime.Scheme, cheNamespaceName string, cheName string) { var cheNamespace metav1.Object - if util.IsOpenShift4 { + if infrastructure.IsOpenShift() { cheNamespace = &projectv1.Project{} } else { cheNamespace = &corev1.Namespace{} @@ -55,20 +53,15 @@ func setupCheCluster(t *testing.T, ctx context.Context, cl client.Client, scheme t.Fatal(err) } - cheCluster := v1.CheCluster{ + cheCluster := chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: cheName, Namespace: cheNamespaceName, }, - Spec: v1.CheClusterSpec{ - Server: v1.CheClusterSpecServer{ - CheHost: "che-host", - GitSelfSignedCert: true, - CustomCheProperties: map[string]string{ - "CHE_INFRA_OPENSHIFT_ROUTE_HOST_DOMAIN__SUFFIX": "root-domain", - }, - WorkspacePodNodeSelector: map[string]string{"a": "b", "c": "d"}, - WorkspacePodTolerations: []corev1.Toleration{ + Spec: chev2.CheClusterSpec{ + DevEnvironments: chev2.CheClusterDevEnvironments{ + NodeSelector: map[string]string{"a": "b", "c": "d"}, + Tolerations: []corev1.Toleration{ { Key: "a", Operator: corev1.TolerationOpEqual, @@ -80,14 +73,17 @@ func setupCheCluster(t *testing.T, ctx context.Context, cl client.Client, scheme Value: "d", }, }, + TrustedCerts: chev2.TrustedCerts{ + GitTrustedCertsConfigMapName: "che-git-self-signed-cert", + }, }, - DevWorkspace: v1.CheClusterSpecDevWorkspace{ - Enable: true, - }, - K8s: v1.CheClusterSpecK8SOnly{ - IngressDomain: "root-domain", + Networking: chev2.CheClusterSpecNetworking{ + Domain: "root-domain", }, }, + Status: chev2.CheClusterStatus{ + CheURL: "https://che-host", + }, } if err := cl.Create(ctx, &cheCluster); err != nil { t.Fatal(err) @@ -96,7 +92,7 @@ func setupCheCluster(t *testing.T, ctx context.Context, cl client.Client, scheme // also create the self-signed-certificate secret to pretend we have TLS set up cert := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: deploy.CheTLSSelfSignedCertificateSecretName, + Name: constants.DefaultSelfSignedCertificateSecretName, Namespace: cheNamespaceName, }, Data: map[string][]byte{ @@ -104,7 +100,7 @@ func setupCheCluster(t *testing.T, ctx context.Context, cl client.Client, scheme "other.data": []byte("should not be copied to target ns"), }, Type: "Opaque", - Immutable: util.NewBoolPointer(true), + Immutable: pointer.BoolPtr(true), } if err := cl.Create(ctx, cert); err != nil { t.Fatal(err) @@ -126,7 +122,7 @@ func setupCheCluster(t *testing.T, ctx context.Context, cl client.Client, scheme gitTlsCredentials := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: deploy.GitSelfSignedCertsConfigMapName, + Name: "che-git-self-signed-cert", Namespace: cheNamespaceName, }, Data: map[string]string{ @@ -138,39 +134,6 @@ func setupCheCluster(t *testing.T, ctx context.Context, cl client.Client, scheme t.Fatal(err) } - // create che route and ingress - ingress := &networkingv1.Ingress{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ingress", - Namespace: cheNamespaceName, - Labels: deploy.GetLabels(&cheCluster, os.Getenv("CHE_FLAVOR")), - }, - Spec: networkingv1.IngressSpec{ - Rules: []networkingv1.IngressRule{ - { - Host: "che-host", - }, - }, - }, - } - if err := cl.Create(ctx, ingress); err != nil { - t.Fatal(err) - } - - route := &routev1.Route{ - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Namespace: cheNamespaceName, - Labels: deploy.GetLabels(&cheCluster, os.Getenv("CHE_FLAVOR")), - }, - Spec: routev1.RouteSpec{ - Host: "che-host", - }, - } - if err := cl.Create(ctx, route); err != nil { - t.Fatal(err) - } - r := devworkspace.New(cl, scheme) // the reconciliation needs to run twice for it to be truly finished - we're setting up finalizers etc... if _, err := r.Reconcile(context.TODO(), reconcile.Request{NamespacedName: types.NamespacedName{Name: cheName, Namespace: cheNamespaceName}}); err != nil { @@ -181,11 +144,10 @@ func setupCheCluster(t *testing.T, ctx context.Context, cl client.Client, scheme } } -func setup(infraType infrastructure.Type, objs ...runtime.Object) (*runtime.Scheme, client.Client, *CheUserNamespaceReconciler) { - infrastructure.InitializeForTesting(infraType) +func setup(infraType devworkspaceinfra.Type, objs ...runtime.Object) (*runtime.Scheme, client.Client, *CheUserNamespaceReconciler) { + devworkspaceinfra.InitializeForTesting(infraType) devworkspace.CleanCheClusterInstancesForTest() - util.IsOpenShift = infraType == infrastructure.OpenShiftv4 - util.IsOpenShift4 = infraType == infrastructure.OpenShiftv4 + infrastructure.InitializeForTesting(infraType) scheme := createTestScheme() @@ -205,7 +167,7 @@ func setup(infraType infrastructure.Type, objs ...runtime.Object) (*runtime.Sche } func TestSkipsUnlabeledNamespaces(t *testing.T) { - test := func(t *testing.T, infraType infrastructure.Type, namespace metav1.Object) { + test := func(t *testing.T, infraType devworkspaceinfra.Type, namespace metav1.Object) { ctx := context.TODO() scheme, cl, r := setup(infraType, namespace.(runtime.Object)) setupCheCluster(t, ctx, cl, scheme, "che", "che") @@ -230,7 +192,7 @@ func TestSkipsUnlabeledNamespaces(t *testing.T) { } t.Run("k8s", func(t *testing.T) { - test(t, infrastructure.Kubernetes, &corev1.Namespace{ + test(t, devworkspaceinfra.Kubernetes, &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "ns", }, @@ -238,7 +200,7 @@ func TestSkipsUnlabeledNamespaces(t *testing.T) { }) t.Run("openshift", func(t *testing.T) { - test(t, infrastructure.OpenShiftv4, &projectv1.Project{ + test(t, devworkspaceinfra.OpenShiftv4, &projectv1.Project{ ObjectMeta: metav1.ObjectMeta{ Name: "prj", }, @@ -247,7 +209,7 @@ func TestSkipsUnlabeledNamespaces(t *testing.T) { } func TestRequiresLabelsToMatchOneOfMultipleCheCluster(t *testing.T) { - test := func(t *testing.T, infraType infrastructure.Type, namespace metav1.Object) { + test := func(t *testing.T, infraType devworkspaceinfra.Type, namespace metav1.Object) { ctx := context.TODO() scheme, cl, r := setup(infraType, namespace.(runtime.Object)) setupCheCluster(t, ctx, cl, scheme, "che1", "che") @@ -260,7 +222,7 @@ func TestRequiresLabelsToMatchOneOfMultipleCheCluster(t *testing.T) { } t.Run("k8s", func(t *testing.T) { - test(t, infrastructure.Kubernetes, &corev1.Namespace{ + test(t, devworkspaceinfra.Kubernetes, &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "ns", Labels: map[string]string{ @@ -271,7 +233,7 @@ func TestRequiresLabelsToMatchOneOfMultipleCheCluster(t *testing.T) { }) t.Run("openshift", func(t *testing.T) { - test(t, infrastructure.OpenShiftv4, &projectv1.Project{ + test(t, devworkspaceinfra.OpenShiftv4, &projectv1.Project{ ObjectMeta: metav1.ObjectMeta{ Name: "prj", Labels: map[string]string{ @@ -283,7 +245,7 @@ func TestRequiresLabelsToMatchOneOfMultipleCheCluster(t *testing.T) { } func TestMatchingCheClusterCanBeSelectedUsingLabels(t *testing.T) { - test := func(t *testing.T, infraType infrastructure.Type, namespace metav1.Object) { + test := func(t *testing.T, infraType devworkspaceinfra.Type, namespace metav1.Object) { ctx := context.TODO() scheme, cl, r := setup(infraType, namespace.(runtime.Object)) setupCheCluster(t, ctx, cl, scheme, "che1", "che") @@ -296,7 +258,7 @@ func TestMatchingCheClusterCanBeSelectedUsingLabels(t *testing.T) { } t.Run("k8s", func(t *testing.T) { - test(t, infrastructure.Kubernetes, &corev1.Namespace{ + test(t, devworkspaceinfra.Kubernetes, &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "ns", Labels: map[string]string{ @@ -309,7 +271,7 @@ func TestMatchingCheClusterCanBeSelectedUsingLabels(t *testing.T) { }) t.Run("openshift", func(t *testing.T) { - test(t, infrastructure.OpenShiftv4, &projectv1.Project{ + test(t, devworkspaceinfra.OpenShiftv4, &projectv1.Project{ ObjectMeta: metav1.ObjectMeta{ Name: "prj", Labels: map[string]string{ @@ -323,6 +285,8 @@ func TestMatchingCheClusterCanBeSelectedUsingLabels(t *testing.T) { } func TestCreatesDataInNamespace(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.Kubernetes) + expectedPodTolerations, err := json.Marshal([]corev1.Toleration{ { Key: "a", @@ -337,7 +301,7 @@ func TestCreatesDataInNamespace(t *testing.T) { }) assert.NoError(t, err) - test := func(t *testing.T, infraType infrastructure.Type, namespace client.Object, objs ...runtime.Object) { + test := func(t *testing.T, infraType devworkspaceinfra.Type, namespace client.Object, objs ...runtime.Object) { ctx := context.TODO() allObjs := append(objs, namespace.(runtime.Object)) scheme, cl, r := setup(infraType, allObjs...) @@ -351,10 +315,10 @@ func TestCreatesDataInNamespace(t *testing.T) { proxySettings := corev1.ConfigMap{} assert.NoError(t, cl.Get(ctx, client.ObjectKey{Name: "che-proxy-settings", Namespace: namespace.GetName()}, &proxySettings)) - assert.Equal(t, "env", proxySettings.GetAnnotations()[constants.DevWorkspaceMountAsAnnotation], + assert.Equal(t, "env", proxySettings.GetAnnotations()[dwconstants.DevWorkspaceMountAsAnnotation], "proxy settings should be annotated as mount as 'env'") - assert.Equal(t, "true", proxySettings.GetLabels()[constants.DevWorkspaceMountLabel], + assert.Equal(t, "true", proxySettings.GetLabels()[dwconstants.DevWorkspaceMountLabel], "proxy settings should be labeled as mounted") assert.Equal(t, 1, len(proxySettings.Data), "Expecting just 1 element in the default proxy settings") @@ -364,9 +328,9 @@ func TestCreatesDataInNamespace(t *testing.T) { cert := corev1.Secret{} assert.NoError(t, cl.Get(ctx, client.ObjectKey{Name: "che-server-cert", Namespace: namespace.GetName()}, &cert)) - assert.Equal(t, "file", cert.GetAnnotations()[constants.DevWorkspaceMountAsAnnotation], "server cert should be annotated as mount as 'file'") - assert.Equal(t, "/tmp/che/secret/", cert.GetAnnotations()[constants.DevWorkspaceMountPathAnnotation], "server cert annotated as mounted to an unexpected path") - assert.Equal(t, "true", cert.GetLabels()[constants.DevWorkspaceMountLabel], "server cert should be labeled as mounted") + assert.Equal(t, "file", cert.GetAnnotations()[dwconstants.DevWorkspaceMountAsAnnotation], "server cert should be annotated as mount as 'file'") + assert.Equal(t, "/tmp/che/secret/", cert.GetAnnotations()[dwconstants.DevWorkspaceMountPathAnnotation], "server cert annotated as mounted to an unexpected path") + assert.Equal(t, "true", cert.GetLabels()[dwconstants.DevWorkspaceMountLabel], "server cert should be labeled as mounted") assert.Equal(t, 1, len(cert.Data), "Expecting just 1 element in the self-signed cert") assert.Equal(t, "my certificate", string(cert.Data["ca.crt"]), "Unexpected self-signed certificate") assert.Equal(t, corev1.SecretTypeOpaque, cert.Type, "Unexpected secret type") @@ -374,18 +338,18 @@ func TestCreatesDataInNamespace(t *testing.T) { caCerts := corev1.ConfigMap{} assert.NoError(t, cl.Get(ctx, client.ObjectKey{Name: "che-trusted-ca-certs", Namespace: namespace.GetName()}, &caCerts)) - assert.Equal(t, "file", caCerts.GetAnnotations()[constants.DevWorkspaceMountAsAnnotation], "trusted certs should be annotated as mount as 'file'") - assert.Equal(t, "/public-certs", caCerts.GetAnnotations()[constants.DevWorkspaceMountPathAnnotation], "trusted certs annotated as mounted to an unexpected path") - assert.Equal(t, "true", caCerts.GetLabels()[constants.DevWorkspaceMountLabel], "trusted certs should be labeled as mounted") + assert.Equal(t, "file", caCerts.GetAnnotations()[dwconstants.DevWorkspaceMountAsAnnotation], "trusted certs should be annotated as mount as 'file'") + assert.Equal(t, "/public-certs", caCerts.GetAnnotations()[dwconstants.DevWorkspaceMountPathAnnotation], "trusted certs annotated as mounted to an unexpected path") + assert.Equal(t, "true", caCerts.GetLabels()[dwconstants.DevWorkspaceMountLabel], "trusted certs should be labeled as mounted") assert.Equal(t, 2, len(caCerts.Data), "Expecting exactly 2 data entries in the trusted cert config map") assert.Equal(t, "trusted cert 1", string(caCerts.Data["trusted1"]), "Unexpected trusted cert 1 value") assert.Equal(t, "trusted cert 2", string(caCerts.Data["trusted2"]), "Unexpected trusted cert 2 value") gitTlsConfig := corev1.ConfigMap{} assert.NoError(t, cl.Get(ctx, client.ObjectKey{Name: "che-git-tls-creds", Namespace: namespace.GetName()}, &gitTlsConfig)) - assert.Equal(t, "true", gitTlsConfig.Labels[constants.DevWorkspaceGitTLSLabel]) - assert.Equal(t, "true", gitTlsConfig.Labels[constants.DevWorkspaceMountLabel]) - assert.Equal(t, "true", gitTlsConfig.Labels[constants.DevWorkspaceWatchConfigMapLabel]) + assert.Equal(t, "true", gitTlsConfig.Labels[dwconstants.DevWorkspaceGitTLSLabel]) + assert.Equal(t, "true", gitTlsConfig.Labels[dwconstants.DevWorkspaceMountLabel]) + assert.Equal(t, "true", gitTlsConfig.Labels[dwconstants.DevWorkspaceWatchConfigMapLabel]) assert.Equal(t, "the.host.of.git", gitTlsConfig.Data["host"]) assert.Equal(t, "the public certificate of the.host.of.git", gitTlsConfig.Data["certificate"]) @@ -396,7 +360,7 @@ func TestCreatesDataInNamespace(t *testing.T) { } t.Run("k8s", func(t *testing.T) { - test(t, infrastructure.Kubernetes, &corev1.Namespace{ + test(t, devworkspaceinfra.Kubernetes, &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "ns", Labels: map[string]string{ @@ -407,7 +371,7 @@ func TestCreatesDataInNamespace(t *testing.T) { }) t.Run("openshift", func(t *testing.T) { - test(t, infrastructure.OpenShiftv4, &projectv1.Project{ + test(t, devworkspaceinfra.OpenShiftv4, &projectv1.Project{ ObjectMeta: metav1.ObjectMeta{ Name: "prj", Labels: map[string]string{ @@ -437,7 +401,7 @@ func TestWatchRulesForSecretsInSameNamespace(t *testing.T) { }, } - _, _, r := setup(infrastructure.Kubernetes, &corev1.Namespace{ + _, _, r := setup(devworkspaceinfra.Kubernetes, &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "ns", Labels: map[string]string{ @@ -469,7 +433,7 @@ func TestWatchRulesForConfigMapsInSameNamespace(t *testing.T) { }, } - _, _, r := setup(infrastructure.Kubernetes, &corev1.Namespace{ + _, _, r := setup(devworkspaceinfra.Kubernetes, &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "ns", Labels: map[string]string{ @@ -499,12 +463,12 @@ func TestWatchRulesForSecretsInOtherNamespaces(t *testing.T) { APIVersion: "v1", }, ObjectMeta: metav1.ObjectMeta{ - Name: deploy.CheTLSSelfSignedCertificateSecretName, + Name: constants.DefaultSelfSignedCertificateSecretName, Namespace: "eclipse-che", }, } - _, _, r := setup(infrastructure.Kubernetes, + _, _, r := setup(devworkspaceinfra.Kubernetes, &corev1.Namespace{ TypeMeta: metav1.TypeMeta{ Kind: "Namespace", @@ -538,7 +502,7 @@ func TestWatchRulesForSecretsInOtherNamespaces(t *testing.T) { Name: "eclipse-che", }, }, - &v1.CheCluster{ + &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "che", Namespace: "eclipse-che", @@ -581,7 +545,7 @@ func TestWatchRulesForConfigMapsInOtherNamespaces(t *testing.T) { }, } - _, _, r := setup(infrastructure.Kubernetes, + _, _, r := setup(devworkspaceinfra.Kubernetes, &corev1.Namespace{ TypeMeta: metav1.TypeMeta{ Kind: "Namespace", @@ -615,7 +579,7 @@ func TestWatchRulesForConfigMapsInOtherNamespaces(t *testing.T) { Name: "eclipse-che", }, }, - &v1.CheCluster{ + &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "che", Namespace: "eclipse-che", diff --git a/controllers/usernamespace/init_test.go b/controllers/usernamespace/init_test.go index 6e20b6c0c..2b7d6c714 100644 --- a/controllers/usernamespace/init_test.go +++ b/controllers/usernamespace/init_test.go @@ -12,12 +12,14 @@ package usernamespace import ( - "github.com/eclipse-che/che-operator/pkg/deploy" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" ) func init() { - err := deploy.InitTestDefaultsFromDeployment("../../config/manager/manager.yaml") - if err != nil { - panic(err) - } + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../../config/manager/manager.yaml") } diff --git a/controllers/usernamespace/namespacecache.go b/controllers/usernamespace/namespacecache.go index 43b7b8b2f..8672656c1 100644 --- a/controllers/usernamespace/namespacecache.go +++ b/controllers/usernamespace/namespacecache.go @@ -16,8 +16,7 @@ import ( "context" "sync" - "github.com/eclipse-che/che-operator/pkg/util" - + "github.com/devfile/devworkspace-operator/pkg/infrastructure" projectv1 "github.com/openshift/api/project/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -95,7 +94,7 @@ func (c *namespaceCache) GetAllKnownNamespaces() []string { func (c *namespaceCache) examineNamespaceUnsafe(ctx context.Context, ns string) (*namespaceInfo, error) { var obj client.Object - if util.IsOpenShift4 { + if infrastructure.IsOpenShift() { obj = &projectv1.Project{} } else { obj = &corev1.Namespace{} diff --git a/controllers/usernamespace/namespacecache_test.go b/controllers/usernamespace/namespacecache_test.go index a304d96bd..22dfc42eb 100644 --- a/controllers/usernamespace/namespacecache_test.go +++ b/controllers/usernamespace/namespacecache_test.go @@ -17,11 +17,9 @@ import ( "sync" "testing" - "github.com/eclipse-che/che-operator/pkg/util" - dwo "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" "github.com/devfile/devworkspace-operator/pkg/infrastructure" - v1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" "github.com/stretchr/testify/assert" projectv1 "github.com/openshift/api/project/v1" @@ -49,7 +47,7 @@ func createTestScheme() *runtime.Scheme { utilruntime.Must(appsv1.AddToScheme(scheme)) utilruntime.Must(rbac.AddToScheme(scheme)) utilruntime.Must(routev1.AddToScheme(scheme)) - utilruntime.Must(v1.AddToScheme(scheme)) + utilruntime.Must(chev2.AddToScheme(scheme)) utilruntime.Must(dwo.AddToScheme(scheme)) utilruntime.Must(projectv1.AddToScheme(scheme)) utilruntime.Must(configv1.AddToScheme(scheme)) @@ -61,8 +59,7 @@ func createTestScheme() *runtime.Scheme { func TestGetNamespaceInfoReadsFromCache(t *testing.T) { test := func(infraType infrastructure.Type, namespace metav1.Object) { - util.IsOpenShift = infraType == infrastructure.OpenShiftv4 - util.IsOpenShift4 = infraType == infrastructure.OpenShiftv4 + infrastructure.InitializeForTesting(infraType) ctx := context.TODO() ns := namespace.GetName() @@ -98,8 +95,7 @@ func TestExamineUpdatesCache(t *testing.T) { nsName := namespace.GetName() cl := fake.NewFakeClientWithScheme(createTestScheme(), namespace.(runtime.Object)) - util.IsOpenShift = infraType == infrastructure.OpenShiftv4 - util.IsOpenShift4 = infraType == infrastructure.OpenShiftv4 + infrastructure.InitializeForTesting(infraType) nsc := namespaceCache{ client: cl, diff --git a/deploy/deployment/kubernetes/combined.yaml b/deploy/deployment/kubernetes/combined.yaml new file mode 100644 index 000000000..4d10b5b60 --- /dev/null +++ b/deploy/deployment/kubernetes/combined.yaml @@ -0,0 +1,2722 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: v1 +kind: Namespace +metadata: + name: eclipse-che +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: eclipse-che/che-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.4.1 + labels: + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: checlusters.org.eclipse.che +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: che-operator-service + namespace: eclipse-che + path: /convert + conversionReviewVersions: + - v1 + - v2 + group: org.eclipse.che + names: + kind: CheCluster + listKind: CheClusterList + plural: checlusters + singular: checluster + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: The `CheCluster` custom resource allows defining and managing a Che server installation + 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: Desired configuration of the Che installation. Based on these settings, the Operator automatically creates and maintains several ConfigMaps that will contain the appropriate environment variables the various components of the Che installation. These generated ConfigMaps must NOT be updated manually. + properties: + auth: + description: Configuration settings related to the Authentication used by the Che installation. + properties: + debug: + description: Deprecated. The value of this flag is ignored. Debug internal identity provider. + type: boolean + externalIdentityProvider: + description: 'Deprecated. The value of this flag is ignored. Instructs the Operator on whether or not to deploy a dedicated Identity Provider (Keycloak or RH SSO instance). Instructs the Operator on whether to deploy a dedicated Identity Provider (Keycloak or RH-SSO instance). By default, a dedicated Identity Provider server is deployed as part of the Che installation. When `externalIdentityProvider` is `true`, no dedicated identity provider will be deployed by the Operator and you will need to provide details about the external identity provider you are about to use. See also all the other fields starting with: `identityProvider`.' + type: boolean + gatewayAuthenticationSidecarImage: + description: Gateway sidecar responsible for authentication when NativeUserMode is enabled. See link:https://github.com/oauth2-proxy/oauth2-proxy[oauth2-proxy] or link:https://github.com/openshift/oauth-proxy[openshift/oauth-proxy]. + type: string + gatewayAuthorizationSidecarImage: + description: Gateway sidecar responsible for authorization when NativeUserMode is enabled. See link:https://github.com/brancz/kube-rbac-proxy[kube-rbac-proxy] or link:https://github.com/openshift/kube-rbac-proxy[openshift/kube-rbac-proxy] + type: string + gatewayHeaderRewriteSidecarImage: + description: Deprecated. The value of this flag is ignored. Sidecar functionality is now implemented in Traefik plugin. + type: string + identityProviderAdminUserName: + description: Deprecated. The value of this flag is ignored. Overrides the name of the Identity Provider administrator user. Defaults to `admin`. + type: string + identityProviderClientId: + description: Deprecated. The value of this flag is ignored. Name of a Identity provider, Keycloak or RH-SSO, `client-id` that is used for Che. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to the value of the `flavour` field suffixed with `-public`. + type: string + identityProviderContainerResources: + description: Deprecated. The value of this flag is ignored. Identity provider container custom settings. + properties: + limits: + description: Limits describes the maximum amount of compute resources allowed. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + request: + description: Requests describes the minimum amount of compute resources required. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + type: object + identityProviderImage: + description: Deprecated. The value of this flag is ignored. Overrides the container image used in the Identity Provider, Keycloak or RH-SSO, deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + identityProviderImagePullPolicy: + description: Deprecated. The value of this flag is ignored. Overrides the image pull policy used in the Identity Provider, Keycloak or RH-SSO, deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + identityProviderIngress: + description: Deprecated. The value of this flag is ignored. Ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + identityProviderPassword: + description: Deprecated. The value of this flag is ignored. Overrides the password of Keycloak administrator user. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to an auto-generated password. + type: string + identityProviderPostgresPassword: + description: Deprecated. The value of this flag is ignored. Password for a Identity Provider, Keycloak or RH-SSO, to connect to the database. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to an auto-generated password. + type: string + identityProviderPostgresSecret: + description: 'Deprecated. The value of this flag is ignored. The secret that contains `password` for the Identity Provider, Keycloak or RH-SSO, to connect to the database. When the secret is defined, the `identityProviderPostgresPassword` is ignored. When the value is omitted or left blank, the one of following scenarios applies: 1. `identityProviderPostgresPassword` is defined, then it will be used to connect to the database. 2. `identityProviderPostgresPassword` is not defined, then a new secret with the name `che-identity-postgres-secret` will be created with an auto-generated value for `password`. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + identityProviderRealm: + description: Deprecated. The value of this flag is ignored. Name of a Identity provider, Keycloak or RH-SSO, realm that is used for Che. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to the value of the `flavour` field. + type: string + identityProviderRoute: + description: Deprecated. The value of this flag is ignored. Route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + identityProviderSecret: + description: 'Deprecated. The value of this flag is ignored. The secret that contains `user` and `password` for Identity Provider. When the secret is defined, the `identityProviderAdminUserName` and `identityProviderPassword` are ignored. When the value is omitted or left blank, the one of following scenarios applies: 1. `identityProviderAdminUserName` and `identityProviderPassword` are defined, then they will be used. 2. `identityProviderAdminUserName` or `identityProviderPassword` are not defined, then a new secret with the name `che-identity-secret` will be created with default value `admin` for `user` and with an auto-generated value for `password`. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + identityProviderURL: + description: Public URL of the Identity Provider server (Keycloak / RH-SSO server). Set this ONLY when a use of an external Identity Provider is needed. See the `externalIdentityProvider` field. By default, this will be automatically calculated and set by the Operator. + type: string + initialOpenShiftOAuthUser: + description: Deprecated. The value of this flag is ignored. For operating with the OpenShift OAuth authentication, create a new user account since the kubeadmin can not be used. If the value is true, then a new OpenShift OAuth user will be created for the HTPasswd identity provider. If the value is false and the user has already been created, then it will be removed. If value is an empty, then do nothing. The user's credentials are stored in the `openshift-oauth-user-credentials` secret in 'openshift-config' namespace by Operator. Note that this solution is Openshift 4 platform-specific. + type: boolean + nativeUserMode: + description: Deprecated. The value of this flag is ignored. Enables native user mode. Currently works only on OpenShift and DevWorkspace engine. Native User mode uses OpenShift OAuth directly as identity provider, without Keycloak. + type: boolean + oAuthClientName: + description: Name of the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OpenShiftoAuth` field. + type: string + oAuthSecret: + description: Name of the secret set in the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OAuthClientName` field. + type: string + openShiftoAuth: + description: 'Deprecated. The value of this flag is ignored. Enables the integration of the identity provider (Keycloak / RHSSO) with OpenShift OAuth. Empty value on OpenShift by default. This will allow users to directly login with their OpenShift user through the OpenShift login, and have their workspaces created under personal OpenShift namespaces. WARNING: the `kubeadmin` user is NOT supported, and logging through it will NOT allow accessing the Che Dashboard.' + type: boolean + updateAdminPassword: + description: Deprecated. The value of this flag is ignored. Forces the default `admin` Che user to update password on first login. Defaults to `false`. + type: boolean + type: object + dashboard: + description: Configuration settings related to the User Dashboard used by the Che installation. + properties: + warning: + description: Warning message that will be displayed on the User Dashboard + type: string + type: object + database: + description: Configuration settings related to the database used by the Che installation. + properties: + chePostgresContainerResources: + description: PostgreSQL container custom settings + properties: + limits: + description: Limits describes the maximum amount of compute resources allowed. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + request: + description: Requests describes the minimum amount of compute resources required. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + type: object + chePostgresDb: + description: PostgreSQL database name that the Che server uses to connect to the DB. Defaults to `dbche`. + type: string + chePostgresHostName: + description: PostgreSQL Database host name that the Che server uses to connect to. Defaults is `postgres`. Override this value ONLY when using an external database. See field `externalDb`. In the default case it will be automatically set by the Operator. + type: string + chePostgresPassword: + description: PostgreSQL password that the Che server uses to connect to the DB. When omitted or left blank, it will be set to an automatically generated value. + type: string + chePostgresPort: + description: PostgreSQL Database port that the Che server uses to connect to. Defaults to 5432. Override this value ONLY when using an external database. See field `externalDb`. In the default case it will be automatically set by the Operator. + type: string + chePostgresSecret: + description: 'The secret that contains PostgreSQL`user` and `password` that the Che server uses to connect to the DB. When the secret is defined, the `chePostgresUser` and `chePostgresPassword` are ignored. When the value is omitted or left blank, the one of following scenarios applies: 1. `chePostgresUser` and `chePostgresPassword` are defined, then they will be used to connect to the DB. 2. `chePostgresUser` or `chePostgresPassword` are not defined, then a new secret with the name `postgres-credentials` will be created with default value of `pgche` for `user` and with an auto-generated value for `password`. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + chePostgresUser: + description: PostgreSQL user that the Che server uses to connect to the DB. Defaults to `pgche`. + type: string + externalDb: + description: 'Instructs the Operator on whether to deploy a dedicated database. By default, a dedicated PostgreSQL database is deployed as part of the Che installation. When `externalDb` is `true`, no dedicated database will be deployed by the Operator and you will need to provide connection details to the external DB you are about to use. See also all the fields starting with: `chePostgres`.' + type: boolean + postgresImage: + description: Overrides the container image used in the PostgreSQL database deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + postgresImagePullPolicy: + description: Overrides the image pull policy used in the PostgreSQL database deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + postgresVersion: + description: 'Indicates a PostgreSQL version image to use. Allowed values are: `9.6` and `13.3`. Migrate your PostgreSQL database to switch from one version to another.' + type: string + pvcClaimSize: + description: Size of the persistent volume claim for database. Defaults to `1Gi`. To update pvc storageclass that provisions it must support resize when Eclipse Che has been already deployed. + type: string + type: object + devWorkspace: + description: DevWorkspace operator configuration + properties: + controllerImage: + description: Overrides the container image used in the DevWorkspace controller deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + enable: + description: Deploys the DevWorkspace Operator in the cluster. Does nothing when a matching version of the Operator is already installed. Fails when a non-matching version of the Operator is already installed. + type: boolean + runningLimit: + description: Maximum number of the running workspaces per user. + type: string + required: + - enable + type: object + imagePuller: + description: Kubernetes Image Puller configuration + properties: + enable: + description: Install and configure the Community Supported Kubernetes Image Puller Operator. When set to `true` and no spec is provided, it will create a default KubernetesImagePuller object to be managed by the Operator. When set to `false`, the KubernetesImagePuller object will be deleted, and the Operator will be uninstalled, regardless of whether a spec is provided. If the `spec.images` field is empty, a set of recommended workspace-related images will be automatically detected and pre-pulled after installation. Note that while this Operator and its behavior is community-supported, its payload may be commercially-supported for pulling commercially-supported images. + type: boolean + spec: + description: A KubernetesImagePullerSpec to configure the image puller in the CheCluster + properties: + affinity: + type: string + cachingCPULimit: + type: string + cachingCPURequest: + type: string + cachingIntervalHours: + type: string + cachingMemoryLimit: + type: string + cachingMemoryRequest: + type: string + configMapName: + type: string + daemonsetName: + type: string + deploymentName: + type: string + imagePullSecrets: + type: string + imagePullerImage: + type: string + images: + type: string + nodeSelector: + type: string + type: object + required: + - enable + type: object + k8s: + description: Configuration settings specific to Che installations made on upstream Kubernetes. + properties: + ingressClass: + description: 'Ingress class that will define the which controller will manage ingresses. Defaults to `nginx`. NB: This drives the `kubernetes.io/ingress.class` annotation on Che-related ingresses.' + type: string + ingressDomain: + description: 'Global ingress domain for a Kubernetes cluster. This MUST be explicitly specified: there are no defaults.' + type: string + ingressStrategy: + description: 'Deprecated. The value of this flag is ignored. Strategy for ingress creation. Options are: `multi-host` (host is explicitly provided in ingress), `single-host` (host is provided, path-based rules) and `default-host` (no host is provided, path-based rules). Defaults to `multi-host` Deprecated in favor of `serverExposureStrategy` in the `server` section, which defines this regardless of the cluster type. When both are defined, the `serverExposureStrategy` option takes precedence.' + type: string + securityContextFsGroup: + description: The FSGroup in which the Che Pod and workspace Pods containers runs in. Default value is `1724`. + type: string + securityContextRunAsUser: + description: ID of the user the Che Pod and workspace Pods containers run as. Default value is `1724`. + type: string + singleHostExposureType: + description: Deprecated. The value of this flag is ignored. When the serverExposureStrategy is set to `single-host`, the way the server, registries and workspaces are exposed is further configured by this property. The possible values are `native`, which means that the server and workspaces are exposed using ingresses on K8s or `gateway` where the server and workspaces are exposed using a custom gateway based on link:https://doc.traefik.io/traefik/[Traefik]. All the endpoints whether backed by the ingress or gateway `route` always point to the subpaths on the same domain. Defaults to `native`. + type: string + tlsSecretName: + description: Name of a secret that will be used to setup ingress TLS termination when TLS is enabled. When the field is empty string, the default cluster certificate will be used. See also the `tlsSupport` field. + type: string + type: object + metrics: + description: Configuration settings related to the metrics collection used by the Che installation. + properties: + enable: + description: Enables `metrics` the Che server endpoint. Default to `true`. + type: boolean + type: object + server: + description: General configuration settings related to the Che server, the plugin and devfile registries + properties: + airGapContainerRegistryHostname: + description: Optional host name, or URL, to an alternate container registry to pull images from. This value overrides the container registry host name defined in all the default container images involved in a Che deployment. This is particularly useful to install Che in a restricted environment. + type: string + airGapContainerRegistryOrganization: + description: Optional repository name of an alternate container registry to pull images from. This value overrides the container registry organization defined in all the default container images involved in a Che deployment. This is particularly useful to install Eclipse Che in a restricted environment. + type: string + allowUserDefinedWorkspaceNamespaces: + description: Deprecated. The value of this flag is ignored. Defines that a user is allowed to specify a Kubernetes namespace, or an OpenShift project, which differs from the default. It's NOT RECOMMENDED to set to `true` without OpenShift OAuth configured. The OpenShift infrastructure also uses this property. + type: boolean + cheClusterRoles: + description: A comma-separated list of ClusterRoles that will be assigned to Che ServiceAccount. Each role must have `app.kubernetes.io/part-of=che.eclipse.org` label. Be aware that the Che Operator has to already have all permissions in these ClusterRoles to grant them. + type: string + cheDebug: + description: Enables the debug mode for Che server. Defaults to `false`. + type: string + cheFlavor: + description: Deprecated. The value of this flag is ignored. Specifies a variation of the installation. The options are `che` for upstream Che installations or `devspaces` for Red Hat OpenShift Dev Spaces (formerly Red Hat CodeReady Workspaces) installation + type: string + cheHost: + description: Public host name of the installed Che server. When value is omitted, the value it will be automatically set by the Operator. See the `cheHostTLSSecret` field. + type: string + cheHostTLSSecret: + description: Name of a secret containing certificates to secure ingress or route for the custom host name of the installed Che server. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label. See the `cheHost` field. + type: string + cheImage: + description: Overrides the container image used in Che deployment. This does NOT include the container image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + cheImagePullPolicy: + description: Overrides the image pull policy used in Che deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + cheImageTag: + description: Overrides the tag of the container image used in Che deployment. Omit it or leave it empty to use the default image tag provided by the Operator. + type: string + cheLogLevel: + description: 'Log level for the Che server: `INFO` or `DEBUG`. Defaults to `INFO`.' + type: string + cheServerIngress: + description: The Che server ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + cheServerRoute: + description: The Che server route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + cheWorkspaceClusterRole: + description: Custom cluster role bound to the user for the Che workspaces. The role must have `app.kubernetes.io/part-of=che.eclipse.org` label. The default roles are used when omitted or left blank. + type: string + customCheProperties: + additionalProperties: + type: string + description: Map of additional environment variables that will be applied in the generated `che` ConfigMap to be used by the Che server, in addition to the values already generated from other fields of the `CheCluster` custom resource (CR). When `customCheProperties` contains a property that would be normally generated in `che` ConfigMap from other CR fields, the value defined in the `customCheProperties` is used instead. + type: object + dashboardCpuLimit: + description: Overrides the CPU limit used in the dashboard deployment. In cores. (500m = .5 cores). Default to 500m. + type: string + dashboardCpuRequest: + description: Overrides the CPU request used in the dashboard deployment. In cores. (500m = .5 cores). Default to 100m. + type: string + dashboardImage: + description: Overrides the container image used in the dashboard deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + dashboardImagePullPolicy: + description: Overrides the image pull policy used in the dashboard deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + dashboardIngress: + description: Deprecated. The value of this flag is ignored. Dashboard ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + dashboardMemoryLimit: + description: Overrides the memory limit used in the dashboard deployment. Defaults to 256Mi. + type: string + dashboardMemoryRequest: + description: Overrides the memory request used in the dashboard deployment. Defaults to 16Mi. + type: string + dashboardRoute: + description: Deprecated. The value of this flag is ignored. Dashboard route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + devfileRegistryCpuLimit: + description: Overrides the CPU limit used in the devfile registry deployment. In cores. (500m = .5 cores). Default to 500m. + type: string + devfileRegistryCpuRequest: + description: Overrides the CPU request used in the devfile registry deployment. In cores. (500m = .5 cores). Default to 100m. + type: string + devfileRegistryImage: + description: Overrides the container image used in the devfile registry deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + devfileRegistryIngress: + description: Deprecated. The value of this flag is ignored. The devfile registry ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + devfileRegistryMemoryLimit: + description: Overrides the memory limit used in the devfile registry deployment. Defaults to 256Mi. + type: string + devfileRegistryMemoryRequest: + description: Overrides the memory request used in the devfile registry deployment. Defaults to 16Mi. + type: string + devfileRegistryPullPolicy: + description: Overrides the image pull policy used in the devfile registry deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + devfileRegistryRoute: + description: Deprecated. The value of this flag is ignored. The devfile registry route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + devfileRegistryUrl: + description: Deprecated in favor of `externalDevfileRegistries` fields. + type: string + disableInternalClusterSVCNames: + description: Deprecated. The value of this flag is ignored. Disable internal cluster SVC names usage to communicate between components to speed up the traffic and avoid proxy issues. + type: boolean + externalDevfileRegistries: + description: External devfile registries, that serves sample, ready-to-use devfiles. Configure this in addition to a dedicated devfile registry (when `externalDevfileRegistry` is `false`) or instead of it (when `externalDevfileRegistry` is `true`) + items: + description: Settings for a configuration of the external devfile registries. + properties: + url: + description: Public URL of the devfile registry. + type: string + type: object + type: array + externalDevfileRegistry: + description: Instructs the Operator on whether to deploy a dedicated devfile registry server. By default, a dedicated devfile registry server is started. When `externalDevfileRegistry` is `true`, no such dedicated server will be started by the Operator and configure at least one devfile registry with `externalDevfileRegistries` field. + type: boolean + externalPluginRegistry: + description: Instructs the Operator on whether to deploy a dedicated plugin registry server. By default, a dedicated plugin registry server is started. When `externalPluginRegistry` is `true`, no such dedicated server will be started by the Operator and you will have to manually set the `pluginRegistryUrl` field. + type: boolean + gitSelfSignedCert: + description: When enabled, the certificate from `che-git-self-signed-cert` ConfigMap will be propagated to the Che components and provide particular configuration for Git. Note, the `che-git-self-signed-cert` ConfigMap must have `app.kubernetes.io/part-of=che.eclipse.org` label. + type: boolean + nonProxyHosts: + description: 'List of hosts that will be reached directly, bypassing the proxy. Specify wild card domain use the following form `.` and `|` as delimiter, for example: `localhost|.my.host.com|123.42.12.32` Only use when configuring a proxy is required. Operator respects OpenShift cluster wide proxy configuration and no additional configuration is required, but defining `nonProxyHosts` in a custom resource leads to merging non proxy hosts lists from the cluster proxy configuration and ones defined in the custom resources. See the doc https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyURL` fields.' + type: string + pluginRegistryCpuLimit: + description: Overrides the CPU limit used in the plugin registry deployment. In cores. (500m = .5 cores). Default to 500m. + type: string + pluginRegistryCpuRequest: + description: Overrides the CPU request used in the plugin registry deployment. In cores. (500m = .5 cores). Default to 100m. + type: string + pluginRegistryImage: + description: Overrides the container image used in the plugin registry deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + pluginRegistryIngress: + description: Deprecated. The value of this flag is ignored. Plugin registry ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + pluginRegistryMemoryLimit: + description: Overrides the memory limit used in the plugin registry deployment. Defaults to 256Mi. + type: string + pluginRegistryMemoryRequest: + description: Overrides the memory request used in the plugin registry deployment. Defaults to 16Mi. + type: string + pluginRegistryPullPolicy: + description: Overrides the image pull policy used in the plugin registry deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + pluginRegistryRoute: + description: Deprecated. The value of this flag is ignored. Plugin registry route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + pluginRegistryUrl: + description: Public URL of the plugin registry that serves sample ready-to-use devfiles. Set this ONLY when a use of an external devfile registry is needed. See the `externalPluginRegistry` field. By default, this will be automatically calculated by the Operator. + type: string + proxyPassword: + description: Password of the proxy server. Only use when proxy configuration is required. See the `proxyURL`, `proxyUser` and `proxySecret` fields. + type: string + proxyPort: + description: Port of the proxy server. Only use when configuring a proxy is required. See also the `proxyURL` and `nonProxyHosts` fields. + type: string + proxySecret: + description: The secret that contains `user` and `password` for a proxy server. When the secret is defined, the `proxyUser` and `proxyPassword` are ignored. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + proxyURL: + description: URL (protocol+host name) of the proxy server. This drives the appropriate changes in the `JAVA_OPTS` and `https(s)_proxy` variables in the Che server and workspaces containers. Only use when configuring a proxy is required. Operator respects OpenShift cluster wide proxy configuration and no additional configuration is required, but defining `proxyUrl` in a custom resource leads to overrides the cluster proxy configuration with fields `proxyUrl`, `proxyPort`, `proxyUser` and `proxyPassword` from the custom resource. See the doc https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyPort` and `nonProxyHosts` fields. + type: string + proxyUser: + description: User name of the proxy server. Only use when configuring a proxy is required. See also the `proxyURL`, `proxyPassword` and `proxySecret` fields. + type: string + selfSignedCert: + description: Deprecated. The value of this flag is ignored. The Che Operator will automatically detect whether the router certificate is self-signed and propagate it to other components, such as the Che server. + type: boolean + serverCpuLimit: + description: Overrides the CPU limit used in the Che server deployment In cores. (500m = .5 cores). Default to 1. + type: string + serverCpuRequest: + description: Overrides the CPU request used in the Che server deployment In cores. (500m = .5 cores). Default to 100m. + type: string + serverExposureStrategy: + description: Deprecated. The value of this flag is ignored. Sets the server and workspaces exposure type. Possible values are `multi-host`, `single-host`, `default-host`. Defaults to `multi-host`, which creates a separate ingress, or OpenShift routes, for every required endpoint. `single-host` makes Che exposed on a single host name with workspaces exposed on subpaths. Read the docs to learn about the limitations of this approach. Also consult the `singleHostExposureType` property to further configure how the Operator and the Che server make that happen on Kubernetes. `default-host` exposes the Che server on the host of the cluster. Read the docs to learn about the limitations of this approach. + type: string + serverMemoryLimit: + description: Overrides the memory limit used in the Che server deployment. Defaults to 1Gi. + type: string + serverMemoryRequest: + description: Overrides the memory request used in the Che server deployment. Defaults to 512Mi. + type: string + serverTrustStoreConfigMapName: + description: Name of the ConfigMap with public certificates to add to Java trust store of the Che server. This is often required when adding the OpenShift OAuth provider, which has HTTPS endpoint signed with self-signed cert. The Che server must be aware of its CA cert to be able to request it. This is disabled by default. The Config Map must have `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + singleHostGatewayConfigMapLabels: + additionalProperties: + type: string + description: The labels that need to be present in the ConfigMaps representing the gateway configuration. + type: object + singleHostGatewayConfigSidecarImage: + description: The image used for the gateway sidecar that provides configuration to the gateway. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + singleHostGatewayImage: + description: The image used for the gateway in the single host mode. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + tlsSupport: + description: Deprecated. Instructs the Operator to deploy Che in TLS mode. This is enabled by default. Disabling TLS sometimes cause malfunction of some Che components. + type: boolean + useInternalClusterSVCNames: + description: Deprecated in favor of `disableInternalClusterSVCNames`. + type: boolean + workspaceNamespaceDefault: + description: Defines Kubernetes default namespace in which user's workspaces are created for a case when a user does not override it. It's possible to use ``, `` and `` placeholders, such as che-workspace-. In that case, a new namespace will be created for each user or workspace. + type: string + workspacePodNodeSelector: + additionalProperties: + type: string + description: The node selector that limits the nodes that can run the workspace pods. + type: object + workspacePodTolerations: + description: The pod tolerations put on the workspace pods to limit where the workspace pods can run. + items: + description: The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + workspacesDefaultPlugins: + description: Default plug-ins applied to Devworkspaces. + items: + properties: + editor: + description: The editor id to specify default plug-ins for. + type: string + plugins: + description: Default plug-in uris for the specified editor. + items: + type: string + type: array + type: object + type: array + type: object + storage: + description: Configuration settings related to the persistent storage used by the Che installation. + properties: + postgresPVCStorageClassName: + description: Storage class for the Persistent Volume Claim dedicated to the PostgreSQL database. When omitted or left blank, a default storage class is used. + type: string + preCreateSubPaths: + description: Instructs the Che server to start a special Pod to pre-create a sub-path in the Persistent Volumes. Defaults to `false`, however it will need to enable it according to the configuration of your Kubernetes cluster. + type: boolean + pvcClaimSize: + description: Size of the persistent volume claim for workspaces. Defaults to `10Gi`. + type: string + pvcJobsImage: + description: Overrides the container image used to create sub-paths in the Persistent Volumes. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. See also the `preCreateSubPaths` field. + type: string + pvcStrategy: + description: Persistent volume claim strategy for the Che server. This Can be:`common` (all workspaces PVCs in one volume), `per-workspace` (one PVC per workspace for all declared volumes) and `unique` (one PVC per declared volume). Defaults to `common`. + type: string + workspacePVCStorageClassName: + description: Storage class for the Persistent Volume Claims dedicated to the Che workspaces. When omitted or left blank, a default storage class is used. + type: string + type: object + type: object + status: + description: CheClusterStatus defines the observed state of Che installation + properties: + cheClusterRunning: + description: Status of a Che installation. Can be `Available`, `Unavailable`, or `Available, Rolling Update in Progress`. + type: string + cheURL: + description: Public URL to the Che server. + type: string + cheVersion: + description: Current installed Che version. + type: string + dbProvisioned: + description: Indicates that a PostgreSQL instance has been correctly provisioned or not. + type: boolean + devfileRegistryURL: + description: Public URL to the devfile registry. + type: string + devworkspaceStatus: + description: The status of the Devworkspace subsystem + properties: + gatewayHost: + description: GatewayHost is the resolved host of the ingress/route. This is equal to the Host in the spec on Kubernetes but contains the actual host name of the route if Host is unspecified on OpenShift. + type: string + gatewayPhase: + description: GatewayPhase specifies the phase in which the gateway deployment currently is. If the gateway is disabled, the phase is "Inactive". + type: string + message: + description: Message contains further human-readable info for why the Che cluster is in the phase it currently is. + type: string + phase: + description: Phase is the phase in which the Che cluster as a whole finds itself in. + type: string + reason: + description: A brief CamelCase message indicating details about why the Che cluster is in this state. + type: string + workspaceBaseDomain: + description: The resolved workspace base domain. This is either the copy of the explicitly defined property of the same name in the spec or, if it is undefined in the spec and we're running on OpenShift, the automatically resolved basedomain for routes. + type: string + type: object + gitHubOAuthProvisioned: + description: Indicates whether an Identity Provider instance, Keycloak or RH-SSO, has been configured to integrate with the GitHub OAuth. + type: boolean + gitServerTLSCertificateConfigMapName: + description: The ConfigMap containing certificates to propagate to the Che components and to provide particular configuration for Git. + type: string + helpLink: + description: A URL that points to some URL where to find help related to the current Operator status. + type: string + keycloakProvisioned: + description: Indicates whether an Identity Provider instance, Keycloak or RH-SSO, has been provisioned with realm, client and user. + type: boolean + keycloakURL: + description: Public URL to the Identity Provider server, Keycloak or RH-SSO,. + type: string + message: + description: A human readable message indicating details about why the Pod is in this condition. + type: string + openShiftOAuthUserCredentialsSecret: + description: OpenShift OAuth secret in `openshift-config` namespace that contains user credentials for HTPasswd identity provider. + type: string + openShiftoAuthProvisioned: + description: Indicates whether an Identity Provider instance, Keycloak or RH-SSO, has been configured to integrate with the OpenShift OAuth. + type: boolean + pluginRegistryURL: + description: Public URL to the plugin registry. + type: string + reason: + description: A brief CamelCase message indicating details about why the Pod is in this state. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - name: v2 + schema: + openAPIV3Schema: + description: 'The `CheCluster` custom resource allows defining and managing Eclipse Che server installation. Based on these settings, the Operator automatically creates and maintains several ConfigMaps: `che`, `plugin-registry`, `devfile-registry` that will contain the appropriate environment variables of the various components of the installation. These generated ConfigMaps must NOT be updated manually.' + 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: Desired configuration of Eclipse Che installation. + properties: + components: + description: Che components configuration. + properties: + cheServer: + description: General configuration settings related to the Che server. + properties: + clusterRoles: + description: 'ClusterRoles assigned to Che ServiceAccount. The defaults roles are: - `-cheworkspaces-namespaces-clusterrole` - `-cheworkspaces-clusterrole` - `-cheworkspaces-devworkspace-clusterrole` where the is the namespace where the CheCluster CRD is created. Each role must have a `app.kubernetes.io/part-of=che.eclipse.org` label. The Che Operator must already have all permissions in these ClusterRoles to grant them.' + items: + type: string + type: array + debug: + description: Enables the debug mode for Che server. + type: boolean + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + extraProperties: + additionalProperties: + type: string + description: A map of additional environment variables applied in the generated `che` ConfigMap to be used by the Che server, in addition to the values already generated from other fields of the `CheCluster` custom resource (CR). If the `extraProperties` fields contains a property normally generated in `che` ConfigMap from other CR fields, the value defined in the `extraProperties` is used instead. + type: object + logLevel: + default: INFO + description: 'The log level for the Che server: `INFO` or `DEBUG`.' + type: string + proxy: + description: Proxy server settings for Kubernetes cluster. No additional configuration is required for OpenShift cluster. By specifying these settings for the OpenShift cluster, you override the OpenShift proxy configuration. + properties: + credentialsSecretName: + description: The secret name that contains `user` and `password` for a proxy server. The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + nonProxyHosts: + description: 'A list of hosts that can be reached directly, bypassing the proxy. Specify wild card domain use the following form `.`, for example: - localhost - my.host.com - 123.42.12.32 Use only when a proxy configuration is required. Operator respects OpenShift cluster-wide proxy configuration, defining `nonProxyHosts` in a custom resource leads to merging non-proxy hosts lists from the cluster proxy configuration, and the ones defined in the custom resources. See the following page: https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyURL` fields.' + items: + type: string + type: array + port: + description: Proxy server port. + type: string + url: + description: 'URL (protocol+hostname) of the proxy server. Use only when a proxy configuration is required. Operator respects OpenShift cluster-wide proxy configuration, defining `url` in a custom resource leads to overriding the cluster proxy configuration. See the following page: https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyPort` and `nonProxyHosts` fields.' + type: string + type: object + type: object + dashboard: + description: Configuration settings related to the Dashboard used by the Che installation. + properties: + HeaderMessage: + description: Dashboard header message. + properties: + show: + description: Instructs dashboard to show the message. + type: boolean + warning: + description: Warning message displayed on the user dashboard. + type: string + type: object + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + type: object + database: + description: Configuration settings related to the database used by the Che installation. + properties: + credentialsSecretName: + default: postgres-credentials + description: The secret that contains PostgreSQL `user` and `password` that the Che server uses to connect to the DB. The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + externalDb: + description: Instructs the Operator to deploy a dedicated database. By default, a dedicated PostgreSQL database is deployed as part of the Che installation. When `externalDb` is set as `true`, no dedicated database is deployed by the Operator and you need to provide connection details to the external DB you want to use. + type: boolean + postgresDb: + default: dbche + description: PostgreSQL database name that the Che server uses to connect to the DB. + type: string + postgresHostName: + default: postgres + description: PostgreSQL database hostname that the Che server connects to. Override this value only when using an external database. See field `externalDb`. + type: string + postgresPort: + default: "5432" + description: PostgreSQL Database port the Che server connects to. Override this value only when using an external database. See field `externalDb`. + type: string + pvc: + description: PVC settings for PostgreSQL database. + properties: + claimSize: + description: Persistent Volume Claim size. To update the claim size, Storage class that provisions it must support resize. + type: string + storageClass: + description: Storage class for the Persistent Volume Claim. When omitted or left blank, a default storage class is used. + type: string + type: object + type: object + devWorkspace: + description: DevWorkspace operator configuration. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + runningLimit: + description: The maximum number of running workspaces per user. + type: string + type: object + devfileRegistry: + description: Configuration settings related to the Devfile registry used by the Che installation. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + disableInternalRegistry: + description: Disables internal devfile registry. + type: boolean + externalDevfileRegistries: + description: External devfile registries serving sample ready-to-use devfiles. + items: + description: External devfile registries configuration. + properties: + url: + description: The public UR of the devfile registry that serves sample ready-to-use devfiles. + type: string + type: object + type: array + type: object + imagePuller: + description: Kubernetes Image Puller configuration. + properties: + enable: + description: Install and configure the community supported Kubernetes Image Puller Operator. When you set the value to `true` without providing any specs, it creates a default Kubernetes Image Puller object managed by the Operator. When you set the value to `false`, the Kubernetes Image Puller object is deleted, and the Operator uninstalled, regardless of whether a spec is provided. If you leave the `spec.images` field empty, a set of recommended workspace-related images is automatically detected and pre-pulled after installation. Note that while this Operator and its behavior is community-supported, its payload may be commercially-supported for pulling commercially-supported images. + type: boolean + spec: + description: A Kubernetes Image Puller spec to configure the image puller in the CheCluster + properties: + affinity: + type: string + cachingCPULimit: + type: string + cachingCPURequest: + type: string + cachingIntervalHours: + type: string + cachingMemoryLimit: + type: string + cachingMemoryRequest: + type: string + configMapName: + type: string + daemonsetName: + type: string + deploymentName: + type: string + imagePullSecrets: + type: string + imagePullerImage: + type: string + images: + type: string + nodeSelector: + type: string + type: object + required: + - enable + type: object + metrics: + description: Che server metrics configuration. + properties: + enable: + default: true + description: Enables `metrics` for the Che server endpoint. + type: boolean + type: object + pluginRegistry: + description: Configuration settings related to the plug-in registry used by the Che installation. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + disableInternalRegistry: + description: Disables internal plug-in registry. + type: boolean + externalPluginRegistries: + description: External plugin registries. + items: + description: External plug-in registries configuration. + properties: + url: + description: Public URL of the plug-in registry. + type: string + type: object + type: array + type: object + type: object + containerRegistry: + description: Configuration of an alternative registry that stores Che images. + properties: + hostname: + description: An optional hostname or URL of an alternative container registry to pull images from. This value overrides the container registry hostname defined in all the default container images involved in a Che deployment. This is particularly useful for installing Che in a restricted environment. + type: string + organization: + description: An optional repository name of an alternative registry to pull images from. This value overrides the container registry organization defined in all the default container images involved in a Che deployment. This is particularly useful for installing Eclipse Che in a restricted environment. + type: string + type: object + devEnvironments: + description: Development environment default configuration options. + properties: + defaultNamespace: + description: User's default namespace. + properties: + template: + description: If you don't create user namespaces in advance, this field defines the Kubernetes namespace created when you start your first workspace. You can use ``, `` and `` placeholders, such as che-workspace-. + type: string + type: object + defaultPlugins: + description: Default plug-ins applied to Dev Workspaces. + items: + properties: + editor: + description: The editor id to specify default plug-ins for. + type: string + plugins: + description: Default plug-in URIs for the specified editor. + items: + type: string + type: array + type: object + type: array + nodeSelector: + additionalProperties: + type: string + description: The node selector limits the nodes that can run the workspace pods. + type: object + storage: + description: Workspaces persistent storage. + properties: + pvc: + description: PVC settings. + properties: + claimSize: + description: Persistent Volume Claim size. To update the claim size, Storage class that provisions it must support resize. + type: string + storageClass: + description: Storage class for the Persistent Volume Claim. When omitted or left blank, a default storage class is used. + type: string + type: object + pvcStrategy: + default: common + description: Persistent volume claim strategy for the Che server. Only the `common` strategy is supported (all workspaces PVCs in one volume). For details, see https://github.com/eclipse/che/issues/21185. + type: string + type: object + tolerations: + description: The pod tolerations of the workspace pods limit where the workspace pods can run. + items: + description: The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + trustedCerts: + description: Trusted certificate settings. + properties: + gitTrustedCertsConfigMapName: + description: 'The ConfigMap contains certificates to propagate to the Che components and to provide a particular configuration for Git. See the following page: https://www.eclipse.org/che/docs/stable/administration-guide/deploying-che-with-support-for-git-repositories-with-self-signed-certificates/ The ConfigMap must have a `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + type: object + type: object + networking: + description: Networking, Che authentication and TLS configuration. + properties: + annotations: + additionalProperties: + type: string + description: 'Defines annotations which will be set to an ingress (a route for OpenShift platform). The defaults for kubernetes platforms are: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/proxy-read-timeout: "3600", nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600", nginx.ingress.kubernetes.io/ssl-redirect: "true"' + type: object + auth: + description: Authentication settings. + properties: + gateway: + description: Gateway settings. + properties: + configLabels: + additionalProperties: + type: string + description: Gate configuration labels. + type: object + deployment: + description: 'Deployment override options. Since gateway deployment consists of several containers, they must be distinguished in the configuration by their names: - `gateway` - `configbump` - `oauth-proxy` - `kube-rbac-proxy`' + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + type: object + identityProviderURL: + description: Public URL of the Identity Provider server. + type: string + oAuthClientName: + description: Name of the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side. + type: string + oAuthSecret: + description: Name of the secret set in the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side. + type: string + type: object + domain: + description: 'For an OpenShift cluster, the Operator uses the domain to generate a hostname for a route. The generated hostname follows this pattern: che-.. The is the namespace where the CheCluster CRD is created. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. For Kubernetes cluster it contains a global ingress domain. This MUST be explicitly specified: there are no defaults.' + type: string + hostname: + description: The public hostname of the installed Che server. + type: string + labels: + additionalProperties: + type: string + description: Defines labels which will be set to an ingress (a route for OpenShift platform). + type: object + tlsSecretName: + description: The name of the secret used to set up Ingress TLS termination. If the field is an empty string, the default cluster certificate is used. The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + type: object + type: object + status: + description: CheClusterStatus defines the observed state of Che installation. + properties: + chePhase: + description: Specifies the current phase of the Che deployment. + type: string + cheURL: + description: Public URL to the Che server. + type: string + cheVersion: + description: Current installed Che version. + type: string + devfileRegistryURL: + description: The public URL to the internal devfile registry. + type: string + gatewayPhase: + description: Specifies the current phase of the gateway deployment. + type: string + message: + description: A human readable message indicating details about why the Che deployment is in current phase. + type: string + pluginRegistryURL: + description: The public URL to the internal plug-in registry. + type: string + postgresVersion: + description: The PostgreSQL version of the image in use. + type: string + reason: + description: A brief CamelCase message indicating details about why the Che deployment is in current phase. + type: string + workspaceBaseDomain: + description: The resolved workspace base domain. This is either the copy of the explicitly defined property of the same name in the spec or, if it is undefined in the spec and we're running on OpenShift, the automatically resolved basedomain for routes. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator + namespace: eclipse-che +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator +rules: +- apiGroups: + - extensions + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - batch + resources: + - jobs + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes + - routes/custom-host + verbs: + - '*' +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + - rolebindings + verbs: + - '*' +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterroles + - clusterrolebindings + verbs: + - '*' +- apiGroups: + - "" + resources: + - pods + - services + - serviceaccounts + - endpoints + - persistentvolumeclaims + - events + - configmaps + - secrets + - pods/exec + - pods/log + verbs: + - '*' +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get +- apiGroups: + - apps + - extensions + resources: + - deployments + - replicasets + verbs: + - '*' +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - get + - create +- apiGroups: + - org.eclipse.che + resources: + - checlusters + - checlusters/status + - checlusters/finalizers + verbs: + - '*' +- apiGroups: + - metrics.k8s.io + resources: + - pods + - nodes + verbs: + - get + - list + - watch +- apiGroups: + - operators.coreos.com + resources: + - subscriptions + - clusterserviceversions + - operatorgroups + verbs: + - '*' +- apiGroups: + - packages.operators.coreos.com + resources: + - packagemanifests + verbs: + - get + - list +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create +- apiGroups: + - apps + resourceNames: + - che-operator + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/finalizers + verbs: + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/status + verbs: + - get + - patch + - update +- apiGroups: + - oauth.openshift.io + resources: + - oauthclients + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator +rules: +- apiGroups: + - "" + resources: + - nodes + verbs: + - get +- apiGroups: + - oauth.openshift.io + resources: + - oauthclients + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - config.openshift.io + resources: + - oauths + verbs: + - get + - list + - watch + - patch +- apiGroups: + - config.openshift.io + resources: + - infrastructures + - proxies + verbs: + - get + - list + - watch +- apiGroups: + - user.openshift.io + resources: + - users + verbs: + - list + - delete +- apiGroups: + - user.openshift.io + resources: + - identities + verbs: + - delete +- apiGroups: + - console.openshift.io + resources: + - consolelinks + verbs: + - get + - list + - create + - update + - patch + - delete +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + verbs: + - list + - create + - watch + - update + - get + - delete +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + - rolebindings + verbs: + - get + - create + - update + - delete +- apiGroups: + - authorization.openshift.io + resources: + - roles + - rolebindings + verbs: + - get + - create + - update + - delete +- apiGroups: + - org.eclipse.che + resources: + - checlusters + - checlusters/status + - checlusters/finalizers + - checlusters/status + verbs: + - '*' +- apiGroups: + - project.openshift.io + resources: + - projectrequests + verbs: + - create + - update +- apiGroups: + - project.openshift.io + resources: + - projects + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - create + - update + - watch +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create +- apiGroups: + - apps + resources: + - secrets + verbs: + - list +- apiGroups: + - "" + resources: + - secrets + verbs: + - list + - get + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - create + - get + - list + - watch + - delete +- apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - create + - watch + - delete +- apiGroups: + - apps + - extensions + resources: + - deployments + - replicasets + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes/custom-host + verbs: + - create +- apiGroups: + - "" + resources: + - events + verbs: + - watch +- apiGroups: + - apps + resources: + - replicasets + verbs: + - list + - get + - patch + - delete +- apiGroups: + - extensions + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - list + - create + - watch + - get + - delete +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get + - create + - update +- apiGroups: + - operators.coreos.com + resources: + - subscriptions + verbs: + - get +- apiGroups: + - operators.coreos.com + resources: + - clusterserviceversions + verbs: + - list + - get + - watch +- apiGroups: + - metrics.k8s.io + resources: + - pods + - nodes + verbs: + - get + - list + - watch +- apiGroups: + - cert-manager.io + resources: + - issuers + - certificates + verbs: + - create + - get + - list + - update +- apiGroups: + - "" + resources: + - configmaps + - persistentvolumeclaims + - pods + - secrets + - serviceaccounts + - services + verbs: + - '*' +- apiGroups: + - apps + resourceNames: + - che-operator + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - update + - watch +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/finalizers + verbs: + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/status + verbs: + - get + - patch + - update +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - create + - get +- nonResourceURLs: + - /metrics + verbs: + - get +- apiGroups: + - che.eclipse.org + resources: + - kubernetesimagepullers + verbs: + - '*' +- apiGroups: + - config.openshift.io + resourceNames: + - cluster + resources: + - consoles + verbs: + - get +- apiGroups: + - "" + resources: + - pods/log + verbs: + - get + - list + - watch +- apiGroups: + - workspace.devfile.io + resources: + - devworkspaces + - devworkspacetemplates + verbs: + - get + - list + - watch +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + - components + verbs: + - get + - list + - watch +- apiGroups: + - workspace.devfile.io + resources: + - devworkspaces + - devworkspacetemplates + verbs: + - create + - delete + - deletecollection + - patch + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + - components + verbs: + - create + - delete + - deletecollection + - patch + - update +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +- apiGroups: + - "" + resourceNames: + - workspace-preferences-configmap + resources: + - configmaps + verbs: + - create + - delete + - get + - patch +- apiGroups: + - "" + resources: + - configmaps + - persistentvolumeclaims + - pods + - secrets + - serviceaccounts + verbs: + - '*' +- apiGroups: + - "" + resources: + - events + - namespaces + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create +- apiGroups: + - "" + resourceNames: + - workspace-credentials-secret + resources: + - secrets + verbs: + - create + - delete + - get + - patch +- apiGroups: + - "" + resources: + - services + verbs: + - '*' +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resourceNames: + - devworkspace-controller + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - apps + - extensions + resources: + - deployments + verbs: + - get + - list + - watch +- apiGroups: + - apps + - extensions + resources: + - deployments + - replicasets + verbs: + - '*' +- apiGroups: + - apps + - extensions + resources: + - replicasets + verbs: + - get + - list + - watch +- apiGroups: + - authorization.k8s.io + resources: + - localsubjectaccessreviews + - subjectaccessreviews + verbs: + - create +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - config.openshift.io + resourceNames: + - cluster + resources: + - proxies + verbs: + - get +- apiGroups: + - controller.devfile.io + resources: + - '*' + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/status + verbs: + - get + - patch + - update +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - update +- apiGroups: + - metrics.k8s.io + resources: + - pods + verbs: + - get + - list + - watch +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - create + - get +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - oauth.openshift.io + resources: + - oauthclients + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + - rolebindings + - roles + verbs: + - create + - get + - list + - update + - watch +- apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes/custom-host + verbs: + - create +- apiGroups: + - workspace.devfile.io + resources: + - '*' + verbs: + - '*' +- nonResourceURLs: + - /metrics + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: che-operator +subjects: +- kind: ServiceAccount + name: che-operator +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: che-operator +subjects: +- kind: ServiceAccount + name: che-operator + namespace: eclipse-che +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator-service + namespace: eclipse-che +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + app: che-operator +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: che-operator + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator + namespace: eclipse-che +spec: + replicas: 1 + selector: + matchLabels: + app: che-operator + strategy: + type: RollingUpdate + template: + metadata: + labels: + app: che-operator + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + spec: + containers: + - args: + - --leader-elect + command: + - /manager + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: OPERATOR_NAME + value: che-operator + - name: CHE_VERSION + value: next + - name: RELATED_IMAGE_che_server + value: quay.io/eclipse/che-server:next + - name: RELATED_IMAGE_dashboard + value: quay.io/eclipse/che-dashboard:next + - name: RELATED_IMAGE_plugin_registry + value: quay.io/eclipse/che-plugin-registry:next + - name: RELATED_IMAGE_devfile_registry + value: quay.io/eclipse/che-devfile-registry:next + - name: RELATED_IMAGE_che_tls_secrets_creation_job + value: quay.io/eclipse/che-tls-secret-creator:alpine-01a4c34 + - name: RELATED_IMAGE_postgres + value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392 + - name: RELATED_IMAGE_postgres_13_3 + value: quay.io/eclipse/che--centos--postgresql-13-centos7:1-71b24684d64da46f960682cc4216222a7e4ed8b1a31dd5a865b3e71afdea20d2 + - name: RELATED_IMAGE_single_host_gateway + value: quay.io/eclipse/che--traefik:v2.5.0-eb30f9f09a65cee1fab5ef9c64cb4ec91b800dc3fdd738d62a9d4334f0114683 + - name: RELATED_IMAGE_single_host_gateway_config_sidecar + value: quay.io/che-incubator/configbump:0.1.4 + - name: RELATED_IMAGE_devworkspace_controller + value: quay.io/devfile/devworkspace-controller:v0.14.1 + - name: RELATED_IMAGE_gateway_authentication_sidecar + value: quay.io/openshift/origin-oauth-proxy:4.7 + - name: RELATED_IMAGE_gateway_authorization_sidecar + value: quay.io/openshift/origin-kube-rbac-proxy:4.7 + - name: RELATED_IMAGE_gateway_authentication_sidecar_k8s + value: quay.io/oauth2-proxy/oauth2-proxy:v7.2.0 + - name: RELATED_IMAGE_gateway_authorization_sidecar_k8s + value: quay.io/brancz/kube-rbac-proxy:v0.11.0 + - name: RELATED_IMAGE_gateway_header_sidecar + value: quay.io/che-incubator/header-rewrite-proxy:latest + - name: CHE_FLAVOR + value: che + - name: CONSOLE_LINK_NAME + value: che + - name: CONSOLE_LINK_DISPLAY_NAME + value: Eclipse Che + - name: CONSOLE_LINK_SECTION + value: Red Hat Applications + - name: CONSOLE_LINK_IMAGE + value: /dashboard/assets/branding/loader.svg + - name: MAX_CONCURRENT_RECONCILES + value: "1" + - name: ADD_COMPONENT_READINESS_INIT_CONTAINERS + value: "false" + image: quay.io/eclipse/che-operator:next + imagePullPolicy: Always + livenessProbe: + failureThreshold: 10 + httpGet: + path: /healthz + port: 6789 + initialDelaySeconds: 15 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + name: che-operator + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + - containerPort: 60000 + name: metrics + readinessProbe: + failureThreshold: 10 + httpGet: + path: /readyz + port: 6789 + initialDelaySeconds: 5 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 100m + memory: 64Mi + securityContext: + capabilities: + drop: + - ALL + privileged: false + readOnlyRootFilesystem: false + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: webhook-tls-certs + readOnly: true + hostIPC: false + hostNetwork: false + hostPID: false + restartPolicy: Always + serviceAccountName: che-operator + terminationGracePeriodSeconds: 20 + volumes: + - name: webhook-tls-certs + secret: + defaultMode: 420 + secretName: che-operator-webhook-server-cert +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator-serving-cert + namespace: eclipse-che +spec: + dnsNames: + - che-operator-service.eclipse-che.svc + - che-operator-service.eclipse-che.svc.cluster.local + issuerRef: + kind: Issuer + name: che-operator-selfsigned-issuer + secretName: che-operator-webhook-server-cert +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator-selfsigned-issuer + namespace: eclipse-che +spec: + selfSigned: {} diff --git a/deploy/deployment/kubernetes/objects/che-operator-selfsigned-issuer.Issuer.yaml b/deploy/deployment/kubernetes/objects/che-operator-selfsigned-issuer.Issuer.yaml new file mode 100644 index 000000000..373eda80e --- /dev/null +++ b/deploy/deployment/kubernetes/objects/che-operator-selfsigned-issuer.Issuer.yaml @@ -0,0 +1,24 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator-selfsigned-issuer + namespace: eclipse-che +spec: + selfSigned: {} diff --git a/deploy/deployment/kubernetes/objects/che-operator-service.Service.yaml b/deploy/deployment/kubernetes/objects/che-operator-service.Service.yaml new file mode 100644 index 000000000..03b34f1a5 --- /dev/null +++ b/deploy/deployment/kubernetes/objects/che-operator-service.Service.yaml @@ -0,0 +1,28 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator-service + namespace: eclipse-che +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + app: che-operator diff --git a/deploy/deployment/kubernetes/objects/che-operator-serving-cert.Certificate.yaml b/deploy/deployment/kubernetes/objects/che-operator-serving-cert.Certificate.yaml new file mode 100644 index 000000000..1ff97ad7a --- /dev/null +++ b/deploy/deployment/kubernetes/objects/che-operator-serving-cert.Certificate.yaml @@ -0,0 +1,30 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator-serving-cert + namespace: eclipse-che +spec: + dnsNames: + - che-operator-service.eclipse-che.svc + - che-operator-service.eclipse-che.svc.cluster.local + issuerRef: + kind: Issuer + name: che-operator-selfsigned-issuer + secretName: che-operator-webhook-server-cert diff --git a/deploy/deployment/kubernetes/objects/che-operator.ClusterRole.yaml b/deploy/deployment/kubernetes/objects/che-operator.ClusterRole.yaml new file mode 100644 index 000000000..0c62c51d7 --- /dev/null +++ b/deploy/deployment/kubernetes/objects/che-operator.ClusterRole.yaml @@ -0,0 +1,669 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator +rules: +- apiGroups: + - "" + resources: + - nodes + verbs: + - get +- apiGroups: + - oauth.openshift.io + resources: + - oauthclients + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - config.openshift.io + resources: + - oauths + verbs: + - get + - list + - watch + - patch +- apiGroups: + - config.openshift.io + resources: + - infrastructures + - proxies + verbs: + - get + - list + - watch +- apiGroups: + - user.openshift.io + resources: + - users + verbs: + - list + - delete +- apiGroups: + - user.openshift.io + resources: + - identities + verbs: + - delete +- apiGroups: + - console.openshift.io + resources: + - consolelinks + verbs: + - get + - list + - create + - update + - patch + - delete +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + verbs: + - list + - create + - watch + - update + - get + - delete +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + - rolebindings + verbs: + - get + - create + - update + - delete +- apiGroups: + - authorization.openshift.io + resources: + - roles + - rolebindings + verbs: + - get + - create + - update + - delete +- apiGroups: + - org.eclipse.che + resources: + - checlusters + - checlusters/status + - checlusters/finalizers + - checlusters/status + verbs: + - '*' +- apiGroups: + - project.openshift.io + resources: + - projectrequests + verbs: + - create + - update +- apiGroups: + - project.openshift.io + resources: + - projects + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - create + - update + - watch +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create +- apiGroups: + - apps + resources: + - secrets + verbs: + - list +- apiGroups: + - "" + resources: + - secrets + verbs: + - list + - get + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - create + - get + - list + - watch + - delete +- apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - create + - watch + - delete +- apiGroups: + - apps + - extensions + resources: + - deployments + - replicasets + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes/custom-host + verbs: + - create +- apiGroups: + - "" + resources: + - events + verbs: + - watch +- apiGroups: + - apps + resources: + - replicasets + verbs: + - list + - get + - patch + - delete +- apiGroups: + - extensions + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - list + - create + - watch + - get + - delete +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get + - create + - update +- apiGroups: + - operators.coreos.com + resources: + - subscriptions + verbs: + - get +- apiGroups: + - operators.coreos.com + resources: + - clusterserviceversions + verbs: + - list + - get + - watch +- apiGroups: + - metrics.k8s.io + resources: + - pods + - nodes + verbs: + - get + - list + - watch +- apiGroups: + - cert-manager.io + resources: + - issuers + - certificates + verbs: + - create + - get + - list + - update +- apiGroups: + - "" + resources: + - configmaps + - persistentvolumeclaims + - pods + - secrets + - serviceaccounts + - services + verbs: + - '*' +- apiGroups: + - apps + resourceNames: + - che-operator + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - update + - watch +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/finalizers + verbs: + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/status + verbs: + - get + - patch + - update +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - create + - get +- nonResourceURLs: + - /metrics + verbs: + - get +- apiGroups: + - che.eclipse.org + resources: + - kubernetesimagepullers + verbs: + - '*' +- apiGroups: + - config.openshift.io + resourceNames: + - cluster + resources: + - consoles + verbs: + - get +- apiGroups: + - "" + resources: + - pods/log + verbs: + - get + - list + - watch +- apiGroups: + - workspace.devfile.io + resources: + - devworkspaces + - devworkspacetemplates + verbs: + - get + - list + - watch +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + - components + verbs: + - get + - list + - watch +- apiGroups: + - workspace.devfile.io + resources: + - devworkspaces + - devworkspacetemplates + verbs: + - create + - delete + - deletecollection + - patch + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + - components + verbs: + - create + - delete + - deletecollection + - patch + - update +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +- apiGroups: + - "" + resourceNames: + - workspace-preferences-configmap + resources: + - configmaps + verbs: + - create + - delete + - get + - patch +- apiGroups: + - "" + resources: + - configmaps + - persistentvolumeclaims + - pods + - secrets + - serviceaccounts + verbs: + - '*' +- apiGroups: + - "" + resources: + - events + - namespaces + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create +- apiGroups: + - "" + resourceNames: + - workspace-credentials-secret + resources: + - secrets + verbs: + - create + - delete + - get + - patch +- apiGroups: + - "" + resources: + - services + verbs: + - '*' +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resourceNames: + - devworkspace-controller + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - apps + - extensions + resources: + - deployments + verbs: + - get + - list + - watch +- apiGroups: + - apps + - extensions + resources: + - deployments + - replicasets + verbs: + - '*' +- apiGroups: + - apps + - extensions + resources: + - replicasets + verbs: + - get + - list + - watch +- apiGroups: + - authorization.k8s.io + resources: + - localsubjectaccessreviews + - subjectaccessreviews + verbs: + - create +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - config.openshift.io + resourceNames: + - cluster + resources: + - proxies + verbs: + - get +- apiGroups: + - controller.devfile.io + resources: + - '*' + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/status + verbs: + - get + - patch + - update +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - update +- apiGroups: + - metrics.k8s.io + resources: + - pods + verbs: + - get + - list + - watch +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - create + - get +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - oauth.openshift.io + resources: + - oauthclients + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + - rolebindings + - roles + verbs: + - create + - get + - list + - update + - watch +- apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes/custom-host + verbs: + - create +- apiGroups: + - workspace.devfile.io + resources: + - '*' + verbs: + - '*' +- nonResourceURLs: + - /metrics + verbs: + - get diff --git a/helmcharts/next/templates/cluster_rolebinding.yaml b/deploy/deployment/kubernetes/objects/che-operator.ClusterRoleBinding.yaml similarity index 90% rename from helmcharts/next/templates/cluster_rolebinding.yaml rename to deploy/deployment/kubernetes/objects/che-operator.ClusterRoleBinding.yaml index 0741789a6..d67afa6e7 100644 --- a/helmcharts/next/templates/cluster_rolebinding.yaml +++ b/deploy/deployment/kubernetes/objects/che-operator.ClusterRoleBinding.yaml @@ -10,20 +10,20 @@ # Red Hat, Inc. - initial API and implementation # -kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding metadata: - name: che-operator labels: - app.kubernetes.io/name: che - app.kubernetes.io/instance: che - app.kubernetes.io/part-of: che.eclipse.org app.kubernetes.io/component: che-operator -subjects: - - kind: ServiceAccount - name: che-operator - namespace: eclipse-che + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator roleRef: + apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: che-operator - apiGroup: rbac.authorization.k8s.io +subjects: +- kind: ServiceAccount + name: che-operator + namespace: eclipse-che diff --git a/deploy/deployment/kubernetes/objects/che-operator.Deployment.yaml b/deploy/deployment/kubernetes/objects/che-operator.Deployment.yaml new file mode 100644 index 000000000..7c98d7d57 --- /dev/null +++ b/deploy/deployment/kubernetes/objects/che-operator.Deployment.yaml @@ -0,0 +1,156 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: che-operator + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator + namespace: eclipse-che +spec: + replicas: 1 + selector: + matchLabels: + app: che-operator + strategy: + type: RollingUpdate + template: + metadata: + labels: + app: che-operator + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + spec: + containers: + - args: + - --leader-elect + command: + - /manager + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: OPERATOR_NAME + value: che-operator + - name: CHE_VERSION + value: next + - name: RELATED_IMAGE_che_server + value: quay.io/eclipse/che-server:next + - name: RELATED_IMAGE_dashboard + value: quay.io/eclipse/che-dashboard:next + - name: RELATED_IMAGE_plugin_registry + value: quay.io/eclipse/che-plugin-registry:next + - name: RELATED_IMAGE_devfile_registry + value: quay.io/eclipse/che-devfile-registry:next + - name: RELATED_IMAGE_che_tls_secrets_creation_job + value: quay.io/eclipse/che-tls-secret-creator:alpine-01a4c34 + - name: RELATED_IMAGE_postgres + value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392 + - name: RELATED_IMAGE_postgres_13_3 + value: quay.io/eclipse/che--centos--postgresql-13-centos7:1-71b24684d64da46f960682cc4216222a7e4ed8b1a31dd5a865b3e71afdea20d2 + - name: RELATED_IMAGE_single_host_gateway + value: quay.io/eclipse/che--traefik:v2.5.0-eb30f9f09a65cee1fab5ef9c64cb4ec91b800dc3fdd738d62a9d4334f0114683 + - name: RELATED_IMAGE_single_host_gateway_config_sidecar + value: quay.io/che-incubator/configbump:0.1.4 + - name: RELATED_IMAGE_devworkspace_controller + value: quay.io/devfile/devworkspace-controller:v0.14.1 + - name: RELATED_IMAGE_gateway_authentication_sidecar + value: quay.io/openshift/origin-oauth-proxy:4.7 + - name: RELATED_IMAGE_gateway_authorization_sidecar + value: quay.io/openshift/origin-kube-rbac-proxy:4.7 + - name: RELATED_IMAGE_gateway_authentication_sidecar_k8s + value: quay.io/oauth2-proxy/oauth2-proxy:v7.2.0 + - name: RELATED_IMAGE_gateway_authorization_sidecar_k8s + value: quay.io/brancz/kube-rbac-proxy:v0.11.0 + - name: RELATED_IMAGE_gateway_header_sidecar + value: quay.io/che-incubator/header-rewrite-proxy:latest + - name: CHE_FLAVOR + value: che + - name: CONSOLE_LINK_NAME + value: che + - name: CONSOLE_LINK_DISPLAY_NAME + value: Eclipse Che + - name: CONSOLE_LINK_SECTION + value: Red Hat Applications + - name: CONSOLE_LINK_IMAGE + value: /dashboard/assets/branding/loader.svg + - name: MAX_CONCURRENT_RECONCILES + value: "1" + - name: ADD_COMPONENT_READINESS_INIT_CONTAINERS + value: "false" + image: quay.io/eclipse/che-operator:next + imagePullPolicy: Always + livenessProbe: + failureThreshold: 10 + httpGet: + path: /healthz + port: 6789 + initialDelaySeconds: 15 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + name: che-operator + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + - containerPort: 60000 + name: metrics + readinessProbe: + failureThreshold: 10 + httpGet: + path: /readyz + port: 6789 + initialDelaySeconds: 5 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 100m + memory: 64Mi + securityContext: + capabilities: + drop: + - ALL + privileged: false + readOnlyRootFilesystem: false + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: webhook-tls-certs + readOnly: true + hostIPC: false + hostNetwork: false + hostPID: false + restartPolicy: Always + serviceAccountName: che-operator + terminationGracePeriodSeconds: 20 + volumes: + - name: webhook-tls-certs + secret: + defaultMode: 420 + secretName: che-operator-webhook-server-cert diff --git a/deploy/deployment/kubernetes/objects/che-operator.Role.yaml b/deploy/deployment/kubernetes/objects/che-operator.Role.yaml new file mode 100644 index 000000000..1c02ee053 --- /dev/null +++ b/deploy/deployment/kubernetes/objects/che-operator.Role.yaml @@ -0,0 +1,184 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator +rules: +- apiGroups: + - extensions + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - batch + resources: + - jobs + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes + - routes/custom-host + verbs: + - '*' +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + - rolebindings + verbs: + - '*' +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterroles + - clusterrolebindings + verbs: + - '*' +- apiGroups: + - "" + resources: + - pods + - services + - serviceaccounts + - endpoints + - persistentvolumeclaims + - events + - configmaps + - secrets + - pods/exec + - pods/log + verbs: + - '*' +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get +- apiGroups: + - apps + - extensions + resources: + - deployments + - replicasets + verbs: + - '*' +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - get + - create +- apiGroups: + - org.eclipse.che + resources: + - checlusters + - checlusters/status + - checlusters/finalizers + verbs: + - '*' +- apiGroups: + - metrics.k8s.io + resources: + - pods + - nodes + verbs: + - get + - list + - watch +- apiGroups: + - operators.coreos.com + resources: + - subscriptions + - clusterserviceversions + - operatorgroups + verbs: + - '*' +- apiGroups: + - packages.operators.coreos.com + resources: + - packagemanifests + verbs: + - get + - list +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create +- apiGroups: + - apps + resourceNames: + - che-operator + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/finalizers + verbs: + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/status + verbs: + - get + - patch + - update +- apiGroups: + - oauth.openshift.io + resources: + - oauthclients + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch diff --git a/helmcharts/next/templates/role_binding.yaml b/deploy/deployment/kubernetes/objects/che-operator.RoleBinding.yaml similarity index 90% rename from helmcharts/next/templates/role_binding.yaml rename to deploy/deployment/kubernetes/objects/che-operator.RoleBinding.yaml index 332455c95..f145c0454 100644 --- a/helmcharts/next/templates/role_binding.yaml +++ b/deploy/deployment/kubernetes/objects/che-operator.RoleBinding.yaml @@ -13,17 +13,16 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: che-operator labels: - app.kubernetes.io/name: che - app.kubernetes.io/instance: che - app.kubernetes.io/part-of: che.eclipse.org app.kubernetes.io/component: che-operator - namespace: eclipse-che + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: che-operator subjects: - - kind: ServiceAccount - name: che-operator +- kind: ServiceAccount + name: che-operator diff --git a/helmcharts/next/templates/service_account.yaml b/deploy/deployment/kubernetes/objects/che-operator.ServiceAccount.yaml similarity index 100% rename from helmcharts/next/templates/service_account.yaml rename to deploy/deployment/kubernetes/objects/che-operator.ServiceAccount.yaml index 434722d78..71a022293 100644 --- a/helmcharts/next/templates/service_account.yaml +++ b/deploy/deployment/kubernetes/objects/che-operator.ServiceAccount.yaml @@ -13,10 +13,10 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: che-operator labels: - app.kubernetes.io/name: che - app.kubernetes.io/instance: che - app.kubernetes.io/part-of: che.eclipse.org app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator namespace: eclipse-che diff --git a/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml b/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml new file mode 100644 index 000000000..c764c6d2d --- /dev/null +++ b/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml @@ -0,0 +1,1646 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: eclipse-che/che-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.4.1 + labels: + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: checlusters.org.eclipse.che +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: che-operator-service + namespace: eclipse-che + path: /convert + conversionReviewVersions: + - v1 + - v2 + group: org.eclipse.che + names: + kind: CheCluster + listKind: CheClusterList + plural: checlusters + singular: checluster + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: The `CheCluster` custom resource allows defining and managing a Che server installation + 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: Desired configuration of the Che installation. Based on these settings, the Operator automatically creates and maintains several ConfigMaps that will contain the appropriate environment variables the various components of the Che installation. These generated ConfigMaps must NOT be updated manually. + properties: + auth: + description: Configuration settings related to the Authentication used by the Che installation. + properties: + debug: + description: Deprecated. The value of this flag is ignored. Debug internal identity provider. + type: boolean + externalIdentityProvider: + description: 'Deprecated. The value of this flag is ignored. Instructs the Operator on whether or not to deploy a dedicated Identity Provider (Keycloak or RH SSO instance). Instructs the Operator on whether to deploy a dedicated Identity Provider (Keycloak or RH-SSO instance). By default, a dedicated Identity Provider server is deployed as part of the Che installation. When `externalIdentityProvider` is `true`, no dedicated identity provider will be deployed by the Operator and you will need to provide details about the external identity provider you are about to use. See also all the other fields starting with: `identityProvider`.' + type: boolean + gatewayAuthenticationSidecarImage: + description: Gateway sidecar responsible for authentication when NativeUserMode is enabled. See link:https://github.com/oauth2-proxy/oauth2-proxy[oauth2-proxy] or link:https://github.com/openshift/oauth-proxy[openshift/oauth-proxy]. + type: string + gatewayAuthorizationSidecarImage: + description: Gateway sidecar responsible for authorization when NativeUserMode is enabled. See link:https://github.com/brancz/kube-rbac-proxy[kube-rbac-proxy] or link:https://github.com/openshift/kube-rbac-proxy[openshift/kube-rbac-proxy] + type: string + gatewayHeaderRewriteSidecarImage: + description: Deprecated. The value of this flag is ignored. Sidecar functionality is now implemented in Traefik plugin. + type: string + identityProviderAdminUserName: + description: Deprecated. The value of this flag is ignored. Overrides the name of the Identity Provider administrator user. Defaults to `admin`. + type: string + identityProviderClientId: + description: Deprecated. The value of this flag is ignored. Name of a Identity provider, Keycloak or RH-SSO, `client-id` that is used for Che. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to the value of the `flavour` field suffixed with `-public`. + type: string + identityProviderContainerResources: + description: Deprecated. The value of this flag is ignored. Identity provider container custom settings. + properties: + limits: + description: Limits describes the maximum amount of compute resources allowed. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + request: + description: Requests describes the minimum amount of compute resources required. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + type: object + identityProviderImage: + description: Deprecated. The value of this flag is ignored. Overrides the container image used in the Identity Provider, Keycloak or RH-SSO, deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + identityProviderImagePullPolicy: + description: Deprecated. The value of this flag is ignored. Overrides the image pull policy used in the Identity Provider, Keycloak or RH-SSO, deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + identityProviderIngress: + description: Deprecated. The value of this flag is ignored. Ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + identityProviderPassword: + description: Deprecated. The value of this flag is ignored. Overrides the password of Keycloak administrator user. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to an auto-generated password. + type: string + identityProviderPostgresPassword: + description: Deprecated. The value of this flag is ignored. Password for a Identity Provider, Keycloak or RH-SSO, to connect to the database. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to an auto-generated password. + type: string + identityProviderPostgresSecret: + description: 'Deprecated. The value of this flag is ignored. The secret that contains `password` for the Identity Provider, Keycloak or RH-SSO, to connect to the database. When the secret is defined, the `identityProviderPostgresPassword` is ignored. When the value is omitted or left blank, the one of following scenarios applies: 1. `identityProviderPostgresPassword` is defined, then it will be used to connect to the database. 2. `identityProviderPostgresPassword` is not defined, then a new secret with the name `che-identity-postgres-secret` will be created with an auto-generated value for `password`. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + identityProviderRealm: + description: Deprecated. The value of this flag is ignored. Name of a Identity provider, Keycloak or RH-SSO, realm that is used for Che. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to the value of the `flavour` field. + type: string + identityProviderRoute: + description: Deprecated. The value of this flag is ignored. Route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + identityProviderSecret: + description: 'Deprecated. The value of this flag is ignored. The secret that contains `user` and `password` for Identity Provider. When the secret is defined, the `identityProviderAdminUserName` and `identityProviderPassword` are ignored. When the value is omitted or left blank, the one of following scenarios applies: 1. `identityProviderAdminUserName` and `identityProviderPassword` are defined, then they will be used. 2. `identityProviderAdminUserName` or `identityProviderPassword` are not defined, then a new secret with the name `che-identity-secret` will be created with default value `admin` for `user` and with an auto-generated value for `password`. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + identityProviderURL: + description: Public URL of the Identity Provider server (Keycloak / RH-SSO server). Set this ONLY when a use of an external Identity Provider is needed. See the `externalIdentityProvider` field. By default, this will be automatically calculated and set by the Operator. + type: string + initialOpenShiftOAuthUser: + description: Deprecated. The value of this flag is ignored. For operating with the OpenShift OAuth authentication, create a new user account since the kubeadmin can not be used. If the value is true, then a new OpenShift OAuth user will be created for the HTPasswd identity provider. If the value is false and the user has already been created, then it will be removed. If value is an empty, then do nothing. The user's credentials are stored in the `openshift-oauth-user-credentials` secret in 'openshift-config' namespace by Operator. Note that this solution is Openshift 4 platform-specific. + type: boolean + nativeUserMode: + description: Deprecated. The value of this flag is ignored. Enables native user mode. Currently works only on OpenShift and DevWorkspace engine. Native User mode uses OpenShift OAuth directly as identity provider, without Keycloak. + type: boolean + oAuthClientName: + description: Name of the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OpenShiftoAuth` field. + type: string + oAuthSecret: + description: Name of the secret set in the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OAuthClientName` field. + type: string + openShiftoAuth: + description: 'Deprecated. The value of this flag is ignored. Enables the integration of the identity provider (Keycloak / RHSSO) with OpenShift OAuth. Empty value on OpenShift by default. This will allow users to directly login with their OpenShift user through the OpenShift login, and have their workspaces created under personal OpenShift namespaces. WARNING: the `kubeadmin` user is NOT supported, and logging through it will NOT allow accessing the Che Dashboard.' + type: boolean + updateAdminPassword: + description: Deprecated. The value of this flag is ignored. Forces the default `admin` Che user to update password on first login. Defaults to `false`. + type: boolean + type: object + dashboard: + description: Configuration settings related to the User Dashboard used by the Che installation. + properties: + warning: + description: Warning message that will be displayed on the User Dashboard + type: string + type: object + database: + description: Configuration settings related to the database used by the Che installation. + properties: + chePostgresContainerResources: + description: PostgreSQL container custom settings + properties: + limits: + description: Limits describes the maximum amount of compute resources allowed. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + request: + description: Requests describes the minimum amount of compute resources required. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + type: object + chePostgresDb: + description: PostgreSQL database name that the Che server uses to connect to the DB. Defaults to `dbche`. + type: string + chePostgresHostName: + description: PostgreSQL Database host name that the Che server uses to connect to. Defaults is `postgres`. Override this value ONLY when using an external database. See field `externalDb`. In the default case it will be automatically set by the Operator. + type: string + chePostgresPassword: + description: PostgreSQL password that the Che server uses to connect to the DB. When omitted or left blank, it will be set to an automatically generated value. + type: string + chePostgresPort: + description: PostgreSQL Database port that the Che server uses to connect to. Defaults to 5432. Override this value ONLY when using an external database. See field `externalDb`. In the default case it will be automatically set by the Operator. + type: string + chePostgresSecret: + description: 'The secret that contains PostgreSQL`user` and `password` that the Che server uses to connect to the DB. When the secret is defined, the `chePostgresUser` and `chePostgresPassword` are ignored. When the value is omitted or left blank, the one of following scenarios applies: 1. `chePostgresUser` and `chePostgresPassword` are defined, then they will be used to connect to the DB. 2. `chePostgresUser` or `chePostgresPassword` are not defined, then a new secret with the name `postgres-credentials` will be created with default value of `pgche` for `user` and with an auto-generated value for `password`. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + chePostgresUser: + description: PostgreSQL user that the Che server uses to connect to the DB. Defaults to `pgche`. + type: string + externalDb: + description: 'Instructs the Operator on whether to deploy a dedicated database. By default, a dedicated PostgreSQL database is deployed as part of the Che installation. When `externalDb` is `true`, no dedicated database will be deployed by the Operator and you will need to provide connection details to the external DB you are about to use. See also all the fields starting with: `chePostgres`.' + type: boolean + postgresImage: + description: Overrides the container image used in the PostgreSQL database deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + postgresImagePullPolicy: + description: Overrides the image pull policy used in the PostgreSQL database deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + postgresVersion: + description: 'Indicates a PostgreSQL version image to use. Allowed values are: `9.6` and `13.3`. Migrate your PostgreSQL database to switch from one version to another.' + type: string + pvcClaimSize: + description: Size of the persistent volume claim for database. Defaults to `1Gi`. To update pvc storageclass that provisions it must support resize when Eclipse Che has been already deployed. + type: string + type: object + devWorkspace: + description: DevWorkspace operator configuration + properties: + controllerImage: + description: Overrides the container image used in the DevWorkspace controller deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + enable: + description: Deploys the DevWorkspace Operator in the cluster. Does nothing when a matching version of the Operator is already installed. Fails when a non-matching version of the Operator is already installed. + type: boolean + runningLimit: + description: Maximum number of the running workspaces per user. + type: string + required: + - enable + type: object + imagePuller: + description: Kubernetes Image Puller configuration + properties: + enable: + description: Install and configure the Community Supported Kubernetes Image Puller Operator. When set to `true` and no spec is provided, it will create a default KubernetesImagePuller object to be managed by the Operator. When set to `false`, the KubernetesImagePuller object will be deleted, and the Operator will be uninstalled, regardless of whether a spec is provided. If the `spec.images` field is empty, a set of recommended workspace-related images will be automatically detected and pre-pulled after installation. Note that while this Operator and its behavior is community-supported, its payload may be commercially-supported for pulling commercially-supported images. + type: boolean + spec: + description: A KubernetesImagePullerSpec to configure the image puller in the CheCluster + properties: + affinity: + type: string + cachingCPULimit: + type: string + cachingCPURequest: + type: string + cachingIntervalHours: + type: string + cachingMemoryLimit: + type: string + cachingMemoryRequest: + type: string + configMapName: + type: string + daemonsetName: + type: string + deploymentName: + type: string + imagePullSecrets: + type: string + imagePullerImage: + type: string + images: + type: string + nodeSelector: + type: string + type: object + required: + - enable + type: object + k8s: + description: Configuration settings specific to Che installations made on upstream Kubernetes. + properties: + ingressClass: + description: 'Ingress class that will define the which controller will manage ingresses. Defaults to `nginx`. NB: This drives the `kubernetes.io/ingress.class` annotation on Che-related ingresses.' + type: string + ingressDomain: + description: 'Global ingress domain for a Kubernetes cluster. This MUST be explicitly specified: there are no defaults.' + type: string + ingressStrategy: + description: 'Deprecated. The value of this flag is ignored. Strategy for ingress creation. Options are: `multi-host` (host is explicitly provided in ingress), `single-host` (host is provided, path-based rules) and `default-host` (no host is provided, path-based rules). Defaults to `multi-host` Deprecated in favor of `serverExposureStrategy` in the `server` section, which defines this regardless of the cluster type. When both are defined, the `serverExposureStrategy` option takes precedence.' + type: string + securityContextFsGroup: + description: The FSGroup in which the Che Pod and workspace Pods containers runs in. Default value is `1724`. + type: string + securityContextRunAsUser: + description: ID of the user the Che Pod and workspace Pods containers run as. Default value is `1724`. + type: string + singleHostExposureType: + description: Deprecated. The value of this flag is ignored. When the serverExposureStrategy is set to `single-host`, the way the server, registries and workspaces are exposed is further configured by this property. The possible values are `native`, which means that the server and workspaces are exposed using ingresses on K8s or `gateway` where the server and workspaces are exposed using a custom gateway based on link:https://doc.traefik.io/traefik/[Traefik]. All the endpoints whether backed by the ingress or gateway `route` always point to the subpaths on the same domain. Defaults to `native`. + type: string + tlsSecretName: + description: Name of a secret that will be used to setup ingress TLS termination when TLS is enabled. When the field is empty string, the default cluster certificate will be used. See also the `tlsSupport` field. + type: string + type: object + metrics: + description: Configuration settings related to the metrics collection used by the Che installation. + properties: + enable: + description: Enables `metrics` the Che server endpoint. Default to `true`. + type: boolean + type: object + server: + description: General configuration settings related to the Che server, the plugin and devfile registries + properties: + airGapContainerRegistryHostname: + description: Optional host name, or URL, to an alternate container registry to pull images from. This value overrides the container registry host name defined in all the default container images involved in a Che deployment. This is particularly useful to install Che in a restricted environment. + type: string + airGapContainerRegistryOrganization: + description: Optional repository name of an alternate container registry to pull images from. This value overrides the container registry organization defined in all the default container images involved in a Che deployment. This is particularly useful to install Eclipse Che in a restricted environment. + type: string + allowUserDefinedWorkspaceNamespaces: + description: Deprecated. The value of this flag is ignored. Defines that a user is allowed to specify a Kubernetes namespace, or an OpenShift project, which differs from the default. It's NOT RECOMMENDED to set to `true` without OpenShift OAuth configured. The OpenShift infrastructure also uses this property. + type: boolean + cheClusterRoles: + description: A comma-separated list of ClusterRoles that will be assigned to Che ServiceAccount. Each role must have `app.kubernetes.io/part-of=che.eclipse.org` label. Be aware that the Che Operator has to already have all permissions in these ClusterRoles to grant them. + type: string + cheDebug: + description: Enables the debug mode for Che server. Defaults to `false`. + type: string + cheFlavor: + description: Deprecated. The value of this flag is ignored. Specifies a variation of the installation. The options are `che` for upstream Che installations or `devspaces` for Red Hat OpenShift Dev Spaces (formerly Red Hat CodeReady Workspaces) installation + type: string + cheHost: + description: Public host name of the installed Che server. When value is omitted, the value it will be automatically set by the Operator. See the `cheHostTLSSecret` field. + type: string + cheHostTLSSecret: + description: Name of a secret containing certificates to secure ingress or route for the custom host name of the installed Che server. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label. See the `cheHost` field. + type: string + cheImage: + description: Overrides the container image used in Che deployment. This does NOT include the container image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + cheImagePullPolicy: + description: Overrides the image pull policy used in Che deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + cheImageTag: + description: Overrides the tag of the container image used in Che deployment. Omit it or leave it empty to use the default image tag provided by the Operator. + type: string + cheLogLevel: + description: 'Log level for the Che server: `INFO` or `DEBUG`. Defaults to `INFO`.' + type: string + cheServerIngress: + description: The Che server ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + cheServerRoute: + description: The Che server route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + cheWorkspaceClusterRole: + description: Custom cluster role bound to the user for the Che workspaces. The role must have `app.kubernetes.io/part-of=che.eclipse.org` label. The default roles are used when omitted or left blank. + type: string + customCheProperties: + additionalProperties: + type: string + description: Map of additional environment variables that will be applied in the generated `che` ConfigMap to be used by the Che server, in addition to the values already generated from other fields of the `CheCluster` custom resource (CR). When `customCheProperties` contains a property that would be normally generated in `che` ConfigMap from other CR fields, the value defined in the `customCheProperties` is used instead. + type: object + dashboardCpuLimit: + description: Overrides the CPU limit used in the dashboard deployment. In cores. (500m = .5 cores). Default to 500m. + type: string + dashboardCpuRequest: + description: Overrides the CPU request used in the dashboard deployment. In cores. (500m = .5 cores). Default to 100m. + type: string + dashboardImage: + description: Overrides the container image used in the dashboard deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + dashboardImagePullPolicy: + description: Overrides the image pull policy used in the dashboard deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + dashboardIngress: + description: Deprecated. The value of this flag is ignored. Dashboard ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + dashboardMemoryLimit: + description: Overrides the memory limit used in the dashboard deployment. Defaults to 256Mi. + type: string + dashboardMemoryRequest: + description: Overrides the memory request used in the dashboard deployment. Defaults to 16Mi. + type: string + dashboardRoute: + description: Deprecated. The value of this flag is ignored. Dashboard route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + devfileRegistryCpuLimit: + description: Overrides the CPU limit used in the devfile registry deployment. In cores. (500m = .5 cores). Default to 500m. + type: string + devfileRegistryCpuRequest: + description: Overrides the CPU request used in the devfile registry deployment. In cores. (500m = .5 cores). Default to 100m. + type: string + devfileRegistryImage: + description: Overrides the container image used in the devfile registry deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + devfileRegistryIngress: + description: Deprecated. The value of this flag is ignored. The devfile registry ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + devfileRegistryMemoryLimit: + description: Overrides the memory limit used in the devfile registry deployment. Defaults to 256Mi. + type: string + devfileRegistryMemoryRequest: + description: Overrides the memory request used in the devfile registry deployment. Defaults to 16Mi. + type: string + devfileRegistryPullPolicy: + description: Overrides the image pull policy used in the devfile registry deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + devfileRegistryRoute: + description: Deprecated. The value of this flag is ignored. The devfile registry route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + devfileRegistryUrl: + description: Deprecated in favor of `externalDevfileRegistries` fields. + type: string + disableInternalClusterSVCNames: + description: Deprecated. The value of this flag is ignored. Disable internal cluster SVC names usage to communicate between components to speed up the traffic and avoid proxy issues. + type: boolean + externalDevfileRegistries: + description: External devfile registries, that serves sample, ready-to-use devfiles. Configure this in addition to a dedicated devfile registry (when `externalDevfileRegistry` is `false`) or instead of it (when `externalDevfileRegistry` is `true`) + items: + description: Settings for a configuration of the external devfile registries. + properties: + url: + description: Public URL of the devfile registry. + type: string + type: object + type: array + externalDevfileRegistry: + description: Instructs the Operator on whether to deploy a dedicated devfile registry server. By default, a dedicated devfile registry server is started. When `externalDevfileRegistry` is `true`, no such dedicated server will be started by the Operator and configure at least one devfile registry with `externalDevfileRegistries` field. + type: boolean + externalPluginRegistry: + description: Instructs the Operator on whether to deploy a dedicated plugin registry server. By default, a dedicated plugin registry server is started. When `externalPluginRegistry` is `true`, no such dedicated server will be started by the Operator and you will have to manually set the `pluginRegistryUrl` field. + type: boolean + gitSelfSignedCert: + description: When enabled, the certificate from `che-git-self-signed-cert` ConfigMap will be propagated to the Che components and provide particular configuration for Git. Note, the `che-git-self-signed-cert` ConfigMap must have `app.kubernetes.io/part-of=che.eclipse.org` label. + type: boolean + nonProxyHosts: + description: 'List of hosts that will be reached directly, bypassing the proxy. Specify wild card domain use the following form `.` and `|` as delimiter, for example: `localhost|.my.host.com|123.42.12.32` Only use when configuring a proxy is required. Operator respects OpenShift cluster wide proxy configuration and no additional configuration is required, but defining `nonProxyHosts` in a custom resource leads to merging non proxy hosts lists from the cluster proxy configuration and ones defined in the custom resources. See the doc https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyURL` fields.' + type: string + pluginRegistryCpuLimit: + description: Overrides the CPU limit used in the plugin registry deployment. In cores. (500m = .5 cores). Default to 500m. + type: string + pluginRegistryCpuRequest: + description: Overrides the CPU request used in the plugin registry deployment. In cores. (500m = .5 cores). Default to 100m. + type: string + pluginRegistryImage: + description: Overrides the container image used in the plugin registry deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + pluginRegistryIngress: + description: Deprecated. The value of this flag is ignored. Plugin registry ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + pluginRegistryMemoryLimit: + description: Overrides the memory limit used in the plugin registry deployment. Defaults to 256Mi. + type: string + pluginRegistryMemoryRequest: + description: Overrides the memory request used in the plugin registry deployment. Defaults to 16Mi. + type: string + pluginRegistryPullPolicy: + description: Overrides the image pull policy used in the plugin registry deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + pluginRegistryRoute: + description: Deprecated. The value of this flag is ignored. Plugin registry route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + pluginRegistryUrl: + description: Public URL of the plugin registry that serves sample ready-to-use devfiles. Set this ONLY when a use of an external devfile registry is needed. See the `externalPluginRegistry` field. By default, this will be automatically calculated by the Operator. + type: string + proxyPassword: + description: Password of the proxy server. Only use when proxy configuration is required. See the `proxyURL`, `proxyUser` and `proxySecret` fields. + type: string + proxyPort: + description: Port of the proxy server. Only use when configuring a proxy is required. See also the `proxyURL` and `nonProxyHosts` fields. + type: string + proxySecret: + description: The secret that contains `user` and `password` for a proxy server. When the secret is defined, the `proxyUser` and `proxyPassword` are ignored. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + proxyURL: + description: URL (protocol+host name) of the proxy server. This drives the appropriate changes in the `JAVA_OPTS` and `https(s)_proxy` variables in the Che server and workspaces containers. Only use when configuring a proxy is required. Operator respects OpenShift cluster wide proxy configuration and no additional configuration is required, but defining `proxyUrl` in a custom resource leads to overrides the cluster proxy configuration with fields `proxyUrl`, `proxyPort`, `proxyUser` and `proxyPassword` from the custom resource. See the doc https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyPort` and `nonProxyHosts` fields. + type: string + proxyUser: + description: User name of the proxy server. Only use when configuring a proxy is required. See also the `proxyURL`, `proxyPassword` and `proxySecret` fields. + type: string + selfSignedCert: + description: Deprecated. The value of this flag is ignored. The Che Operator will automatically detect whether the router certificate is self-signed and propagate it to other components, such as the Che server. + type: boolean + serverCpuLimit: + description: Overrides the CPU limit used in the Che server deployment In cores. (500m = .5 cores). Default to 1. + type: string + serverCpuRequest: + description: Overrides the CPU request used in the Che server deployment In cores. (500m = .5 cores). Default to 100m. + type: string + serverExposureStrategy: + description: Deprecated. The value of this flag is ignored. Sets the server and workspaces exposure type. Possible values are `multi-host`, `single-host`, `default-host`. Defaults to `multi-host`, which creates a separate ingress, or OpenShift routes, for every required endpoint. `single-host` makes Che exposed on a single host name with workspaces exposed on subpaths. Read the docs to learn about the limitations of this approach. Also consult the `singleHostExposureType` property to further configure how the Operator and the Che server make that happen on Kubernetes. `default-host` exposes the Che server on the host of the cluster. Read the docs to learn about the limitations of this approach. + type: string + serverMemoryLimit: + description: Overrides the memory limit used in the Che server deployment. Defaults to 1Gi. + type: string + serverMemoryRequest: + description: Overrides the memory request used in the Che server deployment. Defaults to 512Mi. + type: string + serverTrustStoreConfigMapName: + description: Name of the ConfigMap with public certificates to add to Java trust store of the Che server. This is often required when adding the OpenShift OAuth provider, which has HTTPS endpoint signed with self-signed cert. The Che server must be aware of its CA cert to be able to request it. This is disabled by default. The Config Map must have `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + singleHostGatewayConfigMapLabels: + additionalProperties: + type: string + description: The labels that need to be present in the ConfigMaps representing the gateway configuration. + type: object + singleHostGatewayConfigSidecarImage: + description: The image used for the gateway sidecar that provides configuration to the gateway. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + singleHostGatewayImage: + description: The image used for the gateway in the single host mode. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + tlsSupport: + description: Deprecated. Instructs the Operator to deploy Che in TLS mode. This is enabled by default. Disabling TLS sometimes cause malfunction of some Che components. + type: boolean + useInternalClusterSVCNames: + description: Deprecated in favor of `disableInternalClusterSVCNames`. + type: boolean + workspaceNamespaceDefault: + description: Defines Kubernetes default namespace in which user's workspaces are created for a case when a user does not override it. It's possible to use ``, `` and `` placeholders, such as che-workspace-. In that case, a new namespace will be created for each user or workspace. + type: string + workspacePodNodeSelector: + additionalProperties: + type: string + description: The node selector that limits the nodes that can run the workspace pods. + type: object + workspacePodTolerations: + description: The pod tolerations put on the workspace pods to limit where the workspace pods can run. + items: + description: The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + workspacesDefaultPlugins: + description: Default plug-ins applied to Devworkspaces. + items: + properties: + editor: + description: The editor id to specify default plug-ins for. + type: string + plugins: + description: Default plug-in uris for the specified editor. + items: + type: string + type: array + type: object + type: array + type: object + storage: + description: Configuration settings related to the persistent storage used by the Che installation. + properties: + postgresPVCStorageClassName: + description: Storage class for the Persistent Volume Claim dedicated to the PostgreSQL database. When omitted or left blank, a default storage class is used. + type: string + preCreateSubPaths: + description: Instructs the Che server to start a special Pod to pre-create a sub-path in the Persistent Volumes. Defaults to `false`, however it will need to enable it according to the configuration of your Kubernetes cluster. + type: boolean + pvcClaimSize: + description: Size of the persistent volume claim for workspaces. Defaults to `10Gi`. + type: string + pvcJobsImage: + description: Overrides the container image used to create sub-paths in the Persistent Volumes. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. See also the `preCreateSubPaths` field. + type: string + pvcStrategy: + description: Persistent volume claim strategy for the Che server. This Can be:`common` (all workspaces PVCs in one volume), `per-workspace` (one PVC per workspace for all declared volumes) and `unique` (one PVC per declared volume). Defaults to `common`. + type: string + workspacePVCStorageClassName: + description: Storage class for the Persistent Volume Claims dedicated to the Che workspaces. When omitted or left blank, a default storage class is used. + type: string + type: object + type: object + status: + description: CheClusterStatus defines the observed state of Che installation + properties: + cheClusterRunning: + description: Status of a Che installation. Can be `Available`, `Unavailable`, or `Available, Rolling Update in Progress`. + type: string + cheURL: + description: Public URL to the Che server. + type: string + cheVersion: + description: Current installed Che version. + type: string + dbProvisioned: + description: Indicates that a PostgreSQL instance has been correctly provisioned or not. + type: boolean + devfileRegistryURL: + description: Public URL to the devfile registry. + type: string + devworkspaceStatus: + description: The status of the Devworkspace subsystem + properties: + gatewayHost: + description: GatewayHost is the resolved host of the ingress/route. This is equal to the Host in the spec on Kubernetes but contains the actual host name of the route if Host is unspecified on OpenShift. + type: string + gatewayPhase: + description: GatewayPhase specifies the phase in which the gateway deployment currently is. If the gateway is disabled, the phase is "Inactive". + type: string + message: + description: Message contains further human-readable info for why the Che cluster is in the phase it currently is. + type: string + phase: + description: Phase is the phase in which the Che cluster as a whole finds itself in. + type: string + reason: + description: A brief CamelCase message indicating details about why the Che cluster is in this state. + type: string + workspaceBaseDomain: + description: The resolved workspace base domain. This is either the copy of the explicitly defined property of the same name in the spec or, if it is undefined in the spec and we're running on OpenShift, the automatically resolved basedomain for routes. + type: string + type: object + gitHubOAuthProvisioned: + description: Indicates whether an Identity Provider instance, Keycloak or RH-SSO, has been configured to integrate with the GitHub OAuth. + type: boolean + gitServerTLSCertificateConfigMapName: + description: The ConfigMap containing certificates to propagate to the Che components and to provide particular configuration for Git. + type: string + helpLink: + description: A URL that points to some URL where to find help related to the current Operator status. + type: string + keycloakProvisioned: + description: Indicates whether an Identity Provider instance, Keycloak or RH-SSO, has been provisioned with realm, client and user. + type: boolean + keycloakURL: + description: Public URL to the Identity Provider server, Keycloak or RH-SSO,. + type: string + message: + description: A human readable message indicating details about why the Pod is in this condition. + type: string + openShiftOAuthUserCredentialsSecret: + description: OpenShift OAuth secret in `openshift-config` namespace that contains user credentials for HTPasswd identity provider. + type: string + openShiftoAuthProvisioned: + description: Indicates whether an Identity Provider instance, Keycloak or RH-SSO, has been configured to integrate with the OpenShift OAuth. + type: boolean + pluginRegistryURL: + description: Public URL to the plugin registry. + type: string + reason: + description: A brief CamelCase message indicating details about why the Pod is in this state. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - name: v2 + schema: + openAPIV3Schema: + description: 'The `CheCluster` custom resource allows defining and managing Eclipse Che server installation. Based on these settings, the Operator automatically creates and maintains several ConfigMaps: `che`, `plugin-registry`, `devfile-registry` that will contain the appropriate environment variables of the various components of the installation. These generated ConfigMaps must NOT be updated manually.' + 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: Desired configuration of Eclipse Che installation. + properties: + components: + description: Che components configuration. + properties: + cheServer: + description: General configuration settings related to the Che server. + properties: + clusterRoles: + description: 'ClusterRoles assigned to Che ServiceAccount. The defaults roles are: - `-cheworkspaces-namespaces-clusterrole` - `-cheworkspaces-clusterrole` - `-cheworkspaces-devworkspace-clusterrole` where the is the namespace where the CheCluster CRD is created. Each role must have a `app.kubernetes.io/part-of=che.eclipse.org` label. The Che Operator must already have all permissions in these ClusterRoles to grant them.' + items: + type: string + type: array + debug: + description: Enables the debug mode for Che server. + type: boolean + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + extraProperties: + additionalProperties: + type: string + description: A map of additional environment variables applied in the generated `che` ConfigMap to be used by the Che server, in addition to the values already generated from other fields of the `CheCluster` custom resource (CR). If the `extraProperties` fields contains a property normally generated in `che` ConfigMap from other CR fields, the value defined in the `extraProperties` is used instead. + type: object + logLevel: + default: INFO + description: 'The log level for the Che server: `INFO` or `DEBUG`.' + type: string + proxy: + description: Proxy server settings for Kubernetes cluster. No additional configuration is required for OpenShift cluster. By specifying these settings for the OpenShift cluster, you override the OpenShift proxy configuration. + properties: + credentialsSecretName: + description: The secret name that contains `user` and `password` for a proxy server. The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + nonProxyHosts: + description: 'A list of hosts that can be reached directly, bypassing the proxy. Specify wild card domain use the following form `.`, for example: - localhost - my.host.com - 123.42.12.32 Use only when a proxy configuration is required. Operator respects OpenShift cluster-wide proxy configuration, defining `nonProxyHosts` in a custom resource leads to merging non-proxy hosts lists from the cluster proxy configuration, and the ones defined in the custom resources. See the following page: https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyURL` fields.' + items: + type: string + type: array + port: + description: Proxy server port. + type: string + url: + description: 'URL (protocol+hostname) of the proxy server. Use only when a proxy configuration is required. Operator respects OpenShift cluster-wide proxy configuration, defining `url` in a custom resource leads to overriding the cluster proxy configuration. See the following page: https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyPort` and `nonProxyHosts` fields.' + type: string + type: object + type: object + dashboard: + description: Configuration settings related to the Dashboard used by the Che installation. + properties: + HeaderMessage: + description: Dashboard header message. + properties: + show: + description: Instructs dashboard to show the message. + type: boolean + warning: + description: Warning message displayed on the user dashboard. + type: string + type: object + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + type: object + database: + description: Configuration settings related to the database used by the Che installation. + properties: + credentialsSecretName: + default: postgres-credentials + description: The secret that contains PostgreSQL `user` and `password` that the Che server uses to connect to the DB. The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + externalDb: + description: Instructs the Operator to deploy a dedicated database. By default, a dedicated PostgreSQL database is deployed as part of the Che installation. When `externalDb` is set as `true`, no dedicated database is deployed by the Operator and you need to provide connection details to the external DB you want to use. + type: boolean + postgresDb: + default: dbche + description: PostgreSQL database name that the Che server uses to connect to the DB. + type: string + postgresHostName: + default: postgres + description: PostgreSQL database hostname that the Che server connects to. Override this value only when using an external database. See field `externalDb`. + type: string + postgresPort: + default: "5432" + description: PostgreSQL Database port the Che server connects to. Override this value only when using an external database. See field `externalDb`. + type: string + pvc: + description: PVC settings for PostgreSQL database. + properties: + claimSize: + description: Persistent Volume Claim size. To update the claim size, Storage class that provisions it must support resize. + type: string + storageClass: + description: Storage class for the Persistent Volume Claim. When omitted or left blank, a default storage class is used. + type: string + type: object + type: object + devWorkspace: + description: DevWorkspace operator configuration. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + runningLimit: + description: The maximum number of running workspaces per user. + type: string + type: object + devfileRegistry: + description: Configuration settings related to the Devfile registry used by the Che installation. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + disableInternalRegistry: + description: Disables internal devfile registry. + type: boolean + externalDevfileRegistries: + description: External devfile registries serving sample ready-to-use devfiles. + items: + description: External devfile registries configuration. + properties: + url: + description: The public UR of the devfile registry that serves sample ready-to-use devfiles. + type: string + type: object + type: array + type: object + imagePuller: + description: Kubernetes Image Puller configuration. + properties: + enable: + description: Install and configure the community supported Kubernetes Image Puller Operator. When you set the value to `true` without providing any specs, it creates a default Kubernetes Image Puller object managed by the Operator. When you set the value to `false`, the Kubernetes Image Puller object is deleted, and the Operator uninstalled, regardless of whether a spec is provided. If you leave the `spec.images` field empty, a set of recommended workspace-related images is automatically detected and pre-pulled after installation. Note that while this Operator and its behavior is community-supported, its payload may be commercially-supported for pulling commercially-supported images. + type: boolean + spec: + description: A Kubernetes Image Puller spec to configure the image puller in the CheCluster + properties: + affinity: + type: string + cachingCPULimit: + type: string + cachingCPURequest: + type: string + cachingIntervalHours: + type: string + cachingMemoryLimit: + type: string + cachingMemoryRequest: + type: string + configMapName: + type: string + daemonsetName: + type: string + deploymentName: + type: string + imagePullSecrets: + type: string + imagePullerImage: + type: string + images: + type: string + nodeSelector: + type: string + type: object + required: + - enable + type: object + metrics: + description: Che server metrics configuration. + properties: + enable: + default: true + description: Enables `metrics` for the Che server endpoint. + type: boolean + type: object + pluginRegistry: + description: Configuration settings related to the plug-in registry used by the Che installation. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + disableInternalRegistry: + description: Disables internal plug-in registry. + type: boolean + externalPluginRegistries: + description: External plugin registries. + items: + description: External plug-in registries configuration. + properties: + url: + description: Public URL of the plug-in registry. + type: string + type: object + type: array + type: object + type: object + containerRegistry: + description: Configuration of an alternative registry that stores Che images. + properties: + hostname: + description: An optional hostname or URL of an alternative container registry to pull images from. This value overrides the container registry hostname defined in all the default container images involved in a Che deployment. This is particularly useful for installing Che in a restricted environment. + type: string + organization: + description: An optional repository name of an alternative registry to pull images from. This value overrides the container registry organization defined in all the default container images involved in a Che deployment. This is particularly useful for installing Eclipse Che in a restricted environment. + type: string + type: object + devEnvironments: + description: Development environment default configuration options. + properties: + defaultNamespace: + description: User's default namespace. + properties: + template: + description: If you don't create user namespaces in advance, this field defines the Kubernetes namespace created when you start your first workspace. You can use ``, `` and `` placeholders, such as che-workspace-. + type: string + type: object + defaultPlugins: + description: Default plug-ins applied to Dev Workspaces. + items: + properties: + editor: + description: The editor id to specify default plug-ins for. + type: string + plugins: + description: Default plug-in URIs for the specified editor. + items: + type: string + type: array + type: object + type: array + nodeSelector: + additionalProperties: + type: string + description: The node selector limits the nodes that can run the workspace pods. + type: object + storage: + description: Workspaces persistent storage. + properties: + pvc: + description: PVC settings. + properties: + claimSize: + description: Persistent Volume Claim size. To update the claim size, Storage class that provisions it must support resize. + type: string + storageClass: + description: Storage class for the Persistent Volume Claim. When omitted or left blank, a default storage class is used. + type: string + type: object + pvcStrategy: + default: common + description: Persistent volume claim strategy for the Che server. Only the `common` strategy is supported (all workspaces PVCs in one volume). For details, see https://github.com/eclipse/che/issues/21185. + type: string + type: object + tolerations: + description: The pod tolerations of the workspace pods limit where the workspace pods can run. + items: + description: The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + trustedCerts: + description: Trusted certificate settings. + properties: + gitTrustedCertsConfigMapName: + description: 'The ConfigMap contains certificates to propagate to the Che components and to provide a particular configuration for Git. See the following page: https://www.eclipse.org/che/docs/stable/administration-guide/deploying-che-with-support-for-git-repositories-with-self-signed-certificates/ The ConfigMap must have a `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + type: object + type: object + networking: + description: Networking, Che authentication and TLS configuration. + properties: + annotations: + additionalProperties: + type: string + description: 'Defines annotations which will be set to an ingress (a route for OpenShift platform). The defaults for kubernetes platforms are: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/proxy-read-timeout: "3600", nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600", nginx.ingress.kubernetes.io/ssl-redirect: "true"' + type: object + auth: + description: Authentication settings. + properties: + gateway: + description: Gateway settings. + properties: + configLabels: + additionalProperties: + type: string + description: Gate configuration labels. + type: object + deployment: + description: 'Deployment override options. Since gateway deployment consists of several containers, they must be distinguished in the configuration by their names: - `gateway` - `configbump` - `oauth-proxy` - `kube-rbac-proxy`' + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + type: object + identityProviderURL: + description: Public URL of the Identity Provider server. + type: string + oAuthClientName: + description: Name of the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side. + type: string + oAuthSecret: + description: Name of the secret set in the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side. + type: string + type: object + domain: + description: 'For an OpenShift cluster, the Operator uses the domain to generate a hostname for a route. The generated hostname follows this pattern: che-.. The is the namespace where the CheCluster CRD is created. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. For Kubernetes cluster it contains a global ingress domain. This MUST be explicitly specified: there are no defaults.' + type: string + hostname: + description: The public hostname of the installed Che server. + type: string + labels: + additionalProperties: + type: string + description: Defines labels which will be set to an ingress (a route for OpenShift platform). + type: object + tlsSecretName: + description: The name of the secret used to set up Ingress TLS termination. If the field is an empty string, the default cluster certificate is used. The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + type: object + type: object + status: + description: CheClusterStatus defines the observed state of Che installation. + properties: + chePhase: + description: Specifies the current phase of the Che deployment. + type: string + cheURL: + description: Public URL to the Che server. + type: string + cheVersion: + description: Current installed Che version. + type: string + devfileRegistryURL: + description: The public URL to the internal devfile registry. + type: string + gatewayPhase: + description: Specifies the current phase of the gateway deployment. + type: string + message: + description: A human readable message indicating details about why the Che deployment is in current phase. + type: string + pluginRegistryURL: + description: The public URL to the internal plug-in registry. + type: string + postgresVersion: + description: The PostgreSQL version of the image in use. + type: string + reason: + description: A brief CamelCase message indicating details about why the Che deployment is in current phase. + type: string + workspaceBaseDomain: + description: The resolved workspace base domain. This is either the copy of the explicitly defined property of the same name in the spec or, if it is undefined in the spec and we're running on OpenShift, the automatically resolved basedomain for routes. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/samples/org_v2alpha1_checluster.yaml b/deploy/deployment/kubernetes/objects/eclipse-che.Namespace.yaml similarity index 81% rename from config/samples/org_v2alpha1_checluster.yaml rename to deploy/deployment/kubernetes/objects/eclipse-che.Namespace.yaml index 4952c9a4f..9d6210af9 100644 --- a/config/samples/org_v2alpha1_checluster.yaml +++ b/deploy/deployment/kubernetes/objects/eclipse-che.Namespace.yaml @@ -10,9 +10,7 @@ # Red Hat, Inc. - initial API and implementation # -apiVersion: org.eclipse.che/v2alpha1 -kind: CheCluster +apiVersion: v1 +kind: Namespace metadata: name: eclipse-che -spec: - # Add fields here diff --git a/deploy/deployment/kubernetes/org_v2_checluster.yaml b/deploy/deployment/kubernetes/org_v2_checluster.yaml new file mode 100644 index 000000000..44f6f381c --- /dev/null +++ b/deploy/deployment/kubernetes/org_v2_checluster.yaml @@ -0,0 +1,35 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: org.eclipse.che/v2 +kind: CheCluster +metadata: + name: eclipse-che + namespace: eclipse-che +spec: + devEnvironments: + defaultNamespace: + template: '-che' + storage: + pvcStrategy: 'common' + components: + database: + externalDb: false + metrics: + enable: true + networking: + domain: '' + tlsSecretName: '' + auth: + identityProviderURL: '' + oAuthClientName: '' + oAuthSecret: '' diff --git a/deploy/deployment/openshift/combined.yaml b/deploy/deployment/openshift/combined.yaml new file mode 100644 index 000000000..5619834d1 --- /dev/null +++ b/deploy/deployment/openshift/combined.yaml @@ -0,0 +1,2692 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: v1 +kind: Namespace +metadata: + name: eclipse-che +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.4.1 + service.beta.openshift.io/inject-cabundle: "true" + labels: + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: checlusters.org.eclipse.che +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: che-operator-service + namespace: eclipse-che + path: /convert + conversionReviewVersions: + - v1 + - v2 + group: org.eclipse.che + names: + kind: CheCluster + listKind: CheClusterList + plural: checlusters + singular: checluster + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: The `CheCluster` custom resource allows defining and managing a Che server installation + 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: Desired configuration of the Che installation. Based on these settings, the Operator automatically creates and maintains several ConfigMaps that will contain the appropriate environment variables the various components of the Che installation. These generated ConfigMaps must NOT be updated manually. + properties: + auth: + description: Configuration settings related to the Authentication used by the Che installation. + properties: + debug: + description: Deprecated. The value of this flag is ignored. Debug internal identity provider. + type: boolean + externalIdentityProvider: + description: 'Deprecated. The value of this flag is ignored. Instructs the Operator on whether or not to deploy a dedicated Identity Provider (Keycloak or RH SSO instance). Instructs the Operator on whether to deploy a dedicated Identity Provider (Keycloak or RH-SSO instance). By default, a dedicated Identity Provider server is deployed as part of the Che installation. When `externalIdentityProvider` is `true`, no dedicated identity provider will be deployed by the Operator and you will need to provide details about the external identity provider you are about to use. See also all the other fields starting with: `identityProvider`.' + type: boolean + gatewayAuthenticationSidecarImage: + description: Gateway sidecar responsible for authentication when NativeUserMode is enabled. See link:https://github.com/oauth2-proxy/oauth2-proxy[oauth2-proxy] or link:https://github.com/openshift/oauth-proxy[openshift/oauth-proxy]. + type: string + gatewayAuthorizationSidecarImage: + description: Gateway sidecar responsible for authorization when NativeUserMode is enabled. See link:https://github.com/brancz/kube-rbac-proxy[kube-rbac-proxy] or link:https://github.com/openshift/kube-rbac-proxy[openshift/kube-rbac-proxy] + type: string + gatewayHeaderRewriteSidecarImage: + description: Deprecated. The value of this flag is ignored. Sidecar functionality is now implemented in Traefik plugin. + type: string + identityProviderAdminUserName: + description: Deprecated. The value of this flag is ignored. Overrides the name of the Identity Provider administrator user. Defaults to `admin`. + type: string + identityProviderClientId: + description: Deprecated. The value of this flag is ignored. Name of a Identity provider, Keycloak or RH-SSO, `client-id` that is used for Che. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to the value of the `flavour` field suffixed with `-public`. + type: string + identityProviderContainerResources: + description: Deprecated. The value of this flag is ignored. Identity provider container custom settings. + properties: + limits: + description: Limits describes the maximum amount of compute resources allowed. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + request: + description: Requests describes the minimum amount of compute resources required. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + type: object + identityProviderImage: + description: Deprecated. The value of this flag is ignored. Overrides the container image used in the Identity Provider, Keycloak or RH-SSO, deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + identityProviderImagePullPolicy: + description: Deprecated. The value of this flag is ignored. Overrides the image pull policy used in the Identity Provider, Keycloak or RH-SSO, deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + identityProviderIngress: + description: Deprecated. The value of this flag is ignored. Ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + identityProviderPassword: + description: Deprecated. The value of this flag is ignored. Overrides the password of Keycloak administrator user. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to an auto-generated password. + type: string + identityProviderPostgresPassword: + description: Deprecated. The value of this flag is ignored. Password for a Identity Provider, Keycloak or RH-SSO, to connect to the database. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to an auto-generated password. + type: string + identityProviderPostgresSecret: + description: 'Deprecated. The value of this flag is ignored. The secret that contains `password` for the Identity Provider, Keycloak or RH-SSO, to connect to the database. When the secret is defined, the `identityProviderPostgresPassword` is ignored. When the value is omitted or left blank, the one of following scenarios applies: 1. `identityProviderPostgresPassword` is defined, then it will be used to connect to the database. 2. `identityProviderPostgresPassword` is not defined, then a new secret with the name `che-identity-postgres-secret` will be created with an auto-generated value for `password`. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + identityProviderRealm: + description: Deprecated. The value of this flag is ignored. Name of a Identity provider, Keycloak or RH-SSO, realm that is used for Che. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to the value of the `flavour` field. + type: string + identityProviderRoute: + description: Deprecated. The value of this flag is ignored. Route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + identityProviderSecret: + description: 'Deprecated. The value of this flag is ignored. The secret that contains `user` and `password` for Identity Provider. When the secret is defined, the `identityProviderAdminUserName` and `identityProviderPassword` are ignored. When the value is omitted or left blank, the one of following scenarios applies: 1. `identityProviderAdminUserName` and `identityProviderPassword` are defined, then they will be used. 2. `identityProviderAdminUserName` or `identityProviderPassword` are not defined, then a new secret with the name `che-identity-secret` will be created with default value `admin` for `user` and with an auto-generated value for `password`. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + identityProviderURL: + description: Public URL of the Identity Provider server (Keycloak / RH-SSO server). Set this ONLY when a use of an external Identity Provider is needed. See the `externalIdentityProvider` field. By default, this will be automatically calculated and set by the Operator. + type: string + initialOpenShiftOAuthUser: + description: Deprecated. The value of this flag is ignored. For operating with the OpenShift OAuth authentication, create a new user account since the kubeadmin can not be used. If the value is true, then a new OpenShift OAuth user will be created for the HTPasswd identity provider. If the value is false and the user has already been created, then it will be removed. If value is an empty, then do nothing. The user's credentials are stored in the `openshift-oauth-user-credentials` secret in 'openshift-config' namespace by Operator. Note that this solution is Openshift 4 platform-specific. + type: boolean + nativeUserMode: + description: Deprecated. The value of this flag is ignored. Enables native user mode. Currently works only on OpenShift and DevWorkspace engine. Native User mode uses OpenShift OAuth directly as identity provider, without Keycloak. + type: boolean + oAuthClientName: + description: Name of the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OpenShiftoAuth` field. + type: string + oAuthSecret: + description: Name of the secret set in the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OAuthClientName` field. + type: string + openShiftoAuth: + description: 'Deprecated. The value of this flag is ignored. Enables the integration of the identity provider (Keycloak / RHSSO) with OpenShift OAuth. Empty value on OpenShift by default. This will allow users to directly login with their OpenShift user through the OpenShift login, and have their workspaces created under personal OpenShift namespaces. WARNING: the `kubeadmin` user is NOT supported, and logging through it will NOT allow accessing the Che Dashboard.' + type: boolean + updateAdminPassword: + description: Deprecated. The value of this flag is ignored. Forces the default `admin` Che user to update password on first login. Defaults to `false`. + type: boolean + type: object + dashboard: + description: Configuration settings related to the User Dashboard used by the Che installation. + properties: + warning: + description: Warning message that will be displayed on the User Dashboard + type: string + type: object + database: + description: Configuration settings related to the database used by the Che installation. + properties: + chePostgresContainerResources: + description: PostgreSQL container custom settings + properties: + limits: + description: Limits describes the maximum amount of compute resources allowed. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + request: + description: Requests describes the minimum amount of compute resources required. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + type: object + chePostgresDb: + description: PostgreSQL database name that the Che server uses to connect to the DB. Defaults to `dbche`. + type: string + chePostgresHostName: + description: PostgreSQL Database host name that the Che server uses to connect to. Defaults is `postgres`. Override this value ONLY when using an external database. See field `externalDb`. In the default case it will be automatically set by the Operator. + type: string + chePostgresPassword: + description: PostgreSQL password that the Che server uses to connect to the DB. When omitted or left blank, it will be set to an automatically generated value. + type: string + chePostgresPort: + description: PostgreSQL Database port that the Che server uses to connect to. Defaults to 5432. Override this value ONLY when using an external database. See field `externalDb`. In the default case it will be automatically set by the Operator. + type: string + chePostgresSecret: + description: 'The secret that contains PostgreSQL`user` and `password` that the Che server uses to connect to the DB. When the secret is defined, the `chePostgresUser` and `chePostgresPassword` are ignored. When the value is omitted or left blank, the one of following scenarios applies: 1. `chePostgresUser` and `chePostgresPassword` are defined, then they will be used to connect to the DB. 2. `chePostgresUser` or `chePostgresPassword` are not defined, then a new secret with the name `postgres-credentials` will be created with default value of `pgche` for `user` and with an auto-generated value for `password`. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + chePostgresUser: + description: PostgreSQL user that the Che server uses to connect to the DB. Defaults to `pgche`. + type: string + externalDb: + description: 'Instructs the Operator on whether to deploy a dedicated database. By default, a dedicated PostgreSQL database is deployed as part of the Che installation. When `externalDb` is `true`, no dedicated database will be deployed by the Operator and you will need to provide connection details to the external DB you are about to use. See also all the fields starting with: `chePostgres`.' + type: boolean + postgresImage: + description: Overrides the container image used in the PostgreSQL database deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + postgresImagePullPolicy: + description: Overrides the image pull policy used in the PostgreSQL database deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + postgresVersion: + description: 'Indicates a PostgreSQL version image to use. Allowed values are: `9.6` and `13.3`. Migrate your PostgreSQL database to switch from one version to another.' + type: string + pvcClaimSize: + description: Size of the persistent volume claim for database. Defaults to `1Gi`. To update pvc storageclass that provisions it must support resize when Eclipse Che has been already deployed. + type: string + type: object + devWorkspace: + description: DevWorkspace operator configuration + properties: + controllerImage: + description: Overrides the container image used in the DevWorkspace controller deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + enable: + description: Deploys the DevWorkspace Operator in the cluster. Does nothing when a matching version of the Operator is already installed. Fails when a non-matching version of the Operator is already installed. + type: boolean + runningLimit: + description: Maximum number of the running workspaces per user. + type: string + required: + - enable + type: object + imagePuller: + description: Kubernetes Image Puller configuration + properties: + enable: + description: Install and configure the Community Supported Kubernetes Image Puller Operator. When set to `true` and no spec is provided, it will create a default KubernetesImagePuller object to be managed by the Operator. When set to `false`, the KubernetesImagePuller object will be deleted, and the Operator will be uninstalled, regardless of whether a spec is provided. If the `spec.images` field is empty, a set of recommended workspace-related images will be automatically detected and pre-pulled after installation. Note that while this Operator and its behavior is community-supported, its payload may be commercially-supported for pulling commercially-supported images. + type: boolean + spec: + description: A KubernetesImagePullerSpec to configure the image puller in the CheCluster + properties: + affinity: + type: string + cachingCPULimit: + type: string + cachingCPURequest: + type: string + cachingIntervalHours: + type: string + cachingMemoryLimit: + type: string + cachingMemoryRequest: + type: string + configMapName: + type: string + daemonsetName: + type: string + deploymentName: + type: string + imagePullSecrets: + type: string + imagePullerImage: + type: string + images: + type: string + nodeSelector: + type: string + type: object + required: + - enable + type: object + k8s: + description: Configuration settings specific to Che installations made on upstream Kubernetes. + properties: + ingressClass: + description: 'Ingress class that will define the which controller will manage ingresses. Defaults to `nginx`. NB: This drives the `kubernetes.io/ingress.class` annotation on Che-related ingresses.' + type: string + ingressDomain: + description: 'Global ingress domain for a Kubernetes cluster. This MUST be explicitly specified: there are no defaults.' + type: string + ingressStrategy: + description: 'Deprecated. The value of this flag is ignored. Strategy for ingress creation. Options are: `multi-host` (host is explicitly provided in ingress), `single-host` (host is provided, path-based rules) and `default-host` (no host is provided, path-based rules). Defaults to `multi-host` Deprecated in favor of `serverExposureStrategy` in the `server` section, which defines this regardless of the cluster type. When both are defined, the `serverExposureStrategy` option takes precedence.' + type: string + securityContextFsGroup: + description: The FSGroup in which the Che Pod and workspace Pods containers runs in. Default value is `1724`. + type: string + securityContextRunAsUser: + description: ID of the user the Che Pod and workspace Pods containers run as. Default value is `1724`. + type: string + singleHostExposureType: + description: Deprecated. The value of this flag is ignored. When the serverExposureStrategy is set to `single-host`, the way the server, registries and workspaces are exposed is further configured by this property. The possible values are `native`, which means that the server and workspaces are exposed using ingresses on K8s or `gateway` where the server and workspaces are exposed using a custom gateway based on link:https://doc.traefik.io/traefik/[Traefik]. All the endpoints whether backed by the ingress or gateway `route` always point to the subpaths on the same domain. Defaults to `native`. + type: string + tlsSecretName: + description: Name of a secret that will be used to setup ingress TLS termination when TLS is enabled. When the field is empty string, the default cluster certificate will be used. See also the `tlsSupport` field. + type: string + type: object + metrics: + description: Configuration settings related to the metrics collection used by the Che installation. + properties: + enable: + description: Enables `metrics` the Che server endpoint. Default to `true`. + type: boolean + type: object + server: + description: General configuration settings related to the Che server, the plugin and devfile registries + properties: + airGapContainerRegistryHostname: + description: Optional host name, or URL, to an alternate container registry to pull images from. This value overrides the container registry host name defined in all the default container images involved in a Che deployment. This is particularly useful to install Che in a restricted environment. + type: string + airGapContainerRegistryOrganization: + description: Optional repository name of an alternate container registry to pull images from. This value overrides the container registry organization defined in all the default container images involved in a Che deployment. This is particularly useful to install Eclipse Che in a restricted environment. + type: string + allowUserDefinedWorkspaceNamespaces: + description: Deprecated. The value of this flag is ignored. Defines that a user is allowed to specify a Kubernetes namespace, or an OpenShift project, which differs from the default. It's NOT RECOMMENDED to set to `true` without OpenShift OAuth configured. The OpenShift infrastructure also uses this property. + type: boolean + cheClusterRoles: + description: A comma-separated list of ClusterRoles that will be assigned to Che ServiceAccount. Each role must have `app.kubernetes.io/part-of=che.eclipse.org` label. Be aware that the Che Operator has to already have all permissions in these ClusterRoles to grant them. + type: string + cheDebug: + description: Enables the debug mode for Che server. Defaults to `false`. + type: string + cheFlavor: + description: Deprecated. The value of this flag is ignored. Specifies a variation of the installation. The options are `che` for upstream Che installations or `devspaces` for Red Hat OpenShift Dev Spaces (formerly Red Hat CodeReady Workspaces) installation + type: string + cheHost: + description: Public host name of the installed Che server. When value is omitted, the value it will be automatically set by the Operator. See the `cheHostTLSSecret` field. + type: string + cheHostTLSSecret: + description: Name of a secret containing certificates to secure ingress or route for the custom host name of the installed Che server. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label. See the `cheHost` field. + type: string + cheImage: + description: Overrides the container image used in Che deployment. This does NOT include the container image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + cheImagePullPolicy: + description: Overrides the image pull policy used in Che deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + cheImageTag: + description: Overrides the tag of the container image used in Che deployment. Omit it or leave it empty to use the default image tag provided by the Operator. + type: string + cheLogLevel: + description: 'Log level for the Che server: `INFO` or `DEBUG`. Defaults to `INFO`.' + type: string + cheServerIngress: + description: The Che server ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + cheServerRoute: + description: The Che server route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + cheWorkspaceClusterRole: + description: Custom cluster role bound to the user for the Che workspaces. The role must have `app.kubernetes.io/part-of=che.eclipse.org` label. The default roles are used when omitted or left blank. + type: string + customCheProperties: + additionalProperties: + type: string + description: Map of additional environment variables that will be applied in the generated `che` ConfigMap to be used by the Che server, in addition to the values already generated from other fields of the `CheCluster` custom resource (CR). When `customCheProperties` contains a property that would be normally generated in `che` ConfigMap from other CR fields, the value defined in the `customCheProperties` is used instead. + type: object + dashboardCpuLimit: + description: Overrides the CPU limit used in the dashboard deployment. In cores. (500m = .5 cores). Default to 500m. + type: string + dashboardCpuRequest: + description: Overrides the CPU request used in the dashboard deployment. In cores. (500m = .5 cores). Default to 100m. + type: string + dashboardImage: + description: Overrides the container image used in the dashboard deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + dashboardImagePullPolicy: + description: Overrides the image pull policy used in the dashboard deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + dashboardIngress: + description: Deprecated. The value of this flag is ignored. Dashboard ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + dashboardMemoryLimit: + description: Overrides the memory limit used in the dashboard deployment. Defaults to 256Mi. + type: string + dashboardMemoryRequest: + description: Overrides the memory request used in the dashboard deployment. Defaults to 16Mi. + type: string + dashboardRoute: + description: Deprecated. The value of this flag is ignored. Dashboard route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + devfileRegistryCpuLimit: + description: Overrides the CPU limit used in the devfile registry deployment. In cores. (500m = .5 cores). Default to 500m. + type: string + devfileRegistryCpuRequest: + description: Overrides the CPU request used in the devfile registry deployment. In cores. (500m = .5 cores). Default to 100m. + type: string + devfileRegistryImage: + description: Overrides the container image used in the devfile registry deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + devfileRegistryIngress: + description: Deprecated. The value of this flag is ignored. The devfile registry ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + devfileRegistryMemoryLimit: + description: Overrides the memory limit used in the devfile registry deployment. Defaults to 256Mi. + type: string + devfileRegistryMemoryRequest: + description: Overrides the memory request used in the devfile registry deployment. Defaults to 16Mi. + type: string + devfileRegistryPullPolicy: + description: Overrides the image pull policy used in the devfile registry deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + devfileRegistryRoute: + description: Deprecated. The value of this flag is ignored. The devfile registry route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + devfileRegistryUrl: + description: Deprecated in favor of `externalDevfileRegistries` fields. + type: string + disableInternalClusterSVCNames: + description: Deprecated. The value of this flag is ignored. Disable internal cluster SVC names usage to communicate between components to speed up the traffic and avoid proxy issues. + type: boolean + externalDevfileRegistries: + description: External devfile registries, that serves sample, ready-to-use devfiles. Configure this in addition to a dedicated devfile registry (when `externalDevfileRegistry` is `false`) or instead of it (when `externalDevfileRegistry` is `true`) + items: + description: Settings for a configuration of the external devfile registries. + properties: + url: + description: Public URL of the devfile registry. + type: string + type: object + type: array + externalDevfileRegistry: + description: Instructs the Operator on whether to deploy a dedicated devfile registry server. By default, a dedicated devfile registry server is started. When `externalDevfileRegistry` is `true`, no such dedicated server will be started by the Operator and configure at least one devfile registry with `externalDevfileRegistries` field. + type: boolean + externalPluginRegistry: + description: Instructs the Operator on whether to deploy a dedicated plugin registry server. By default, a dedicated plugin registry server is started. When `externalPluginRegistry` is `true`, no such dedicated server will be started by the Operator and you will have to manually set the `pluginRegistryUrl` field. + type: boolean + gitSelfSignedCert: + description: When enabled, the certificate from `che-git-self-signed-cert` ConfigMap will be propagated to the Che components and provide particular configuration for Git. Note, the `che-git-self-signed-cert` ConfigMap must have `app.kubernetes.io/part-of=che.eclipse.org` label. + type: boolean + nonProxyHosts: + description: 'List of hosts that will be reached directly, bypassing the proxy. Specify wild card domain use the following form `.` and `|` as delimiter, for example: `localhost|.my.host.com|123.42.12.32` Only use when configuring a proxy is required. Operator respects OpenShift cluster wide proxy configuration and no additional configuration is required, but defining `nonProxyHosts` in a custom resource leads to merging non proxy hosts lists from the cluster proxy configuration and ones defined in the custom resources. See the doc https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyURL` fields.' + type: string + pluginRegistryCpuLimit: + description: Overrides the CPU limit used in the plugin registry deployment. In cores. (500m = .5 cores). Default to 500m. + type: string + pluginRegistryCpuRequest: + description: Overrides the CPU request used in the plugin registry deployment. In cores. (500m = .5 cores). Default to 100m. + type: string + pluginRegistryImage: + description: Overrides the container image used in the plugin registry deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + pluginRegistryIngress: + description: Deprecated. The value of this flag is ignored. Plugin registry ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + pluginRegistryMemoryLimit: + description: Overrides the memory limit used in the plugin registry deployment. Defaults to 256Mi. + type: string + pluginRegistryMemoryRequest: + description: Overrides the memory request used in the plugin registry deployment. Defaults to 16Mi. + type: string + pluginRegistryPullPolicy: + description: Overrides the image pull policy used in the plugin registry deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + pluginRegistryRoute: + description: Deprecated. The value of this flag is ignored. Plugin registry route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + pluginRegistryUrl: + description: Public URL of the plugin registry that serves sample ready-to-use devfiles. Set this ONLY when a use of an external devfile registry is needed. See the `externalPluginRegistry` field. By default, this will be automatically calculated by the Operator. + type: string + proxyPassword: + description: Password of the proxy server. Only use when proxy configuration is required. See the `proxyURL`, `proxyUser` and `proxySecret` fields. + type: string + proxyPort: + description: Port of the proxy server. Only use when configuring a proxy is required. See also the `proxyURL` and `nonProxyHosts` fields. + type: string + proxySecret: + description: The secret that contains `user` and `password` for a proxy server. When the secret is defined, the `proxyUser` and `proxyPassword` are ignored. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + proxyURL: + description: URL (protocol+host name) of the proxy server. This drives the appropriate changes in the `JAVA_OPTS` and `https(s)_proxy` variables in the Che server and workspaces containers. Only use when configuring a proxy is required. Operator respects OpenShift cluster wide proxy configuration and no additional configuration is required, but defining `proxyUrl` in a custom resource leads to overrides the cluster proxy configuration with fields `proxyUrl`, `proxyPort`, `proxyUser` and `proxyPassword` from the custom resource. See the doc https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyPort` and `nonProxyHosts` fields. + type: string + proxyUser: + description: User name of the proxy server. Only use when configuring a proxy is required. See also the `proxyURL`, `proxyPassword` and `proxySecret` fields. + type: string + selfSignedCert: + description: Deprecated. The value of this flag is ignored. The Che Operator will automatically detect whether the router certificate is self-signed and propagate it to other components, such as the Che server. + type: boolean + serverCpuLimit: + description: Overrides the CPU limit used in the Che server deployment In cores. (500m = .5 cores). Default to 1. + type: string + serverCpuRequest: + description: Overrides the CPU request used in the Che server deployment In cores. (500m = .5 cores). Default to 100m. + type: string + serverExposureStrategy: + description: Deprecated. The value of this flag is ignored. Sets the server and workspaces exposure type. Possible values are `multi-host`, `single-host`, `default-host`. Defaults to `multi-host`, which creates a separate ingress, or OpenShift routes, for every required endpoint. `single-host` makes Che exposed on a single host name with workspaces exposed on subpaths. Read the docs to learn about the limitations of this approach. Also consult the `singleHostExposureType` property to further configure how the Operator and the Che server make that happen on Kubernetes. `default-host` exposes the Che server on the host of the cluster. Read the docs to learn about the limitations of this approach. + type: string + serverMemoryLimit: + description: Overrides the memory limit used in the Che server deployment. Defaults to 1Gi. + type: string + serverMemoryRequest: + description: Overrides the memory request used in the Che server deployment. Defaults to 512Mi. + type: string + serverTrustStoreConfigMapName: + description: Name of the ConfigMap with public certificates to add to Java trust store of the Che server. This is often required when adding the OpenShift OAuth provider, which has HTTPS endpoint signed with self-signed cert. The Che server must be aware of its CA cert to be able to request it. This is disabled by default. The Config Map must have `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + singleHostGatewayConfigMapLabels: + additionalProperties: + type: string + description: The labels that need to be present in the ConfigMaps representing the gateway configuration. + type: object + singleHostGatewayConfigSidecarImage: + description: The image used for the gateway sidecar that provides configuration to the gateway. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + singleHostGatewayImage: + description: The image used for the gateway in the single host mode. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + tlsSupport: + description: Deprecated. Instructs the Operator to deploy Che in TLS mode. This is enabled by default. Disabling TLS sometimes cause malfunction of some Che components. + type: boolean + useInternalClusterSVCNames: + description: Deprecated in favor of `disableInternalClusterSVCNames`. + type: boolean + workspaceNamespaceDefault: + description: Defines Kubernetes default namespace in which user's workspaces are created for a case when a user does not override it. It's possible to use ``, `` and `` placeholders, such as che-workspace-. In that case, a new namespace will be created for each user or workspace. + type: string + workspacePodNodeSelector: + additionalProperties: + type: string + description: The node selector that limits the nodes that can run the workspace pods. + type: object + workspacePodTolerations: + description: The pod tolerations put on the workspace pods to limit where the workspace pods can run. + items: + description: The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + workspacesDefaultPlugins: + description: Default plug-ins applied to Devworkspaces. + items: + properties: + editor: + description: The editor id to specify default plug-ins for. + type: string + plugins: + description: Default plug-in uris for the specified editor. + items: + type: string + type: array + type: object + type: array + type: object + storage: + description: Configuration settings related to the persistent storage used by the Che installation. + properties: + postgresPVCStorageClassName: + description: Storage class for the Persistent Volume Claim dedicated to the PostgreSQL database. When omitted or left blank, a default storage class is used. + type: string + preCreateSubPaths: + description: Instructs the Che server to start a special Pod to pre-create a sub-path in the Persistent Volumes. Defaults to `false`, however it will need to enable it according to the configuration of your Kubernetes cluster. + type: boolean + pvcClaimSize: + description: Size of the persistent volume claim for workspaces. Defaults to `10Gi`. + type: string + pvcJobsImage: + description: Overrides the container image used to create sub-paths in the Persistent Volumes. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. See also the `preCreateSubPaths` field. + type: string + pvcStrategy: + description: Persistent volume claim strategy for the Che server. This Can be:`common` (all workspaces PVCs in one volume), `per-workspace` (one PVC per workspace for all declared volumes) and `unique` (one PVC per declared volume). Defaults to `common`. + type: string + workspacePVCStorageClassName: + description: Storage class for the Persistent Volume Claims dedicated to the Che workspaces. When omitted or left blank, a default storage class is used. + type: string + type: object + type: object + status: + description: CheClusterStatus defines the observed state of Che installation + properties: + cheClusterRunning: + description: Status of a Che installation. Can be `Available`, `Unavailable`, or `Available, Rolling Update in Progress`. + type: string + cheURL: + description: Public URL to the Che server. + type: string + cheVersion: + description: Current installed Che version. + type: string + dbProvisioned: + description: Indicates that a PostgreSQL instance has been correctly provisioned or not. + type: boolean + devfileRegistryURL: + description: Public URL to the devfile registry. + type: string + devworkspaceStatus: + description: The status of the Devworkspace subsystem + properties: + gatewayHost: + description: GatewayHost is the resolved host of the ingress/route. This is equal to the Host in the spec on Kubernetes but contains the actual host name of the route if Host is unspecified on OpenShift. + type: string + gatewayPhase: + description: GatewayPhase specifies the phase in which the gateway deployment currently is. If the gateway is disabled, the phase is "Inactive". + type: string + message: + description: Message contains further human-readable info for why the Che cluster is in the phase it currently is. + type: string + phase: + description: Phase is the phase in which the Che cluster as a whole finds itself in. + type: string + reason: + description: A brief CamelCase message indicating details about why the Che cluster is in this state. + type: string + workspaceBaseDomain: + description: The resolved workspace base domain. This is either the copy of the explicitly defined property of the same name in the spec or, if it is undefined in the spec and we're running on OpenShift, the automatically resolved basedomain for routes. + type: string + type: object + gitHubOAuthProvisioned: + description: Indicates whether an Identity Provider instance, Keycloak or RH-SSO, has been configured to integrate with the GitHub OAuth. + type: boolean + gitServerTLSCertificateConfigMapName: + description: The ConfigMap containing certificates to propagate to the Che components and to provide particular configuration for Git. + type: string + helpLink: + description: A URL that points to some URL where to find help related to the current Operator status. + type: string + keycloakProvisioned: + description: Indicates whether an Identity Provider instance, Keycloak or RH-SSO, has been provisioned with realm, client and user. + type: boolean + keycloakURL: + description: Public URL to the Identity Provider server, Keycloak or RH-SSO,. + type: string + message: + description: A human readable message indicating details about why the Pod is in this condition. + type: string + openShiftOAuthUserCredentialsSecret: + description: OpenShift OAuth secret in `openshift-config` namespace that contains user credentials for HTPasswd identity provider. + type: string + openShiftoAuthProvisioned: + description: Indicates whether an Identity Provider instance, Keycloak or RH-SSO, has been configured to integrate with the OpenShift OAuth. + type: boolean + pluginRegistryURL: + description: Public URL to the plugin registry. + type: string + reason: + description: A brief CamelCase message indicating details about why the Pod is in this state. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - name: v2 + schema: + openAPIV3Schema: + description: 'The `CheCluster` custom resource allows defining and managing Eclipse Che server installation. Based on these settings, the Operator automatically creates and maintains several ConfigMaps: `che`, `plugin-registry`, `devfile-registry` that will contain the appropriate environment variables of the various components of the installation. These generated ConfigMaps must NOT be updated manually.' + 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: Desired configuration of Eclipse Che installation. + properties: + components: + description: Che components configuration. + properties: + cheServer: + description: General configuration settings related to the Che server. + properties: + clusterRoles: + description: 'ClusterRoles assigned to Che ServiceAccount. The defaults roles are: - `-cheworkspaces-namespaces-clusterrole` - `-cheworkspaces-clusterrole` - `-cheworkspaces-devworkspace-clusterrole` where the is the namespace where the CheCluster CRD is created. Each role must have a `app.kubernetes.io/part-of=che.eclipse.org` label. The Che Operator must already have all permissions in these ClusterRoles to grant them.' + items: + type: string + type: array + debug: + description: Enables the debug mode for Che server. + type: boolean + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + extraProperties: + additionalProperties: + type: string + description: A map of additional environment variables applied in the generated `che` ConfigMap to be used by the Che server, in addition to the values already generated from other fields of the `CheCluster` custom resource (CR). If the `extraProperties` fields contains a property normally generated in `che` ConfigMap from other CR fields, the value defined in the `extraProperties` is used instead. + type: object + logLevel: + default: INFO + description: 'The log level for the Che server: `INFO` or `DEBUG`.' + type: string + proxy: + description: Proxy server settings for Kubernetes cluster. No additional configuration is required for OpenShift cluster. By specifying these settings for the OpenShift cluster, you override the OpenShift proxy configuration. + properties: + credentialsSecretName: + description: The secret name that contains `user` and `password` for a proxy server. The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + nonProxyHosts: + description: 'A list of hosts that can be reached directly, bypassing the proxy. Specify wild card domain use the following form `.`, for example: - localhost - my.host.com - 123.42.12.32 Use only when a proxy configuration is required. Operator respects OpenShift cluster-wide proxy configuration, defining `nonProxyHosts` in a custom resource leads to merging non-proxy hosts lists from the cluster proxy configuration, and the ones defined in the custom resources. See the following page: https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyURL` fields.' + items: + type: string + type: array + port: + description: Proxy server port. + type: string + url: + description: 'URL (protocol+hostname) of the proxy server. Use only when a proxy configuration is required. Operator respects OpenShift cluster-wide proxy configuration, defining `url` in a custom resource leads to overriding the cluster proxy configuration. See the following page: https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyPort` and `nonProxyHosts` fields.' + type: string + type: object + type: object + dashboard: + description: Configuration settings related to the Dashboard used by the Che installation. + properties: + HeaderMessage: + description: Dashboard header message. + properties: + show: + description: Instructs dashboard to show the message. + type: boolean + warning: + description: Warning message displayed on the user dashboard. + type: string + type: object + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + type: object + database: + description: Configuration settings related to the database used by the Che installation. + properties: + credentialsSecretName: + default: postgres-credentials + description: The secret that contains PostgreSQL `user` and `password` that the Che server uses to connect to the DB. The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + externalDb: + description: Instructs the Operator to deploy a dedicated database. By default, a dedicated PostgreSQL database is deployed as part of the Che installation. When `externalDb` is set as `true`, no dedicated database is deployed by the Operator and you need to provide connection details to the external DB you want to use. + type: boolean + postgresDb: + default: dbche + description: PostgreSQL database name that the Che server uses to connect to the DB. + type: string + postgresHostName: + default: postgres + description: PostgreSQL database hostname that the Che server connects to. Override this value only when using an external database. See field `externalDb`. + type: string + postgresPort: + default: "5432" + description: PostgreSQL Database port the Che server connects to. Override this value only when using an external database. See field `externalDb`. + type: string + pvc: + description: PVC settings for PostgreSQL database. + properties: + claimSize: + description: Persistent Volume Claim size. To update the claim size, Storage class that provisions it must support resize. + type: string + storageClass: + description: Storage class for the Persistent Volume Claim. When omitted or left blank, a default storage class is used. + type: string + type: object + type: object + devWorkspace: + description: DevWorkspace operator configuration. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + runningLimit: + description: The maximum number of running workspaces per user. + type: string + type: object + devfileRegistry: + description: Configuration settings related to the Devfile registry used by the Che installation. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + disableInternalRegistry: + description: Disables internal devfile registry. + type: boolean + externalDevfileRegistries: + description: External devfile registries serving sample ready-to-use devfiles. + items: + description: External devfile registries configuration. + properties: + url: + description: The public UR of the devfile registry that serves sample ready-to-use devfiles. + type: string + type: object + type: array + type: object + imagePuller: + description: Kubernetes Image Puller configuration. + properties: + enable: + description: Install and configure the community supported Kubernetes Image Puller Operator. When you set the value to `true` without providing any specs, it creates a default Kubernetes Image Puller object managed by the Operator. When you set the value to `false`, the Kubernetes Image Puller object is deleted, and the Operator uninstalled, regardless of whether a spec is provided. If you leave the `spec.images` field empty, a set of recommended workspace-related images is automatically detected and pre-pulled after installation. Note that while this Operator and its behavior is community-supported, its payload may be commercially-supported for pulling commercially-supported images. + type: boolean + spec: + description: A Kubernetes Image Puller spec to configure the image puller in the CheCluster + properties: + affinity: + type: string + cachingCPULimit: + type: string + cachingCPURequest: + type: string + cachingIntervalHours: + type: string + cachingMemoryLimit: + type: string + cachingMemoryRequest: + type: string + configMapName: + type: string + daemonsetName: + type: string + deploymentName: + type: string + imagePullSecrets: + type: string + imagePullerImage: + type: string + images: + type: string + nodeSelector: + type: string + type: object + required: + - enable + type: object + metrics: + description: Che server metrics configuration. + properties: + enable: + default: true + description: Enables `metrics` for the Che server endpoint. + type: boolean + type: object + pluginRegistry: + description: Configuration settings related to the plug-in registry used by the Che installation. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + disableInternalRegistry: + description: Disables internal plug-in registry. + type: boolean + externalPluginRegistries: + description: External plugin registries. + items: + description: External plug-in registries configuration. + properties: + url: + description: Public URL of the plug-in registry. + type: string + type: object + type: array + type: object + type: object + containerRegistry: + description: Configuration of an alternative registry that stores Che images. + properties: + hostname: + description: An optional hostname or URL of an alternative container registry to pull images from. This value overrides the container registry hostname defined in all the default container images involved in a Che deployment. This is particularly useful for installing Che in a restricted environment. + type: string + organization: + description: An optional repository name of an alternative registry to pull images from. This value overrides the container registry organization defined in all the default container images involved in a Che deployment. This is particularly useful for installing Eclipse Che in a restricted environment. + type: string + type: object + devEnvironments: + description: Development environment default configuration options. + properties: + defaultNamespace: + description: User's default namespace. + properties: + template: + description: If you don't create user namespaces in advance, this field defines the Kubernetes namespace created when you start your first workspace. You can use ``, `` and `` placeholders, such as che-workspace-. + type: string + type: object + defaultPlugins: + description: Default plug-ins applied to Dev Workspaces. + items: + properties: + editor: + description: The editor id to specify default plug-ins for. + type: string + plugins: + description: Default plug-in URIs for the specified editor. + items: + type: string + type: array + type: object + type: array + nodeSelector: + additionalProperties: + type: string + description: The node selector limits the nodes that can run the workspace pods. + type: object + storage: + description: Workspaces persistent storage. + properties: + pvc: + description: PVC settings. + properties: + claimSize: + description: Persistent Volume Claim size. To update the claim size, Storage class that provisions it must support resize. + type: string + storageClass: + description: Storage class for the Persistent Volume Claim. When omitted or left blank, a default storage class is used. + type: string + type: object + pvcStrategy: + default: common + description: Persistent volume claim strategy for the Che server. Only the `common` strategy is supported (all workspaces PVCs in one volume). For details, see https://github.com/eclipse/che/issues/21185. + type: string + type: object + tolerations: + description: The pod tolerations of the workspace pods limit where the workspace pods can run. + items: + description: The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + trustedCerts: + description: Trusted certificate settings. + properties: + gitTrustedCertsConfigMapName: + description: 'The ConfigMap contains certificates to propagate to the Che components and to provide a particular configuration for Git. See the following page: https://www.eclipse.org/che/docs/stable/administration-guide/deploying-che-with-support-for-git-repositories-with-self-signed-certificates/ The ConfigMap must have a `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + type: object + type: object + networking: + description: Networking, Che authentication and TLS configuration. + properties: + annotations: + additionalProperties: + type: string + description: 'Defines annotations which will be set to an ingress (a route for OpenShift platform). The defaults for kubernetes platforms are: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/proxy-read-timeout: "3600", nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600", nginx.ingress.kubernetes.io/ssl-redirect: "true"' + type: object + auth: + description: Authentication settings. + properties: + gateway: + description: Gateway settings. + properties: + configLabels: + additionalProperties: + type: string + description: Gate configuration labels. + type: object + deployment: + description: 'Deployment override options. Since gateway deployment consists of several containers, they must be distinguished in the configuration by their names: - `gateway` - `configbump` - `oauth-proxy` - `kube-rbac-proxy`' + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + type: object + identityProviderURL: + description: Public URL of the Identity Provider server. + type: string + oAuthClientName: + description: Name of the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side. + type: string + oAuthSecret: + description: Name of the secret set in the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side. + type: string + type: object + domain: + description: 'For an OpenShift cluster, the Operator uses the domain to generate a hostname for a route. The generated hostname follows this pattern: che-.. The is the namespace where the CheCluster CRD is created. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. For Kubernetes cluster it contains a global ingress domain. This MUST be explicitly specified: there are no defaults.' + type: string + hostname: + description: The public hostname of the installed Che server. + type: string + labels: + additionalProperties: + type: string + description: Defines labels which will be set to an ingress (a route for OpenShift platform). + type: object + tlsSecretName: + description: The name of the secret used to set up Ingress TLS termination. If the field is an empty string, the default cluster certificate is used. The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + type: object + type: object + status: + description: CheClusterStatus defines the observed state of Che installation. + properties: + chePhase: + description: Specifies the current phase of the Che deployment. + type: string + cheURL: + description: Public URL to the Che server. + type: string + cheVersion: + description: Current installed Che version. + type: string + devfileRegistryURL: + description: The public URL to the internal devfile registry. + type: string + gatewayPhase: + description: Specifies the current phase of the gateway deployment. + type: string + message: + description: A human readable message indicating details about why the Che deployment is in current phase. + type: string + pluginRegistryURL: + description: The public URL to the internal plug-in registry. + type: string + postgresVersion: + description: The PostgreSQL version of the image in use. + type: string + reason: + description: A brief CamelCase message indicating details about why the Che deployment is in current phase. + type: string + workspaceBaseDomain: + description: The resolved workspace base domain. This is either the copy of the explicitly defined property of the same name in the spec or, if it is undefined in the spec and we're running on OpenShift, the automatically resolved basedomain for routes. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator + namespace: eclipse-che +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator +rules: +- apiGroups: + - extensions + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - batch + resources: + - jobs + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes + - routes/custom-host + verbs: + - '*' +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + - rolebindings + verbs: + - '*' +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterroles + - clusterrolebindings + verbs: + - '*' +- apiGroups: + - "" + resources: + - pods + - services + - serviceaccounts + - endpoints + - persistentvolumeclaims + - events + - configmaps + - secrets + - pods/exec + - pods/log + verbs: + - '*' +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get +- apiGroups: + - apps + - extensions + resources: + - deployments + - replicasets + verbs: + - '*' +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - get + - create +- apiGroups: + - org.eclipse.che + resources: + - checlusters + - checlusters/status + - checlusters/finalizers + verbs: + - '*' +- apiGroups: + - metrics.k8s.io + resources: + - pods + - nodes + verbs: + - get + - list + - watch +- apiGroups: + - operators.coreos.com + resources: + - subscriptions + - clusterserviceversions + - operatorgroups + verbs: + - '*' +- apiGroups: + - packages.operators.coreos.com + resources: + - packagemanifests + verbs: + - get + - list +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create +- apiGroups: + - apps + resourceNames: + - che-operator + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/finalizers + verbs: + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/status + verbs: + - get + - patch + - update +- apiGroups: + - oauth.openshift.io + resources: + - oauthclients + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator +rules: +- apiGroups: + - "" + resources: + - nodes + verbs: + - get +- apiGroups: + - oauth.openshift.io + resources: + - oauthclients + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - config.openshift.io + resources: + - oauths + verbs: + - get + - list + - watch + - patch +- apiGroups: + - config.openshift.io + resources: + - infrastructures + - proxies + verbs: + - get + - list + - watch +- apiGroups: + - user.openshift.io + resources: + - users + verbs: + - list + - delete +- apiGroups: + - user.openshift.io + resources: + - identities + verbs: + - delete +- apiGroups: + - console.openshift.io + resources: + - consolelinks + verbs: + - get + - list + - create + - update + - patch + - delete +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + verbs: + - list + - create + - watch + - update + - get + - delete +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + - rolebindings + verbs: + - get + - create + - update + - delete +- apiGroups: + - authorization.openshift.io + resources: + - roles + - rolebindings + verbs: + - get + - create + - update + - delete +- apiGroups: + - org.eclipse.che + resources: + - checlusters + - checlusters/status + - checlusters/finalizers + - checlusters/status + verbs: + - '*' +- apiGroups: + - project.openshift.io + resources: + - projectrequests + verbs: + - create + - update +- apiGroups: + - project.openshift.io + resources: + - projects + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - create + - update + - watch +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create +- apiGroups: + - apps + resources: + - secrets + verbs: + - list +- apiGroups: + - "" + resources: + - secrets + verbs: + - list + - get + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - create + - get + - list + - watch + - delete +- apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - create + - watch + - delete +- apiGroups: + - apps + - extensions + resources: + - deployments + - replicasets + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes/custom-host + verbs: + - create +- apiGroups: + - "" + resources: + - events + verbs: + - watch +- apiGroups: + - apps + resources: + - replicasets + verbs: + - list + - get + - patch + - delete +- apiGroups: + - extensions + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - list + - create + - watch + - get + - delete +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get + - create + - update +- apiGroups: + - operators.coreos.com + resources: + - subscriptions + verbs: + - get +- apiGroups: + - operators.coreos.com + resources: + - clusterserviceversions + verbs: + - list + - get + - watch +- apiGroups: + - metrics.k8s.io + resources: + - pods + - nodes + verbs: + - get + - list + - watch +- apiGroups: + - cert-manager.io + resources: + - issuers + - certificates + verbs: + - create + - get + - list + - update +- apiGroups: + - "" + resources: + - configmaps + - persistentvolumeclaims + - pods + - secrets + - serviceaccounts + - services + verbs: + - '*' +- apiGroups: + - apps + resourceNames: + - che-operator + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - update + - watch +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/finalizers + verbs: + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/status + verbs: + - get + - patch + - update +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - create + - get +- nonResourceURLs: + - /metrics + verbs: + - get +- apiGroups: + - che.eclipse.org + resources: + - kubernetesimagepullers + verbs: + - '*' +- apiGroups: + - config.openshift.io + resourceNames: + - cluster + resources: + - consoles + verbs: + - get +- apiGroups: + - "" + resources: + - pods/log + verbs: + - get + - list + - watch +- apiGroups: + - workspace.devfile.io + resources: + - devworkspaces + - devworkspacetemplates + verbs: + - get + - list + - watch +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + - components + verbs: + - get + - list + - watch +- apiGroups: + - workspace.devfile.io + resources: + - devworkspaces + - devworkspacetemplates + verbs: + - create + - delete + - deletecollection + - patch + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + - components + verbs: + - create + - delete + - deletecollection + - patch + - update +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +- apiGroups: + - "" + resourceNames: + - workspace-preferences-configmap + resources: + - configmaps + verbs: + - create + - delete + - get + - patch +- apiGroups: + - "" + resources: + - configmaps + - persistentvolumeclaims + - pods + - secrets + - serviceaccounts + verbs: + - '*' +- apiGroups: + - "" + resources: + - events + - namespaces + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create +- apiGroups: + - "" + resourceNames: + - workspace-credentials-secret + resources: + - secrets + verbs: + - create + - delete + - get + - patch +- apiGroups: + - "" + resources: + - services + verbs: + - '*' +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resourceNames: + - devworkspace-controller + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - apps + - extensions + resources: + - deployments + verbs: + - get + - list + - watch +- apiGroups: + - apps + - extensions + resources: + - deployments + - replicasets + verbs: + - '*' +- apiGroups: + - apps + - extensions + resources: + - replicasets + verbs: + - get + - list + - watch +- apiGroups: + - authorization.k8s.io + resources: + - localsubjectaccessreviews + - subjectaccessreviews + verbs: + - create +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - config.openshift.io + resourceNames: + - cluster + resources: + - proxies + verbs: + - get +- apiGroups: + - controller.devfile.io + resources: + - '*' + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/status + verbs: + - get + - patch + - update +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - update +- apiGroups: + - metrics.k8s.io + resources: + - pods + verbs: + - get + - list + - watch +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - create + - get +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - oauth.openshift.io + resources: + - oauthclients + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + - rolebindings + - roles + verbs: + - create + - get + - list + - update + - watch +- apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes/custom-host + verbs: + - create +- apiGroups: + - workspace.devfile.io + resources: + - '*' + verbs: + - '*' +- nonResourceURLs: + - /metrics + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: che-operator +subjects: +- kind: ServiceAccount + name: che-operator +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: che-operator +subjects: +- kind: ServiceAccount + name: che-operator + namespace: eclipse-che +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + service.beta.openshift.io/serving-cert-secret-name: che-operator-webhook-server-cert + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator-service + namespace: eclipse-che +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + app: che-operator +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: che-operator + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator + namespace: eclipse-che +spec: + replicas: 1 + selector: + matchLabels: + app: che-operator + strategy: + type: RollingUpdate + template: + metadata: + labels: + app: che-operator + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + spec: + containers: + - args: + - --leader-elect + command: + - /manager + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: OPERATOR_NAME + value: che-operator + - name: CHE_VERSION + value: next + - name: RELATED_IMAGE_che_server + value: quay.io/eclipse/che-server:next + - name: RELATED_IMAGE_dashboard + value: quay.io/eclipse/che-dashboard:next + - name: RELATED_IMAGE_plugin_registry + value: quay.io/eclipse/che-plugin-registry:next + - name: RELATED_IMAGE_devfile_registry + value: quay.io/eclipse/che-devfile-registry:next + - name: RELATED_IMAGE_che_tls_secrets_creation_job + value: quay.io/eclipse/che-tls-secret-creator:alpine-01a4c34 + - name: RELATED_IMAGE_postgres + value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392 + - name: RELATED_IMAGE_postgres_13_3 + value: quay.io/eclipse/che--centos--postgresql-13-centos7:1-71b24684d64da46f960682cc4216222a7e4ed8b1a31dd5a865b3e71afdea20d2 + - name: RELATED_IMAGE_single_host_gateway + value: quay.io/eclipse/che--traefik:v2.5.0-eb30f9f09a65cee1fab5ef9c64cb4ec91b800dc3fdd738d62a9d4334f0114683 + - name: RELATED_IMAGE_single_host_gateway_config_sidecar + value: quay.io/che-incubator/configbump:0.1.4 + - name: RELATED_IMAGE_devworkspace_controller + value: quay.io/devfile/devworkspace-controller:v0.14.1 + - name: RELATED_IMAGE_gateway_authentication_sidecar + value: quay.io/openshift/origin-oauth-proxy:4.7 + - name: RELATED_IMAGE_gateway_authorization_sidecar + value: quay.io/openshift/origin-kube-rbac-proxy:4.7 + - name: RELATED_IMAGE_gateway_authentication_sidecar_k8s + value: quay.io/oauth2-proxy/oauth2-proxy:v7.2.0 + - name: RELATED_IMAGE_gateway_authorization_sidecar_k8s + value: quay.io/brancz/kube-rbac-proxy:v0.11.0 + - name: RELATED_IMAGE_gateway_header_sidecar + value: quay.io/che-incubator/header-rewrite-proxy:latest + - name: CHE_FLAVOR + value: che + - name: CONSOLE_LINK_NAME + value: che + - name: CONSOLE_LINK_DISPLAY_NAME + value: Eclipse Che + - name: CONSOLE_LINK_SECTION + value: Red Hat Applications + - name: CONSOLE_LINK_IMAGE + value: /dashboard/assets/branding/loader.svg + - name: MAX_CONCURRENT_RECONCILES + value: "1" + - name: ADD_COMPONENT_READINESS_INIT_CONTAINERS + value: "false" + image: quay.io/eclipse/che-operator:next + imagePullPolicy: Always + livenessProbe: + failureThreshold: 10 + httpGet: + path: /healthz + port: 6789 + initialDelaySeconds: 15 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + name: che-operator + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + - containerPort: 60000 + name: metrics + readinessProbe: + failureThreshold: 10 + httpGet: + path: /readyz + port: 6789 + initialDelaySeconds: 5 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 100m + memory: 64Mi + securityContext: + capabilities: + drop: + - ALL + privileged: false + readOnlyRootFilesystem: false + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: webhook-tls-certs + readOnly: true + hostIPC: false + hostNetwork: false + hostPID: false + restartPolicy: Always + serviceAccountName: che-operator + terminationGracePeriodSeconds: 20 + volumes: + - name: webhook-tls-certs + secret: + defaultMode: 420 + secretName: che-operator-webhook-server-cert diff --git a/deploy/deployment/openshift/objects/che-operator-service.Service.yaml b/deploy/deployment/openshift/objects/che-operator-service.Service.yaml new file mode 100644 index 000000000..005fcc774 --- /dev/null +++ b/deploy/deployment/openshift/objects/che-operator-service.Service.yaml @@ -0,0 +1,30 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: v1 +kind: Service +metadata: + annotations: + service.beta.openshift.io/serving-cert-secret-name: che-operator-webhook-server-cert + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator-service + namespace: eclipse-che +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + app: che-operator diff --git a/deploy/deployment/openshift/objects/che-operator.ClusterRole.yaml b/deploy/deployment/openshift/objects/che-operator.ClusterRole.yaml new file mode 100644 index 000000000..0c62c51d7 --- /dev/null +++ b/deploy/deployment/openshift/objects/che-operator.ClusterRole.yaml @@ -0,0 +1,669 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator +rules: +- apiGroups: + - "" + resources: + - nodes + verbs: + - get +- apiGroups: + - oauth.openshift.io + resources: + - oauthclients + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - config.openshift.io + resources: + - oauths + verbs: + - get + - list + - watch + - patch +- apiGroups: + - config.openshift.io + resources: + - infrastructures + - proxies + verbs: + - get + - list + - watch +- apiGroups: + - user.openshift.io + resources: + - users + verbs: + - list + - delete +- apiGroups: + - user.openshift.io + resources: + - identities + verbs: + - delete +- apiGroups: + - console.openshift.io + resources: + - consolelinks + verbs: + - get + - list + - create + - update + - patch + - delete +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + verbs: + - list + - create + - watch + - update + - get + - delete +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + - rolebindings + verbs: + - get + - create + - update + - delete +- apiGroups: + - authorization.openshift.io + resources: + - roles + - rolebindings + verbs: + - get + - create + - update + - delete +- apiGroups: + - org.eclipse.che + resources: + - checlusters + - checlusters/status + - checlusters/finalizers + - checlusters/status + verbs: + - '*' +- apiGroups: + - project.openshift.io + resources: + - projectrequests + verbs: + - create + - update +- apiGroups: + - project.openshift.io + resources: + - projects + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - create + - update + - watch +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create +- apiGroups: + - apps + resources: + - secrets + verbs: + - list +- apiGroups: + - "" + resources: + - secrets + verbs: + - list + - get + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - create + - get + - list + - watch + - delete +- apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - create + - watch + - delete +- apiGroups: + - apps + - extensions + resources: + - deployments + - replicasets + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes/custom-host + verbs: + - create +- apiGroups: + - "" + resources: + - events + verbs: + - watch +- apiGroups: + - apps + resources: + - replicasets + verbs: + - list + - get + - patch + - delete +- apiGroups: + - extensions + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - list + - create + - watch + - get + - delete +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get + - create + - update +- apiGroups: + - operators.coreos.com + resources: + - subscriptions + verbs: + - get +- apiGroups: + - operators.coreos.com + resources: + - clusterserviceversions + verbs: + - list + - get + - watch +- apiGroups: + - metrics.k8s.io + resources: + - pods + - nodes + verbs: + - get + - list + - watch +- apiGroups: + - cert-manager.io + resources: + - issuers + - certificates + verbs: + - create + - get + - list + - update +- apiGroups: + - "" + resources: + - configmaps + - persistentvolumeclaims + - pods + - secrets + - serviceaccounts + - services + verbs: + - '*' +- apiGroups: + - apps + resourceNames: + - che-operator + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - update + - watch +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/finalizers + verbs: + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/status + verbs: + - get + - patch + - update +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - create + - get +- nonResourceURLs: + - /metrics + verbs: + - get +- apiGroups: + - che.eclipse.org + resources: + - kubernetesimagepullers + verbs: + - '*' +- apiGroups: + - config.openshift.io + resourceNames: + - cluster + resources: + - consoles + verbs: + - get +- apiGroups: + - "" + resources: + - pods/log + verbs: + - get + - list + - watch +- apiGroups: + - workspace.devfile.io + resources: + - devworkspaces + - devworkspacetemplates + verbs: + - get + - list + - watch +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + - components + verbs: + - get + - list + - watch +- apiGroups: + - workspace.devfile.io + resources: + - devworkspaces + - devworkspacetemplates + verbs: + - create + - delete + - deletecollection + - patch + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + - components + verbs: + - create + - delete + - deletecollection + - patch + - update +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +- apiGroups: + - "" + resourceNames: + - workspace-preferences-configmap + resources: + - configmaps + verbs: + - create + - delete + - get + - patch +- apiGroups: + - "" + resources: + - configmaps + - persistentvolumeclaims + - pods + - secrets + - serviceaccounts + verbs: + - '*' +- apiGroups: + - "" + resources: + - events + - namespaces + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create +- apiGroups: + - "" + resourceNames: + - workspace-credentials-secret + resources: + - secrets + verbs: + - create + - delete + - get + - patch +- apiGroups: + - "" + resources: + - services + verbs: + - '*' +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resourceNames: + - devworkspace-controller + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - apps + - extensions + resources: + - deployments + verbs: + - get + - list + - watch +- apiGroups: + - apps + - extensions + resources: + - deployments + - replicasets + verbs: + - '*' +- apiGroups: + - apps + - extensions + resources: + - replicasets + verbs: + - get + - list + - watch +- apiGroups: + - authorization.k8s.io + resources: + - localsubjectaccessreviews + - subjectaccessreviews + verbs: + - create +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - config.openshift.io + resourceNames: + - cluster + resources: + - proxies + verbs: + - get +- apiGroups: + - controller.devfile.io + resources: + - '*' + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/status + verbs: + - get + - patch + - update +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - update +- apiGroups: + - metrics.k8s.io + resources: + - pods + verbs: + - get + - list + - watch +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - create + - get +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - oauth.openshift.io + resources: + - oauthclients + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + - rolebindings + - roles + verbs: + - create + - get + - list + - update + - watch +- apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes/custom-host + verbs: + - create +- apiGroups: + - workspace.devfile.io + resources: + - '*' + verbs: + - '*' +- nonResourceURLs: + - /metrics + verbs: + - get diff --git a/deploy/deployment/openshift/objects/che-operator.ClusterRoleBinding.yaml b/deploy/deployment/openshift/objects/che-operator.ClusterRoleBinding.yaml new file mode 100644 index 000000000..d67afa6e7 --- /dev/null +++ b/deploy/deployment/openshift/objects/che-operator.ClusterRoleBinding.yaml @@ -0,0 +1,29 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: che-operator +subjects: +- kind: ServiceAccount + name: che-operator + namespace: eclipse-che diff --git a/deploy/deployment/openshift/objects/che-operator.Deployment.yaml b/deploy/deployment/openshift/objects/che-operator.Deployment.yaml new file mode 100644 index 000000000..7c98d7d57 --- /dev/null +++ b/deploy/deployment/openshift/objects/che-operator.Deployment.yaml @@ -0,0 +1,156 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: che-operator + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator + namespace: eclipse-che +spec: + replicas: 1 + selector: + matchLabels: + app: che-operator + strategy: + type: RollingUpdate + template: + metadata: + labels: + app: che-operator + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + spec: + containers: + - args: + - --leader-elect + command: + - /manager + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: OPERATOR_NAME + value: che-operator + - name: CHE_VERSION + value: next + - name: RELATED_IMAGE_che_server + value: quay.io/eclipse/che-server:next + - name: RELATED_IMAGE_dashboard + value: quay.io/eclipse/che-dashboard:next + - name: RELATED_IMAGE_plugin_registry + value: quay.io/eclipse/che-plugin-registry:next + - name: RELATED_IMAGE_devfile_registry + value: quay.io/eclipse/che-devfile-registry:next + - name: RELATED_IMAGE_che_tls_secrets_creation_job + value: quay.io/eclipse/che-tls-secret-creator:alpine-01a4c34 + - name: RELATED_IMAGE_postgres + value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392 + - name: RELATED_IMAGE_postgres_13_3 + value: quay.io/eclipse/che--centos--postgresql-13-centos7:1-71b24684d64da46f960682cc4216222a7e4ed8b1a31dd5a865b3e71afdea20d2 + - name: RELATED_IMAGE_single_host_gateway + value: quay.io/eclipse/che--traefik:v2.5.0-eb30f9f09a65cee1fab5ef9c64cb4ec91b800dc3fdd738d62a9d4334f0114683 + - name: RELATED_IMAGE_single_host_gateway_config_sidecar + value: quay.io/che-incubator/configbump:0.1.4 + - name: RELATED_IMAGE_devworkspace_controller + value: quay.io/devfile/devworkspace-controller:v0.14.1 + - name: RELATED_IMAGE_gateway_authentication_sidecar + value: quay.io/openshift/origin-oauth-proxy:4.7 + - name: RELATED_IMAGE_gateway_authorization_sidecar + value: quay.io/openshift/origin-kube-rbac-proxy:4.7 + - name: RELATED_IMAGE_gateway_authentication_sidecar_k8s + value: quay.io/oauth2-proxy/oauth2-proxy:v7.2.0 + - name: RELATED_IMAGE_gateway_authorization_sidecar_k8s + value: quay.io/brancz/kube-rbac-proxy:v0.11.0 + - name: RELATED_IMAGE_gateway_header_sidecar + value: quay.io/che-incubator/header-rewrite-proxy:latest + - name: CHE_FLAVOR + value: che + - name: CONSOLE_LINK_NAME + value: che + - name: CONSOLE_LINK_DISPLAY_NAME + value: Eclipse Che + - name: CONSOLE_LINK_SECTION + value: Red Hat Applications + - name: CONSOLE_LINK_IMAGE + value: /dashboard/assets/branding/loader.svg + - name: MAX_CONCURRENT_RECONCILES + value: "1" + - name: ADD_COMPONENT_READINESS_INIT_CONTAINERS + value: "false" + image: quay.io/eclipse/che-operator:next + imagePullPolicy: Always + livenessProbe: + failureThreshold: 10 + httpGet: + path: /healthz + port: 6789 + initialDelaySeconds: 15 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + name: che-operator + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + - containerPort: 60000 + name: metrics + readinessProbe: + failureThreshold: 10 + httpGet: + path: /readyz + port: 6789 + initialDelaySeconds: 5 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 100m + memory: 64Mi + securityContext: + capabilities: + drop: + - ALL + privileged: false + readOnlyRootFilesystem: false + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: webhook-tls-certs + readOnly: true + hostIPC: false + hostNetwork: false + hostPID: false + restartPolicy: Always + serviceAccountName: che-operator + terminationGracePeriodSeconds: 20 + volumes: + - name: webhook-tls-certs + secret: + defaultMode: 420 + secretName: che-operator-webhook-server-cert diff --git a/deploy/deployment/openshift/objects/che-operator.Role.yaml b/deploy/deployment/openshift/objects/che-operator.Role.yaml new file mode 100644 index 000000000..1c02ee053 --- /dev/null +++ b/deploy/deployment/openshift/objects/che-operator.Role.yaml @@ -0,0 +1,184 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator +rules: +- apiGroups: + - extensions + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - batch + resources: + - jobs + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes + - routes/custom-host + verbs: + - '*' +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + - rolebindings + verbs: + - '*' +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterroles + - clusterrolebindings + verbs: + - '*' +- apiGroups: + - "" + resources: + - pods + - services + - serviceaccounts + - endpoints + - persistentvolumeclaims + - events + - configmaps + - secrets + - pods/exec + - pods/log + verbs: + - '*' +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get +- apiGroups: + - apps + - extensions + resources: + - deployments + - replicasets + verbs: + - '*' +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - get + - create +- apiGroups: + - org.eclipse.che + resources: + - checlusters + - checlusters/status + - checlusters/finalizers + verbs: + - '*' +- apiGroups: + - metrics.k8s.io + resources: + - pods + - nodes + verbs: + - get + - list + - watch +- apiGroups: + - operators.coreos.com + resources: + - subscriptions + - clusterserviceversions + - operatorgroups + verbs: + - '*' +- apiGroups: + - packages.operators.coreos.com + resources: + - packagemanifests + verbs: + - get + - list +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create +- apiGroups: + - apps + resourceNames: + - che-operator + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/finalizers + verbs: + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/status + verbs: + - get + - patch + - update +- apiGroups: + - oauth.openshift.io + resources: + - oauthclients + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch diff --git a/deploy/deployment/openshift/objects/che-operator.RoleBinding.yaml b/deploy/deployment/openshift/objects/che-operator.RoleBinding.yaml new file mode 100644 index 000000000..f145c0454 --- /dev/null +++ b/deploy/deployment/openshift/objects/che-operator.RoleBinding.yaml @@ -0,0 +1,28 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: che-operator +subjects: +- kind: ServiceAccount + name: che-operator diff --git a/bundle/next/eclipse-che-preview-openshift/manifests/che-operator_v1_serviceaccount.yaml b/deploy/deployment/openshift/objects/che-operator.ServiceAccount.yaml similarity index 95% rename from bundle/next/eclipse-che-preview-openshift/manifests/che-operator_v1_serviceaccount.yaml rename to deploy/deployment/openshift/objects/che-operator.ServiceAccount.yaml index 010a50d51..71a022293 100644 --- a/bundle/next/eclipse-che-preview-openshift/manifests/che-operator_v1_serviceaccount.yaml +++ b/deploy/deployment/openshift/objects/che-operator.ServiceAccount.yaml @@ -13,10 +13,10 @@ apiVersion: v1 kind: ServiceAccount metadata: - creationTimestamp: null labels: app.kubernetes.io/component: che-operator app.kubernetes.io/instance: che app.kubernetes.io/name: che app.kubernetes.io/part-of: che.eclipse.org name: che-operator + namespace: eclipse-che diff --git a/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml b/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml new file mode 100644 index 000000000..2772d6368 --- /dev/null +++ b/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml @@ -0,0 +1,1646 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.4.1 + service.beta.openshift.io/inject-cabundle: "true" + labels: + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: checlusters.org.eclipse.che +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: che-operator-service + namespace: eclipse-che + path: /convert + conversionReviewVersions: + - v1 + - v2 + group: org.eclipse.che + names: + kind: CheCluster + listKind: CheClusterList + plural: checlusters + singular: checluster + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: The `CheCluster` custom resource allows defining and managing a Che server installation + 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: Desired configuration of the Che installation. Based on these settings, the Operator automatically creates and maintains several ConfigMaps that will contain the appropriate environment variables the various components of the Che installation. These generated ConfigMaps must NOT be updated manually. + properties: + auth: + description: Configuration settings related to the Authentication used by the Che installation. + properties: + debug: + description: Deprecated. The value of this flag is ignored. Debug internal identity provider. + type: boolean + externalIdentityProvider: + description: 'Deprecated. The value of this flag is ignored. Instructs the Operator on whether or not to deploy a dedicated Identity Provider (Keycloak or RH SSO instance). Instructs the Operator on whether to deploy a dedicated Identity Provider (Keycloak or RH-SSO instance). By default, a dedicated Identity Provider server is deployed as part of the Che installation. When `externalIdentityProvider` is `true`, no dedicated identity provider will be deployed by the Operator and you will need to provide details about the external identity provider you are about to use. See also all the other fields starting with: `identityProvider`.' + type: boolean + gatewayAuthenticationSidecarImage: + description: Gateway sidecar responsible for authentication when NativeUserMode is enabled. See link:https://github.com/oauth2-proxy/oauth2-proxy[oauth2-proxy] or link:https://github.com/openshift/oauth-proxy[openshift/oauth-proxy]. + type: string + gatewayAuthorizationSidecarImage: + description: Gateway sidecar responsible for authorization when NativeUserMode is enabled. See link:https://github.com/brancz/kube-rbac-proxy[kube-rbac-proxy] or link:https://github.com/openshift/kube-rbac-proxy[openshift/kube-rbac-proxy] + type: string + gatewayHeaderRewriteSidecarImage: + description: Deprecated. The value of this flag is ignored. Sidecar functionality is now implemented in Traefik plugin. + type: string + identityProviderAdminUserName: + description: Deprecated. The value of this flag is ignored. Overrides the name of the Identity Provider administrator user. Defaults to `admin`. + type: string + identityProviderClientId: + description: Deprecated. The value of this flag is ignored. Name of a Identity provider, Keycloak or RH-SSO, `client-id` that is used for Che. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to the value of the `flavour` field suffixed with `-public`. + type: string + identityProviderContainerResources: + description: Deprecated. The value of this flag is ignored. Identity provider container custom settings. + properties: + limits: + description: Limits describes the maximum amount of compute resources allowed. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + request: + description: Requests describes the minimum amount of compute resources required. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + type: object + identityProviderImage: + description: Deprecated. The value of this flag is ignored. Overrides the container image used in the Identity Provider, Keycloak or RH-SSO, deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + identityProviderImagePullPolicy: + description: Deprecated. The value of this flag is ignored. Overrides the image pull policy used in the Identity Provider, Keycloak or RH-SSO, deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + identityProviderIngress: + description: Deprecated. The value of this flag is ignored. Ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + identityProviderPassword: + description: Deprecated. The value of this flag is ignored. Overrides the password of Keycloak administrator user. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to an auto-generated password. + type: string + identityProviderPostgresPassword: + description: Deprecated. The value of this flag is ignored. Password for a Identity Provider, Keycloak or RH-SSO, to connect to the database. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to an auto-generated password. + type: string + identityProviderPostgresSecret: + description: 'Deprecated. The value of this flag is ignored. The secret that contains `password` for the Identity Provider, Keycloak or RH-SSO, to connect to the database. When the secret is defined, the `identityProviderPostgresPassword` is ignored. When the value is omitted or left blank, the one of following scenarios applies: 1. `identityProviderPostgresPassword` is defined, then it will be used to connect to the database. 2. `identityProviderPostgresPassword` is not defined, then a new secret with the name `che-identity-postgres-secret` will be created with an auto-generated value for `password`. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + identityProviderRealm: + description: Deprecated. The value of this flag is ignored. Name of a Identity provider, Keycloak or RH-SSO, realm that is used for Che. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to the value of the `flavour` field. + type: string + identityProviderRoute: + description: Deprecated. The value of this flag is ignored. Route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + identityProviderSecret: + description: 'Deprecated. The value of this flag is ignored. The secret that contains `user` and `password` for Identity Provider. When the secret is defined, the `identityProviderAdminUserName` and `identityProviderPassword` are ignored. When the value is omitted or left blank, the one of following scenarios applies: 1. `identityProviderAdminUserName` and `identityProviderPassword` are defined, then they will be used. 2. `identityProviderAdminUserName` or `identityProviderPassword` are not defined, then a new secret with the name `che-identity-secret` will be created with default value `admin` for `user` and with an auto-generated value for `password`. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + identityProviderURL: + description: Public URL of the Identity Provider server (Keycloak / RH-SSO server). Set this ONLY when a use of an external Identity Provider is needed. See the `externalIdentityProvider` field. By default, this will be automatically calculated and set by the Operator. + type: string + initialOpenShiftOAuthUser: + description: Deprecated. The value of this flag is ignored. For operating with the OpenShift OAuth authentication, create a new user account since the kubeadmin can not be used. If the value is true, then a new OpenShift OAuth user will be created for the HTPasswd identity provider. If the value is false and the user has already been created, then it will be removed. If value is an empty, then do nothing. The user's credentials are stored in the `openshift-oauth-user-credentials` secret in 'openshift-config' namespace by Operator. Note that this solution is Openshift 4 platform-specific. + type: boolean + nativeUserMode: + description: Deprecated. The value of this flag is ignored. Enables native user mode. Currently works only on OpenShift and DevWorkspace engine. Native User mode uses OpenShift OAuth directly as identity provider, without Keycloak. + type: boolean + oAuthClientName: + description: Name of the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OpenShiftoAuth` field. + type: string + oAuthSecret: + description: Name of the secret set in the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OAuthClientName` field. + type: string + openShiftoAuth: + description: 'Deprecated. The value of this flag is ignored. Enables the integration of the identity provider (Keycloak / RHSSO) with OpenShift OAuth. Empty value on OpenShift by default. This will allow users to directly login with their OpenShift user through the OpenShift login, and have their workspaces created under personal OpenShift namespaces. WARNING: the `kubeadmin` user is NOT supported, and logging through it will NOT allow accessing the Che Dashboard.' + type: boolean + updateAdminPassword: + description: Deprecated. The value of this flag is ignored. Forces the default `admin` Che user to update password on first login. Defaults to `false`. + type: boolean + type: object + dashboard: + description: Configuration settings related to the User Dashboard used by the Che installation. + properties: + warning: + description: Warning message that will be displayed on the User Dashboard + type: string + type: object + database: + description: Configuration settings related to the database used by the Che installation. + properties: + chePostgresContainerResources: + description: PostgreSQL container custom settings + properties: + limits: + description: Limits describes the maximum amount of compute resources allowed. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + request: + description: Requests describes the minimum amount of compute resources required. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + type: object + chePostgresDb: + description: PostgreSQL database name that the Che server uses to connect to the DB. Defaults to `dbche`. + type: string + chePostgresHostName: + description: PostgreSQL Database host name that the Che server uses to connect to. Defaults is `postgres`. Override this value ONLY when using an external database. See field `externalDb`. In the default case it will be automatically set by the Operator. + type: string + chePostgresPassword: + description: PostgreSQL password that the Che server uses to connect to the DB. When omitted or left blank, it will be set to an automatically generated value. + type: string + chePostgresPort: + description: PostgreSQL Database port that the Che server uses to connect to. Defaults to 5432. Override this value ONLY when using an external database. See field `externalDb`. In the default case it will be automatically set by the Operator. + type: string + chePostgresSecret: + description: 'The secret that contains PostgreSQL`user` and `password` that the Che server uses to connect to the DB. When the secret is defined, the `chePostgresUser` and `chePostgresPassword` are ignored. When the value is omitted or left blank, the one of following scenarios applies: 1. `chePostgresUser` and `chePostgresPassword` are defined, then they will be used to connect to the DB. 2. `chePostgresUser` or `chePostgresPassword` are not defined, then a new secret with the name `postgres-credentials` will be created with default value of `pgche` for `user` and with an auto-generated value for `password`. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + chePostgresUser: + description: PostgreSQL user that the Che server uses to connect to the DB. Defaults to `pgche`. + type: string + externalDb: + description: 'Instructs the Operator on whether to deploy a dedicated database. By default, a dedicated PostgreSQL database is deployed as part of the Che installation. When `externalDb` is `true`, no dedicated database will be deployed by the Operator and you will need to provide connection details to the external DB you are about to use. See also all the fields starting with: `chePostgres`.' + type: boolean + postgresImage: + description: Overrides the container image used in the PostgreSQL database deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + postgresImagePullPolicy: + description: Overrides the image pull policy used in the PostgreSQL database deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + postgresVersion: + description: 'Indicates a PostgreSQL version image to use. Allowed values are: `9.6` and `13.3`. Migrate your PostgreSQL database to switch from one version to another.' + type: string + pvcClaimSize: + description: Size of the persistent volume claim for database. Defaults to `1Gi`. To update pvc storageclass that provisions it must support resize when Eclipse Che has been already deployed. + type: string + type: object + devWorkspace: + description: DevWorkspace operator configuration + properties: + controllerImage: + description: Overrides the container image used in the DevWorkspace controller deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + enable: + description: Deploys the DevWorkspace Operator in the cluster. Does nothing when a matching version of the Operator is already installed. Fails when a non-matching version of the Operator is already installed. + type: boolean + runningLimit: + description: Maximum number of the running workspaces per user. + type: string + required: + - enable + type: object + imagePuller: + description: Kubernetes Image Puller configuration + properties: + enable: + description: Install and configure the Community Supported Kubernetes Image Puller Operator. When set to `true` and no spec is provided, it will create a default KubernetesImagePuller object to be managed by the Operator. When set to `false`, the KubernetesImagePuller object will be deleted, and the Operator will be uninstalled, regardless of whether a spec is provided. If the `spec.images` field is empty, a set of recommended workspace-related images will be automatically detected and pre-pulled after installation. Note that while this Operator and its behavior is community-supported, its payload may be commercially-supported for pulling commercially-supported images. + type: boolean + spec: + description: A KubernetesImagePullerSpec to configure the image puller in the CheCluster + properties: + affinity: + type: string + cachingCPULimit: + type: string + cachingCPURequest: + type: string + cachingIntervalHours: + type: string + cachingMemoryLimit: + type: string + cachingMemoryRequest: + type: string + configMapName: + type: string + daemonsetName: + type: string + deploymentName: + type: string + imagePullSecrets: + type: string + imagePullerImage: + type: string + images: + type: string + nodeSelector: + type: string + type: object + required: + - enable + type: object + k8s: + description: Configuration settings specific to Che installations made on upstream Kubernetes. + properties: + ingressClass: + description: 'Ingress class that will define the which controller will manage ingresses. Defaults to `nginx`. NB: This drives the `kubernetes.io/ingress.class` annotation on Che-related ingresses.' + type: string + ingressDomain: + description: 'Global ingress domain for a Kubernetes cluster. This MUST be explicitly specified: there are no defaults.' + type: string + ingressStrategy: + description: 'Deprecated. The value of this flag is ignored. Strategy for ingress creation. Options are: `multi-host` (host is explicitly provided in ingress), `single-host` (host is provided, path-based rules) and `default-host` (no host is provided, path-based rules). Defaults to `multi-host` Deprecated in favor of `serverExposureStrategy` in the `server` section, which defines this regardless of the cluster type. When both are defined, the `serverExposureStrategy` option takes precedence.' + type: string + securityContextFsGroup: + description: The FSGroup in which the Che Pod and workspace Pods containers runs in. Default value is `1724`. + type: string + securityContextRunAsUser: + description: ID of the user the Che Pod and workspace Pods containers run as. Default value is `1724`. + type: string + singleHostExposureType: + description: Deprecated. The value of this flag is ignored. When the serverExposureStrategy is set to `single-host`, the way the server, registries and workspaces are exposed is further configured by this property. The possible values are `native`, which means that the server and workspaces are exposed using ingresses on K8s or `gateway` where the server and workspaces are exposed using a custom gateway based on link:https://doc.traefik.io/traefik/[Traefik]. All the endpoints whether backed by the ingress or gateway `route` always point to the subpaths on the same domain. Defaults to `native`. + type: string + tlsSecretName: + description: Name of a secret that will be used to setup ingress TLS termination when TLS is enabled. When the field is empty string, the default cluster certificate will be used. See also the `tlsSupport` field. + type: string + type: object + metrics: + description: Configuration settings related to the metrics collection used by the Che installation. + properties: + enable: + description: Enables `metrics` the Che server endpoint. Default to `true`. + type: boolean + type: object + server: + description: General configuration settings related to the Che server, the plugin and devfile registries + properties: + airGapContainerRegistryHostname: + description: Optional host name, or URL, to an alternate container registry to pull images from. This value overrides the container registry host name defined in all the default container images involved in a Che deployment. This is particularly useful to install Che in a restricted environment. + type: string + airGapContainerRegistryOrganization: + description: Optional repository name of an alternate container registry to pull images from. This value overrides the container registry organization defined in all the default container images involved in a Che deployment. This is particularly useful to install Eclipse Che in a restricted environment. + type: string + allowUserDefinedWorkspaceNamespaces: + description: Deprecated. The value of this flag is ignored. Defines that a user is allowed to specify a Kubernetes namespace, or an OpenShift project, which differs from the default. It's NOT RECOMMENDED to set to `true` without OpenShift OAuth configured. The OpenShift infrastructure also uses this property. + type: boolean + cheClusterRoles: + description: A comma-separated list of ClusterRoles that will be assigned to Che ServiceAccount. Each role must have `app.kubernetes.io/part-of=che.eclipse.org` label. Be aware that the Che Operator has to already have all permissions in these ClusterRoles to grant them. + type: string + cheDebug: + description: Enables the debug mode for Che server. Defaults to `false`. + type: string + cheFlavor: + description: Deprecated. The value of this flag is ignored. Specifies a variation of the installation. The options are `che` for upstream Che installations or `devspaces` for Red Hat OpenShift Dev Spaces (formerly Red Hat CodeReady Workspaces) installation + type: string + cheHost: + description: Public host name of the installed Che server. When value is omitted, the value it will be automatically set by the Operator. See the `cheHostTLSSecret` field. + type: string + cheHostTLSSecret: + description: Name of a secret containing certificates to secure ingress or route for the custom host name of the installed Che server. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label. See the `cheHost` field. + type: string + cheImage: + description: Overrides the container image used in Che deployment. This does NOT include the container image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + cheImagePullPolicy: + description: Overrides the image pull policy used in Che deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + cheImageTag: + description: Overrides the tag of the container image used in Che deployment. Omit it or leave it empty to use the default image tag provided by the Operator. + type: string + cheLogLevel: + description: 'Log level for the Che server: `INFO` or `DEBUG`. Defaults to `INFO`.' + type: string + cheServerIngress: + description: The Che server ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + cheServerRoute: + description: The Che server route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + cheWorkspaceClusterRole: + description: Custom cluster role bound to the user for the Che workspaces. The role must have `app.kubernetes.io/part-of=che.eclipse.org` label. The default roles are used when omitted or left blank. + type: string + customCheProperties: + additionalProperties: + type: string + description: Map of additional environment variables that will be applied in the generated `che` ConfigMap to be used by the Che server, in addition to the values already generated from other fields of the `CheCluster` custom resource (CR). When `customCheProperties` contains a property that would be normally generated in `che` ConfigMap from other CR fields, the value defined in the `customCheProperties` is used instead. + type: object + dashboardCpuLimit: + description: Overrides the CPU limit used in the dashboard deployment. In cores. (500m = .5 cores). Default to 500m. + type: string + dashboardCpuRequest: + description: Overrides the CPU request used in the dashboard deployment. In cores. (500m = .5 cores). Default to 100m. + type: string + dashboardImage: + description: Overrides the container image used in the dashboard deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + dashboardImagePullPolicy: + description: Overrides the image pull policy used in the dashboard deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + dashboardIngress: + description: Deprecated. The value of this flag is ignored. Dashboard ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + dashboardMemoryLimit: + description: Overrides the memory limit used in the dashboard deployment. Defaults to 256Mi. + type: string + dashboardMemoryRequest: + description: Overrides the memory request used in the dashboard deployment. Defaults to 16Mi. + type: string + dashboardRoute: + description: Deprecated. The value of this flag is ignored. Dashboard route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + devfileRegistryCpuLimit: + description: Overrides the CPU limit used in the devfile registry deployment. In cores. (500m = .5 cores). Default to 500m. + type: string + devfileRegistryCpuRequest: + description: Overrides the CPU request used in the devfile registry deployment. In cores. (500m = .5 cores). Default to 100m. + type: string + devfileRegistryImage: + description: Overrides the container image used in the devfile registry deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + devfileRegistryIngress: + description: Deprecated. The value of this flag is ignored. The devfile registry ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + devfileRegistryMemoryLimit: + description: Overrides the memory limit used in the devfile registry deployment. Defaults to 256Mi. + type: string + devfileRegistryMemoryRequest: + description: Overrides the memory request used in the devfile registry deployment. Defaults to 16Mi. + type: string + devfileRegistryPullPolicy: + description: Overrides the image pull policy used in the devfile registry deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + devfileRegistryRoute: + description: Deprecated. The value of this flag is ignored. The devfile registry route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + devfileRegistryUrl: + description: Deprecated in favor of `externalDevfileRegistries` fields. + type: string + disableInternalClusterSVCNames: + description: Deprecated. The value of this flag is ignored. Disable internal cluster SVC names usage to communicate between components to speed up the traffic and avoid proxy issues. + type: boolean + externalDevfileRegistries: + description: External devfile registries, that serves sample, ready-to-use devfiles. Configure this in addition to a dedicated devfile registry (when `externalDevfileRegistry` is `false`) or instead of it (when `externalDevfileRegistry` is `true`) + items: + description: Settings for a configuration of the external devfile registries. + properties: + url: + description: Public URL of the devfile registry. + type: string + type: object + type: array + externalDevfileRegistry: + description: Instructs the Operator on whether to deploy a dedicated devfile registry server. By default, a dedicated devfile registry server is started. When `externalDevfileRegistry` is `true`, no such dedicated server will be started by the Operator and configure at least one devfile registry with `externalDevfileRegistries` field. + type: boolean + externalPluginRegistry: + description: Instructs the Operator on whether to deploy a dedicated plugin registry server. By default, a dedicated plugin registry server is started. When `externalPluginRegistry` is `true`, no such dedicated server will be started by the Operator and you will have to manually set the `pluginRegistryUrl` field. + type: boolean + gitSelfSignedCert: + description: When enabled, the certificate from `che-git-self-signed-cert` ConfigMap will be propagated to the Che components and provide particular configuration for Git. Note, the `che-git-self-signed-cert` ConfigMap must have `app.kubernetes.io/part-of=che.eclipse.org` label. + type: boolean + nonProxyHosts: + description: 'List of hosts that will be reached directly, bypassing the proxy. Specify wild card domain use the following form `.` and `|` as delimiter, for example: `localhost|.my.host.com|123.42.12.32` Only use when configuring a proxy is required. Operator respects OpenShift cluster wide proxy configuration and no additional configuration is required, but defining `nonProxyHosts` in a custom resource leads to merging non proxy hosts lists from the cluster proxy configuration and ones defined in the custom resources. See the doc https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyURL` fields.' + type: string + pluginRegistryCpuLimit: + description: Overrides the CPU limit used in the plugin registry deployment. In cores. (500m = .5 cores). Default to 500m. + type: string + pluginRegistryCpuRequest: + description: Overrides the CPU request used in the plugin registry deployment. In cores. (500m = .5 cores). Default to 100m. + type: string + pluginRegistryImage: + description: Overrides the container image used in the plugin registry deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + pluginRegistryIngress: + description: Deprecated. The value of this flag is ignored. Plugin registry ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + pluginRegistryMemoryLimit: + description: Overrides the memory limit used in the plugin registry deployment. Defaults to 256Mi. + type: string + pluginRegistryMemoryRequest: + description: Overrides the memory request used in the plugin registry deployment. Defaults to 16Mi. + type: string + pluginRegistryPullPolicy: + description: Overrides the image pull policy used in the plugin registry deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + pluginRegistryRoute: + description: Deprecated. The value of this flag is ignored. Plugin registry route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + pluginRegistryUrl: + description: Public URL of the plugin registry that serves sample ready-to-use devfiles. Set this ONLY when a use of an external devfile registry is needed. See the `externalPluginRegistry` field. By default, this will be automatically calculated by the Operator. + type: string + proxyPassword: + description: Password of the proxy server. Only use when proxy configuration is required. See the `proxyURL`, `proxyUser` and `proxySecret` fields. + type: string + proxyPort: + description: Port of the proxy server. Only use when configuring a proxy is required. See also the `proxyURL` and `nonProxyHosts` fields. + type: string + proxySecret: + description: The secret that contains `user` and `password` for a proxy server. When the secret is defined, the `proxyUser` and `proxyPassword` are ignored. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + proxyURL: + description: URL (protocol+host name) of the proxy server. This drives the appropriate changes in the `JAVA_OPTS` and `https(s)_proxy` variables in the Che server and workspaces containers. Only use when configuring a proxy is required. Operator respects OpenShift cluster wide proxy configuration and no additional configuration is required, but defining `proxyUrl` in a custom resource leads to overrides the cluster proxy configuration with fields `proxyUrl`, `proxyPort`, `proxyUser` and `proxyPassword` from the custom resource. See the doc https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyPort` and `nonProxyHosts` fields. + type: string + proxyUser: + description: User name of the proxy server. Only use when configuring a proxy is required. See also the `proxyURL`, `proxyPassword` and `proxySecret` fields. + type: string + selfSignedCert: + description: Deprecated. The value of this flag is ignored. The Che Operator will automatically detect whether the router certificate is self-signed and propagate it to other components, such as the Che server. + type: boolean + serverCpuLimit: + description: Overrides the CPU limit used in the Che server deployment In cores. (500m = .5 cores). Default to 1. + type: string + serverCpuRequest: + description: Overrides the CPU request used in the Che server deployment In cores. (500m = .5 cores). Default to 100m. + type: string + serverExposureStrategy: + description: Deprecated. The value of this flag is ignored. Sets the server and workspaces exposure type. Possible values are `multi-host`, `single-host`, `default-host`. Defaults to `multi-host`, which creates a separate ingress, or OpenShift routes, for every required endpoint. `single-host` makes Che exposed on a single host name with workspaces exposed on subpaths. Read the docs to learn about the limitations of this approach. Also consult the `singleHostExposureType` property to further configure how the Operator and the Che server make that happen on Kubernetes. `default-host` exposes the Che server on the host of the cluster. Read the docs to learn about the limitations of this approach. + type: string + serverMemoryLimit: + description: Overrides the memory limit used in the Che server deployment. Defaults to 1Gi. + type: string + serverMemoryRequest: + description: Overrides the memory request used in the Che server deployment. Defaults to 512Mi. + type: string + serverTrustStoreConfigMapName: + description: Name of the ConfigMap with public certificates to add to Java trust store of the Che server. This is often required when adding the OpenShift OAuth provider, which has HTTPS endpoint signed with self-signed cert. The Che server must be aware of its CA cert to be able to request it. This is disabled by default. The Config Map must have `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + singleHostGatewayConfigMapLabels: + additionalProperties: + type: string + description: The labels that need to be present in the ConfigMaps representing the gateway configuration. + type: object + singleHostGatewayConfigSidecarImage: + description: The image used for the gateway sidecar that provides configuration to the gateway. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + singleHostGatewayImage: + description: The image used for the gateway in the single host mode. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + tlsSupport: + description: Deprecated. Instructs the Operator to deploy Che in TLS mode. This is enabled by default. Disabling TLS sometimes cause malfunction of some Che components. + type: boolean + useInternalClusterSVCNames: + description: Deprecated in favor of `disableInternalClusterSVCNames`. + type: boolean + workspaceNamespaceDefault: + description: Defines Kubernetes default namespace in which user's workspaces are created for a case when a user does not override it. It's possible to use ``, `` and `` placeholders, such as che-workspace-. In that case, a new namespace will be created for each user or workspace. + type: string + workspacePodNodeSelector: + additionalProperties: + type: string + description: The node selector that limits the nodes that can run the workspace pods. + type: object + workspacePodTolerations: + description: The pod tolerations put on the workspace pods to limit where the workspace pods can run. + items: + description: The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + workspacesDefaultPlugins: + description: Default plug-ins applied to Devworkspaces. + items: + properties: + editor: + description: The editor id to specify default plug-ins for. + type: string + plugins: + description: Default plug-in uris for the specified editor. + items: + type: string + type: array + type: object + type: array + type: object + storage: + description: Configuration settings related to the persistent storage used by the Che installation. + properties: + postgresPVCStorageClassName: + description: Storage class for the Persistent Volume Claim dedicated to the PostgreSQL database. When omitted or left blank, a default storage class is used. + type: string + preCreateSubPaths: + description: Instructs the Che server to start a special Pod to pre-create a sub-path in the Persistent Volumes. Defaults to `false`, however it will need to enable it according to the configuration of your Kubernetes cluster. + type: boolean + pvcClaimSize: + description: Size of the persistent volume claim for workspaces. Defaults to `10Gi`. + type: string + pvcJobsImage: + description: Overrides the container image used to create sub-paths in the Persistent Volumes. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. See also the `preCreateSubPaths` field. + type: string + pvcStrategy: + description: Persistent volume claim strategy for the Che server. This Can be:`common` (all workspaces PVCs in one volume), `per-workspace` (one PVC per workspace for all declared volumes) and `unique` (one PVC per declared volume). Defaults to `common`. + type: string + workspacePVCStorageClassName: + description: Storage class for the Persistent Volume Claims dedicated to the Che workspaces. When omitted or left blank, a default storage class is used. + type: string + type: object + type: object + status: + description: CheClusterStatus defines the observed state of Che installation + properties: + cheClusterRunning: + description: Status of a Che installation. Can be `Available`, `Unavailable`, or `Available, Rolling Update in Progress`. + type: string + cheURL: + description: Public URL to the Che server. + type: string + cheVersion: + description: Current installed Che version. + type: string + dbProvisioned: + description: Indicates that a PostgreSQL instance has been correctly provisioned or not. + type: boolean + devfileRegistryURL: + description: Public URL to the devfile registry. + type: string + devworkspaceStatus: + description: The status of the Devworkspace subsystem + properties: + gatewayHost: + description: GatewayHost is the resolved host of the ingress/route. This is equal to the Host in the spec on Kubernetes but contains the actual host name of the route if Host is unspecified on OpenShift. + type: string + gatewayPhase: + description: GatewayPhase specifies the phase in which the gateway deployment currently is. If the gateway is disabled, the phase is "Inactive". + type: string + message: + description: Message contains further human-readable info for why the Che cluster is in the phase it currently is. + type: string + phase: + description: Phase is the phase in which the Che cluster as a whole finds itself in. + type: string + reason: + description: A brief CamelCase message indicating details about why the Che cluster is in this state. + type: string + workspaceBaseDomain: + description: The resolved workspace base domain. This is either the copy of the explicitly defined property of the same name in the spec or, if it is undefined in the spec and we're running on OpenShift, the automatically resolved basedomain for routes. + type: string + type: object + gitHubOAuthProvisioned: + description: Indicates whether an Identity Provider instance, Keycloak or RH-SSO, has been configured to integrate with the GitHub OAuth. + type: boolean + gitServerTLSCertificateConfigMapName: + description: The ConfigMap containing certificates to propagate to the Che components and to provide particular configuration for Git. + type: string + helpLink: + description: A URL that points to some URL where to find help related to the current Operator status. + type: string + keycloakProvisioned: + description: Indicates whether an Identity Provider instance, Keycloak or RH-SSO, has been provisioned with realm, client and user. + type: boolean + keycloakURL: + description: Public URL to the Identity Provider server, Keycloak or RH-SSO,. + type: string + message: + description: A human readable message indicating details about why the Pod is in this condition. + type: string + openShiftOAuthUserCredentialsSecret: + description: OpenShift OAuth secret in `openshift-config` namespace that contains user credentials for HTPasswd identity provider. + type: string + openShiftoAuthProvisioned: + description: Indicates whether an Identity Provider instance, Keycloak or RH-SSO, has been configured to integrate with the OpenShift OAuth. + type: boolean + pluginRegistryURL: + description: Public URL to the plugin registry. + type: string + reason: + description: A brief CamelCase message indicating details about why the Pod is in this state. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - name: v2 + schema: + openAPIV3Schema: + description: 'The `CheCluster` custom resource allows defining and managing Eclipse Che server installation. Based on these settings, the Operator automatically creates and maintains several ConfigMaps: `che`, `plugin-registry`, `devfile-registry` that will contain the appropriate environment variables of the various components of the installation. These generated ConfigMaps must NOT be updated manually.' + 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: Desired configuration of Eclipse Che installation. + properties: + components: + description: Che components configuration. + properties: + cheServer: + description: General configuration settings related to the Che server. + properties: + clusterRoles: + description: 'ClusterRoles assigned to Che ServiceAccount. The defaults roles are: - `-cheworkspaces-namespaces-clusterrole` - `-cheworkspaces-clusterrole` - `-cheworkspaces-devworkspace-clusterrole` where the is the namespace where the CheCluster CRD is created. Each role must have a `app.kubernetes.io/part-of=che.eclipse.org` label. The Che Operator must already have all permissions in these ClusterRoles to grant them.' + items: + type: string + type: array + debug: + description: Enables the debug mode for Che server. + type: boolean + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + extraProperties: + additionalProperties: + type: string + description: A map of additional environment variables applied in the generated `che` ConfigMap to be used by the Che server, in addition to the values already generated from other fields of the `CheCluster` custom resource (CR). If the `extraProperties` fields contains a property normally generated in `che` ConfigMap from other CR fields, the value defined in the `extraProperties` is used instead. + type: object + logLevel: + default: INFO + description: 'The log level for the Che server: `INFO` or `DEBUG`.' + type: string + proxy: + description: Proxy server settings for Kubernetes cluster. No additional configuration is required for OpenShift cluster. By specifying these settings for the OpenShift cluster, you override the OpenShift proxy configuration. + properties: + credentialsSecretName: + description: The secret name that contains `user` and `password` for a proxy server. The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + nonProxyHosts: + description: 'A list of hosts that can be reached directly, bypassing the proxy. Specify wild card domain use the following form `.`, for example: - localhost - my.host.com - 123.42.12.32 Use only when a proxy configuration is required. Operator respects OpenShift cluster-wide proxy configuration, defining `nonProxyHosts` in a custom resource leads to merging non-proxy hosts lists from the cluster proxy configuration, and the ones defined in the custom resources. See the following page: https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyURL` fields.' + items: + type: string + type: array + port: + description: Proxy server port. + type: string + url: + description: 'URL (protocol+hostname) of the proxy server. Use only when a proxy configuration is required. Operator respects OpenShift cluster-wide proxy configuration, defining `url` in a custom resource leads to overriding the cluster proxy configuration. See the following page: https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyPort` and `nonProxyHosts` fields.' + type: string + type: object + type: object + dashboard: + description: Configuration settings related to the Dashboard used by the Che installation. + properties: + HeaderMessage: + description: Dashboard header message. + properties: + show: + description: Instructs dashboard to show the message. + type: boolean + warning: + description: Warning message displayed on the user dashboard. + type: string + type: object + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + type: object + database: + description: Configuration settings related to the database used by the Che installation. + properties: + credentialsSecretName: + default: postgres-credentials + description: The secret that contains PostgreSQL `user` and `password` that the Che server uses to connect to the DB. The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + externalDb: + description: Instructs the Operator to deploy a dedicated database. By default, a dedicated PostgreSQL database is deployed as part of the Che installation. When `externalDb` is set as `true`, no dedicated database is deployed by the Operator and you need to provide connection details to the external DB you want to use. + type: boolean + postgresDb: + default: dbche + description: PostgreSQL database name that the Che server uses to connect to the DB. + type: string + postgresHostName: + default: postgres + description: PostgreSQL database hostname that the Che server connects to. Override this value only when using an external database. See field `externalDb`. + type: string + postgresPort: + default: "5432" + description: PostgreSQL Database port the Che server connects to. Override this value only when using an external database. See field `externalDb`. + type: string + pvc: + description: PVC settings for PostgreSQL database. + properties: + claimSize: + description: Persistent Volume Claim size. To update the claim size, Storage class that provisions it must support resize. + type: string + storageClass: + description: Storage class for the Persistent Volume Claim. When omitted or left blank, a default storage class is used. + type: string + type: object + type: object + devWorkspace: + description: DevWorkspace operator configuration. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + runningLimit: + description: The maximum number of running workspaces per user. + type: string + type: object + devfileRegistry: + description: Configuration settings related to the Devfile registry used by the Che installation. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + disableInternalRegistry: + description: Disables internal devfile registry. + type: boolean + externalDevfileRegistries: + description: External devfile registries serving sample ready-to-use devfiles. + items: + description: External devfile registries configuration. + properties: + url: + description: The public UR of the devfile registry that serves sample ready-to-use devfiles. + type: string + type: object + type: array + type: object + imagePuller: + description: Kubernetes Image Puller configuration. + properties: + enable: + description: Install and configure the community supported Kubernetes Image Puller Operator. When you set the value to `true` without providing any specs, it creates a default Kubernetes Image Puller object managed by the Operator. When you set the value to `false`, the Kubernetes Image Puller object is deleted, and the Operator uninstalled, regardless of whether a spec is provided. If you leave the `spec.images` field empty, a set of recommended workspace-related images is automatically detected and pre-pulled after installation. Note that while this Operator and its behavior is community-supported, its payload may be commercially-supported for pulling commercially-supported images. + type: boolean + spec: + description: A Kubernetes Image Puller spec to configure the image puller in the CheCluster + properties: + affinity: + type: string + cachingCPULimit: + type: string + cachingCPURequest: + type: string + cachingIntervalHours: + type: string + cachingMemoryLimit: + type: string + cachingMemoryRequest: + type: string + configMapName: + type: string + daemonsetName: + type: string + deploymentName: + type: string + imagePullSecrets: + type: string + imagePullerImage: + type: string + images: + type: string + nodeSelector: + type: string + type: object + required: + - enable + type: object + metrics: + description: Che server metrics configuration. + properties: + enable: + default: true + description: Enables `metrics` for the Che server endpoint. + type: boolean + type: object + pluginRegistry: + description: Configuration settings related to the plug-in registry used by the Che installation. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + disableInternalRegistry: + description: Disables internal plug-in registry. + type: boolean + externalPluginRegistries: + description: External plugin registries. + items: + description: External plug-in registries configuration. + properties: + url: + description: Public URL of the plug-in registry. + type: string + type: object + type: array + type: object + type: object + containerRegistry: + description: Configuration of an alternative registry that stores Che images. + properties: + hostname: + description: An optional hostname or URL of an alternative container registry to pull images from. This value overrides the container registry hostname defined in all the default container images involved in a Che deployment. This is particularly useful for installing Che in a restricted environment. + type: string + organization: + description: An optional repository name of an alternative registry to pull images from. This value overrides the container registry organization defined in all the default container images involved in a Che deployment. This is particularly useful for installing Eclipse Che in a restricted environment. + type: string + type: object + devEnvironments: + description: Development environment default configuration options. + properties: + defaultNamespace: + description: User's default namespace. + properties: + template: + description: If you don't create user namespaces in advance, this field defines the Kubernetes namespace created when you start your first workspace. You can use ``, `` and `` placeholders, such as che-workspace-. + type: string + type: object + defaultPlugins: + description: Default plug-ins applied to Dev Workspaces. + items: + properties: + editor: + description: The editor id to specify default plug-ins for. + type: string + plugins: + description: Default plug-in URIs for the specified editor. + items: + type: string + type: array + type: object + type: array + nodeSelector: + additionalProperties: + type: string + description: The node selector limits the nodes that can run the workspace pods. + type: object + storage: + description: Workspaces persistent storage. + properties: + pvc: + description: PVC settings. + properties: + claimSize: + description: Persistent Volume Claim size. To update the claim size, Storage class that provisions it must support resize. + type: string + storageClass: + description: Storage class for the Persistent Volume Claim. When omitted or left blank, a default storage class is used. + type: string + type: object + pvcStrategy: + default: common + description: Persistent volume claim strategy for the Che server. Only the `common` strategy is supported (all workspaces PVCs in one volume). For details, see https://github.com/eclipse/che/issues/21185. + type: string + type: object + tolerations: + description: The pod tolerations of the workspace pods limit where the workspace pods can run. + items: + description: The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + trustedCerts: + description: Trusted certificate settings. + properties: + gitTrustedCertsConfigMapName: + description: 'The ConfigMap contains certificates to propagate to the Che components and to provide a particular configuration for Git. See the following page: https://www.eclipse.org/che/docs/stable/administration-guide/deploying-che-with-support-for-git-repositories-with-self-signed-certificates/ The ConfigMap must have a `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + type: object + type: object + networking: + description: Networking, Che authentication and TLS configuration. + properties: + annotations: + additionalProperties: + type: string + description: 'Defines annotations which will be set to an ingress (a route for OpenShift platform). The defaults for kubernetes platforms are: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/proxy-read-timeout: "3600", nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600", nginx.ingress.kubernetes.io/ssl-redirect: "true"' + type: object + auth: + description: Authentication settings. + properties: + gateway: + description: Gateway settings. + properties: + configLabels: + additionalProperties: + type: string + description: Gate configuration labels. + type: object + deployment: + description: 'Deployment override options. Since gateway deployment consists of several containers, they must be distinguished in the configuration by their names: - `gateway` - `configbump` - `oauth-proxy` - `kube-rbac-proxy`' + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + type: object + identityProviderURL: + description: Public URL of the Identity Provider server. + type: string + oAuthClientName: + description: Name of the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side. + type: string + oAuthSecret: + description: Name of the secret set in the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side. + type: string + type: object + domain: + description: 'For an OpenShift cluster, the Operator uses the domain to generate a hostname for a route. The generated hostname follows this pattern: che-.. The is the namespace where the CheCluster CRD is created. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. For Kubernetes cluster it contains a global ingress domain. This MUST be explicitly specified: there are no defaults.' + type: string + hostname: + description: The public hostname of the installed Che server. + type: string + labels: + additionalProperties: + type: string + description: Defines labels which will be set to an ingress (a route for OpenShift platform). + type: object + tlsSecretName: + description: The name of the secret used to set up Ingress TLS termination. If the field is an empty string, the default cluster certificate is used. The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + type: object + type: object + status: + description: CheClusterStatus defines the observed state of Che installation. + properties: + chePhase: + description: Specifies the current phase of the Che deployment. + type: string + cheURL: + description: Public URL to the Che server. + type: string + cheVersion: + description: Current installed Che version. + type: string + devfileRegistryURL: + description: The public URL to the internal devfile registry. + type: string + gatewayPhase: + description: Specifies the current phase of the gateway deployment. + type: string + message: + description: A human readable message indicating details about why the Che deployment is in current phase. + type: string + pluginRegistryURL: + description: The public URL to the internal plug-in registry. + type: string + postgresVersion: + description: The PostgreSQL version of the image in use. + type: string + reason: + description: A brief CamelCase message indicating details about why the Che deployment is in current phase. + type: string + workspaceBaseDomain: + description: The resolved workspace base domain. This is either the copy of the explicitly defined property of the same name in the spec or, if it is undefined in the spec and we're running on OpenShift, the automatically resolved basedomain for routes. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/deploy/deployment/openshift/objects/eclipse-che.Namespace.yaml b/deploy/deployment/openshift/objects/eclipse-che.Namespace.yaml new file mode 100644 index 000000000..9d6210af9 --- /dev/null +++ b/deploy/deployment/openshift/objects/eclipse-che.Namespace.yaml @@ -0,0 +1,16 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: v1 +kind: Namespace +metadata: + name: eclipse-che diff --git a/deploy/deployment/openshift/org_v2_checluster.yaml b/deploy/deployment/openshift/org_v2_checluster.yaml new file mode 100644 index 000000000..44f6f381c --- /dev/null +++ b/deploy/deployment/openshift/org_v2_checluster.yaml @@ -0,0 +1,35 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: org.eclipse.che/v2 +kind: CheCluster +metadata: + name: eclipse-che + namespace: eclipse-che +spec: + devEnvironments: + defaultNamespace: + template: '-che' + storage: + pvcStrategy: 'common' + components: + database: + externalDb: false + metrics: + enable: true + networking: + domain: '' + tlsSecretName: '' + auth: + identityProviderURL: '' + oAuthClientName: '' + oAuthSecret: '' diff --git a/helmcharts/next/README.md b/helmcharts/next/README.md index b386aca4c..418976e48 100644 --- a/helmcharts/next/README.md +++ b/helmcharts/next/README.md @@ -12,17 +12,19 @@ Helm charts to deploy [Eclipse Che](https://www.eclipse.org/che/) ### Prerequisites * Minimal Kubernetes version is 1.19 -* Minimal Helm version is 3.2.2 +* Minimal [Helm](https://helm.sh/) version is 3.2.2 +* [Cert manager](https://cert-manager.io/docs/installation/) installed +* [OIDC Identity Provider](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#configuring-the-api-server) configured ### Installation - -Install the Helm Charts for Eclipse Che Operator. +Install the Helm Charts for Eclipse Che Operator ``` -kubectl create namespace eclipse-che - -DOMAIN= - -# Install charts -helm install che --set k8s.ingressDomain=$DOMAIN . +helm install che \ + --create-namespace \ + --namespace eclipse-che \ + --set networking.domain= \ + --set networking.auth.oAuthSecret= \ + --set networking.auth.oAuthClientName= \ + --set networking.auth.identityProviderURL= . ``` diff --git a/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml b/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml new file mode 100644 index 000000000..c764c6d2d --- /dev/null +++ b/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml @@ -0,0 +1,1646 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: eclipse-che/che-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.4.1 + labels: + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: checlusters.org.eclipse.che +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: che-operator-service + namespace: eclipse-che + path: /convert + conversionReviewVersions: + - v1 + - v2 + group: org.eclipse.che + names: + kind: CheCluster + listKind: CheClusterList + plural: checlusters + singular: checluster + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: The `CheCluster` custom resource allows defining and managing a Che server installation + 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: Desired configuration of the Che installation. Based on these settings, the Operator automatically creates and maintains several ConfigMaps that will contain the appropriate environment variables the various components of the Che installation. These generated ConfigMaps must NOT be updated manually. + properties: + auth: + description: Configuration settings related to the Authentication used by the Che installation. + properties: + debug: + description: Deprecated. The value of this flag is ignored. Debug internal identity provider. + type: boolean + externalIdentityProvider: + description: 'Deprecated. The value of this flag is ignored. Instructs the Operator on whether or not to deploy a dedicated Identity Provider (Keycloak or RH SSO instance). Instructs the Operator on whether to deploy a dedicated Identity Provider (Keycloak or RH-SSO instance). By default, a dedicated Identity Provider server is deployed as part of the Che installation. When `externalIdentityProvider` is `true`, no dedicated identity provider will be deployed by the Operator and you will need to provide details about the external identity provider you are about to use. See also all the other fields starting with: `identityProvider`.' + type: boolean + gatewayAuthenticationSidecarImage: + description: Gateway sidecar responsible for authentication when NativeUserMode is enabled. See link:https://github.com/oauth2-proxy/oauth2-proxy[oauth2-proxy] or link:https://github.com/openshift/oauth-proxy[openshift/oauth-proxy]. + type: string + gatewayAuthorizationSidecarImage: + description: Gateway sidecar responsible for authorization when NativeUserMode is enabled. See link:https://github.com/brancz/kube-rbac-proxy[kube-rbac-proxy] or link:https://github.com/openshift/kube-rbac-proxy[openshift/kube-rbac-proxy] + type: string + gatewayHeaderRewriteSidecarImage: + description: Deprecated. The value of this flag is ignored. Sidecar functionality is now implemented in Traefik plugin. + type: string + identityProviderAdminUserName: + description: Deprecated. The value of this flag is ignored. Overrides the name of the Identity Provider administrator user. Defaults to `admin`. + type: string + identityProviderClientId: + description: Deprecated. The value of this flag is ignored. Name of a Identity provider, Keycloak or RH-SSO, `client-id` that is used for Che. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to the value of the `flavour` field suffixed with `-public`. + type: string + identityProviderContainerResources: + description: Deprecated. The value of this flag is ignored. Identity provider container custom settings. + properties: + limits: + description: Limits describes the maximum amount of compute resources allowed. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + request: + description: Requests describes the minimum amount of compute resources required. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + type: object + identityProviderImage: + description: Deprecated. The value of this flag is ignored. Overrides the container image used in the Identity Provider, Keycloak or RH-SSO, deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + identityProviderImagePullPolicy: + description: Deprecated. The value of this flag is ignored. Overrides the image pull policy used in the Identity Provider, Keycloak or RH-SSO, deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + identityProviderIngress: + description: Deprecated. The value of this flag is ignored. Ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + identityProviderPassword: + description: Deprecated. The value of this flag is ignored. Overrides the password of Keycloak administrator user. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to an auto-generated password. + type: string + identityProviderPostgresPassword: + description: Deprecated. The value of this flag is ignored. Password for a Identity Provider, Keycloak or RH-SSO, to connect to the database. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to an auto-generated password. + type: string + identityProviderPostgresSecret: + description: 'Deprecated. The value of this flag is ignored. The secret that contains `password` for the Identity Provider, Keycloak or RH-SSO, to connect to the database. When the secret is defined, the `identityProviderPostgresPassword` is ignored. When the value is omitted or left blank, the one of following scenarios applies: 1. `identityProviderPostgresPassword` is defined, then it will be used to connect to the database. 2. `identityProviderPostgresPassword` is not defined, then a new secret with the name `che-identity-postgres-secret` will be created with an auto-generated value for `password`. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + identityProviderRealm: + description: Deprecated. The value of this flag is ignored. Name of a Identity provider, Keycloak or RH-SSO, realm that is used for Che. Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. When omitted or left blank, it is set to the value of the `flavour` field. + type: string + identityProviderRoute: + description: Deprecated. The value of this flag is ignored. Route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + identityProviderSecret: + description: 'Deprecated. The value of this flag is ignored. The secret that contains `user` and `password` for Identity Provider. When the secret is defined, the `identityProviderAdminUserName` and `identityProviderPassword` are ignored. When the value is omitted or left blank, the one of following scenarios applies: 1. `identityProviderAdminUserName` and `identityProviderPassword` are defined, then they will be used. 2. `identityProviderAdminUserName` or `identityProviderPassword` are not defined, then a new secret with the name `che-identity-secret` will be created with default value `admin` for `user` and with an auto-generated value for `password`. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + identityProviderURL: + description: Public URL of the Identity Provider server (Keycloak / RH-SSO server). Set this ONLY when a use of an external Identity Provider is needed. See the `externalIdentityProvider` field. By default, this will be automatically calculated and set by the Operator. + type: string + initialOpenShiftOAuthUser: + description: Deprecated. The value of this flag is ignored. For operating with the OpenShift OAuth authentication, create a new user account since the kubeadmin can not be used. If the value is true, then a new OpenShift OAuth user will be created for the HTPasswd identity provider. If the value is false and the user has already been created, then it will be removed. If value is an empty, then do nothing. The user's credentials are stored in the `openshift-oauth-user-credentials` secret in 'openshift-config' namespace by Operator. Note that this solution is Openshift 4 platform-specific. + type: boolean + nativeUserMode: + description: Deprecated. The value of this flag is ignored. Enables native user mode. Currently works only on OpenShift and DevWorkspace engine. Native User mode uses OpenShift OAuth directly as identity provider, without Keycloak. + type: boolean + oAuthClientName: + description: Name of the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OpenShiftoAuth` field. + type: string + oAuthSecret: + description: Name of the secret set in the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OAuthClientName` field. + type: string + openShiftoAuth: + description: 'Deprecated. The value of this flag is ignored. Enables the integration of the identity provider (Keycloak / RHSSO) with OpenShift OAuth. Empty value on OpenShift by default. This will allow users to directly login with their OpenShift user through the OpenShift login, and have their workspaces created under personal OpenShift namespaces. WARNING: the `kubeadmin` user is NOT supported, and logging through it will NOT allow accessing the Che Dashboard.' + type: boolean + updateAdminPassword: + description: Deprecated. The value of this flag is ignored. Forces the default `admin` Che user to update password on first login. Defaults to `false`. + type: boolean + type: object + dashboard: + description: Configuration settings related to the User Dashboard used by the Che installation. + properties: + warning: + description: Warning message that will be displayed on the User Dashboard + type: string + type: object + database: + description: Configuration settings related to the database used by the Che installation. + properties: + chePostgresContainerResources: + description: PostgreSQL container custom settings + properties: + limits: + description: Limits describes the maximum amount of compute resources allowed. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + request: + description: Requests describes the minimum amount of compute resources required. + properties: + cpu: + description: CPU, in cores. (500m = .5 cores) + type: string + memory: + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + type: string + type: object + type: object + chePostgresDb: + description: PostgreSQL database name that the Che server uses to connect to the DB. Defaults to `dbche`. + type: string + chePostgresHostName: + description: PostgreSQL Database host name that the Che server uses to connect to. Defaults is `postgres`. Override this value ONLY when using an external database. See field `externalDb`. In the default case it will be automatically set by the Operator. + type: string + chePostgresPassword: + description: PostgreSQL password that the Che server uses to connect to the DB. When omitted or left blank, it will be set to an automatically generated value. + type: string + chePostgresPort: + description: PostgreSQL Database port that the Che server uses to connect to. Defaults to 5432. Override this value ONLY when using an external database. See field `externalDb`. In the default case it will be automatically set by the Operator. + type: string + chePostgresSecret: + description: 'The secret that contains PostgreSQL`user` and `password` that the Che server uses to connect to the DB. When the secret is defined, the `chePostgresUser` and `chePostgresPassword` are ignored. When the value is omitted or left blank, the one of following scenarios applies: 1. `chePostgresUser` and `chePostgresPassword` are defined, then they will be used to connect to the DB. 2. `chePostgresUser` or `chePostgresPassword` are not defined, then a new secret with the name `postgres-credentials` will be created with default value of `pgche` for `user` and with an auto-generated value for `password`. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + chePostgresUser: + description: PostgreSQL user that the Che server uses to connect to the DB. Defaults to `pgche`. + type: string + externalDb: + description: 'Instructs the Operator on whether to deploy a dedicated database. By default, a dedicated PostgreSQL database is deployed as part of the Che installation. When `externalDb` is `true`, no dedicated database will be deployed by the Operator and you will need to provide connection details to the external DB you are about to use. See also all the fields starting with: `chePostgres`.' + type: boolean + postgresImage: + description: Overrides the container image used in the PostgreSQL database deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + postgresImagePullPolicy: + description: Overrides the image pull policy used in the PostgreSQL database deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + postgresVersion: + description: 'Indicates a PostgreSQL version image to use. Allowed values are: `9.6` and `13.3`. Migrate your PostgreSQL database to switch from one version to another.' + type: string + pvcClaimSize: + description: Size of the persistent volume claim for database. Defaults to `1Gi`. To update pvc storageclass that provisions it must support resize when Eclipse Che has been already deployed. + type: string + type: object + devWorkspace: + description: DevWorkspace operator configuration + properties: + controllerImage: + description: Overrides the container image used in the DevWorkspace controller deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + enable: + description: Deploys the DevWorkspace Operator in the cluster. Does nothing when a matching version of the Operator is already installed. Fails when a non-matching version of the Operator is already installed. + type: boolean + runningLimit: + description: Maximum number of the running workspaces per user. + type: string + required: + - enable + type: object + imagePuller: + description: Kubernetes Image Puller configuration + properties: + enable: + description: Install and configure the Community Supported Kubernetes Image Puller Operator. When set to `true` and no spec is provided, it will create a default KubernetesImagePuller object to be managed by the Operator. When set to `false`, the KubernetesImagePuller object will be deleted, and the Operator will be uninstalled, regardless of whether a spec is provided. If the `spec.images` field is empty, a set of recommended workspace-related images will be automatically detected and pre-pulled after installation. Note that while this Operator and its behavior is community-supported, its payload may be commercially-supported for pulling commercially-supported images. + type: boolean + spec: + description: A KubernetesImagePullerSpec to configure the image puller in the CheCluster + properties: + affinity: + type: string + cachingCPULimit: + type: string + cachingCPURequest: + type: string + cachingIntervalHours: + type: string + cachingMemoryLimit: + type: string + cachingMemoryRequest: + type: string + configMapName: + type: string + daemonsetName: + type: string + deploymentName: + type: string + imagePullSecrets: + type: string + imagePullerImage: + type: string + images: + type: string + nodeSelector: + type: string + type: object + required: + - enable + type: object + k8s: + description: Configuration settings specific to Che installations made on upstream Kubernetes. + properties: + ingressClass: + description: 'Ingress class that will define the which controller will manage ingresses. Defaults to `nginx`. NB: This drives the `kubernetes.io/ingress.class` annotation on Che-related ingresses.' + type: string + ingressDomain: + description: 'Global ingress domain for a Kubernetes cluster. This MUST be explicitly specified: there are no defaults.' + type: string + ingressStrategy: + description: 'Deprecated. The value of this flag is ignored. Strategy for ingress creation. Options are: `multi-host` (host is explicitly provided in ingress), `single-host` (host is provided, path-based rules) and `default-host` (no host is provided, path-based rules). Defaults to `multi-host` Deprecated in favor of `serverExposureStrategy` in the `server` section, which defines this regardless of the cluster type. When both are defined, the `serverExposureStrategy` option takes precedence.' + type: string + securityContextFsGroup: + description: The FSGroup in which the Che Pod and workspace Pods containers runs in. Default value is `1724`. + type: string + securityContextRunAsUser: + description: ID of the user the Che Pod and workspace Pods containers run as. Default value is `1724`. + type: string + singleHostExposureType: + description: Deprecated. The value of this flag is ignored. When the serverExposureStrategy is set to `single-host`, the way the server, registries and workspaces are exposed is further configured by this property. The possible values are `native`, which means that the server and workspaces are exposed using ingresses on K8s or `gateway` where the server and workspaces are exposed using a custom gateway based on link:https://doc.traefik.io/traefik/[Traefik]. All the endpoints whether backed by the ingress or gateway `route` always point to the subpaths on the same domain. Defaults to `native`. + type: string + tlsSecretName: + description: Name of a secret that will be used to setup ingress TLS termination when TLS is enabled. When the field is empty string, the default cluster certificate will be used. See also the `tlsSupport` field. + type: string + type: object + metrics: + description: Configuration settings related to the metrics collection used by the Che installation. + properties: + enable: + description: Enables `metrics` the Che server endpoint. Default to `true`. + type: boolean + type: object + server: + description: General configuration settings related to the Che server, the plugin and devfile registries + properties: + airGapContainerRegistryHostname: + description: Optional host name, or URL, to an alternate container registry to pull images from. This value overrides the container registry host name defined in all the default container images involved in a Che deployment. This is particularly useful to install Che in a restricted environment. + type: string + airGapContainerRegistryOrganization: + description: Optional repository name of an alternate container registry to pull images from. This value overrides the container registry organization defined in all the default container images involved in a Che deployment. This is particularly useful to install Eclipse Che in a restricted environment. + type: string + allowUserDefinedWorkspaceNamespaces: + description: Deprecated. The value of this flag is ignored. Defines that a user is allowed to specify a Kubernetes namespace, or an OpenShift project, which differs from the default. It's NOT RECOMMENDED to set to `true` without OpenShift OAuth configured. The OpenShift infrastructure also uses this property. + type: boolean + cheClusterRoles: + description: A comma-separated list of ClusterRoles that will be assigned to Che ServiceAccount. Each role must have `app.kubernetes.io/part-of=che.eclipse.org` label. Be aware that the Che Operator has to already have all permissions in these ClusterRoles to grant them. + type: string + cheDebug: + description: Enables the debug mode for Che server. Defaults to `false`. + type: string + cheFlavor: + description: Deprecated. The value of this flag is ignored. Specifies a variation of the installation. The options are `che` for upstream Che installations or `devspaces` for Red Hat OpenShift Dev Spaces (formerly Red Hat CodeReady Workspaces) installation + type: string + cheHost: + description: Public host name of the installed Che server. When value is omitted, the value it will be automatically set by the Operator. See the `cheHostTLSSecret` field. + type: string + cheHostTLSSecret: + description: Name of a secret containing certificates to secure ingress or route for the custom host name of the installed Che server. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label. See the `cheHost` field. + type: string + cheImage: + description: Overrides the container image used in Che deployment. This does NOT include the container image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + cheImagePullPolicy: + description: Overrides the image pull policy used in Che deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + cheImageTag: + description: Overrides the tag of the container image used in Che deployment. Omit it or leave it empty to use the default image tag provided by the Operator. + type: string + cheLogLevel: + description: 'Log level for the Che server: `INFO` or `DEBUG`. Defaults to `INFO`.' + type: string + cheServerIngress: + description: The Che server ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + cheServerRoute: + description: The Che server route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + cheWorkspaceClusterRole: + description: Custom cluster role bound to the user for the Che workspaces. The role must have `app.kubernetes.io/part-of=che.eclipse.org` label. The default roles are used when omitted or left blank. + type: string + customCheProperties: + additionalProperties: + type: string + description: Map of additional environment variables that will be applied in the generated `che` ConfigMap to be used by the Che server, in addition to the values already generated from other fields of the `CheCluster` custom resource (CR). When `customCheProperties` contains a property that would be normally generated in `che` ConfigMap from other CR fields, the value defined in the `customCheProperties` is used instead. + type: object + dashboardCpuLimit: + description: Overrides the CPU limit used in the dashboard deployment. In cores. (500m = .5 cores). Default to 500m. + type: string + dashboardCpuRequest: + description: Overrides the CPU request used in the dashboard deployment. In cores. (500m = .5 cores). Default to 100m. + type: string + dashboardImage: + description: Overrides the container image used in the dashboard deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + dashboardImagePullPolicy: + description: Overrides the image pull policy used in the dashboard deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + dashboardIngress: + description: Deprecated. The value of this flag is ignored. Dashboard ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + dashboardMemoryLimit: + description: Overrides the memory limit used in the dashboard deployment. Defaults to 256Mi. + type: string + dashboardMemoryRequest: + description: Overrides the memory request used in the dashboard deployment. Defaults to 16Mi. + type: string + dashboardRoute: + description: Deprecated. The value of this flag is ignored. Dashboard route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + devfileRegistryCpuLimit: + description: Overrides the CPU limit used in the devfile registry deployment. In cores. (500m = .5 cores). Default to 500m. + type: string + devfileRegistryCpuRequest: + description: Overrides the CPU request used in the devfile registry deployment. In cores. (500m = .5 cores). Default to 100m. + type: string + devfileRegistryImage: + description: Overrides the container image used in the devfile registry deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + devfileRegistryIngress: + description: Deprecated. The value of this flag is ignored. The devfile registry ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + devfileRegistryMemoryLimit: + description: Overrides the memory limit used in the devfile registry deployment. Defaults to 256Mi. + type: string + devfileRegistryMemoryRequest: + description: Overrides the memory request used in the devfile registry deployment. Defaults to 16Mi. + type: string + devfileRegistryPullPolicy: + description: Overrides the image pull policy used in the devfile registry deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + devfileRegistryRoute: + description: Deprecated. The value of this flag is ignored. The devfile registry route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + devfileRegistryUrl: + description: Deprecated in favor of `externalDevfileRegistries` fields. + type: string + disableInternalClusterSVCNames: + description: Deprecated. The value of this flag is ignored. Disable internal cluster SVC names usage to communicate between components to speed up the traffic and avoid proxy issues. + type: boolean + externalDevfileRegistries: + description: External devfile registries, that serves sample, ready-to-use devfiles. Configure this in addition to a dedicated devfile registry (when `externalDevfileRegistry` is `false`) or instead of it (when `externalDevfileRegistry` is `true`) + items: + description: Settings for a configuration of the external devfile registries. + properties: + url: + description: Public URL of the devfile registry. + type: string + type: object + type: array + externalDevfileRegistry: + description: Instructs the Operator on whether to deploy a dedicated devfile registry server. By default, a dedicated devfile registry server is started. When `externalDevfileRegistry` is `true`, no such dedicated server will be started by the Operator and configure at least one devfile registry with `externalDevfileRegistries` field. + type: boolean + externalPluginRegistry: + description: Instructs the Operator on whether to deploy a dedicated plugin registry server. By default, a dedicated plugin registry server is started. When `externalPluginRegistry` is `true`, no such dedicated server will be started by the Operator and you will have to manually set the `pluginRegistryUrl` field. + type: boolean + gitSelfSignedCert: + description: When enabled, the certificate from `che-git-self-signed-cert` ConfigMap will be propagated to the Che components and provide particular configuration for Git. Note, the `che-git-self-signed-cert` ConfigMap must have `app.kubernetes.io/part-of=che.eclipse.org` label. + type: boolean + nonProxyHosts: + description: 'List of hosts that will be reached directly, bypassing the proxy. Specify wild card domain use the following form `.` and `|` as delimiter, for example: `localhost|.my.host.com|123.42.12.32` Only use when configuring a proxy is required. Operator respects OpenShift cluster wide proxy configuration and no additional configuration is required, but defining `nonProxyHosts` in a custom resource leads to merging non proxy hosts lists from the cluster proxy configuration and ones defined in the custom resources. See the doc https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyURL` fields.' + type: string + pluginRegistryCpuLimit: + description: Overrides the CPU limit used in the plugin registry deployment. In cores. (500m = .5 cores). Default to 500m. + type: string + pluginRegistryCpuRequest: + description: Overrides the CPU request used in the plugin registry deployment. In cores. (500m = .5 cores). Default to 100m. + type: string + pluginRegistryImage: + description: Overrides the container image used in the plugin registry deployment. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + pluginRegistryIngress: + description: Deprecated. The value of this flag is ignored. Plugin registry ingress custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + pluginRegistryMemoryLimit: + description: Overrides the memory limit used in the plugin registry deployment. Defaults to 256Mi. + type: string + pluginRegistryMemoryRequest: + description: Overrides the memory request used in the plugin registry deployment. Defaults to 16Mi. + type: string + pluginRegistryPullPolicy: + description: Overrides the image pull policy used in the plugin registry deployment. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + pluginRegistryRoute: + description: Deprecated. The value of this flag is ignored. Plugin registry route custom settings. + properties: + annotations: + additionalProperties: + type: string + description: Unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + domain: + description: 'Operator uses the domain to generate a hostname for a route. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. The generated host name will follow this pattern: `-.`.' + type: string + labels: + description: Comma separated list of labels that can be used to organize and categorize objects by scoping and selecting. + type: string + type: object + pluginRegistryUrl: + description: Public URL of the plugin registry that serves sample ready-to-use devfiles. Set this ONLY when a use of an external devfile registry is needed. See the `externalPluginRegistry` field. By default, this will be automatically calculated by the Operator. + type: string + proxyPassword: + description: Password of the proxy server. Only use when proxy configuration is required. See the `proxyURL`, `proxyUser` and `proxySecret` fields. + type: string + proxyPort: + description: Port of the proxy server. Only use when configuring a proxy is required. See also the `proxyURL` and `nonProxyHosts` fields. + type: string + proxySecret: + description: The secret that contains `user` and `password` for a proxy server. When the secret is defined, the `proxyUser` and `proxyPassword` are ignored. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + proxyURL: + description: URL (protocol+host name) of the proxy server. This drives the appropriate changes in the `JAVA_OPTS` and `https(s)_proxy` variables in the Che server and workspaces containers. Only use when configuring a proxy is required. Operator respects OpenShift cluster wide proxy configuration and no additional configuration is required, but defining `proxyUrl` in a custom resource leads to overrides the cluster proxy configuration with fields `proxyUrl`, `proxyPort`, `proxyUser` and `proxyPassword` from the custom resource. See the doc https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyPort` and `nonProxyHosts` fields. + type: string + proxyUser: + description: User name of the proxy server. Only use when configuring a proxy is required. See also the `proxyURL`, `proxyPassword` and `proxySecret` fields. + type: string + selfSignedCert: + description: Deprecated. The value of this flag is ignored. The Che Operator will automatically detect whether the router certificate is self-signed and propagate it to other components, such as the Che server. + type: boolean + serverCpuLimit: + description: Overrides the CPU limit used in the Che server deployment In cores. (500m = .5 cores). Default to 1. + type: string + serverCpuRequest: + description: Overrides the CPU request used in the Che server deployment In cores. (500m = .5 cores). Default to 100m. + type: string + serverExposureStrategy: + description: Deprecated. The value of this flag is ignored. Sets the server and workspaces exposure type. Possible values are `multi-host`, `single-host`, `default-host`. Defaults to `multi-host`, which creates a separate ingress, or OpenShift routes, for every required endpoint. `single-host` makes Che exposed on a single host name with workspaces exposed on subpaths. Read the docs to learn about the limitations of this approach. Also consult the `singleHostExposureType` property to further configure how the Operator and the Che server make that happen on Kubernetes. `default-host` exposes the Che server on the host of the cluster. Read the docs to learn about the limitations of this approach. + type: string + serverMemoryLimit: + description: Overrides the memory limit used in the Che server deployment. Defaults to 1Gi. + type: string + serverMemoryRequest: + description: Overrides the memory request used in the Che server deployment. Defaults to 512Mi. + type: string + serverTrustStoreConfigMapName: + description: Name of the ConfigMap with public certificates to add to Java trust store of the Che server. This is often required when adding the OpenShift OAuth provider, which has HTTPS endpoint signed with self-signed cert. The Che server must be aware of its CA cert to be able to request it. This is disabled by default. The Config Map must have `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + singleHostGatewayConfigMapLabels: + additionalProperties: + type: string + description: The labels that need to be present in the ConfigMaps representing the gateway configuration. + type: object + singleHostGatewayConfigSidecarImage: + description: The image used for the gateway sidecar that provides configuration to the gateway. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + singleHostGatewayImage: + description: The image used for the gateway in the single host mode. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + tlsSupport: + description: Deprecated. Instructs the Operator to deploy Che in TLS mode. This is enabled by default. Disabling TLS sometimes cause malfunction of some Che components. + type: boolean + useInternalClusterSVCNames: + description: Deprecated in favor of `disableInternalClusterSVCNames`. + type: boolean + workspaceNamespaceDefault: + description: Defines Kubernetes default namespace in which user's workspaces are created for a case when a user does not override it. It's possible to use ``, `` and `` placeholders, such as che-workspace-. In that case, a new namespace will be created for each user or workspace. + type: string + workspacePodNodeSelector: + additionalProperties: + type: string + description: The node selector that limits the nodes that can run the workspace pods. + type: object + workspacePodTolerations: + description: The pod tolerations put on the workspace pods to limit where the workspace pods can run. + items: + description: The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + workspacesDefaultPlugins: + description: Default plug-ins applied to Devworkspaces. + items: + properties: + editor: + description: The editor id to specify default plug-ins for. + type: string + plugins: + description: Default plug-in uris for the specified editor. + items: + type: string + type: array + type: object + type: array + type: object + storage: + description: Configuration settings related to the persistent storage used by the Che installation. + properties: + postgresPVCStorageClassName: + description: Storage class for the Persistent Volume Claim dedicated to the PostgreSQL database. When omitted or left blank, a default storage class is used. + type: string + preCreateSubPaths: + description: Instructs the Che server to start a special Pod to pre-create a sub-path in the Persistent Volumes. Defaults to `false`, however it will need to enable it according to the configuration of your Kubernetes cluster. + type: boolean + pvcClaimSize: + description: Size of the persistent volume claim for workspaces. Defaults to `10Gi`. + type: string + pvcJobsImage: + description: Overrides the container image used to create sub-paths in the Persistent Volumes. This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. See also the `preCreateSubPaths` field. + type: string + pvcStrategy: + description: Persistent volume claim strategy for the Che server. This Can be:`common` (all workspaces PVCs in one volume), `per-workspace` (one PVC per workspace for all declared volumes) and `unique` (one PVC per declared volume). Defaults to `common`. + type: string + workspacePVCStorageClassName: + description: Storage class for the Persistent Volume Claims dedicated to the Che workspaces. When omitted or left blank, a default storage class is used. + type: string + type: object + type: object + status: + description: CheClusterStatus defines the observed state of Che installation + properties: + cheClusterRunning: + description: Status of a Che installation. Can be `Available`, `Unavailable`, or `Available, Rolling Update in Progress`. + type: string + cheURL: + description: Public URL to the Che server. + type: string + cheVersion: + description: Current installed Che version. + type: string + dbProvisioned: + description: Indicates that a PostgreSQL instance has been correctly provisioned or not. + type: boolean + devfileRegistryURL: + description: Public URL to the devfile registry. + type: string + devworkspaceStatus: + description: The status of the Devworkspace subsystem + properties: + gatewayHost: + description: GatewayHost is the resolved host of the ingress/route. This is equal to the Host in the spec on Kubernetes but contains the actual host name of the route if Host is unspecified on OpenShift. + type: string + gatewayPhase: + description: GatewayPhase specifies the phase in which the gateway deployment currently is. If the gateway is disabled, the phase is "Inactive". + type: string + message: + description: Message contains further human-readable info for why the Che cluster is in the phase it currently is. + type: string + phase: + description: Phase is the phase in which the Che cluster as a whole finds itself in. + type: string + reason: + description: A brief CamelCase message indicating details about why the Che cluster is in this state. + type: string + workspaceBaseDomain: + description: The resolved workspace base domain. This is either the copy of the explicitly defined property of the same name in the spec or, if it is undefined in the spec and we're running on OpenShift, the automatically resolved basedomain for routes. + type: string + type: object + gitHubOAuthProvisioned: + description: Indicates whether an Identity Provider instance, Keycloak or RH-SSO, has been configured to integrate with the GitHub OAuth. + type: boolean + gitServerTLSCertificateConfigMapName: + description: The ConfigMap containing certificates to propagate to the Che components and to provide particular configuration for Git. + type: string + helpLink: + description: A URL that points to some URL where to find help related to the current Operator status. + type: string + keycloakProvisioned: + description: Indicates whether an Identity Provider instance, Keycloak or RH-SSO, has been provisioned with realm, client and user. + type: boolean + keycloakURL: + description: Public URL to the Identity Provider server, Keycloak or RH-SSO,. + type: string + message: + description: A human readable message indicating details about why the Pod is in this condition. + type: string + openShiftOAuthUserCredentialsSecret: + description: OpenShift OAuth secret in `openshift-config` namespace that contains user credentials for HTPasswd identity provider. + type: string + openShiftoAuthProvisioned: + description: Indicates whether an Identity Provider instance, Keycloak or RH-SSO, has been configured to integrate with the OpenShift OAuth. + type: boolean + pluginRegistryURL: + description: Public URL to the plugin registry. + type: string + reason: + description: A brief CamelCase message indicating details about why the Pod is in this state. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - name: v2 + schema: + openAPIV3Schema: + description: 'The `CheCluster` custom resource allows defining and managing Eclipse Che server installation. Based on these settings, the Operator automatically creates and maintains several ConfigMaps: `che`, `plugin-registry`, `devfile-registry` that will contain the appropriate environment variables of the various components of the installation. These generated ConfigMaps must NOT be updated manually.' + 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: Desired configuration of Eclipse Che installation. + properties: + components: + description: Che components configuration. + properties: + cheServer: + description: General configuration settings related to the Che server. + properties: + clusterRoles: + description: 'ClusterRoles assigned to Che ServiceAccount. The defaults roles are: - `-cheworkspaces-namespaces-clusterrole` - `-cheworkspaces-clusterrole` - `-cheworkspaces-devworkspace-clusterrole` where the is the namespace where the CheCluster CRD is created. Each role must have a `app.kubernetes.io/part-of=che.eclipse.org` label. The Che Operator must already have all permissions in these ClusterRoles to grant them.' + items: + type: string + type: array + debug: + description: Enables the debug mode for Che server. + type: boolean + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + extraProperties: + additionalProperties: + type: string + description: A map of additional environment variables applied in the generated `che` ConfigMap to be used by the Che server, in addition to the values already generated from other fields of the `CheCluster` custom resource (CR). If the `extraProperties` fields contains a property normally generated in `che` ConfigMap from other CR fields, the value defined in the `extraProperties` is used instead. + type: object + logLevel: + default: INFO + description: 'The log level for the Che server: `INFO` or `DEBUG`.' + type: string + proxy: + description: Proxy server settings for Kubernetes cluster. No additional configuration is required for OpenShift cluster. By specifying these settings for the OpenShift cluster, you override the OpenShift proxy configuration. + properties: + credentialsSecretName: + description: The secret name that contains `user` and `password` for a proxy server. The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + nonProxyHosts: + description: 'A list of hosts that can be reached directly, bypassing the proxy. Specify wild card domain use the following form `.`, for example: - localhost - my.host.com - 123.42.12.32 Use only when a proxy configuration is required. Operator respects OpenShift cluster-wide proxy configuration, defining `nonProxyHosts` in a custom resource leads to merging non-proxy hosts lists from the cluster proxy configuration, and the ones defined in the custom resources. See the following page: https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyURL` fields.' + items: + type: string + type: array + port: + description: Proxy server port. + type: string + url: + description: 'URL (protocol+hostname) of the proxy server. Use only when a proxy configuration is required. Operator respects OpenShift cluster-wide proxy configuration, defining `url` in a custom resource leads to overriding the cluster proxy configuration. See the following page: https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. See also the `proxyPort` and `nonProxyHosts` fields.' + type: string + type: object + type: object + dashboard: + description: Configuration settings related to the Dashboard used by the Che installation. + properties: + HeaderMessage: + description: Dashboard header message. + properties: + show: + description: Instructs dashboard to show the message. + type: boolean + warning: + description: Warning message displayed on the user dashboard. + type: string + type: object + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + type: object + database: + description: Configuration settings related to the database used by the Che installation. + properties: + credentialsSecretName: + default: postgres-credentials + description: The secret that contains PostgreSQL `user` and `password` that the Che server uses to connect to the DB. The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + externalDb: + description: Instructs the Operator to deploy a dedicated database. By default, a dedicated PostgreSQL database is deployed as part of the Che installation. When `externalDb` is set as `true`, no dedicated database is deployed by the Operator and you need to provide connection details to the external DB you want to use. + type: boolean + postgresDb: + default: dbche + description: PostgreSQL database name that the Che server uses to connect to the DB. + type: string + postgresHostName: + default: postgres + description: PostgreSQL database hostname that the Che server connects to. Override this value only when using an external database. See field `externalDb`. + type: string + postgresPort: + default: "5432" + description: PostgreSQL Database port the Che server connects to. Override this value only when using an external database. See field `externalDb`. + type: string + pvc: + description: PVC settings for PostgreSQL database. + properties: + claimSize: + description: Persistent Volume Claim size. To update the claim size, Storage class that provisions it must support resize. + type: string + storageClass: + description: Storage class for the Persistent Volume Claim. When omitted or left blank, a default storage class is used. + type: string + type: object + type: object + devWorkspace: + description: DevWorkspace operator configuration. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + runningLimit: + description: The maximum number of running workspaces per user. + type: string + type: object + devfileRegistry: + description: Configuration settings related to the Devfile registry used by the Che installation. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + disableInternalRegistry: + description: Disables internal devfile registry. + type: boolean + externalDevfileRegistries: + description: External devfile registries serving sample ready-to-use devfiles. + items: + description: External devfile registries configuration. + properties: + url: + description: The public UR of the devfile registry that serves sample ready-to-use devfiles. + type: string + type: object + type: array + type: object + imagePuller: + description: Kubernetes Image Puller configuration. + properties: + enable: + description: Install and configure the community supported Kubernetes Image Puller Operator. When you set the value to `true` without providing any specs, it creates a default Kubernetes Image Puller object managed by the Operator. When you set the value to `false`, the Kubernetes Image Puller object is deleted, and the Operator uninstalled, regardless of whether a spec is provided. If you leave the `spec.images` field empty, a set of recommended workspace-related images is automatically detected and pre-pulled after installation. Note that while this Operator and its behavior is community-supported, its payload may be commercially-supported for pulling commercially-supported images. + type: boolean + spec: + description: A Kubernetes Image Puller spec to configure the image puller in the CheCluster + properties: + affinity: + type: string + cachingCPULimit: + type: string + cachingCPURequest: + type: string + cachingIntervalHours: + type: string + cachingMemoryLimit: + type: string + cachingMemoryRequest: + type: string + configMapName: + type: string + daemonsetName: + type: string + deploymentName: + type: string + imagePullSecrets: + type: string + imagePullerImage: + type: string + images: + type: string + nodeSelector: + type: string + type: object + required: + - enable + type: object + metrics: + description: Che server metrics configuration. + properties: + enable: + default: true + description: Enables `metrics` for the Che server endpoint. + type: boolean + type: object + pluginRegistry: + description: Configuration settings related to the plug-in registry used by the Che installation. + properties: + deployment: + description: Deployment override options. + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + disableInternalRegistry: + description: Disables internal plug-in registry. + type: boolean + externalPluginRegistries: + description: External plugin registries. + items: + description: External plug-in registries configuration. + properties: + url: + description: Public URL of the plug-in registry. + type: string + type: object + type: array + type: object + type: object + containerRegistry: + description: Configuration of an alternative registry that stores Che images. + properties: + hostname: + description: An optional hostname or URL of an alternative container registry to pull images from. This value overrides the container registry hostname defined in all the default container images involved in a Che deployment. This is particularly useful for installing Che in a restricted environment. + type: string + organization: + description: An optional repository name of an alternative registry to pull images from. This value overrides the container registry organization defined in all the default container images involved in a Che deployment. This is particularly useful for installing Eclipse Che in a restricted environment. + type: string + type: object + devEnvironments: + description: Development environment default configuration options. + properties: + defaultNamespace: + description: User's default namespace. + properties: + template: + description: If you don't create user namespaces in advance, this field defines the Kubernetes namespace created when you start your first workspace. You can use ``, `` and `` placeholders, such as che-workspace-. + type: string + type: object + defaultPlugins: + description: Default plug-ins applied to Dev Workspaces. + items: + properties: + editor: + description: The editor id to specify default plug-ins for. + type: string + plugins: + description: Default plug-in URIs for the specified editor. + items: + type: string + type: array + type: object + type: array + nodeSelector: + additionalProperties: + type: string + description: The node selector limits the nodes that can run the workspace pods. + type: object + storage: + description: Workspaces persistent storage. + properties: + pvc: + description: PVC settings. + properties: + claimSize: + description: Persistent Volume Claim size. To update the claim size, Storage class that provisions it must support resize. + type: string + storageClass: + description: Storage class for the Persistent Volume Claim. When omitted or left blank, a default storage class is used. + type: string + type: object + pvcStrategy: + default: common + description: Persistent volume claim strategy for the Che server. Only the `common` strategy is supported (all workspaces PVCs in one volume). For details, see https://github.com/eclipse/che/issues/21185. + type: string + type: object + tolerations: + description: The pod tolerations of the workspace pods limit where the workspace pods can run. + items: + description: The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + trustedCerts: + description: Trusted certificate settings. + properties: + gitTrustedCertsConfigMapName: + description: 'The ConfigMap contains certificates to propagate to the Che components and to provide a particular configuration for Git. See the following page: https://www.eclipse.org/che/docs/stable/administration-guide/deploying-che-with-support-for-git-repositories-with-self-signed-certificates/ The ConfigMap must have a `app.kubernetes.io/part-of=che.eclipse.org` label.' + type: string + type: object + type: object + networking: + description: Networking, Che authentication and TLS configuration. + properties: + annotations: + additionalProperties: + type: string + description: 'Defines annotations which will be set to an ingress (a route for OpenShift platform). The defaults for kubernetes platforms are: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/proxy-read-timeout: "3600", nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600", nginx.ingress.kubernetes.io/ssl-redirect: "true"' + type: object + auth: + description: Authentication settings. + properties: + gateway: + description: Gateway settings. + properties: + configLabels: + additionalProperties: + type: string + description: Gate configuration labels. + type: object + deployment: + description: 'Deployment override options. Since gateway deployment consists of several containers, they must be distinguished in the configuration by their names: - `gateway` - `configbump` - `oauth-proxy` - `kube-rbac-proxy`' + properties: + container: + description: A single application container. + items: + description: Container custom settings. + properties: + image: + description: Container image. Omit it or leave it empty to use the default container image provided by the Operator. + type: string + imagePullPolicy: + description: Image pull policy. Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. + type: string + name: + description: Container name. + type: string + resources: + description: Compute resources required by this container. + properties: + limits: + description: Limits describe the maximum amount of compute resources allowed. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + request: + description: Request describes the minimum amount of compute resources required. + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU, in cores. (500m = .5 cores) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: object + type: array + securityContext: + description: Security options the pod should run with. + properties: + fsGroup: + description: A special supplemental group that applies to all containers in a pod. The default value is `1724`. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container process. The default value is `1724`. + format: int64 + type: integer + type: object + type: object + type: object + identityProviderURL: + description: Public URL of the Identity Provider server. + type: string + oAuthClientName: + description: Name of the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side. + type: string + oAuthSecret: + description: Name of the secret set in the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side. + type: string + type: object + domain: + description: 'For an OpenShift cluster, the Operator uses the domain to generate a hostname for a route. The generated hostname follows this pattern: che-.. The is the namespace where the CheCluster CRD is created. In a conjunction with labels it creates a route, which is served by a non-default Ingress controller. For Kubernetes cluster it contains a global ingress domain. This MUST be explicitly specified: there are no defaults.' + type: string + hostname: + description: The public hostname of the installed Che server. + type: string + labels: + additionalProperties: + type: string + description: Defines labels which will be set to an ingress (a route for OpenShift platform). + type: object + tlsSecretName: + description: The name of the secret used to set up Ingress TLS termination. If the field is an empty string, the default cluster certificate is used. The secret must have a `app.kubernetes.io/part-of=che.eclipse.org` label. + type: string + type: object + type: object + status: + description: CheClusterStatus defines the observed state of Che installation. + properties: + chePhase: + description: Specifies the current phase of the Che deployment. + type: string + cheURL: + description: Public URL to the Che server. + type: string + cheVersion: + description: Current installed Che version. + type: string + devfileRegistryURL: + description: The public URL to the internal devfile registry. + type: string + gatewayPhase: + description: Specifies the current phase of the gateway deployment. + type: string + message: + description: A human readable message indicating details about why the Che deployment is in current phase. + type: string + pluginRegistryURL: + description: The public URL to the internal plug-in registry. + type: string + postgresVersion: + description: The PostgreSQL version of the image in use. + type: string + reason: + description: A brief CamelCase message indicating details about why the Che deployment is in current phase. + type: string + workspaceBaseDomain: + description: The resolved workspace base domain. This is either the copy of the explicitly defined property of the same name in the spec or, if it is undefined in the spec and we're running on OpenShift, the automatically resolved basedomain for routes. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/helmcharts/next/crds/org_v1_che_crd.yaml b/helmcharts/next/crds/org_v1_che_crd.yaml deleted file mode 100644 index 818937aa9..000000000 --- a/helmcharts/next/crds/org_v1_che_crd.yaml +++ /dev/null @@ -1,1233 +0,0 @@ -# -# Copyright (c) 2019-2021 Red Hat, Inc. -# This program and the accompanying materials are made -# available under the terms of the Eclipse Public License 2.0 -# which is available at https://www.eclipse.org/legal/epl-2.0/ -# -# SPDX-License-Identifier: EPL-2.0 -# -# Contributors: -# Red Hat, Inc. - initial API and implementation -# - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.4.1 - creationTimestamp: null - name: checlusters.org.eclipse.che -spec: - group: org.eclipse.che - names: - kind: CheCluster - listKind: CheClusterList - plural: checlusters - singular: checluster - scope: Namespaced - versions: - - name: v1 - schema: - openAPIV3Schema: - description: The `CheCluster` custom resource allows defining and managing - a Che server installation - 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: Desired configuration of the Che installation. Based on - these settings, the Operator automatically creates and maintains - several ConfigMaps that will contain the appropriate environment variables - the various components of the Che installation. These generated ConfigMaps - must NOT be updated manually. - properties: - auth: - description: Configuration settings related to the Authentication - used by the Che installation. - properties: - debug: - description: Deprecated. The value of this flag is ignored. - Debug internal identity provider. - type: boolean - externalIdentityProvider: - description: 'Deprecated. The value of this flag is ignored. - Instructs the Operator on whether or not to deploy a dedicated - Identity Provider (Keycloak or RH SSO instance). Instructs - the Operator on whether to deploy a dedicated Identity Provider - (Keycloak or RH-SSO instance). By default, a dedicated Identity - Provider server is deployed as part of the Che installation. - When `externalIdentityProvider` is `true`, no dedicated identity - provider will be deployed by the Operator and you will need - to provide details about the external identity provider you - are about to use. See also all the other fields starting with: - `identityProvider`.' - type: boolean - gatewayAuthenticationSidecarImage: - description: Gateway sidecar responsible for authentication - when NativeUserMode is enabled. See link:https://github.com/oauth2-proxy/oauth2-proxy[oauth2-proxy] - or link:https://github.com/openshift/oauth-proxy[openshift/oauth-proxy]. - type: string - gatewayAuthorizationSidecarImage: - description: Gateway sidecar responsible for authorization when - NativeUserMode is enabled. See link:https://github.com/brancz/kube-rbac-proxy[kube-rbac-proxy] - or link:https://github.com/openshift/kube-rbac-proxy[openshift/kube-rbac-proxy] - type: string - gatewayHeaderRewriteSidecarImage: - description: Deprecated. The value of this flag is ignored. - Sidecar functionality is now implemented in Traefik plugin. - type: string - identityProviderAdminUserName: - description: Deprecated. The value of this flag is ignored. - Overrides the name of the Identity Provider administrator - user. Defaults to `admin`. - type: string - identityProviderClientId: - description: Deprecated. The value of this flag is ignored. - Name of a Identity provider, Keycloak or RH-SSO, `client-id` - that is used for Che. Override this when an external Identity - Provider is in use. See the `externalIdentityProvider` field. - When omitted or left blank, it is set to the value of the - `flavour` field suffixed with `-public`. - type: string - identityProviderContainerResources: - description: Deprecated. The value of this flag is ignored. - Identity provider container custom settings. - properties: - limits: - description: Limits describes the maximum amount of compute - resources allowed. - properties: - cpu: - description: CPU, in cores. (500m = .5 cores) - type: string - memory: - description: Memory, in bytes. (500Gi = 500GiB = 500 - * 1024 * 1024 * 1024) - type: string - type: object - request: - description: Requests describes the minimum amount of compute - resources required. - properties: - cpu: - description: CPU, in cores. (500m = .5 cores) - type: string - memory: - description: Memory, in bytes. (500Gi = 500GiB = 500 - * 1024 * 1024 * 1024) - type: string - type: object - type: object - identityProviderImage: - description: Deprecated. The value of this flag is ignored. - Overrides the container image used in the Identity Provider, - Keycloak or RH-SSO, deployment. This includes the image tag. - Omit it or leave it empty to use the default container image - provided by the Operator. - type: string - identityProviderImagePullPolicy: - description: Deprecated. The value of this flag is ignored. - Overrides the image pull policy used in the Identity Provider, - Keycloak or RH-SSO, deployment. Default value is `Always` - for `nightly`, `next` or `latest` images, and `IfNotPresent` - in other cases. - type: string - identityProviderIngress: - description: Deprecated. The value of this flag is ignored. - Ingress custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - identityProviderPassword: - description: Deprecated. The value of this flag is ignored. - Overrides the password of Keycloak administrator user. Override - this when an external Identity Provider is in use. See the - `externalIdentityProvider` field. When omitted or left blank, - it is set to an auto-generated password. - type: string - identityProviderPostgresPassword: - description: Deprecated. The value of this flag is ignored. - Password for a Identity Provider, Keycloak or RH-SSO, to connect - to the database. Override this when an external Identity Provider - is in use. See the `externalIdentityProvider` field. When - omitted or left blank, it is set to an auto-generated password. - type: string - identityProviderPostgresSecret: - description: 'Deprecated. The value of this flag is ignored. - The secret that contains `password` for the Identity Provider, - Keycloak or RH-SSO, to connect to the database. When the secret - is defined, the `identityProviderPostgresPassword` is ignored. - When the value is omitted or left blank, the one of following - scenarios applies: 1. `identityProviderPostgresPassword` is - defined, then it will be used to connect to the database. - 2. `identityProviderPostgresPassword` is not defined, then - a new secret with the name `che-identity-postgres-secret` - will be created with an auto-generated value for `password`. - The secret must have `app.kubernetes.io/part-of=che.eclipse.org` - label.' - type: string - identityProviderRealm: - description: Deprecated. The value of this flag is ignored. - Name of a Identity provider, Keycloak or RH-SSO, realm that - is used for Che. Override this when an external Identity Provider - is in use. See the `externalIdentityProvider` field. When - omitted or left blank, it is set to the value of the `flavour` - field. - type: string - identityProviderRoute: - description: Deprecated. The value of this flag is ignored. - Route custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - domain: - description: 'Operator uses the domain to generate a hostname - for a route. In a conjunction with labels it creates a - route, which is served by a non-default Ingress controller. - The generated host name will follow this pattern: `-.`.' - type: string - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - identityProviderSecret: - description: 'Deprecated. The value of this flag is ignored. - The secret that contains `user` and `password` for Identity - Provider. When the secret is defined, the `identityProviderAdminUserName` - and `identityProviderPassword` are ignored. When the value - is omitted or left blank, the one of following scenarios applies: - 1. `identityProviderAdminUserName` and `identityProviderPassword` - are defined, then they will be used. 2. `identityProviderAdminUserName` - or `identityProviderPassword` are not defined, then a new - secret with the name `che-identity-secret` will be created - with default value `admin` for `user` and with an auto-generated - value for `password`. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` - label.' - type: string - identityProviderURL: - description: Public URL of the Identity Provider server (Keycloak - / RH-SSO server). Set this ONLY when a use of an external - Identity Provider is needed. See the `externalIdentityProvider` - field. By default, this will be automatically calculated and - set by the Operator. - type: string - initialOpenShiftOAuthUser: - description: Deprecated. The value of this flag is ignored. - For operating with the OpenShift OAuth authentication, create - a new user account since the kubeadmin can not be used. If - the value is true, then a new OpenShift OAuth user will be - created for the HTPasswd identity provider. If the value is - false and the user has already been created, then it will - be removed. If value is an empty, then do nothing. The user's - credentials are stored in the `openshift-oauth-user-credentials` - secret in 'openshift-config' namespace by Operator. Note that - this solution is Openshift 4 platform-specific. - type: boolean - nativeUserMode: - description: Deprecated. The value of this flag is ignored. - Enables native user mode. Currently works only on OpenShift - and DevWorkspace engine. Native User mode uses OpenShift OAuth - directly as identity provider, without Keycloak. - type: boolean - oAuthClientName: - description: Name of the OpenShift `OAuthClient` resource used - to setup identity federation on the OpenShift side. Auto-generated - when left blank. See also the `OpenShiftoAuth` field. - type: string - oAuthSecret: - description: Name of the secret set in the OpenShift `OAuthClient` - resource used to setup identity federation on the OpenShift - side. Auto-generated when left blank. See also the `OAuthClientName` - field. - type: string - openShiftoAuth: - description: 'Deprecated. The value of this flag is ignored. - Enables the integration of the identity provider (Keycloak - / RHSSO) with OpenShift OAuth. Empty value on OpenShift by - default. This will allow users to directly login with their - OpenShift user through the OpenShift login, and have their - workspaces created under personal OpenShift namespaces. WARNING: - the `kubeadmin` user is NOT supported, and logging through - it will NOT allow accessing the Che Dashboard.' - type: boolean - updateAdminPassword: - description: Deprecated. The value of this flag is ignored. - Forces the default `admin` Che user to update password on - first login. Defaults to `false`. - type: boolean - type: object - dashboard: - description: Configuration settings related to the User Dashboard - used by the Che installation. - properties: - warning: - description: Warning message that will be displayed on the User - Dashboard - type: string - type: object - database: - description: Configuration settings related to the database used - by the Che installation. - properties: - chePostgresContainerResources: - description: PostgreSQL container custom settings - properties: - limits: - description: Limits describes the maximum amount of compute - resources allowed. - properties: - cpu: - description: CPU, in cores. (500m = .5 cores) - type: string - memory: - description: Memory, in bytes. (500Gi = 500GiB = 500 - * 1024 * 1024 * 1024) - type: string - type: object - request: - description: Requests describes the minimum amount of compute - resources required. - properties: - cpu: - description: CPU, in cores. (500m = .5 cores) - type: string - memory: - description: Memory, in bytes. (500Gi = 500GiB = 500 - * 1024 * 1024 * 1024) - type: string - type: object - type: object - chePostgresDb: - description: PostgreSQL database name that the Che server uses - to connect to the DB. Defaults to `dbche`. - type: string - chePostgresHostName: - description: PostgreSQL Database host name that the Che server - uses to connect to. Defaults is `postgres`. Override this - value ONLY when using an external database. See field `externalDb`. - In the default case it will be automatically set by the Operator. - type: string - chePostgresPassword: - description: PostgreSQL password that the Che server uses to - connect to the DB. When omitted or left blank, it will be - set to an automatically generated value. - type: string - chePostgresPort: - description: PostgreSQL Database port that the Che server uses - to connect to. Defaults to 5432. Override this value ONLY - when using an external database. See field `externalDb`. In - the default case it will be automatically set by the Operator. - type: string - chePostgresSecret: - description: 'The secret that contains PostgreSQL`user` and - `password` that the Che server uses to connect to the DB. - When the secret is defined, the `chePostgresUser` and `chePostgresPassword` - are ignored. When the value is omitted or left blank, the - one of following scenarios applies: 1. `chePostgresUser` and - `chePostgresPassword` are defined, then they will be used - to connect to the DB. 2. `chePostgresUser` or `chePostgresPassword` - are not defined, then a new secret with the name `postgres-credentials` - will be created with default value of `pgche` for `user` and - with an auto-generated value for `password`. The secret must - have `app.kubernetes.io/part-of=che.eclipse.org` label.' - type: string - chePostgresUser: - description: PostgreSQL user that the Che server uses to connect - to the DB. Defaults to `pgche`. - type: string - externalDb: - description: 'Instructs the Operator on whether to deploy a - dedicated database. By default, a dedicated PostgreSQL database - is deployed as part of the Che installation. When `externalDb` - is `true`, no dedicated database will be deployed by the Operator - and you will need to provide connection details to the external - DB you are about to use. See also all the fields starting - with: `chePostgres`.' - type: boolean - postgresImage: - description: Overrides the container image used in the PostgreSQL - database deployment. This includes the image tag. Omit it - or leave it empty to use the default container image provided - by the Operator. - type: string - postgresImagePullPolicy: - description: Overrides the image pull policy used in the PostgreSQL - database deployment. Default value is `Always` for `nightly`, - `next` or `latest` images, and `IfNotPresent` in other cases. - type: string - postgresVersion: - description: 'Indicates a PostgreSQL version image to use. Allowed - values are: `9.6` and `13.3`. Migrate your PostgreSQL database - to switch from one version to another.' - type: string - pvcClaimSize: - description: Size of the persistent volume claim for database. - Defaults to `1Gi`. To update pvc storageclass that provisions - it must support resize when Eclipse Che has been already deployed. - type: string - type: object - devWorkspace: - description: DevWorkspace operator configuration - properties: - controllerImage: - description: Overrides the container image used in the DevWorkspace - controller deployment. This includes the image tag. Omit it - or leave it empty to use the default container image provided - by the Operator. - type: string - enable: - description: Deploys the DevWorkspace Operator in the cluster. - Does nothing when a matching version of the Operator is already - installed. Fails when a non-matching version of the Operator - is already installed. - type: boolean - runningLimit: - description: Maximum number of the running workspaces per user. - type: string - required: - - enable - type: object - imagePuller: - description: Kubernetes Image Puller configuration - properties: - enable: - description: Install and configure the Community Supported Kubernetes - Image Puller Operator. When set to `true` and no spec is provided, - it will create a default KubernetesImagePuller object to be - managed by the Operator. When set to `false`, the KubernetesImagePuller - object will be deleted, and the Operator will be uninstalled, - regardless of whether a spec is provided. If the `spec.images` - field is empty, a set of recommended workspace-related images - will be automatically detected and pre-pulled after installation. - Note that while this Operator and its behavior is community-supported, - its payload may be commercially-supported for pulling commercially-supported - images. - type: boolean - spec: - description: A KubernetesImagePullerSpec to configure the image - puller in the CheCluster - properties: - affinity: - type: string - cachingCPULimit: - type: string - cachingCPURequest: - type: string - cachingIntervalHours: - type: string - cachingMemoryLimit: - type: string - cachingMemoryRequest: - type: string - configMapName: - type: string - daemonsetName: - type: string - deploymentName: - type: string - imagePullSecrets: - type: string - imagePullerImage: - type: string - images: - type: string - nodeSelector: - type: string - type: object - required: - - enable - type: object - k8s: - description: Configuration settings specific to Che installations - made on upstream Kubernetes. - properties: - ingressClass: - description: 'Ingress class that will define the which controller - will manage ingresses. Defaults to `nginx`. NB: This drives - the `kubernetes.io/ingress.class` annotation on Che-related - ingresses.' - type: string - ingressDomain: - description: 'Global ingress domain for a Kubernetes cluster. - This MUST be explicitly specified: there are no defaults.' - type: string - ingressStrategy: - description: 'Deprecated. The value of this flag is ignored. - Strategy for ingress creation. Options are: `multi-host` (host - is explicitly provided in ingress), `single-host` (host is - provided, path-based rules) and `default-host` (no host is - provided, path-based rules). Defaults to `multi-host` Deprecated - in favor of `serverExposureStrategy` in the `server` section, - which defines this regardless of the cluster type. When both - are defined, the `serverExposureStrategy` option takes precedence.' - type: string - securityContextFsGroup: - description: The FSGroup in which the Che Pod and workspace - Pods containers runs in. Default value is `1724`. - type: string - securityContextRunAsUser: - description: ID of the user the Che Pod and workspace Pods containers - run as. Default value is `1724`. - type: string - singleHostExposureType: - description: Deprecated. The value of this flag is ignored. - When the serverExposureStrategy is set to `single-host`, the - way the server, registries and workspaces are exposed is further - configured by this property. The possible values are `native`, - which means that the server and workspaces are exposed using - ingresses on K8s or `gateway` where the server and workspaces - are exposed using a custom gateway based on link:https://doc.traefik.io/traefik/[Traefik]. - All the endpoints whether backed by the ingress or gateway - `route` always point to the subpaths on the same domain. Defaults - to `native`. - type: string - tlsSecretName: - description: Name of a secret that will be used to setup ingress - TLS termination when TLS is enabled. When the field is empty - string, the default cluster certificate will be used. See - also the `tlsSupport` field. - type: string - type: object - metrics: - description: Configuration settings related to the metrics collection - used by the Che installation. - properties: - enable: - description: Enables `metrics` the Che server endpoint. Default - to `true`. - type: boolean - type: object - server: - description: General configuration settings related to the Che server, - the plugin and devfile registries - properties: - airGapContainerRegistryHostname: - description: Optional host name, or URL, to an alternate container - registry to pull images from. This value overrides the container - registry host name defined in all the default container images - involved in a Che deployment. This is particularly useful - to install Che in a restricted environment. - type: string - airGapContainerRegistryOrganization: - description: Optional repository name of an alternate container - registry to pull images from. This value overrides the container - registry organization defined in all the default container - images involved in a Che deployment. This is particularly - useful to install Eclipse Che in a restricted environment. - type: string - allowUserDefinedWorkspaceNamespaces: - description: Deprecated. The value of this flag is ignored. - Defines that a user is allowed to specify a Kubernetes namespace, - or an OpenShift project, which differs from the default. It's - NOT RECOMMENDED to set to `true` without OpenShift OAuth configured. - The OpenShift infrastructure also uses this property. - type: boolean - cheClusterRoles: - description: A comma-separated list of ClusterRoles that will - be assigned to Che ServiceAccount. Each role must have `app.kubernetes.io/part-of=che.eclipse.org` - label. Be aware that the Che Operator has to already have - all permissions in these ClusterRoles to grant them. - type: string - cheDebug: - description: Enables the debug mode for Che server. Defaults - to `false`. - type: string - cheFlavor: - description: Deprecated. The value of this flag is ignored. - Specifies a variation of the installation. The options are `che` - for upstream Che installations or `devspaces` for Red Hat - OpenShift Dev Spaces (formerly Red Hat CodeReady Workspaces) - installation - type: string - cheHost: - description: Public host name of the installed Che server. When - value is omitted, the value it will be automatically set by - the Operator. See the `cheHostTLSSecret` field. - type: string - cheHostTLSSecret: - description: Name of a secret containing certificates to secure - ingress or route for the custom host name of the installed - Che server. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` - label. See the `cheHost` field. - type: string - cheImage: - description: Overrides the container image used in Che deployment. - This does NOT include the container image tag. Omit it or - leave it empty to use the default container image provided - by the Operator. - type: string - cheImagePullPolicy: - description: Overrides the image pull policy used in Che deployment. - Default value is `Always` for `nightly`, `next` or `latest` - images, and `IfNotPresent` in other cases. - type: string - cheImageTag: - description: Overrides the tag of the container image used in - Che deployment. Omit it or leave it empty to use the default - image tag provided by the Operator. - type: string - cheLogLevel: - description: 'Log level for the Che server: `INFO` or `DEBUG`. - Defaults to `INFO`.' - type: string - cheServerIngress: - description: The Che server ingress custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - cheServerRoute: - description: The Che server route custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - domain: - description: 'Operator uses the domain to generate a hostname - for a route. In a conjunction with labels it creates a - route, which is served by a non-default Ingress controller. - The generated host name will follow this pattern: `-.`.' - type: string - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - cheWorkspaceClusterRole: - description: Custom cluster role bound to the user for the Che - workspaces. The role must have `app.kubernetes.io/part-of=che.eclipse.org` - label. The default roles are used when omitted or left blank. - type: string - customCheProperties: - additionalProperties: - type: string - description: Map of additional environment variables that will - be applied in the generated `che` ConfigMap to be used by - the Che server, in addition to the values already generated - from other fields of the `CheCluster` custom resource (CR). - When `customCheProperties` contains a property that would - be normally generated in `che` ConfigMap from other CR fields, - the value defined in the `customCheProperties` is used instead. - type: object - dashboardCpuLimit: - description: Overrides the CPU limit used in the dashboard deployment. - In cores. (500m = .5 cores). Default to 500m. - type: string - dashboardCpuRequest: - description: Overrides the CPU request used in the dashboard - deployment. In cores. (500m = .5 cores). Default to 100m. - type: string - dashboardImage: - description: Overrides the container image used in the dashboard - deployment. This includes the image tag. Omit it or leave - it empty to use the default container image provided by the - Operator. - type: string - dashboardImagePullPolicy: - description: Overrides the image pull policy used in the dashboard - deployment. Default value is `Always` for `nightly`, `next` - or `latest` images, and `IfNotPresent` in other cases. - type: string - dashboardIngress: - description: Deprecated. The value of this flag is ignored. - Dashboard ingress custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - dashboardMemoryLimit: - description: Overrides the memory limit used in the dashboard - deployment. Defaults to 256Mi. - type: string - dashboardMemoryRequest: - description: Overrides the memory request used in the dashboard - deployment. Defaults to 16Mi. - type: string - dashboardRoute: - description: Deprecated. The value of this flag is ignored. - Dashboard route custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - domain: - description: 'Operator uses the domain to generate a hostname - for a route. In a conjunction with labels it creates a - route, which is served by a non-default Ingress controller. - The generated host name will follow this pattern: `-.`.' - type: string - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - devfileRegistryCpuLimit: - description: Overrides the CPU limit used in the devfile registry - deployment. In cores. (500m = .5 cores). Default to 500m. - type: string - devfileRegistryCpuRequest: - description: Overrides the CPU request used in the devfile registry - deployment. In cores. (500m = .5 cores). Default to 100m. - type: string - devfileRegistryImage: - description: Overrides the container image used in the devfile - registry deployment. This includes the image tag. Omit it - or leave it empty to use the default container image provided - by the Operator. - type: string - devfileRegistryIngress: - description: Deprecated. The value of this flag is ignored. - The devfile registry ingress custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - devfileRegistryMemoryLimit: - description: Overrides the memory limit used in the devfile - registry deployment. Defaults to 256Mi. - type: string - devfileRegistryMemoryRequest: - description: Overrides the memory request used in the devfile - registry deployment. Defaults to 16Mi. - type: string - devfileRegistryPullPolicy: - description: Overrides the image pull policy used in the devfile - registry deployment. Default value is `Always` for `nightly`, - `next` or `latest` images, and `IfNotPresent` in other cases. - type: string - devfileRegistryRoute: - description: Deprecated. The value of this flag is ignored. - The devfile registry route custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - domain: - description: 'Operator uses the domain to generate a hostname - for a route. In a conjunction with labels it creates a - route, which is served by a non-default Ingress controller. - The generated host name will follow this pattern: `-.`.' - type: string - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - devfileRegistryUrl: - description: Deprecated in favor of `externalDevfileRegistries` - fields. - type: string - disableInternalClusterSVCNames: - description: Deprecated. The value of this flag is ignored. - Disable internal cluster SVC names usage to communicate between - components to speed up the traffic and avoid proxy issues. - type: boolean - externalDevfileRegistries: - description: External devfile registries, that serves sample, - ready-to-use devfiles. Configure this in addition to a dedicated - devfile registry (when `externalDevfileRegistry` is `false`) - or instead of it (when `externalDevfileRegistry` is `true`) - items: - description: Settings for a configuration of the external - devfile registries. - properties: - url: - description: Public URL of the devfile registry. - type: string - type: object - type: array - externalDevfileRegistry: - description: Instructs the Operator on whether to deploy a dedicated - devfile registry server. By default, a dedicated devfile registry - server is started. When `externalDevfileRegistry` is `true`, - no such dedicated server will be started by the Operator and - configure at least one devfile registry with `externalDevfileRegistries` - field. - type: boolean - externalPluginRegistry: - description: Instructs the Operator on whether to deploy a dedicated - plugin registry server. By default, a dedicated plugin registry - server is started. When `externalPluginRegistry` is `true`, - no such dedicated server will be started by the Operator and - you will have to manually set the `pluginRegistryUrl` field. - type: boolean - gitSelfSignedCert: - description: When enabled, the certificate from `che-git-self-signed-cert` - ConfigMap will be propagated to the Che components and provide - particular configuration for Git. Note, the `che-git-self-signed-cert` - ConfigMap must have `app.kubernetes.io/part-of=che.eclipse.org` - label. - type: boolean - nonProxyHosts: - description: 'List of hosts that will be reached directly, bypassing - the proxy. Specify wild card domain use the following form - `.` and `|` as delimiter, for example: `localhost|.my.host.com|123.42.12.32` - Only use when configuring a proxy is required. Operator respects - OpenShift cluster wide proxy configuration and no additional - configuration is required, but defining `nonProxyHosts` in - a custom resource leads to merging non proxy hosts lists from - the cluster proxy configuration and ones defined in the custom - resources. See the doc https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. - See also the `proxyURL` fields.' - type: string - pluginRegistryCpuLimit: - description: Overrides the CPU limit used in the plugin registry - deployment. In cores. (500m = .5 cores). Default to 500m. - type: string - pluginRegistryCpuRequest: - description: Overrides the CPU request used in the plugin registry - deployment. In cores. (500m = .5 cores). Default to 100m. - type: string - pluginRegistryImage: - description: Overrides the container image used in the plugin - registry deployment. This includes the image tag. Omit it - or leave it empty to use the default container image provided - by the Operator. - type: string - pluginRegistryIngress: - description: Deprecated. The value of this flag is ignored. - Plugin registry ingress custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - pluginRegistryMemoryLimit: - description: Overrides the memory limit used in the plugin registry - deployment. Defaults to 256Mi. - type: string - pluginRegistryMemoryRequest: - description: Overrides the memory request used in the plugin - registry deployment. Defaults to 16Mi. - type: string - pluginRegistryPullPolicy: - description: Overrides the image pull policy used in the plugin - registry deployment. Default value is `Always` for `nightly`, - `next` or `latest` images, and `IfNotPresent` in other cases. - type: string - pluginRegistryRoute: - description: Deprecated. The value of this flag is ignored. - Plugin registry route custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - domain: - description: 'Operator uses the domain to generate a hostname - for a route. In a conjunction with labels it creates a - route, which is served by a non-default Ingress controller. - The generated host name will follow this pattern: `-.`.' - type: string - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - pluginRegistryUrl: - description: Public URL of the plugin registry that serves sample - ready-to-use devfiles. Set this ONLY when a use of an external - devfile registry is needed. See the `externalPluginRegistry` - field. By default, this will be automatically calculated by - the Operator. - type: string - proxyPassword: - description: Password of the proxy server. Only use when proxy - configuration is required. See the `proxyURL`, `proxyUser` - and `proxySecret` fields. - type: string - proxyPort: - description: Port of the proxy server. Only use when configuring - a proxy is required. See also the `proxyURL` and `nonProxyHosts` - fields. - type: string - proxySecret: - description: The secret that contains `user` and `password` - for a proxy server. When the secret is defined, the `proxyUser` - and `proxyPassword` are ignored. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` - label. - type: string - proxyURL: - description: URL (protocol+host name) of the proxy server. This - drives the appropriate changes in the `JAVA_OPTS` and `https(s)_proxy` - variables in the Che server and workspaces containers. Only - use when configuring a proxy is required. Operator respects - OpenShift cluster wide proxy configuration and no additional - configuration is required, but defining `proxyUrl` in a custom - resource leads to overrides the cluster proxy configuration - with fields `proxyUrl`, `proxyPort`, `proxyUser` and `proxyPassword` - from the custom resource. See the doc https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. - See also the `proxyPort` and `nonProxyHosts` fields. - type: string - proxyUser: - description: User name of the proxy server. Only use when configuring - a proxy is required. See also the `proxyURL`, `proxyPassword` - and `proxySecret` fields. - type: string - selfSignedCert: - description: Deprecated. The value of this flag is ignored. - The Che Operator will automatically detect whether the router - certificate is self-signed and propagate it to other components, - such as the Che server. - type: boolean - serverCpuLimit: - description: Overrides the CPU limit used in the Che server - deployment In cores. (500m = .5 cores). Default to 1. - type: string - serverCpuRequest: - description: Overrides the CPU request used in the Che server - deployment In cores. (500m = .5 cores). Default to 100m. - type: string - serverExposureStrategy: - description: Deprecated. The value of this flag is ignored. - Sets the server and workspaces exposure type. Possible values - are `multi-host`, `single-host`, `default-host`. Defaults - to `multi-host`, which creates a separate ingress, or OpenShift - routes, for every required endpoint. `single-host` makes Che - exposed on a single host name with workspaces exposed on subpaths. - Read the docs to learn about the limitations of this approach. - Also consult the `singleHostExposureType` property to further - configure how the Operator and the Che server make that happen - on Kubernetes. `default-host` exposes the Che server on the - host of the cluster. Read the docs to learn about the limitations - of this approach. - type: string - serverMemoryLimit: - description: Overrides the memory limit used in the Che server - deployment. Defaults to 1Gi. - type: string - serverMemoryRequest: - description: Overrides the memory request used in the Che server - deployment. Defaults to 512Mi. - type: string - serverTrustStoreConfigMapName: - description: Name of the ConfigMap with public certificates - to add to Java trust store of the Che server. This is often - required when adding the OpenShift OAuth provider, which has - HTTPS endpoint signed with self-signed cert. The Che server - must be aware of its CA cert to be able to request it. This - is disabled by default. The Config Map must have `app.kubernetes.io/part-of=che.eclipse.org` - label. - type: string - singleHostGatewayConfigMapLabels: - additionalProperties: - type: string - description: The labels that need to be present in the ConfigMaps - representing the gateway configuration. - type: object - singleHostGatewayConfigSidecarImage: - description: The image used for the gateway sidecar that provides - configuration to the gateway. Omit it or leave it empty to - use the default container image provided by the Operator. - type: string - singleHostGatewayImage: - description: The image used for the gateway in the single host - mode. Omit it or leave it empty to use the default container - image provided by the Operator. - type: string - tlsSupport: - description: Deprecated. Instructs the Operator to deploy Che - in TLS mode. This is enabled by default. Disabling TLS sometimes - cause malfunction of some Che components. - type: boolean - useInternalClusterSVCNames: - description: Deprecated in favor of `disableInternalClusterSVCNames`. - type: boolean - workspaceNamespaceDefault: - description: Defines Kubernetes default namespace in which user's - workspaces are created for a case when a user does not override - it. It's possible to use ``, `` and `` - placeholders, such as che-workspace-. In that case, - a new namespace will be created for each user or workspace. - type: string - workspacePodNodeSelector: - additionalProperties: - type: string - description: The node selector that limits the nodes that can - run the workspace pods. - type: object - workspacePodTolerations: - description: The pod tolerations put on the workspace pods to - limit where the workspace pods can run. - items: - description: The pod this Toleration is attached to tolerates - any taint that matches the triple using - the matching operator . - properties: - effect: - description: Effect indicates the taint effect to match. - Empty means match all taint effects. When specified, - allowed values are NoSchedule, PreferNoSchedule and - NoExecute. - type: string - key: - description: Key is the taint key that the toleration - applies to. Empty means match all taint keys. If the - key is empty, operator must be Exists; this combination - means to match all values and all keys. - type: string - operator: - description: Operator represents a key's relationship - to the value. Valid operators are Exists and Equal. - Defaults to Equal. Exists is equivalent to wildcard - for value, so that a pod can tolerate all taints of - a particular category. - type: string - tolerationSeconds: - description: TolerationSeconds represents the period of - time the toleration (which must be of effect NoExecute, - otherwise this field is ignored) tolerates the taint. - By default, it is not set, which means tolerate the - taint forever (do not evict). Zero and negative values - will be treated as 0 (evict immediately) by the system. - format: int64 - type: integer - value: - description: Value is the taint value the toleration matches - to. If the operator is Exists, the value should be empty, - otherwise just a regular string. - type: string - type: object - type: array - workspacesDefaultPlugins: - description: Default plug-ins applied to Devworkspaces. - items: - properties: - editor: - description: The editor id to specify default plug-ins - for. - type: string - plugins: - description: Default plug-in uris for the specified editor. - items: - type: string - type: array - type: object - type: array - type: object - storage: - description: Configuration settings related to the persistent storage - used by the Che installation. - properties: - postgresPVCStorageClassName: - description: Storage class for the Persistent Volume Claim dedicated - to the PostgreSQL database. When omitted or left blank, a - default storage class is used. - type: string - preCreateSubPaths: - description: Instructs the Che server to start a special Pod - to pre-create a sub-path in the Persistent Volumes. Defaults - to `false`, however it will need to enable it according to - the configuration of your Kubernetes cluster. - type: boolean - pvcClaimSize: - description: Size of the persistent volume claim for workspaces. - Defaults to `10Gi`. - type: string - pvcJobsImage: - description: Overrides the container image used to create sub-paths - in the Persistent Volumes. This includes the image tag. Omit - it or leave it empty to use the default container image provided - by the Operator. See also the `preCreateSubPaths` field. - type: string - pvcStrategy: - description: Persistent volume claim strategy for the Che server. - This Can be:`common` (all workspaces PVCs in one volume), - `per-workspace` (one PVC per workspace for all declared volumes) - and `unique` (one PVC per declared volume). Defaults to `common`. - type: string - workspacePVCStorageClassName: - description: Storage class for the Persistent Volume Claims - dedicated to the Che workspaces. When omitted or left blank, - a default storage class is used. - type: string - type: object - type: object - status: - description: CheClusterStatus defines the observed state of Che installation - properties: - cheClusterRunning: - description: Status of a Che installation. Can be `Available`, `Unavailable`, - or `Available, Rolling Update in Progress`. - type: string - cheURL: - description: Public URL to the Che server. - type: string - cheVersion: - description: Current installed Che version. - type: string - dbProvisioned: - description: Indicates that a PostgreSQL instance has been correctly - provisioned or not. - type: boolean - devfileRegistryURL: - description: Public URL to the devfile registry. - type: string - devworkspaceStatus: - description: The status of the Devworkspace subsystem - properties: - gatewayHost: - description: GatewayHost is the resolved host of the ingress/route. - This is equal to the Host in the spec on Kubernetes but contains - the actual host name of the route if Host is unspecified on - OpenShift. - type: string - gatewayPhase: - description: GatewayPhase specifies the phase in which the gateway - deployment currently is. If the gateway is disabled, the phase - is "Inactive". - type: string - message: - description: Message contains further human-readable info for - why the Che cluster is in the phase it currently is. - type: string - phase: - description: Phase is the phase in which the Che cluster as - a whole finds itself in. - type: string - reason: - description: A brief CamelCase message indicating details about - why the Che cluster is in this state. - type: string - workspaceBaseDomain: - description: The resolved workspace base domain. This is either - the copy of the explicitly defined property of the same name - in the spec or, if it is undefined in the spec and we're running - on OpenShift, the automatically resolved basedomain for routes. - type: string - type: object - gitHubOAuthProvisioned: - description: Indicates whether an Identity Provider instance, Keycloak - or RH-SSO, has been configured to integrate with the GitHub OAuth. - type: boolean - helpLink: - description: A URL that points to some URL where to find help related - to the current Operator status. - type: string - keycloakProvisioned: - description: Indicates whether an Identity Provider instance, Keycloak - or RH-SSO, has been provisioned with realm, client and user. - type: boolean - keycloakURL: - description: Public URL to the Identity Provider server, Keycloak - or RH-SSO,. - type: string - message: - description: A human readable message indicating details about why - the Pod is in this condition. - type: string - openShiftOAuthUserCredentialsSecret: - description: OpenShift OAuth secret in `openshift-config` namespace - that contains user credentials for HTPasswd identity provider. - type: string - openShiftoAuthProvisioned: - description: Indicates whether an Identity Provider instance, Keycloak - or RH-SSO, has been configured to integrate with the OpenShift - OAuth. - type: boolean - pluginRegistryURL: - description: Public URL to the plugin registry. - type: string - reason: - description: A brief CamelCase message indicating details about - why the Pod is in this state. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/helmcharts/next/templates/che-operator-selfsigned-issuer.Issuer.yaml b/helmcharts/next/templates/che-operator-selfsigned-issuer.Issuer.yaml new file mode 100644 index 000000000..373eda80e --- /dev/null +++ b/helmcharts/next/templates/che-operator-selfsigned-issuer.Issuer.yaml @@ -0,0 +1,24 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator-selfsigned-issuer + namespace: eclipse-che +spec: + selfSigned: {} diff --git a/helmcharts/next/templates/che-operator-service.Service.yaml b/helmcharts/next/templates/che-operator-service.Service.yaml new file mode 100644 index 000000000..03b34f1a5 --- /dev/null +++ b/helmcharts/next/templates/che-operator-service.Service.yaml @@ -0,0 +1,28 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator-service + namespace: eclipse-che +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + app: che-operator diff --git a/helmcharts/next/templates/che-operator-serving-cert.Certificate.yaml b/helmcharts/next/templates/che-operator-serving-cert.Certificate.yaml new file mode 100644 index 000000000..1ff97ad7a --- /dev/null +++ b/helmcharts/next/templates/che-operator-serving-cert.Certificate.yaml @@ -0,0 +1,30 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator-serving-cert + namespace: eclipse-che +spec: + dnsNames: + - che-operator-service.eclipse-che.svc + - che-operator-service.eclipse-che.svc.cluster.local + issuerRef: + kind: Issuer + name: che-operator-selfsigned-issuer + secretName: che-operator-webhook-server-cert diff --git a/helmcharts/next/templates/che-operator.ClusterRole.yaml b/helmcharts/next/templates/che-operator.ClusterRole.yaml new file mode 100644 index 000000000..0c62c51d7 --- /dev/null +++ b/helmcharts/next/templates/che-operator.ClusterRole.yaml @@ -0,0 +1,669 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator +rules: +- apiGroups: + - "" + resources: + - nodes + verbs: + - get +- apiGroups: + - oauth.openshift.io + resources: + - oauthclients + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - config.openshift.io + resources: + - oauths + verbs: + - get + - list + - watch + - patch +- apiGroups: + - config.openshift.io + resources: + - infrastructures + - proxies + verbs: + - get + - list + - watch +- apiGroups: + - user.openshift.io + resources: + - users + verbs: + - list + - delete +- apiGroups: + - user.openshift.io + resources: + - identities + verbs: + - delete +- apiGroups: + - console.openshift.io + resources: + - consolelinks + verbs: + - get + - list + - create + - update + - patch + - delete +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + verbs: + - list + - create + - watch + - update + - get + - delete +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + - rolebindings + verbs: + - get + - create + - update + - delete +- apiGroups: + - authorization.openshift.io + resources: + - roles + - rolebindings + verbs: + - get + - create + - update + - delete +- apiGroups: + - org.eclipse.che + resources: + - checlusters + - checlusters/status + - checlusters/finalizers + - checlusters/status + verbs: + - '*' +- apiGroups: + - project.openshift.io + resources: + - projectrequests + verbs: + - create + - update +- apiGroups: + - project.openshift.io + resources: + - projects + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - create + - update + - watch +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create +- apiGroups: + - apps + resources: + - secrets + verbs: + - list +- apiGroups: + - "" + resources: + - secrets + verbs: + - list + - get + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - create + - get + - list + - watch + - delete +- apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - create + - watch + - delete +- apiGroups: + - apps + - extensions + resources: + - deployments + - replicasets + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes/custom-host + verbs: + - create +- apiGroups: + - "" + resources: + - events + verbs: + - watch +- apiGroups: + - apps + resources: + - replicasets + verbs: + - list + - get + - patch + - delete +- apiGroups: + - extensions + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - list + - create + - watch + - get + - delete +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get + - create + - update +- apiGroups: + - operators.coreos.com + resources: + - subscriptions + verbs: + - get +- apiGroups: + - operators.coreos.com + resources: + - clusterserviceversions + verbs: + - list + - get + - watch +- apiGroups: + - metrics.k8s.io + resources: + - pods + - nodes + verbs: + - get + - list + - watch +- apiGroups: + - cert-manager.io + resources: + - issuers + - certificates + verbs: + - create + - get + - list + - update +- apiGroups: + - "" + resources: + - configmaps + - persistentvolumeclaims + - pods + - secrets + - serviceaccounts + - services + verbs: + - '*' +- apiGroups: + - apps + resourceNames: + - che-operator + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - update + - watch +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/finalizers + verbs: + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/status + verbs: + - get + - patch + - update +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - create + - get +- nonResourceURLs: + - /metrics + verbs: + - get +- apiGroups: + - che.eclipse.org + resources: + - kubernetesimagepullers + verbs: + - '*' +- apiGroups: + - config.openshift.io + resourceNames: + - cluster + resources: + - consoles + verbs: + - get +- apiGroups: + - "" + resources: + - pods/log + verbs: + - get + - list + - watch +- apiGroups: + - workspace.devfile.io + resources: + - devworkspaces + - devworkspacetemplates + verbs: + - get + - list + - watch +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + - components + verbs: + - get + - list + - watch +- apiGroups: + - workspace.devfile.io + resources: + - devworkspaces + - devworkspacetemplates + verbs: + - create + - delete + - deletecollection + - patch + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + - components + verbs: + - create + - delete + - deletecollection + - patch + - update +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +- apiGroups: + - "" + resourceNames: + - workspace-preferences-configmap + resources: + - configmaps + verbs: + - create + - delete + - get + - patch +- apiGroups: + - "" + resources: + - configmaps + - persistentvolumeclaims + - pods + - secrets + - serviceaccounts + verbs: + - '*' +- apiGroups: + - "" + resources: + - events + - namespaces + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create +- apiGroups: + - "" + resourceNames: + - workspace-credentials-secret + resources: + - secrets + verbs: + - create + - delete + - get + - patch +- apiGroups: + - "" + resources: + - services + verbs: + - '*' +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resourceNames: + - devworkspace-controller + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - apps + - extensions + resources: + - deployments + verbs: + - get + - list + - watch +- apiGroups: + - apps + - extensions + resources: + - deployments + - replicasets + verbs: + - '*' +- apiGroups: + - apps + - extensions + resources: + - replicasets + verbs: + - get + - list + - watch +- apiGroups: + - authorization.k8s.io + resources: + - localsubjectaccessreviews + - subjectaccessreviews + verbs: + - create +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - config.openshift.io + resourceNames: + - cluster + resources: + - proxies + verbs: + - get +- apiGroups: + - controller.devfile.io + resources: + - '*' + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/status + verbs: + - get + - patch + - update +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - update +- apiGroups: + - metrics.k8s.io + resources: + - pods + verbs: + - get + - list + - watch +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - create + - get +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - oauth.openshift.io + resources: + - oauthclients + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + - rolebindings + - roles + verbs: + - create + - get + - list + - update + - watch +- apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes/custom-host + verbs: + - create +- apiGroups: + - workspace.devfile.io + resources: + - '*' + verbs: + - '*' +- nonResourceURLs: + - /metrics + verbs: + - get diff --git a/helmcharts/next/templates/che-operator.ClusterRoleBinding.yaml b/helmcharts/next/templates/che-operator.ClusterRoleBinding.yaml new file mode 100644 index 000000000..d67afa6e7 --- /dev/null +++ b/helmcharts/next/templates/che-operator.ClusterRoleBinding.yaml @@ -0,0 +1,29 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: che-operator +subjects: +- kind: ServiceAccount + name: che-operator + namespace: eclipse-che diff --git a/helmcharts/next/templates/che-operator.Deployment.yaml b/helmcharts/next/templates/che-operator.Deployment.yaml new file mode 100644 index 000000000..7c98d7d57 --- /dev/null +++ b/helmcharts/next/templates/che-operator.Deployment.yaml @@ -0,0 +1,156 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: che-operator + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator + namespace: eclipse-che +spec: + replicas: 1 + selector: + matchLabels: + app: che-operator + strategy: + type: RollingUpdate + template: + metadata: + labels: + app: che-operator + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + spec: + containers: + - args: + - --leader-elect + command: + - /manager + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: OPERATOR_NAME + value: che-operator + - name: CHE_VERSION + value: next + - name: RELATED_IMAGE_che_server + value: quay.io/eclipse/che-server:next + - name: RELATED_IMAGE_dashboard + value: quay.io/eclipse/che-dashboard:next + - name: RELATED_IMAGE_plugin_registry + value: quay.io/eclipse/che-plugin-registry:next + - name: RELATED_IMAGE_devfile_registry + value: quay.io/eclipse/che-devfile-registry:next + - name: RELATED_IMAGE_che_tls_secrets_creation_job + value: quay.io/eclipse/che-tls-secret-creator:alpine-01a4c34 + - name: RELATED_IMAGE_postgres + value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392 + - name: RELATED_IMAGE_postgres_13_3 + value: quay.io/eclipse/che--centos--postgresql-13-centos7:1-71b24684d64da46f960682cc4216222a7e4ed8b1a31dd5a865b3e71afdea20d2 + - name: RELATED_IMAGE_single_host_gateway + value: quay.io/eclipse/che--traefik:v2.5.0-eb30f9f09a65cee1fab5ef9c64cb4ec91b800dc3fdd738d62a9d4334f0114683 + - name: RELATED_IMAGE_single_host_gateway_config_sidecar + value: quay.io/che-incubator/configbump:0.1.4 + - name: RELATED_IMAGE_devworkspace_controller + value: quay.io/devfile/devworkspace-controller:v0.14.1 + - name: RELATED_IMAGE_gateway_authentication_sidecar + value: quay.io/openshift/origin-oauth-proxy:4.7 + - name: RELATED_IMAGE_gateway_authorization_sidecar + value: quay.io/openshift/origin-kube-rbac-proxy:4.7 + - name: RELATED_IMAGE_gateway_authentication_sidecar_k8s + value: quay.io/oauth2-proxy/oauth2-proxy:v7.2.0 + - name: RELATED_IMAGE_gateway_authorization_sidecar_k8s + value: quay.io/brancz/kube-rbac-proxy:v0.11.0 + - name: RELATED_IMAGE_gateway_header_sidecar + value: quay.io/che-incubator/header-rewrite-proxy:latest + - name: CHE_FLAVOR + value: che + - name: CONSOLE_LINK_NAME + value: che + - name: CONSOLE_LINK_DISPLAY_NAME + value: Eclipse Che + - name: CONSOLE_LINK_SECTION + value: Red Hat Applications + - name: CONSOLE_LINK_IMAGE + value: /dashboard/assets/branding/loader.svg + - name: MAX_CONCURRENT_RECONCILES + value: "1" + - name: ADD_COMPONENT_READINESS_INIT_CONTAINERS + value: "false" + image: quay.io/eclipse/che-operator:next + imagePullPolicy: Always + livenessProbe: + failureThreshold: 10 + httpGet: + path: /healthz + port: 6789 + initialDelaySeconds: 15 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + name: che-operator + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + - containerPort: 60000 + name: metrics + readinessProbe: + failureThreshold: 10 + httpGet: + path: /readyz + port: 6789 + initialDelaySeconds: 5 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 100m + memory: 64Mi + securityContext: + capabilities: + drop: + - ALL + privileged: false + readOnlyRootFilesystem: false + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: webhook-tls-certs + readOnly: true + hostIPC: false + hostNetwork: false + hostPID: false + restartPolicy: Always + serviceAccountName: che-operator + terminationGracePeriodSeconds: 20 + volumes: + - name: webhook-tls-certs + secret: + defaultMode: 420 + secretName: che-operator-webhook-server-cert diff --git a/helmcharts/next/templates/che-operator.Role.yaml b/helmcharts/next/templates/che-operator.Role.yaml new file mode 100644 index 000000000..1c02ee053 --- /dev/null +++ b/helmcharts/next/templates/che-operator.Role.yaml @@ -0,0 +1,184 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator +rules: +- apiGroups: + - extensions + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - batch + resources: + - jobs + verbs: + - '*' +- apiGroups: + - route.openshift.io + resources: + - routes + - routes/custom-host + verbs: + - '*' +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + - rolebindings + verbs: + - '*' +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterroles + - clusterrolebindings + verbs: + - '*' +- apiGroups: + - "" + resources: + - pods + - services + - serviceaccounts + - endpoints + - persistentvolumeclaims + - events + - configmaps + - secrets + - pods/exec + - pods/log + verbs: + - '*' +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get +- apiGroups: + - apps + - extensions + resources: + - deployments + - replicasets + verbs: + - '*' +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - get + - create +- apiGroups: + - org.eclipse.che + resources: + - checlusters + - checlusters/status + - checlusters/finalizers + verbs: + - '*' +- apiGroups: + - metrics.k8s.io + resources: + - pods + - nodes + verbs: + - get + - list + - watch +- apiGroups: + - operators.coreos.com + resources: + - subscriptions + - clusterserviceversions + - operatorgroups + verbs: + - '*' +- apiGroups: + - packages.operators.coreos.com + resources: + - packagemanifests + verbs: + - get + - list +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create +- apiGroups: + - apps + resourceNames: + - che-operator + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings + verbs: + - '*' +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/finalizers + verbs: + - update +- apiGroups: + - controller.devfile.io + resources: + - devworkspaceroutings/status + verbs: + - get + - patch + - update +- apiGroups: + - oauth.openshift.io + resources: + - oauthclients + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch diff --git a/helmcharts/next/templates/che-operator.RoleBinding.yaml b/helmcharts/next/templates/che-operator.RoleBinding.yaml new file mode 100644 index 000000000..f145c0454 --- /dev/null +++ b/helmcharts/next/templates/che-operator.RoleBinding.yaml @@ -0,0 +1,28 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: che-operator +subjects: +- kind: ServiceAccount + name: che-operator diff --git a/bundle/next/eclipse-che-preview-openshift/manifests/default_v1_serviceaccount.yaml b/helmcharts/next/templates/che-operator.ServiceAccount.yaml similarity index 84% rename from bundle/next/eclipse-che-preview-openshift/manifests/default_v1_serviceaccount.yaml rename to helmcharts/next/templates/che-operator.ServiceAccount.yaml index 6b313cad8..71a022293 100644 --- a/bundle/next/eclipse-che-preview-openshift/manifests/default_v1_serviceaccount.yaml +++ b/helmcharts/next/templates/che-operator.ServiceAccount.yaml @@ -13,9 +13,10 @@ apiVersion: v1 kind: ServiceAccount metadata: - creationTimestamp: null labels: app.kubernetes.io/component: che-operator app.kubernetes.io/instance: che app.kubernetes.io/name: che - name: default + app.kubernetes.io/part-of: che.eclipse.org + name: che-operator + namespace: eclipse-che diff --git a/helmcharts/next/templates/cluster_role.yaml b/helmcharts/next/templates/cluster_role.yaml deleted file mode 100644 index d41749e4f..000000000 --- a/helmcharts/next/templates/cluster_role.yaml +++ /dev/null @@ -1,677 +0,0 @@ -# -# Copyright (c) 2019-2021 Red Hat, Inc. -# This program and the accompanying materials are made -# available under the terms of the Eclipse Public License 2.0 -# which is available at https://www.eclipse.org/legal/epl-2.0/ -# -# SPDX-License-Identifier: EPL-2.0 -# -# Contributors: -# Red Hat, Inc. - initial API and implementation -# - -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: che-operator - labels: - app.kubernetes.io/name: che - app.kubernetes.io/instance: che - app.kubernetes.io/part-of: che.eclipse.org - app.kubernetes.io/component: che-operator -rules: - ### CHE-OPERATOR ROLES ONLY: BEGIN - - apiGroups: - - "" - resources: - - nodes - verbs: - - get - - apiGroups: - - oauth.openshift.io - resources: - - oauthclients - verbs: - - create - - delete - - deletecollection - - get - - list - - patch - - update - - watch - - apiGroups: - - config.openshift.io - resources: - - oauths - verbs: - - get - - list - - watch - - patch - - apiGroups: - - config.openshift.io - resources: - - infrastructures - - proxies - verbs: - - get - - list - - watch - - apiGroups: - - user.openshift.io - resources: - - users - verbs: - - list - - delete - - apiGroups: - - user.openshift.io - resources: - - identities - verbs: - - delete - - apiGroups: - - console.openshift.io - resources: - - consolelinks - verbs: - - get - - list - - create - - update - - patch - - delete - - apiGroups: - - rbac.authorization.k8s.io - resources: - - clusterrolebindings - - clusterroles - verbs: - - list - - create - - watch - - update - - get - - delete - - apiGroups: - - rbac.authorization.k8s.io - resources: - - roles - - rolebindings - verbs: - - get - - create - - update - - delete - - apiGroups: - - authorization.openshift.io - resources: - - roles - - rolebindings - verbs: - - get - - create - - update - - delete - - apiGroups: - - org.eclipse.che - resources: - - checlusters - - checlusters/status - - checlusters/finalizers - - checlusters/status - verbs: - - '*' - - apiGroups: - - project.openshift.io - resources: - - projectrequests - verbs: - - create - - update - - apiGroups: - - project.openshift.io - resources: - - projects - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - namespaces - verbs: - - get - - list - - create - - update - - watch - - apiGroups: - - '' - resources: - - pods/exec - verbs: - - create - - apiGroups: - - apps - resources: - - secrets - verbs: - - list - - apiGroups: - - '' - resources: - - secrets - verbs: - - list - - get - - create - - update - - patch - - delete - - apiGroups: - - '' - resources: - - persistentvolumeclaims - verbs: - - create - - get - - list - - watch - - delete - - apiGroups: - - '' - resources: - - pods - verbs: - - get - - list - - create - - watch - - delete - - apiGroups: - - apps - - extensions - resources: - - deployments - - replicasets - verbs: - - '*' - - apiGroups: - - route.openshift.io - resources: - - routes - verbs: - - '*' - - apiGroups: - - route.openshift.io - resources: - - routes/custom-host - verbs: - - create - - apiGroups: - - '' - resources: - - events - verbs: - - watch - - apiGroups: - - apps - resources: - - replicasets - verbs: - - list - - get - - patch - - delete - - apiGroups: - - extensions - resources: - - ingresses - verbs: - - '*' - - apiGroups: - - networking.k8s.io - resources: - - ingresses - verbs: - - list - - create - - watch - - get - - delete - - apiGroups: - - apiextensions.k8s.io - resources: - - customresourcedefinitions - verbs: - - get - - create - - update - - apiGroups: - - operators.coreos.com - resources: - - subscriptions - verbs: - - get - - apiGroups: - - operators.coreos.com - resources: - - clusterserviceversions - verbs: - - list - - get - - watch - - apiGroups: - - metrics.k8s.io - resources: - - pods - - nodes - verbs: - - get - - list - - watch - - apiGroups: - - cert-manager.io - resources: - - issuers - - certificates - verbs: - - create - - get - - list - - update - - apiGroups: - - '' - resources: - - configmaps - - persistentvolumeclaims - - pods - - secrets - - serviceaccounts - - services - verbs: - - '*' - - apiGroups: - - apps - resourceNames: - - che-operator - resources: - - deployments/finalizers - verbs: - - update - - apiGroups: - - batch - resources: - - jobs - verbs: - - create - - delete - - get - - update - - watch - - apiGroups: - - controller.devfile.io - resources: - - devworkspaceroutings - verbs: - - '*' - - apiGroups: - - controller.devfile.io - resources: - - devworkspaceroutings/finalizers - verbs: - - update - - apiGroups: - - controller.devfile.io - resources: - - devworkspaceroutings/status - verbs: - - get - - patch - - update - - apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - create - - get - - nonResourceURLs: - - /metrics - verbs: - - get - - apiGroups: - - che.eclipse.org - resources: - - kubernetesimagepullers - verbs: - - '*' - - apiGroups: - - config.openshift.io - resources: - - consoles - resourceNames: - - cluster - verbs: - - get - - apiGroups: - - '' - resources: - - pods/log - verbs: - - get - - list - - watch - ### CHE-OPERATOR ROLES ONLY: END - # devworkspace-controller-view-workspaces.ClusterRole.yaml - - apiGroups: - - workspace.devfile.io - resources: - - devworkspaces - - devworkspacetemplates - verbs: - - get - - list - - watch - - apiGroups: - - controller.devfile.io - resources: - - devworkspaceroutings - - components - verbs: - - get - - list - - watch - # devworkspace-controller-edit-workspaces.ClusterRole.yaml - - apiGroups: - - workspace.devfile.io - resources: - - devworkspaces - - devworkspacetemplates - verbs: - - create - - delete - - deletecollection - - patch - - update - - apiGroups: - - controller.devfile.io - resources: - - devworkspaceroutings - - components - verbs: - - create - - delete - - deletecollection - - patch - - update - # devworkspace-controller-leader-election-role.Role.yaml - - apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete - - apiGroups: - - "" - resources: - - configmaps/status - verbs: - - get - - update - - patch - - apiGroups: - - "" - resources: - - events - verbs: - - create - - patch - # devworkspace-controller-proxy-role.ClusterRole.yaml - - apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create - - apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create - # devworkspace-controller-role.ClusterRole.yaml - - apiGroups: - - "" - resourceNames: - - workspace-preferences-configmap - resources: - - configmaps - verbs: - - create - - delete - - get - - patch - - apiGroups: - - "" - resources: - - configmaps - - persistentvolumeclaims - - pods - - secrets - - serviceaccounts - verbs: - - '*' - - apiGroups: - - "" - resources: - - events - - namespaces - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - pods/exec - verbs: - - create - - apiGroups: - - "" - resourceNames: - - workspace-credentials-secret - resources: - - secrets - verbs: - - create - - delete - - get - - patch - - apiGroups: - - "" - resources: - - services - verbs: - - '*' - - apiGroups: - - admissionregistration.k8s.io - resources: - - mutatingwebhookconfigurations - - validatingwebhookconfigurations - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - apps - resourceNames: - - devworkspace-controller - resources: - - deployments/finalizers - verbs: - - update - - apiGroups: - - apps - - extensions - resources: - - deployments - verbs: - - get - - list - - watch - - apiGroups: - - apps - - extensions - resources: - - deployments - - replicasets - verbs: - - '*' - - apiGroups: - - apps - - extensions - resources: - - replicasets - verbs: - - get - - list - - watch - - apiGroups: - - authorization.k8s.io - resources: - - localsubjectaccessreviews - - subjectaccessreviews - verbs: - - create - - apiGroups: - - batch - resources: - - jobs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - config.openshift.io - resourceNames: - - cluster - resources: - - proxies - verbs: - - get - - apiGroups: - - controller.devfile.io - resources: - - '*' - verbs: - - '*' - - apiGroups: - - controller.devfile.io - resources: - - devworkspaceroutings - verbs: - - '*' - - apiGroups: - - controller.devfile.io - resources: - - devworkspaceroutings/status - verbs: - - get - - patch - - update - - apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - create - - get - - update - - apiGroups: - - metrics.k8s.io - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - create - - get - - apiGroups: - - networking.k8s.io - resources: - - ingresses - verbs: - - '*' - - apiGroups: - - oauth.openshift.io - resources: - - oauthclients - verbs: - - create - - delete - - deletecollection - - get - - list - - patch - - update - - watch - - apiGroups: - - rbac.authorization.k8s.io - resources: - - clusterrolebindings - - clusterroles - - rolebindings - - roles - verbs: - - create - - get - - list - - update - - watch - - apiGroups: - - route.openshift.io - resources: - - routes - verbs: - - '*' - - apiGroups: - - route.openshift.io - resources: - - routes/custom-host - verbs: - - create - - apiGroups: - - workspace.devfile.io - resources: - - '*' - verbs: - - '*' - # devworkspace-controller-metrics-reader.ClusterRole.yaml - - nonResourceURLs: - - /metrics - verbs: - - get diff --git a/helmcharts/next/templates/manager.yaml b/helmcharts/next/templates/manager.yaml deleted file mode 100644 index ae44c8a15..000000000 --- a/helmcharts/next/templates/manager.yaml +++ /dev/null @@ -1,146 +0,0 @@ -# -# Copyright (c) 2019-2021 Red Hat, Inc. -# This program and the accompanying materials are made -# available under the terms of the Eclipse Public License 2.0 -# which is available at https://www.eclipse.org/legal/epl-2.0/ -# -# SPDX-License-Identifier: EPL-2.0 -# -# Contributors: -# Red Hat, Inc. - initial API and implementation -# - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: che-operator - namespace: eclipse-che - labels: - app: che-operator - app.kubernetes.io/name: che - app.kubernetes.io/instance: che - app.kubernetes.io/part-of: che.eclipse.org - app.kubernetes.io/component: che-operator -spec: - replicas: 1 - selector: - matchLabels: - app: che-operator - strategy: - type: RollingUpdate - template: - metadata: - labels: - app: che-operator - app.kubernetes.io/name: che - app.kubernetes.io/instance: che - app.kubernetes.io/part-of: che.eclipse.org - app.kubernetes.io/component: che-operator - spec: - containers: - - name: che-operator - image: quay.io/eclipse/che-operator:next - command: - - /manager - args: - - --leader-elect - imagePullPolicy: Always - ports: - - containerPort: 60000 - name: metrics - env: - - name: WATCH_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: OPERATOR_NAME - value: che-operator - - name: CHE_VERSION - value: next - - name: RELATED_IMAGE_che_server - value: quay.io/eclipse/che-server:next - - name: RELATED_IMAGE_dashboard - value: quay.io/eclipse/che-dashboard:next - - name: RELATED_IMAGE_plugin_registry - value: quay.io/eclipse/che-plugin-registry:next - - name: RELATED_IMAGE_devfile_registry - value: quay.io/eclipse/che-devfile-registry:next - - name: RELATED_IMAGE_che_tls_secrets_creation_job - value: quay.io/eclipse/che-tls-secret-creator:alpine-01a4c34 - - name: RELATED_IMAGE_pvc_jobs - value: registry.access.redhat.com/ubi8-minimal:8.6-751 - - name: RELATED_IMAGE_postgres - value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392 - - name: RELATED_IMAGE_postgres_13_3 - value: quay.io/eclipse/che--centos--postgresql-13-centos7:1-71b24684d64da46f960682cc4216222a7e4ed8b1a31dd5a865b3e71afdea20d2 - - name: RELATED_IMAGE_single_host_gateway - value: quay.io/eclipse/che--traefik:v2.5.0-eb30f9f09a65cee1fab5ef9c64cb4ec91b800dc3fdd738d62a9d4334f0114683 - - name: RELATED_IMAGE_single_host_gateway_config_sidecar - value: quay.io/che-incubator/configbump:0.1.4 - - name: RELATED_IMAGE_devworkspace_controller - value: quay.io/devfile/devworkspace-controller:v0.14.1 - - name: RELATED_IMAGE_gateway_authentication_sidecar - value: quay.io/openshift/origin-oauth-proxy:4.7 - - name: RELATED_IMAGE_gateway_authorization_sidecar - value: quay.io/openshift/origin-kube-rbac-proxy:4.7 - - name: RELATED_IMAGE_gateway_authentication_sidecar_k8s - value: quay.io/oauth2-proxy/oauth2-proxy:v7.2.0 - - name: RELATED_IMAGE_gateway_authorization_sidecar_k8s - value: quay.io/brancz/kube-rbac-proxy:v0.11.0 - - name: RELATED_IMAGE_gateway_header_sidecar - value: quay.io/che-incubator/header-rewrite-proxy:latest - - name: CHE_FLAVOR - value: che - - name: CONSOLE_LINK_NAME - value: che - - name: CONSOLE_LINK_DISPLAY_NAME - value: Eclipse Che - - name: CONSOLE_LINK_SECTION - value: Red Hat Applications - - name: CONSOLE_LINK_IMAGE - value: /dashboard/assets/branding/loader.svg - - name: MAX_CONCURRENT_RECONCILES - value: "1" - - name: ADD_COMPONENT_READINESS_INIT_CONTAINERS - value: "false" - livenessProbe: - httpGet: - path: /healthz - port: 6789 - initialDelaySeconds: 15 - periodSeconds: 10 - failureThreshold: 10 - successThreshold: 1 - timeoutSeconds: 5 - readinessProbe: - httpGet: - path: /readyz - port: 6789 - initialDelaySeconds: 5 - periodSeconds: 10 - failureThreshold: 10 - successThreshold: 1 - timeoutSeconds: 5 - securityContext: - privileged: false - readOnlyRootFilesystem: false - capabilities: - drop: - - ALL - resources: - limits: - cpu: 500m - memory: 256Mi - requests: - cpu: 100m - memory: 64Mi - hostIPC: false - hostNetwork: false - hostPID: false - restartPolicy: Always - serviceAccountName: che-operator - terminationGracePeriodSeconds: 20 diff --git a/helmcharts/next/templates/org_v2_checluster.yaml b/helmcharts/next/templates/org_v2_checluster.yaml new file mode 100644 index 000000000..c9cfb6151 --- /dev/null +++ b/helmcharts/next/templates/org_v2_checluster.yaml @@ -0,0 +1,35 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: org.eclipse.che/v2 +kind: CheCluster +metadata: + name: eclipse-che + namespace: eclipse-che +spec: + devEnvironments: + defaultNamespace: + template: '-che' + storage: + pvcStrategy: 'common' + components: + database: + externalDb: false + metrics: + enable: true + networking: + domain: '{{ .Values.networking.domain }}' + tlsSecretName: 'che-tls' + auth: + identityProviderURL: '{{ .Values.networking.auth.identityProviderURL }}' + oAuthClientName: '{{ .Values.networking.auth.oAuthClientName }}' + oAuthSecret: '{{ .Values.networking.auth.oAuthSecret }}' diff --git a/helmcharts/next/templates/role.yaml b/helmcharts/next/templates/role.yaml deleted file mode 100644 index 00d16e65b..000000000 --- a/helmcharts/next/templates/role.yaml +++ /dev/null @@ -1,185 +0,0 @@ -# -# Copyright (c) 2019-2021 Red Hat, Inc. -# This program and the accompanying materials are made -# available under the terms of the Eclipse Public License 2.0 -# which is available at https://www.eclipse.org/legal/epl-2.0/ -# -# SPDX-License-Identifier: EPL-2.0 -# -# Contributors: -# Red Hat, Inc. - initial API and implementation -# - -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - labels: - app.kubernetes.io/component: che-operator - app.kubernetes.io/instance: che - app.kubernetes.io/part-of: che.eclipse.org - app.kubernetes.io/name: che - name: che-operator - namespace: eclipse-che -rules: - - apiGroups: - - extensions - resources: - - ingresses - verbs: - - '*' - - apiGroups: - - networking.k8s.io - resources: - - ingresses - verbs: - - '*' - - apiGroups: - - batch - resources: - - jobs - verbs: - - '*' - - apiGroups: - - route.openshift.io - resources: - - routes - - routes/custom-host - verbs: - - '*' - - apiGroups: - - rbac.authorization.k8s.io - resources: - - roles - - rolebindings - verbs: - - '*' - - apiGroups: - - rbac.authorization.k8s.io - resources: - - clusterroles - - clusterrolebindings - verbs: - - '*' - - apiGroups: - - "" - resources: - - pods - - services - - serviceaccounts - - endpoints - - persistentvolumeclaims - - events - - configmaps - - secrets - - pods/exec - - pods/log - verbs: - - '*' - - apiGroups: - - "" - resources: - - namespaces - verbs: - - get - - apiGroups: - - apps - - extensions - resources: - - deployments - - replicasets - verbs: - - '*' - - apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - get - - create - - apiGroups: - - org.eclipse.che - resources: - - checlusters - - checlusters/status - - checlusters/finalizers - verbs: - - '*' - - apiGroups: - - metrics.k8s.io - resources: - - pods - - nodes - verbs: - - get - - list - - watch - - apiGroups: - - operators.coreos.com - resources: - - subscriptions - - clusterserviceversions - - operatorgroups - verbs: - - '*' - - apiGroups: - - packages.operators.coreos.com - resources: - - packagemanifests - verbs: - - get - - list - - apiGroups: - - "" - resources: - - configmaps/status - verbs: - - get - - update - - patch - - apiGroups: - - "" - resources: - - events - verbs: - - create - - apiGroups: - - apps - resourceNames: - - che-operator - resources: - - deployments/finalizers - verbs: - - update - - apiGroups: - - controller.devfile.io - resources: - - devworkspaceroutings - verbs: - - '*' - - apiGroups: - - controller.devfile.io - resources: - - devworkspaceroutings/finalizers - verbs: - - update - - apiGroups: - - controller.devfile.io - resources: - - devworkspaceroutings/status - verbs: - - get - - patch - - update - - apiGroups: - - oauth.openshift.io - resources: - - oauthclients - verbs: - - create - - delete - - deletecollection - - get - - list - - patch - - update - - watch diff --git a/helmcharts/next/values.yaml b/helmcharts/next/values.yaml index 57fdf0889..79de04aa2 100644 --- a/helmcharts/next/values.yaml +++ b/helmcharts/next/values.yaml @@ -10,5 +10,9 @@ # Red Hat, Inc. - initial API and implementation # -k8s: - ingressDomain: "192.168.99.101.nip.io" +networking: + domain: "" + auth: + identityProviderURL: "" + oAuthClientName: "" + oAuthSecret: "" diff --git a/helmcharts/stable/Chart.yaml b/helmcharts/stable/Chart.yaml index a2d2b9e63..ddfe96f03 100644 --- a/helmcharts/stable/Chart.yaml +++ b/helmcharts/stable/Chart.yaml @@ -49,22 +49,7 @@ annotations: \ devWorkspace:\n enable: true\n" artifacthub.io/crds: |- - kind: CheCluster - version: v1 + version: v2 name: checlusters.org.eclipse.che displayName: CheCluster description: The `CheCluster` custom resource allows defining and managing a Che server installation" - - kind: CheBackupServerConfiguration - version: v1 - name: chebackupserverconfigurations.org.eclipse.che - displayName: CheBackupServerConfiguration - description: The `CheBackupServerConfiguration` custom resource allows defining and managing Eclipse Che Backup Server Configurations - - kind: CheClusterRestore - version: v1 - name: checlusterrestores.org.eclipse.che - displayName: CheClusterRestore - description: The `CheClusterRestore` custom resource allows defining and managing Eclipse Che restore - - kind: CheClusterBackup - version: v1 - name: checlusterbackups.org.eclipse.che - displayName: CheClusterBackup - description: The `CheClusterBackup` custom resource allows defining and managing Eclipse Che backup diff --git a/helmcharts/stable/README.md b/helmcharts/stable/README.md index 3dce38a73..bb4ba1619 100644 --- a/helmcharts/stable/README.md +++ b/helmcharts/stable/README.md @@ -1,6 +1,6 @@ # Eclipse Che Helm Charts -A collaborative Kubernetes-native development solution that delivers Kubernetes workspaces and in-browser IDE for rapid cloud application development. This operator installs PostgreSQL, Keycloak, Plugin registry, Devfile registry and the Eclipse Che server, as well as configures all these services. +A collaborative Kubernetes-native development solution that delivers Kubernetes workspaces and in-browser IDE for rapid cloud application development. This operator installs PostgreSQL, Plugin registry, Devfile registry and the Eclipse Che server, as well as configures all these services. - [Charts](#charts) - [Prerequisites](#prerequisites) @@ -13,12 +13,19 @@ Helm charts to deploy [Eclipse Che](https://www.eclipse.org/che/) ### Prerequisites * Minimal Kubernetes version is 1.19 -* Minimal Helm version is 3.2.2 +* Minimal [Helm](https://helm.sh/) version is 3.2.2 +* [Cert manager](https://cert-manager.io/docs/installation/) installed +* [OIDC Identity Provider](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#configuring-the-api-server) configured ### Installation -Install `Eclipse Che Operator` by following instructions in top right button `Install`. +Create `ecipse-che` namespace +``` +kubectl create namespace eclipse-che +``` + +Install `Eclipse Che Operator` by following instructions in top right button `Install`. A new pod che-operator is created in `eclipse-che` namespace ```bash @@ -26,23 +33,24 @@ $ kubectl get pods -n eclipse-che eclipse-che che-operator-554c564476-fl98z 1/1 Running 0 13s ``` -Click `CRDS` button, select `CheCluster` template and copy -custom resource Eclipse Che to file org.eclipse.che_v1_checluster.yaml. -Set up valid actual ingress domain `k8s.domain`. -Apply CR: +Click `CRDS` button, select `CheCluster` template and copy custom resource Eclipse Che to file `org_v2_checluster.yaml`. +Set the following fields: +- `spec.networking.domain` +- `spec.networking.auth.identityProviderURL` +- `spec.networking.auth.oAuthClientName` +- `spec.networking.auth.oAuthSecret` +4. Apply CR: ```bash -$ kubectl apply -f org.eclipse.che_v1_checluster.yaml -n eclipse-che +$ kubectl apply -f org_v2_checluster.yaml -n eclipse-che ``` - -Also you can use `kubectl edit checluster/eclipse-che -n eclipse-che` to update Eclipse Che configuration. +Also, you can use `kubectl edit checluster/eclipse-che -n eclipse-che` to update Eclipse Che configuration. See more configuration options in the [Installation guide](https://www.eclipse.org/che/docs/che-7/installation-guide/configuring-the-che-installation/). The deployment process can be tracked by looking at the Operator logs by using the command: ```bash -$ kubectl logs che-operator-554c564476-fl98z -n eclipse-che -f -important: pod name is different on each installation +$ kubectl logs -l app.kubernetes.io/component=che-operator -n eclipse-che -f ``` When all Eclipse Che containers are running, the Eclipse Che URL is printed in the logs: diff --git a/helmcharts/stable/crds/org.eclipse.che_chebackupserverconfigurations_crd.yaml b/helmcharts/stable/crds/org.eclipse.che_chebackupserverconfigurations_crd.yaml deleted file mode 100644 index 7679ba5ac..000000000 --- a/helmcharts/stable/crds/org.eclipse.che_chebackupserverconfigurations_crd.yaml +++ /dev/null @@ -1,162 +0,0 @@ -# -# Copyright (c) 2019-2021 Red Hat, Inc. -# This program and the accompanying materials are made -# available under the terms of the Eclipse Public License 2.0 -# which is available at https://www.eclipse.org/legal/epl-2.0/ -# -# SPDX-License-Identifier: EPL-2.0 -# -# Contributors: -# Red Hat, Inc. - initial API and implementation -# - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.4.1 - creationTimestamp: null - name: chebackupserverconfigurations.org.eclipse.che -spec: - group: org.eclipse.che - names: - kind: CheBackupServerConfiguration - listKind: CheBackupServerConfigurationList - plural: chebackupserverconfigurations - singular: chebackupserverconfiguration - scope: Namespaced - versions: - - name: v1 - schema: - openAPIV3Schema: - description: The `CheBackupServerConfiguration` custom resource allows defining - and managing Eclipse Che Backup Server Configurations - 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: CheBackupServerConfigurationSpec defines the desired state - of CheBackupServerConfiguration Only one type of backup server is - allowed to be configured per CR. - properties: - awss3: - description: Amazon S3 or compatible alternatives. - properties: - awsAccessKeySecretRef: - description: Reference to secret that contains awsAccessKeyId - and awsSecretAccessKey keys. - type: string - hostname: - description: Server hostname, defaults to 's3.amazonaws.com'. - Might be customized in case of alternative server. - type: string - port: - description: Backup server port. Usually default value is used. - Might be customized in case of alternative server. - type: integer - protocol: - description: Protocol to use when connection to the server. - Might be customized in case of alternative server. - type: string - repositoryPasswordSecretRef: - description: Holds reference to a secret with restic repository - password under 'repo-password' field to encrypt / decrypt - its content. - type: string - repositoryPath: - description: Bucket name and repository, e.g. bucket/repo - type: string - required: - - awsAccessKeySecretRef - - repositoryPasswordSecretRef - - repositoryPath - type: object - rest: - description: Rest backup server configuration. - properties: - credentialsSecretRef: - description: Secret that contains username and password fields - to login into restic server. Note, each repository is encrypted - with own password. See ResticRepoPasswordSecretRef field. - type: string - hostname: - description: Backup server host - type: string - port: - description: Backup server port - type: integer - protocol: - description: Protocol to use when connection to the server Defaults - to https. - type: string - repositoryPasswordSecretRef: - description: Holds reference to a secret with restic repository - password under 'repo-password' field to encrypt / decrypt - its content. - type: string - repositoryPath: - description: Restic repository path - type: string - required: - - hostname - - repositoryPasswordSecretRef - type: object - sftp: - description: Sftp backup server configuration. - properties: - hostname: - description: Backup server host - type: string - port: - description: Backup server port - type: integer - repositoryPasswordSecretRef: - description: Holds reference to a secret with restic repository - password under 'repo-password' field to encrypt / decrypt - its content. - type: string - repositoryPath: - description: Restic repository path, relative or absolute, e.g. - /srv/repo - type: string - sshKeySecretRef: - description: Private ssh key under 'ssh-privatekey' field for - passwordless login - type: string - username: - description: User login on the remote server - type: string - required: - - hostname - - repositoryPasswordSecretRef - - repositoryPath - - sshKeySecretRef - - username - type: object - type: object - status: - description: CheBackupServerConfigurationStatus defines the observed - state of CheBackupServerConfiguration - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/helmcharts/stable/crds/org.eclipse.che_checlusterbackups_crd.yaml b/helmcharts/stable/crds/org.eclipse.che_checlusterbackups_crd.yaml deleted file mode 100644 index 6cb073224..000000000 --- a/helmcharts/stable/crds/org.eclipse.che_checlusterbackups_crd.yaml +++ /dev/null @@ -1,92 +0,0 @@ -# -# Copyright (c) 2019-2021 Red Hat, Inc. -# This program and the accompanying materials are made -# available under the terms of the Eclipse Public License 2.0 -# which is available at https://www.eclipse.org/legal/epl-2.0/ -# -# SPDX-License-Identifier: EPL-2.0 -# -# Contributors: -# Red Hat, Inc. - initial API and implementation -# - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.4.1 - creationTimestamp: null - name: checlusterbackups.org.eclipse.che -spec: - group: org.eclipse.che - names: - kind: CheClusterBackup - listKind: CheClusterBackupList - plural: checlusterbackups - singular: checlusterbackup - scope: Namespaced - versions: - - name: v1 - schema: - openAPIV3Schema: - description: The `CheClusterBackup` custom resource allows defining and - managing Eclipse Che backup - 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: CheClusterBackupSpec defines the desired state of CheClusterBackup - properties: - backupServerConfigRef: - description: Name of custom resource with a backup server configuration - to use for this backup. Note, UseInternalBackupServer field can - configure internal backup server automatically. - type: string - useInternalBackupServer: - description: Automatically setup pod with REST backup server and - use the server in this configuration. Note, this flag takes precedence - and will overwrite existing backup server configuration. - type: boolean - type: object - status: - description: CheClusterBackupStatus defines the observed state of CheClusterBackup - properties: - cheVersion: - description: Version that was backed up - type: string - message: - description: Message explaining the state of the backup or an error - message - type: string - snapshotId: - description: Last backup snapshot ID - type: string - stage: - description: Describes backup progress - type: string - state: - description: 'Backup progress state: InProgress, Failed, Succeeded' - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/helmcharts/stable/crds/org.eclipse.che_checlusterrestores_crd.yaml b/helmcharts/stable/crds/org.eclipse.che_checlusterrestores_crd.yaml deleted file mode 100644 index be567b8c9..000000000 --- a/helmcharts/stable/crds/org.eclipse.che_checlusterrestores_crd.yaml +++ /dev/null @@ -1,83 +0,0 @@ -# -# Copyright (c) 2019-2021 Red Hat, Inc. -# This program and the accompanying materials are made -# available under the terms of the Eclipse Public License 2.0 -# which is available at https://www.eclipse.org/legal/epl-2.0/ -# -# SPDX-License-Identifier: EPL-2.0 -# -# Contributors: -# Red Hat, Inc. - initial API and implementation -# - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.4.1 - creationTimestamp: null - name: checlusterrestores.org.eclipse.che -spec: - group: org.eclipse.che - names: - kind: CheClusterRestore - listKind: CheClusterRestoreList - plural: checlusterrestores - singular: checlusterrestore - scope: Namespaced - versions: - - name: v1 - schema: - openAPIV3Schema: - description: The `CheClusterRestore` custom resource allows defining and - managing Eclipse Che restore - 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: CheClusterRestoreSpec defines the desired state of CheClusterRestore - properties: - backupServerConfigRef: - description: Name of custom resource with a backup server configuration - to use for this restore. Can be omitted if only one server configuration - object exists within the namespace. - type: string - snapshotId: - description: If omitted, latest snapshot will be used. - type: string - type: object - status: - description: CheClusterRestoreStatus defines the observed state of CheClusterRestore - properties: - message: - description: Restore result or error message - type: string - stage: - description: Describes phase of restore progress - type: string - state: - description: 'Restore progress state: InProgress, Failed, Succeeded' - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/org_v1_che_crd.yaml b/helmcharts/stable/crds/org.eclipse.che_checlusters.yaml similarity index 100% rename from config/crd/bases/org_v1_che_crd.yaml rename to helmcharts/stable/crds/org.eclipse.che_checlusters.yaml diff --git a/helmcharts/stable/crds/org_v1_che_crd.yaml b/helmcharts/stable/crds/org_v1_che_crd.yaml deleted file mode 100644 index 818937aa9..000000000 --- a/helmcharts/stable/crds/org_v1_che_crd.yaml +++ /dev/null @@ -1,1233 +0,0 @@ -# -# Copyright (c) 2019-2021 Red Hat, Inc. -# This program and the accompanying materials are made -# available under the terms of the Eclipse Public License 2.0 -# which is available at https://www.eclipse.org/legal/epl-2.0/ -# -# SPDX-License-Identifier: EPL-2.0 -# -# Contributors: -# Red Hat, Inc. - initial API and implementation -# - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.4.1 - creationTimestamp: null - name: checlusters.org.eclipse.che -spec: - group: org.eclipse.che - names: - kind: CheCluster - listKind: CheClusterList - plural: checlusters - singular: checluster - scope: Namespaced - versions: - - name: v1 - schema: - openAPIV3Schema: - description: The `CheCluster` custom resource allows defining and managing - a Che server installation - 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: Desired configuration of the Che installation. Based on - these settings, the Operator automatically creates and maintains - several ConfigMaps that will contain the appropriate environment variables - the various components of the Che installation. These generated ConfigMaps - must NOT be updated manually. - properties: - auth: - description: Configuration settings related to the Authentication - used by the Che installation. - properties: - debug: - description: Deprecated. The value of this flag is ignored. - Debug internal identity provider. - type: boolean - externalIdentityProvider: - description: 'Deprecated. The value of this flag is ignored. - Instructs the Operator on whether or not to deploy a dedicated - Identity Provider (Keycloak or RH SSO instance). Instructs - the Operator on whether to deploy a dedicated Identity Provider - (Keycloak or RH-SSO instance). By default, a dedicated Identity - Provider server is deployed as part of the Che installation. - When `externalIdentityProvider` is `true`, no dedicated identity - provider will be deployed by the Operator and you will need - to provide details about the external identity provider you - are about to use. See also all the other fields starting with: - `identityProvider`.' - type: boolean - gatewayAuthenticationSidecarImage: - description: Gateway sidecar responsible for authentication - when NativeUserMode is enabled. See link:https://github.com/oauth2-proxy/oauth2-proxy[oauth2-proxy] - or link:https://github.com/openshift/oauth-proxy[openshift/oauth-proxy]. - type: string - gatewayAuthorizationSidecarImage: - description: Gateway sidecar responsible for authorization when - NativeUserMode is enabled. See link:https://github.com/brancz/kube-rbac-proxy[kube-rbac-proxy] - or link:https://github.com/openshift/kube-rbac-proxy[openshift/kube-rbac-proxy] - type: string - gatewayHeaderRewriteSidecarImage: - description: Deprecated. The value of this flag is ignored. - Sidecar functionality is now implemented in Traefik plugin. - type: string - identityProviderAdminUserName: - description: Deprecated. The value of this flag is ignored. - Overrides the name of the Identity Provider administrator - user. Defaults to `admin`. - type: string - identityProviderClientId: - description: Deprecated. The value of this flag is ignored. - Name of a Identity provider, Keycloak or RH-SSO, `client-id` - that is used for Che. Override this when an external Identity - Provider is in use. See the `externalIdentityProvider` field. - When omitted or left blank, it is set to the value of the - `flavour` field suffixed with `-public`. - type: string - identityProviderContainerResources: - description: Deprecated. The value of this flag is ignored. - Identity provider container custom settings. - properties: - limits: - description: Limits describes the maximum amount of compute - resources allowed. - properties: - cpu: - description: CPU, in cores. (500m = .5 cores) - type: string - memory: - description: Memory, in bytes. (500Gi = 500GiB = 500 - * 1024 * 1024 * 1024) - type: string - type: object - request: - description: Requests describes the minimum amount of compute - resources required. - properties: - cpu: - description: CPU, in cores. (500m = .5 cores) - type: string - memory: - description: Memory, in bytes. (500Gi = 500GiB = 500 - * 1024 * 1024 * 1024) - type: string - type: object - type: object - identityProviderImage: - description: Deprecated. The value of this flag is ignored. - Overrides the container image used in the Identity Provider, - Keycloak or RH-SSO, deployment. This includes the image tag. - Omit it or leave it empty to use the default container image - provided by the Operator. - type: string - identityProviderImagePullPolicy: - description: Deprecated. The value of this flag is ignored. - Overrides the image pull policy used in the Identity Provider, - Keycloak or RH-SSO, deployment. Default value is `Always` - for `nightly`, `next` or `latest` images, and `IfNotPresent` - in other cases. - type: string - identityProviderIngress: - description: Deprecated. The value of this flag is ignored. - Ingress custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - identityProviderPassword: - description: Deprecated. The value of this flag is ignored. - Overrides the password of Keycloak administrator user. Override - this when an external Identity Provider is in use. See the - `externalIdentityProvider` field. When omitted or left blank, - it is set to an auto-generated password. - type: string - identityProviderPostgresPassword: - description: Deprecated. The value of this flag is ignored. - Password for a Identity Provider, Keycloak or RH-SSO, to connect - to the database. Override this when an external Identity Provider - is in use. See the `externalIdentityProvider` field. When - omitted or left blank, it is set to an auto-generated password. - type: string - identityProviderPostgresSecret: - description: 'Deprecated. The value of this flag is ignored. - The secret that contains `password` for the Identity Provider, - Keycloak or RH-SSO, to connect to the database. When the secret - is defined, the `identityProviderPostgresPassword` is ignored. - When the value is omitted or left blank, the one of following - scenarios applies: 1. `identityProviderPostgresPassword` is - defined, then it will be used to connect to the database. - 2. `identityProviderPostgresPassword` is not defined, then - a new secret with the name `che-identity-postgres-secret` - will be created with an auto-generated value for `password`. - The secret must have `app.kubernetes.io/part-of=che.eclipse.org` - label.' - type: string - identityProviderRealm: - description: Deprecated. The value of this flag is ignored. - Name of a Identity provider, Keycloak or RH-SSO, realm that - is used for Che. Override this when an external Identity Provider - is in use. See the `externalIdentityProvider` field. When - omitted or left blank, it is set to the value of the `flavour` - field. - type: string - identityProviderRoute: - description: Deprecated. The value of this flag is ignored. - Route custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - domain: - description: 'Operator uses the domain to generate a hostname - for a route. In a conjunction with labels it creates a - route, which is served by a non-default Ingress controller. - The generated host name will follow this pattern: `-.`.' - type: string - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - identityProviderSecret: - description: 'Deprecated. The value of this flag is ignored. - The secret that contains `user` and `password` for Identity - Provider. When the secret is defined, the `identityProviderAdminUserName` - and `identityProviderPassword` are ignored. When the value - is omitted or left blank, the one of following scenarios applies: - 1. `identityProviderAdminUserName` and `identityProviderPassword` - are defined, then they will be used. 2. `identityProviderAdminUserName` - or `identityProviderPassword` are not defined, then a new - secret with the name `che-identity-secret` will be created - with default value `admin` for `user` and with an auto-generated - value for `password`. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` - label.' - type: string - identityProviderURL: - description: Public URL of the Identity Provider server (Keycloak - / RH-SSO server). Set this ONLY when a use of an external - Identity Provider is needed. See the `externalIdentityProvider` - field. By default, this will be automatically calculated and - set by the Operator. - type: string - initialOpenShiftOAuthUser: - description: Deprecated. The value of this flag is ignored. - For operating with the OpenShift OAuth authentication, create - a new user account since the kubeadmin can not be used. If - the value is true, then a new OpenShift OAuth user will be - created for the HTPasswd identity provider. If the value is - false and the user has already been created, then it will - be removed. If value is an empty, then do nothing. The user's - credentials are stored in the `openshift-oauth-user-credentials` - secret in 'openshift-config' namespace by Operator. Note that - this solution is Openshift 4 platform-specific. - type: boolean - nativeUserMode: - description: Deprecated. The value of this flag is ignored. - Enables native user mode. Currently works only on OpenShift - and DevWorkspace engine. Native User mode uses OpenShift OAuth - directly as identity provider, without Keycloak. - type: boolean - oAuthClientName: - description: Name of the OpenShift `OAuthClient` resource used - to setup identity federation on the OpenShift side. Auto-generated - when left blank. See also the `OpenShiftoAuth` field. - type: string - oAuthSecret: - description: Name of the secret set in the OpenShift `OAuthClient` - resource used to setup identity federation on the OpenShift - side. Auto-generated when left blank. See also the `OAuthClientName` - field. - type: string - openShiftoAuth: - description: 'Deprecated. The value of this flag is ignored. - Enables the integration of the identity provider (Keycloak - / RHSSO) with OpenShift OAuth. Empty value on OpenShift by - default. This will allow users to directly login with their - OpenShift user through the OpenShift login, and have their - workspaces created under personal OpenShift namespaces. WARNING: - the `kubeadmin` user is NOT supported, and logging through - it will NOT allow accessing the Che Dashboard.' - type: boolean - updateAdminPassword: - description: Deprecated. The value of this flag is ignored. - Forces the default `admin` Che user to update password on - first login. Defaults to `false`. - type: boolean - type: object - dashboard: - description: Configuration settings related to the User Dashboard - used by the Che installation. - properties: - warning: - description: Warning message that will be displayed on the User - Dashboard - type: string - type: object - database: - description: Configuration settings related to the database used - by the Che installation. - properties: - chePostgresContainerResources: - description: PostgreSQL container custom settings - properties: - limits: - description: Limits describes the maximum amount of compute - resources allowed. - properties: - cpu: - description: CPU, in cores. (500m = .5 cores) - type: string - memory: - description: Memory, in bytes. (500Gi = 500GiB = 500 - * 1024 * 1024 * 1024) - type: string - type: object - request: - description: Requests describes the minimum amount of compute - resources required. - properties: - cpu: - description: CPU, in cores. (500m = .5 cores) - type: string - memory: - description: Memory, in bytes. (500Gi = 500GiB = 500 - * 1024 * 1024 * 1024) - type: string - type: object - type: object - chePostgresDb: - description: PostgreSQL database name that the Che server uses - to connect to the DB. Defaults to `dbche`. - type: string - chePostgresHostName: - description: PostgreSQL Database host name that the Che server - uses to connect to. Defaults is `postgres`. Override this - value ONLY when using an external database. See field `externalDb`. - In the default case it will be automatically set by the Operator. - type: string - chePostgresPassword: - description: PostgreSQL password that the Che server uses to - connect to the DB. When omitted or left blank, it will be - set to an automatically generated value. - type: string - chePostgresPort: - description: PostgreSQL Database port that the Che server uses - to connect to. Defaults to 5432. Override this value ONLY - when using an external database. See field `externalDb`. In - the default case it will be automatically set by the Operator. - type: string - chePostgresSecret: - description: 'The secret that contains PostgreSQL`user` and - `password` that the Che server uses to connect to the DB. - When the secret is defined, the `chePostgresUser` and `chePostgresPassword` - are ignored. When the value is omitted or left blank, the - one of following scenarios applies: 1. `chePostgresUser` and - `chePostgresPassword` are defined, then they will be used - to connect to the DB. 2. `chePostgresUser` or `chePostgresPassword` - are not defined, then a new secret with the name `postgres-credentials` - will be created with default value of `pgche` for `user` and - with an auto-generated value for `password`. The secret must - have `app.kubernetes.io/part-of=che.eclipse.org` label.' - type: string - chePostgresUser: - description: PostgreSQL user that the Che server uses to connect - to the DB. Defaults to `pgche`. - type: string - externalDb: - description: 'Instructs the Operator on whether to deploy a - dedicated database. By default, a dedicated PostgreSQL database - is deployed as part of the Che installation. When `externalDb` - is `true`, no dedicated database will be deployed by the Operator - and you will need to provide connection details to the external - DB you are about to use. See also all the fields starting - with: `chePostgres`.' - type: boolean - postgresImage: - description: Overrides the container image used in the PostgreSQL - database deployment. This includes the image tag. Omit it - or leave it empty to use the default container image provided - by the Operator. - type: string - postgresImagePullPolicy: - description: Overrides the image pull policy used in the PostgreSQL - database deployment. Default value is `Always` for `nightly`, - `next` or `latest` images, and `IfNotPresent` in other cases. - type: string - postgresVersion: - description: 'Indicates a PostgreSQL version image to use. Allowed - values are: `9.6` and `13.3`. Migrate your PostgreSQL database - to switch from one version to another.' - type: string - pvcClaimSize: - description: Size of the persistent volume claim for database. - Defaults to `1Gi`. To update pvc storageclass that provisions - it must support resize when Eclipse Che has been already deployed. - type: string - type: object - devWorkspace: - description: DevWorkspace operator configuration - properties: - controllerImage: - description: Overrides the container image used in the DevWorkspace - controller deployment. This includes the image tag. Omit it - or leave it empty to use the default container image provided - by the Operator. - type: string - enable: - description: Deploys the DevWorkspace Operator in the cluster. - Does nothing when a matching version of the Operator is already - installed. Fails when a non-matching version of the Operator - is already installed. - type: boolean - runningLimit: - description: Maximum number of the running workspaces per user. - type: string - required: - - enable - type: object - imagePuller: - description: Kubernetes Image Puller configuration - properties: - enable: - description: Install and configure the Community Supported Kubernetes - Image Puller Operator. When set to `true` and no spec is provided, - it will create a default KubernetesImagePuller object to be - managed by the Operator. When set to `false`, the KubernetesImagePuller - object will be deleted, and the Operator will be uninstalled, - regardless of whether a spec is provided. If the `spec.images` - field is empty, a set of recommended workspace-related images - will be automatically detected and pre-pulled after installation. - Note that while this Operator and its behavior is community-supported, - its payload may be commercially-supported for pulling commercially-supported - images. - type: boolean - spec: - description: A KubernetesImagePullerSpec to configure the image - puller in the CheCluster - properties: - affinity: - type: string - cachingCPULimit: - type: string - cachingCPURequest: - type: string - cachingIntervalHours: - type: string - cachingMemoryLimit: - type: string - cachingMemoryRequest: - type: string - configMapName: - type: string - daemonsetName: - type: string - deploymentName: - type: string - imagePullSecrets: - type: string - imagePullerImage: - type: string - images: - type: string - nodeSelector: - type: string - type: object - required: - - enable - type: object - k8s: - description: Configuration settings specific to Che installations - made on upstream Kubernetes. - properties: - ingressClass: - description: 'Ingress class that will define the which controller - will manage ingresses. Defaults to `nginx`. NB: This drives - the `kubernetes.io/ingress.class` annotation on Che-related - ingresses.' - type: string - ingressDomain: - description: 'Global ingress domain for a Kubernetes cluster. - This MUST be explicitly specified: there are no defaults.' - type: string - ingressStrategy: - description: 'Deprecated. The value of this flag is ignored. - Strategy for ingress creation. Options are: `multi-host` (host - is explicitly provided in ingress), `single-host` (host is - provided, path-based rules) and `default-host` (no host is - provided, path-based rules). Defaults to `multi-host` Deprecated - in favor of `serverExposureStrategy` in the `server` section, - which defines this regardless of the cluster type. When both - are defined, the `serverExposureStrategy` option takes precedence.' - type: string - securityContextFsGroup: - description: The FSGroup in which the Che Pod and workspace - Pods containers runs in. Default value is `1724`. - type: string - securityContextRunAsUser: - description: ID of the user the Che Pod and workspace Pods containers - run as. Default value is `1724`. - type: string - singleHostExposureType: - description: Deprecated. The value of this flag is ignored. - When the serverExposureStrategy is set to `single-host`, the - way the server, registries and workspaces are exposed is further - configured by this property. The possible values are `native`, - which means that the server and workspaces are exposed using - ingresses on K8s or `gateway` where the server and workspaces - are exposed using a custom gateway based on link:https://doc.traefik.io/traefik/[Traefik]. - All the endpoints whether backed by the ingress or gateway - `route` always point to the subpaths on the same domain. Defaults - to `native`. - type: string - tlsSecretName: - description: Name of a secret that will be used to setup ingress - TLS termination when TLS is enabled. When the field is empty - string, the default cluster certificate will be used. See - also the `tlsSupport` field. - type: string - type: object - metrics: - description: Configuration settings related to the metrics collection - used by the Che installation. - properties: - enable: - description: Enables `metrics` the Che server endpoint. Default - to `true`. - type: boolean - type: object - server: - description: General configuration settings related to the Che server, - the plugin and devfile registries - properties: - airGapContainerRegistryHostname: - description: Optional host name, or URL, to an alternate container - registry to pull images from. This value overrides the container - registry host name defined in all the default container images - involved in a Che deployment. This is particularly useful - to install Che in a restricted environment. - type: string - airGapContainerRegistryOrganization: - description: Optional repository name of an alternate container - registry to pull images from. This value overrides the container - registry organization defined in all the default container - images involved in a Che deployment. This is particularly - useful to install Eclipse Che in a restricted environment. - type: string - allowUserDefinedWorkspaceNamespaces: - description: Deprecated. The value of this flag is ignored. - Defines that a user is allowed to specify a Kubernetes namespace, - or an OpenShift project, which differs from the default. It's - NOT RECOMMENDED to set to `true` without OpenShift OAuth configured. - The OpenShift infrastructure also uses this property. - type: boolean - cheClusterRoles: - description: A comma-separated list of ClusterRoles that will - be assigned to Che ServiceAccount. Each role must have `app.kubernetes.io/part-of=che.eclipse.org` - label. Be aware that the Che Operator has to already have - all permissions in these ClusterRoles to grant them. - type: string - cheDebug: - description: Enables the debug mode for Che server. Defaults - to `false`. - type: string - cheFlavor: - description: Deprecated. The value of this flag is ignored. - Specifies a variation of the installation. The options are `che` - for upstream Che installations or `devspaces` for Red Hat - OpenShift Dev Spaces (formerly Red Hat CodeReady Workspaces) - installation - type: string - cheHost: - description: Public host name of the installed Che server. When - value is omitted, the value it will be automatically set by - the Operator. See the `cheHostTLSSecret` field. - type: string - cheHostTLSSecret: - description: Name of a secret containing certificates to secure - ingress or route for the custom host name of the installed - Che server. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` - label. See the `cheHost` field. - type: string - cheImage: - description: Overrides the container image used in Che deployment. - This does NOT include the container image tag. Omit it or - leave it empty to use the default container image provided - by the Operator. - type: string - cheImagePullPolicy: - description: Overrides the image pull policy used in Che deployment. - Default value is `Always` for `nightly`, `next` or `latest` - images, and `IfNotPresent` in other cases. - type: string - cheImageTag: - description: Overrides the tag of the container image used in - Che deployment. Omit it or leave it empty to use the default - image tag provided by the Operator. - type: string - cheLogLevel: - description: 'Log level for the Che server: `INFO` or `DEBUG`. - Defaults to `INFO`.' - type: string - cheServerIngress: - description: The Che server ingress custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - cheServerRoute: - description: The Che server route custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - domain: - description: 'Operator uses the domain to generate a hostname - for a route. In a conjunction with labels it creates a - route, which is served by a non-default Ingress controller. - The generated host name will follow this pattern: `-.`.' - type: string - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - cheWorkspaceClusterRole: - description: Custom cluster role bound to the user for the Che - workspaces. The role must have `app.kubernetes.io/part-of=che.eclipse.org` - label. The default roles are used when omitted or left blank. - type: string - customCheProperties: - additionalProperties: - type: string - description: Map of additional environment variables that will - be applied in the generated `che` ConfigMap to be used by - the Che server, in addition to the values already generated - from other fields of the `CheCluster` custom resource (CR). - When `customCheProperties` contains a property that would - be normally generated in `che` ConfigMap from other CR fields, - the value defined in the `customCheProperties` is used instead. - type: object - dashboardCpuLimit: - description: Overrides the CPU limit used in the dashboard deployment. - In cores. (500m = .5 cores). Default to 500m. - type: string - dashboardCpuRequest: - description: Overrides the CPU request used in the dashboard - deployment. In cores. (500m = .5 cores). Default to 100m. - type: string - dashboardImage: - description: Overrides the container image used in the dashboard - deployment. This includes the image tag. Omit it or leave - it empty to use the default container image provided by the - Operator. - type: string - dashboardImagePullPolicy: - description: Overrides the image pull policy used in the dashboard - deployment. Default value is `Always` for `nightly`, `next` - or `latest` images, and `IfNotPresent` in other cases. - type: string - dashboardIngress: - description: Deprecated. The value of this flag is ignored. - Dashboard ingress custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - dashboardMemoryLimit: - description: Overrides the memory limit used in the dashboard - deployment. Defaults to 256Mi. - type: string - dashboardMemoryRequest: - description: Overrides the memory request used in the dashboard - deployment. Defaults to 16Mi. - type: string - dashboardRoute: - description: Deprecated. The value of this flag is ignored. - Dashboard route custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - domain: - description: 'Operator uses the domain to generate a hostname - for a route. In a conjunction with labels it creates a - route, which is served by a non-default Ingress controller. - The generated host name will follow this pattern: `-.`.' - type: string - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - devfileRegistryCpuLimit: - description: Overrides the CPU limit used in the devfile registry - deployment. In cores. (500m = .5 cores). Default to 500m. - type: string - devfileRegistryCpuRequest: - description: Overrides the CPU request used in the devfile registry - deployment. In cores. (500m = .5 cores). Default to 100m. - type: string - devfileRegistryImage: - description: Overrides the container image used in the devfile - registry deployment. This includes the image tag. Omit it - or leave it empty to use the default container image provided - by the Operator. - type: string - devfileRegistryIngress: - description: Deprecated. The value of this flag is ignored. - The devfile registry ingress custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - devfileRegistryMemoryLimit: - description: Overrides the memory limit used in the devfile - registry deployment. Defaults to 256Mi. - type: string - devfileRegistryMemoryRequest: - description: Overrides the memory request used in the devfile - registry deployment. Defaults to 16Mi. - type: string - devfileRegistryPullPolicy: - description: Overrides the image pull policy used in the devfile - registry deployment. Default value is `Always` for `nightly`, - `next` or `latest` images, and `IfNotPresent` in other cases. - type: string - devfileRegistryRoute: - description: Deprecated. The value of this flag is ignored. - The devfile registry route custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - domain: - description: 'Operator uses the domain to generate a hostname - for a route. In a conjunction with labels it creates a - route, which is served by a non-default Ingress controller. - The generated host name will follow this pattern: `-.`.' - type: string - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - devfileRegistryUrl: - description: Deprecated in favor of `externalDevfileRegistries` - fields. - type: string - disableInternalClusterSVCNames: - description: Deprecated. The value of this flag is ignored. - Disable internal cluster SVC names usage to communicate between - components to speed up the traffic and avoid proxy issues. - type: boolean - externalDevfileRegistries: - description: External devfile registries, that serves sample, - ready-to-use devfiles. Configure this in addition to a dedicated - devfile registry (when `externalDevfileRegistry` is `false`) - or instead of it (when `externalDevfileRegistry` is `true`) - items: - description: Settings for a configuration of the external - devfile registries. - properties: - url: - description: Public URL of the devfile registry. - type: string - type: object - type: array - externalDevfileRegistry: - description: Instructs the Operator on whether to deploy a dedicated - devfile registry server. By default, a dedicated devfile registry - server is started. When `externalDevfileRegistry` is `true`, - no such dedicated server will be started by the Operator and - configure at least one devfile registry with `externalDevfileRegistries` - field. - type: boolean - externalPluginRegistry: - description: Instructs the Operator on whether to deploy a dedicated - plugin registry server. By default, a dedicated plugin registry - server is started. When `externalPluginRegistry` is `true`, - no such dedicated server will be started by the Operator and - you will have to manually set the `pluginRegistryUrl` field. - type: boolean - gitSelfSignedCert: - description: When enabled, the certificate from `che-git-self-signed-cert` - ConfigMap will be propagated to the Che components and provide - particular configuration for Git. Note, the `che-git-self-signed-cert` - ConfigMap must have `app.kubernetes.io/part-of=che.eclipse.org` - label. - type: boolean - nonProxyHosts: - description: 'List of hosts that will be reached directly, bypassing - the proxy. Specify wild card domain use the following form - `.` and `|` as delimiter, for example: `localhost|.my.host.com|123.42.12.32` - Only use when configuring a proxy is required. Operator respects - OpenShift cluster wide proxy configuration and no additional - configuration is required, but defining `nonProxyHosts` in - a custom resource leads to merging non proxy hosts lists from - the cluster proxy configuration and ones defined in the custom - resources. See the doc https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. - See also the `proxyURL` fields.' - type: string - pluginRegistryCpuLimit: - description: Overrides the CPU limit used in the plugin registry - deployment. In cores. (500m = .5 cores). Default to 500m. - type: string - pluginRegistryCpuRequest: - description: Overrides the CPU request used in the plugin registry - deployment. In cores. (500m = .5 cores). Default to 100m. - type: string - pluginRegistryImage: - description: Overrides the container image used in the plugin - registry deployment. This includes the image tag. Omit it - or leave it empty to use the default container image provided - by the Operator. - type: string - pluginRegistryIngress: - description: Deprecated. The value of this flag is ignored. - Plugin registry ingress custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - pluginRegistryMemoryLimit: - description: Overrides the memory limit used in the plugin registry - deployment. Defaults to 256Mi. - type: string - pluginRegistryMemoryRequest: - description: Overrides the memory request used in the plugin - registry deployment. Defaults to 16Mi. - type: string - pluginRegistryPullPolicy: - description: Overrides the image pull policy used in the plugin - registry deployment. Default value is `Always` for `nightly`, - `next` or `latest` images, and `IfNotPresent` in other cases. - type: string - pluginRegistryRoute: - description: Deprecated. The value of this flag is ignored. - Plugin registry route custom settings. - properties: - annotations: - additionalProperties: - type: string - description: Unstructured key value map stored with a resource - that may be set by external tools to store and retrieve - arbitrary metadata. - type: object - domain: - description: 'Operator uses the domain to generate a hostname - for a route. In a conjunction with labels it creates a - route, which is served by a non-default Ingress controller. - The generated host name will follow this pattern: `-.`.' - type: string - labels: - description: Comma separated list of labels that can be - used to organize and categorize objects by scoping and - selecting. - type: string - type: object - pluginRegistryUrl: - description: Public URL of the plugin registry that serves sample - ready-to-use devfiles. Set this ONLY when a use of an external - devfile registry is needed. See the `externalPluginRegistry` - field. By default, this will be automatically calculated by - the Operator. - type: string - proxyPassword: - description: Password of the proxy server. Only use when proxy - configuration is required. See the `proxyURL`, `proxyUser` - and `proxySecret` fields. - type: string - proxyPort: - description: Port of the proxy server. Only use when configuring - a proxy is required. See also the `proxyURL` and `nonProxyHosts` - fields. - type: string - proxySecret: - description: The secret that contains `user` and `password` - for a proxy server. When the secret is defined, the `proxyUser` - and `proxyPassword` are ignored. The secret must have `app.kubernetes.io/part-of=che.eclipse.org` - label. - type: string - proxyURL: - description: URL (protocol+host name) of the proxy server. This - drives the appropriate changes in the `JAVA_OPTS` and `https(s)_proxy` - variables in the Che server and workspaces containers. Only - use when configuring a proxy is required. Operator respects - OpenShift cluster wide proxy configuration and no additional - configuration is required, but defining `proxyUrl` in a custom - resource leads to overrides the cluster proxy configuration - with fields `proxyUrl`, `proxyPort`, `proxyUser` and `proxyPassword` - from the custom resource. See the doc https://docs.openshift.com/container-platform/4.4/networking/enable-cluster-wide-proxy.html. - See also the `proxyPort` and `nonProxyHosts` fields. - type: string - proxyUser: - description: User name of the proxy server. Only use when configuring - a proxy is required. See also the `proxyURL`, `proxyPassword` - and `proxySecret` fields. - type: string - selfSignedCert: - description: Deprecated. The value of this flag is ignored. - The Che Operator will automatically detect whether the router - certificate is self-signed and propagate it to other components, - such as the Che server. - type: boolean - serverCpuLimit: - description: Overrides the CPU limit used in the Che server - deployment In cores. (500m = .5 cores). Default to 1. - type: string - serverCpuRequest: - description: Overrides the CPU request used in the Che server - deployment In cores. (500m = .5 cores). Default to 100m. - type: string - serverExposureStrategy: - description: Deprecated. The value of this flag is ignored. - Sets the server and workspaces exposure type. Possible values - are `multi-host`, `single-host`, `default-host`. Defaults - to `multi-host`, which creates a separate ingress, or OpenShift - routes, for every required endpoint. `single-host` makes Che - exposed on a single host name with workspaces exposed on subpaths. - Read the docs to learn about the limitations of this approach. - Also consult the `singleHostExposureType` property to further - configure how the Operator and the Che server make that happen - on Kubernetes. `default-host` exposes the Che server on the - host of the cluster. Read the docs to learn about the limitations - of this approach. - type: string - serverMemoryLimit: - description: Overrides the memory limit used in the Che server - deployment. Defaults to 1Gi. - type: string - serverMemoryRequest: - description: Overrides the memory request used in the Che server - deployment. Defaults to 512Mi. - type: string - serverTrustStoreConfigMapName: - description: Name of the ConfigMap with public certificates - to add to Java trust store of the Che server. This is often - required when adding the OpenShift OAuth provider, which has - HTTPS endpoint signed with self-signed cert. The Che server - must be aware of its CA cert to be able to request it. This - is disabled by default. The Config Map must have `app.kubernetes.io/part-of=che.eclipse.org` - label. - type: string - singleHostGatewayConfigMapLabels: - additionalProperties: - type: string - description: The labels that need to be present in the ConfigMaps - representing the gateway configuration. - type: object - singleHostGatewayConfigSidecarImage: - description: The image used for the gateway sidecar that provides - configuration to the gateway. Omit it or leave it empty to - use the default container image provided by the Operator. - type: string - singleHostGatewayImage: - description: The image used for the gateway in the single host - mode. Omit it or leave it empty to use the default container - image provided by the Operator. - type: string - tlsSupport: - description: Deprecated. Instructs the Operator to deploy Che - in TLS mode. This is enabled by default. Disabling TLS sometimes - cause malfunction of some Che components. - type: boolean - useInternalClusterSVCNames: - description: Deprecated in favor of `disableInternalClusterSVCNames`. - type: boolean - workspaceNamespaceDefault: - description: Defines Kubernetes default namespace in which user's - workspaces are created for a case when a user does not override - it. It's possible to use ``, `` and `` - placeholders, such as che-workspace-. In that case, - a new namespace will be created for each user or workspace. - type: string - workspacePodNodeSelector: - additionalProperties: - type: string - description: The node selector that limits the nodes that can - run the workspace pods. - type: object - workspacePodTolerations: - description: The pod tolerations put on the workspace pods to - limit where the workspace pods can run. - items: - description: The pod this Toleration is attached to tolerates - any taint that matches the triple using - the matching operator . - properties: - effect: - description: Effect indicates the taint effect to match. - Empty means match all taint effects. When specified, - allowed values are NoSchedule, PreferNoSchedule and - NoExecute. - type: string - key: - description: Key is the taint key that the toleration - applies to. Empty means match all taint keys. If the - key is empty, operator must be Exists; this combination - means to match all values and all keys. - type: string - operator: - description: Operator represents a key's relationship - to the value. Valid operators are Exists and Equal. - Defaults to Equal. Exists is equivalent to wildcard - for value, so that a pod can tolerate all taints of - a particular category. - type: string - tolerationSeconds: - description: TolerationSeconds represents the period of - time the toleration (which must be of effect NoExecute, - otherwise this field is ignored) tolerates the taint. - By default, it is not set, which means tolerate the - taint forever (do not evict). Zero and negative values - will be treated as 0 (evict immediately) by the system. - format: int64 - type: integer - value: - description: Value is the taint value the toleration matches - to. If the operator is Exists, the value should be empty, - otherwise just a regular string. - type: string - type: object - type: array - workspacesDefaultPlugins: - description: Default plug-ins applied to Devworkspaces. - items: - properties: - editor: - description: The editor id to specify default plug-ins - for. - type: string - plugins: - description: Default plug-in uris for the specified editor. - items: - type: string - type: array - type: object - type: array - type: object - storage: - description: Configuration settings related to the persistent storage - used by the Che installation. - properties: - postgresPVCStorageClassName: - description: Storage class for the Persistent Volume Claim dedicated - to the PostgreSQL database. When omitted or left blank, a - default storage class is used. - type: string - preCreateSubPaths: - description: Instructs the Che server to start a special Pod - to pre-create a sub-path in the Persistent Volumes. Defaults - to `false`, however it will need to enable it according to - the configuration of your Kubernetes cluster. - type: boolean - pvcClaimSize: - description: Size of the persistent volume claim for workspaces. - Defaults to `10Gi`. - type: string - pvcJobsImage: - description: Overrides the container image used to create sub-paths - in the Persistent Volumes. This includes the image tag. Omit - it or leave it empty to use the default container image provided - by the Operator. See also the `preCreateSubPaths` field. - type: string - pvcStrategy: - description: Persistent volume claim strategy for the Che server. - This Can be:`common` (all workspaces PVCs in one volume), - `per-workspace` (one PVC per workspace for all declared volumes) - and `unique` (one PVC per declared volume). Defaults to `common`. - type: string - workspacePVCStorageClassName: - description: Storage class for the Persistent Volume Claims - dedicated to the Che workspaces. When omitted or left blank, - a default storage class is used. - type: string - type: object - type: object - status: - description: CheClusterStatus defines the observed state of Che installation - properties: - cheClusterRunning: - description: Status of a Che installation. Can be `Available`, `Unavailable`, - or `Available, Rolling Update in Progress`. - type: string - cheURL: - description: Public URL to the Che server. - type: string - cheVersion: - description: Current installed Che version. - type: string - dbProvisioned: - description: Indicates that a PostgreSQL instance has been correctly - provisioned or not. - type: boolean - devfileRegistryURL: - description: Public URL to the devfile registry. - type: string - devworkspaceStatus: - description: The status of the Devworkspace subsystem - properties: - gatewayHost: - description: GatewayHost is the resolved host of the ingress/route. - This is equal to the Host in the spec on Kubernetes but contains - the actual host name of the route if Host is unspecified on - OpenShift. - type: string - gatewayPhase: - description: GatewayPhase specifies the phase in which the gateway - deployment currently is. If the gateway is disabled, the phase - is "Inactive". - type: string - message: - description: Message contains further human-readable info for - why the Che cluster is in the phase it currently is. - type: string - phase: - description: Phase is the phase in which the Che cluster as - a whole finds itself in. - type: string - reason: - description: A brief CamelCase message indicating details about - why the Che cluster is in this state. - type: string - workspaceBaseDomain: - description: The resolved workspace base domain. This is either - the copy of the explicitly defined property of the same name - in the spec or, if it is undefined in the spec and we're running - on OpenShift, the automatically resolved basedomain for routes. - type: string - type: object - gitHubOAuthProvisioned: - description: Indicates whether an Identity Provider instance, Keycloak - or RH-SSO, has been configured to integrate with the GitHub OAuth. - type: boolean - helpLink: - description: A URL that points to some URL where to find help related - to the current Operator status. - type: string - keycloakProvisioned: - description: Indicates whether an Identity Provider instance, Keycloak - or RH-SSO, has been provisioned with realm, client and user. - type: boolean - keycloakURL: - description: Public URL to the Identity Provider server, Keycloak - or RH-SSO,. - type: string - message: - description: A human readable message indicating details about why - the Pod is in this condition. - type: string - openShiftOAuthUserCredentialsSecret: - description: OpenShift OAuth secret in `openshift-config` namespace - that contains user credentials for HTPasswd identity provider. - type: string - openShiftoAuthProvisioned: - description: Indicates whether an Identity Provider instance, Keycloak - or RH-SSO, has been configured to integrate with the OpenShift - OAuth. - type: boolean - pluginRegistryURL: - description: Public URL to the plugin registry. - type: string - reason: - description: A brief CamelCase message indicating details about - why the Pod is in this state. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/helmcharts/stable/values.yaml b/helmcharts/stable/values.yaml index 57fdf0889..81930c53a 100644 --- a/helmcharts/stable/values.yaml +++ b/helmcharts/stable/values.yaml @@ -10,5 +10,9 @@ # Red Hat, Inc. - initial API and implementation # -k8s: - ingressDomain: "192.168.99.101.nip.io" +ingress: + domain: "" + auth: + identityProviderURL: "" + oAuthClientName: "" + oAuthSecret: "" diff --git a/main.go b/main.go index ab75b635e..d0a45bbf0 100644 --- a/main.go +++ b/main.go @@ -18,10 +18,16 @@ import ( "time" "github.com/devfile/devworkspace-operator/pkg/infrastructure" + devworkspaceinfra "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/signal" + "github.com/eclipse-che/che-operator/pkg/common/test" "github.com/sirupsen/logrus" dwoApi "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" dwr "github.com/devfile/devworkspace-operator/controllers/controller/devworkspacerouting" + "github.com/eclipse-che/che-operator/controllers/devworkspace" "github.com/eclipse-che/che-operator/controllers/devworkspace/solver" @@ -51,11 +57,8 @@ import ( consolev1 "github.com/openshift/api/console/v1" oauthv1 "github.com/openshift/api/oauth/v1" - orgv1 "github.com/eclipse-che/che-operator/api/v1" checontroller "github.com/eclipse-che/che-operator/controllers/che" - "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/signal" - "github.com/eclipse-che/che-operator/pkg/util" + "github.com/eclipse-che/che-operator/pkg/common/utils" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" utilruntime "k8s.io/apimachinery/pkg/util/runtime" @@ -76,7 +79,8 @@ import ( networkingv1 "k8s.io/api/networking/v1" rbacv1 "k8s.io/api/rbac/v1" - orgv2alpha1 "github.com/eclipse-che/che-operator/api/v2alpha1" + chev1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" //+kubebuilder:scaffold:imports ) @@ -114,19 +118,17 @@ func init() { logger := zap.New(zap.UseFlagOptions(&opts)) ctrl.SetLogger(logger) - deploy.InitDefaults(defaultsPath) - - if _, _, err := util.DetectOpenShift(); err != nil { + if err := infrastructure.Initialize(); err != nil { logger.Error(err, "Unable determine installation platform") os.Exit(1) } + defaults.Initialize(defaultsPath) + printVersion(logger) - // Uncomment when orgv2alpha1 will be ready - // utilruntime.Must(orgv2alpha1.AddToScheme(scheme)) - - utilruntime.Must(orgv2alpha1.AddToScheme(scheme)) + utilruntime.Must(chev1.AddToScheme(scheme)) + utilruntime.Must(chev2.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(admissionregistrationv1.AddToScheme(scheme)) @@ -134,13 +136,12 @@ func init() { utilruntime.Must(rbacv1.AddToScheme(scheme)) // Setup Scheme for all resources - utilruntime.Must(orgv1.AddToScheme(scheme)) utilruntime.Must(image_puller_api.AddToScheme(scheme)) utilruntime.Must(packagesv1.AddToScheme(scheme)) utilruntime.Must(operatorsv1alpha1.AddToScheme(scheme)) utilruntime.Must(operatorsv1.AddToScheme(scheme)) - if util.IsOpenShift { + if infrastructure.IsOpenShift() { utilruntime.Must(routev1.AddToScheme(scheme)) utilruntime.Must(oauthv1.AddToScheme(scheme)) utilruntime.Must(userv1.AddToScheme(scheme)) @@ -175,13 +176,8 @@ func printVersion(logger logr.Logger) { logger.Info("Address ", "Probe", probeAddr) infra := "Kubernetes" - if util.IsOpenShift { - infra = "OpenShift" - if util.IsOpenShift4 { - infra += " v4.x" - } else { - infra += " v3.x" - } + if infrastructure.IsOpenShift() { + infra = "OpenShift v4.x" } logger.Info("Operator is running on ", "Infrastructure", infra) } @@ -216,7 +212,7 @@ func main() { os.Exit(1) } - if !util.HasK8SResourceObject(discoveryClient, leasesApiResourceName) { + if !utils.IsK8SResourceServed(discoveryClient, leasesApiResourceName) { setupLog.Info("Leader election was disabled", "Cause:", leasesApiResourceName+"k8s api resource is an absent.") enableLeaderElection = false } @@ -263,8 +259,14 @@ func main() { os.Exit(1) } - period := signal.GetTerminationGracePeriodSeconds(mgr.GetAPIReader(), util.GetCheOperatorNamespace()) - sigHandler := signal.SetupSignalHandler(period) + terminationPeriod := int64(20) + if !test.IsTestMode() { + namespace, err := infrastructure.GetOperatorNamespace() + if err == nil { + terminationPeriod = signal.GetTerminationGracePeriodSeconds(mgr.GetAPIReader(), namespace) + } + } + sigHandler := signal.SetupSignalHandler(terminationPeriod) // we install the devworkspace CheCluster reconciler even if dw is not supported so that it // can write meaningful status messages into the CheCluster CRs. @@ -287,7 +289,7 @@ func main() { // DWO use the infrastructure package for openshift detection. It needs to be initialized // but only supports OpenShift v4 or Kubernetes. - if err := infrastructure.Initialize(); err != nil { + if err := devworkspaceinfra.Initialize(); err != nil { setupLog.Error(err, "failed to evaluate infrastructure which is needed for DevWorkspace support") os.Exit(1) } @@ -316,6 +318,11 @@ func main() { }) } + if err = (&chev2.CheCluster{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "CheCluster") + os.Exit(1) + } + // +kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { setupLog.Error(err, "unable to set up health check") @@ -335,7 +342,7 @@ func main() { } func getCacheFunc() (cache.NewCacheFunc, error) { - partOfCheRequirement, err := labels.NewRequirement(deploy.KubernetesPartOfLabelKey, selection.Equals, []string{deploy.CheEclipseOrg}) + partOfCheRequirement, err := labels.NewRequirement(constants.KubernetesPartOfLabelKey, selection.Equals, []string{constants.CheEclipseOrg}) if err != nil { return nil, err } @@ -393,7 +400,7 @@ func getCacheFunc() (cache.NewCacheFunc, error) { }, } - if !util.IsOpenShift { + if !infrastructure.IsOpenShift() { delete(selectors, routeKey) delete(selectors, oauthKey) } diff --git a/make-release.sh b/make-release.sh index b49e81dcb..2ccf4fe76 100755 --- a/make-release.sh +++ b/make-release.sh @@ -188,7 +188,7 @@ updateVersionFile() { releaseHelmPackage() { echo "[INFO] releaseHelmPackage :: release Helm package" yq -rYi ".version=\"${RELEASE}\"" "$RELEASE_DIR/helmcharts/stable/Chart.yaml" - make update-helmcharts HELM_FOLDER="stable" + make update-helmcharts CHANNEL=stable git add -A helmcharts/stable git commit -m "ci: Update Helm Charts to $RELEASE" --signoff } @@ -205,7 +205,7 @@ releaseOlmFiles() { echo "[INFO] releaseOlmFiles :: Validate changes" grep -q "version: "$RELEASE $openshift/che-operator.clusterserviceversion.yaml - test -f $openshift/org_v1_che_crd.yaml + test -f $openshift/org.eclipse.che_checlusters.yaml done echo "[INFO] releaseOlmFiles :: Commit changes" if git status --porcelain; then diff --git a/olm/buildCatalog.sh b/olm/buildCatalog.sh index 1b9892b5a..1aa029ceb 100755 --- a/olm/buildCatalog.sh +++ b/olm/buildCatalog.sh @@ -19,11 +19,12 @@ if [ -z "${OPERATOR_REPO}" ]; then SCRIPT=$(readlink -f "${BASH_SOURCE[0]}") OPERATOR_REPO=$(dirname "$(dirname "$SCRIPT")") fi -source "${OPERATOR_REPO}"/olm/olm.sh +source "${OPERATOR_REPO}/.github/bin/common.sh" init() { FORCE="false" - unset CHANNEL + IMAGE_TOOL="docker" + CHANNEL="next" unset CATALOG_IMAGE unset OPERATOR_IMAGE @@ -32,6 +33,7 @@ init() { '--channel'|'-c') CHANNEL="$2"; shift 1;; '--catalog-image'|'-i') CATALOG_IMAGE="$2"; shift 1;; '--operator-image'|'-o') OPERATOR_IMAGE="$2"; shift 1;; + '--image-tool'|'-t') IMAGE_TOOL="$2"; shift 1;; '--force'|'-f') FORCE="true";; '--help'|'-h') usage; exit;; esac @@ -46,16 +48,16 @@ init() { CSV_NAME=$(yq -r ".metadata.name" "${CSV}") CSV_VERSION=$(yq -r ".spec.version" "${CSV}") - IMAGE_REGISTRY_HOST=$(echo ${CATALOG_IMAGE} | cut -d '/' -f1) - IMAGE_REGISTRY_USER_NAME=$(echo ${CATALOG_IMAGE} | cut -d '/' -f2) + IMAGE_REGISTRY_HOST=$(echo "${CATALOG_IMAGE}" | cut -d '/' -f1) + IMAGE_REGISTRY_USER_NAME=$(echo "${CATALOG_IMAGE}" | cut -d '/' -f2) BUNDLE_IMAGE="${IMAGE_REGISTRY_HOST}/${IMAGE_REGISTRY_USER_NAME}/eclipse-che-openshift-opm-bundles:${CSV_VERSION}" - echo "[INFO] CSV: ${CSV}" - echo "[INFO] CSV name: ${CSV_NAME}" - echo "[INFO] CSV version: ${CSV_VERSION}" + echo "[INFO] CSV : ${CSV}" + echo "[INFO] CSV name : ${CSV_NAME}" + echo "[INFO] CSV version : ${CSV_VERSION}" echo "[INFO] Bundle image: ${BUNDLE_IMAGE}" - if [[ ! -z ${OPERATOR_IMAGE} ]]; then + if [[ -n ${OPERATOR_IMAGE} ]]; then # set a given operator image into CSV before build sed -e "s|image:.*|image: ${OPERATOR_IMAGE}|" -i "${CSV}" fi @@ -65,37 +67,42 @@ usage () { echo "Build and push custom catalog and bundle images." echo echo "Usage:" - echo -e "\t$0 -i CATALOG_IMAGE -c CHANNEL [-o OPERATOR_IMAGE] [--force]" + echo -e "\t$0 -i CATALOG_IMAGE [-c CHANNEL] [-o OPERATOR_IMAGE] [-t IMAGE_TOOL] [--force]" echo echo "OPTIONS:" echo -e "\t-i,--catalog-image Catalog image to build" - echo -e "\t-c,--channel=next|stable Olm channel to build bundle from" + echo -e "\t-c,--channel=next|stable [default: next] Olm channel to build bundle from" echo -e "\t-o,--operator-image Operator image to include into bundle" + echo -e "\t-t,--image-tool [default: docker] Image tool" echo -e "\t-f,--force Force to rebuild a bundle" echo echo "Example:" echo -e "\t$0 -i quay.io/eclipse/eclipse-che-openshift-opm-catalog:next -c next" - echo -e "\t$0 -i quay.io/eclipse/eclipse-che-openshift-opm-catalog:next -c next -f" + echo -e "\t$0 -i quay.io/eclipse/eclipse-che-openshift-opm-catalog:next -c next -t podman -f" echo -e "\t$0 -i quay.io/eclipse/eclipse-che-openshift-opm-catalog:test -c stable" } buildBundle() { if [[ $(isBundleImageExists) == 0 ]] || [[ "${FORCE}" == "true" ]]; then echo "[INFO] Build bundle image" - buildBundleImage "${BUNDLE_IMAGE}" "${CHANNEL}" "docker" + buildBundleImage else echo "[INFO] Bundle image already exists. Use '--force' flag to force build." fi } +buildBundleImage() { + make bundle-build bundle-push CHANNEL="${CHANNEL}" BUNDLE_IMG="${BUNDLE_IMAGE}" IMAGE_TOOL="${IMAGE_TOOL}" +} + buildCatalog () { if [ $(isCatalogExists) == 0 ]; then echo "[INFO] Build a new catalog" - buildCatalogImage "${CATALOG_IMAGE}" "${BUNDLE_IMAGE}" "docker" "${FORCE}" + buildCatalogImage else if [[ $(isBundleExistsInCatalog) == 0 ]]; then echo "[INFO] Add a new bundle ${CSV_NAME} to the catalog" - buildCatalogImage "${CATALOG_IMAGE}" "${BUNDLE_IMAGE}" "docker" "${FORCE}" "${CATALOG_IMAGE}" + buildCatalogImageFromIndex else echo "[INFO] Bundle ${CSV_NAME} already exists in the catalog" fi @@ -103,124 +110,37 @@ buildCatalog () { } isBundleExistsInCatalog() { - local BUNDLE_NAME=$(docker run --entrypoint sh ${CATALOG_IMAGE} -c "apk add sqlite && sqlite3 /database/index.db 'SELECT operatorbundle_name FROM channel_entry WHERE channel_name=\"${CHANNEL}\" and operatorbundle_name=\"${CSV_NAME}\"'" | tail -n1 | tr -d '\r') + local EXISTED_BUNDLE_NAME=$(${IMAGE_TOOL} run --entrypoint sh "${CATALOG_IMAGE}" -c "apk add sqlite && sqlite3 /database/index.db 'SELECT operatorbundle_name FROM channel_entry WHERE channel_name=\"${CHANNEL}\" and operatorbundle_name=\"${CSV_NAME}\"'" | tail -n1 | tr -d '\r') # docker run produce more output then a single line - # so, it is needed to check if the last line is actually a given bunle name - if [[ ${BUNDLE_NAME} == ${CSV_NAME} ]]; then echo 1; else echo 0; fi + # so, it is needed to check if the last line is actually a given bundle name + if [[ "${EXISTED_BUNDLE_NAME}" == "${CSV_NAME}" ]]; then echo 1; else echo 0; fi } isBundleImageExists() { - skopeo inspect docker://${BUNDLE_IMAGE} 2>/dev/null | jq -r ".RepoTags[]|select(. == \"${CSV_VERSION}\")" | wc -l + skopeo inspect docker://"${BUNDLE_IMAGE}" 2>/dev/null | jq -r ".RepoTags[]|select(. == \"${CSV_VERSION}\")" | wc -l } isCatalogExists() { - CATALOG_TAG=$(echo $CATALOG_IMAGE | rev | cut -d ':' -f1 | rev) - skopeo inspect docker://${CATALOG_IMAGE} 2>/dev/null | jq -r ".RepoTags[]|select(. == \"${CATALOG_TAG}\")" | wc -l + CATALOG_TAG=$(echo "${CATALOG_IMAGE}" | rev | cut -d ':' -f1 | rev) + skopeo inspect docker://"${CATALOG_IMAGE}" 2>/dev/null | jq -r ".RepoTags[]|select(. == \"${CATALOG_TAG}\")" | wc -l } -buildBundleImage() { - CATALOG_BUNDLE_IMAGE_NAME_LOCAL="${1}" - if [ -z "${CATALOG_BUNDLE_IMAGE_NAME_LOCAL}" ]; then - echo "[ERROR] 'opm bundle' is not specified" - exit 1 - fi - channel="${2}" - if [ -z "${channel}" ]; then - echo "[ERROR] 'channel' is not specified" - exit 1 - fi - imageTool="${3}" - if [ -z "${imageTool}" ]; then - echo "[ERROR] 'imageTool' is not specified" - exit 1 - fi - - echo "[INFO] build bundle image" - - pushd "${ROOT_DIR}" || exit - - make bundle-build bundle-push channel="${channel}" BUNDLE_IMG="${CATALOG_BUNDLE_IMAGE_NAME_LOCAL}" IMAGE_TOOL="${imageTool}" - popd || exit +buildCatalogImageFromIndex() { + make catalog-build CATALOG_IMG="${CATALOG_IMAGE}" BUNDLE_IMG="${BUNDLE_IMAGE}" IMAGE_TOOL="${IMAGE_TOOL}" FROM_INDEX_OPT="--from-index ${CATALOG_IMAGE}" + make catalog-push CATALOG_IMG="${CATALOG_IMAGE}" IMAGE_TOOL="${IMAGE_TOOL}" } buildCatalogImage() { - CATALOG_IMAGENAME="${1}" - if [ -z "${CATALOG_IMAGENAME}" ]; then - echo "[ERROR] Please specify first argument: 'catalog image'" - exit 1 - fi - - CATALOG_BUNDLE_IMAGE_NAME_LOCAL="${2}" - if [ -z "${CATALOG_BUNDLE_IMAGE_NAME_LOCAL}" ]; then - echo "[ERROR] Please specify second argument: 'opm bundle image'" - exit 1 - fi - - imageTool="${3}" - if [ -z "${imageTool}" ]; then - echo "[ERROR] Please specify third argument: 'image tool'" - exit 1 - fi - - forceBuildAndPush="${4}" - if [ -z "${forceBuildAndPush}" ]; then - echo "[ERROR] Please specify fourth argument: 'force build and push: true or false'" - exit 1 - fi - - # optional argument - FROM_INDEX=${5:-""} - BUILD_INDEX_IMAGE_ARG="" - if [ ! "${FROM_INDEX}" == "" ]; then - BUILD_INDEX_IMAGE_ARG=" --from-index ${FROM_INDEX}" - fi - - SKIP_TLS_ARG="" - SKIP_TLS_VERIFY="" - if [ "${imageTool}" == "podman" ]; then - SKIP_TLS_ARG=" --skip-tls" - SKIP_TLS_VERIFY=" --tls-verify=false" - fi - - pushd "${ROOT_DIR}" || exit - - INDEX_ADD_CMD="make catalog-build \ - CATALOG_IMG=\"${CATALOG_IMAGENAME}\" \ - BUNDLE_IMG=\"${CATALOG_BUNDLE_IMAGE_NAME_LOCAL}\" \ - IMAGE_TOOL=\"${imageTool}\" \ - FROM_INDEX_OPT=\"${BUILD_INDEX_IMAGE_ARG}\"" - - exitCode=0 - # Execute command and store an error output to the variable for following handling. - { - output="$(eval "${INDEX_ADD_CMD}" 2>&1 1>&3 3>&-)"; } 3>&1 || \ - { - exitCode="$?"; - echo "[INFO] ${exitCode}"; - true; - } - echo "${output}" - if [[ "${output}" == *"already exists, Bundle already added that provides package and csv"* ]] && [[ "${forceBuildAndPush}" == "true" ]]; then - echo "[INFO] Ignore error 'Bundle already added'" - # Catalog bundle image contains bundle reference, continue without unnecessary push operation - return - else - echo "[INFO] ${exitCode}" - if [ "${exitCode}" != 0 ]; then - exit "${exitCode}" - fi - fi - - make catalog-push CATALOG_IMG="${CATALOG_IMAGENAME}" - - popd || exit + make catalog-build CATALOG_IMG="${CATALOG_IMAGE}" BUNDLE_IMG="${BUNDLE_IMAGE}" IMAGE_TOOL="${IMAGE_TOOL}" + make catalog-push CATALOG_IMG="${CATALOG_IMAGE}" IMAGE_TOOL="${IMAGE_TOOL}" } +init "$@" -init $@ -installOPM +pushd "${ROOT_DIR}" || exit buildBundle buildCatalog +popd || exit echo "[INFO] Done" diff --git a/olm/check-yq.sh b/olm/check-yq.sh deleted file mode 100755 index 95a9755f1..000000000 --- a/olm/check-yq.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -# -# Copyright (c) 2019-2021 Red Hat, Inc. -# This program and the accompanying materials are made -# available under the terms of the Eclipse Public License 2.0 -# which is available at https://www.eclipse.org/legal/epl-2.0/ -# -# SPDX-License-Identifier: EPL-2.0 -# -# Contributors: -# Red Hat, Inc. - initial API and implementation -# - -if ! command -v yq >/dev/null 2>&1 -then - echo - echo "#### ERROR ####" - echo "####" - echo "#### Please install the 'yq' tool before being able to use this script" - echo "#### see https://github.com/kislyuk/yq" - echo "#### and https://stedolan.github.io/jq/download" - echo "####" - echo "###############" - exit 1 -fi diff --git a/olm/minikube-registry-addon.sh b/olm/minikube-registry-addon.sh index dc3bf4de5..6dc19ac4d 100755 --- a/olm/minikube-registry-addon.sh +++ b/olm/minikube-registry-addon.sh @@ -26,8 +26,6 @@ fi ROOT_DIR=$(dirname "${BASE_DIR}") -source ${ROOT_DIR}/olm/check-yq.sh - minikube addons enable registry registryPod=$(kubectl get pods -n kube-system -o yaml | yq -r ".items[] | select(.metadata.labels.\"actual-registry\") | .metadata.name") kubectl wait --for=condition=ready "pods/${registryPod}" --timeout=120s -n "kube-system" diff --git a/olm/olm.sh b/olm/olm.sh index 65bf659e7..019f572d1 100755 --- a/olm/olm.sh +++ b/olm/olm.sh @@ -14,51 +14,4 @@ # Scripts to prepare OLM(operator lifecycle manager) and install che-operator package # with specific version using OLM. -BASE_DIR=$(dirname $(readlink -f "${BASH_SOURCE[0]}")) -ROOT_DIR=$(dirname "${BASE_DIR}") - -source ${ROOT_DIR}/olm/check-yq.sh - -getPackageName() { - echo "eclipse-che-preview-openshift" -} - -getCustomCatalogSourceName() { - echo "eclipse-che-custom-catalog-source" -} - -getSubscriptionName() { - echo "eclipse-che-subscription" -} - -getDevWorkspaceCustomCatalogSourceName() { - echo "custom-devworkspace-operator-catalog" -} - -getBundlePath() { - channel="${1}" - if [ -z "${channel}" ]; then - echo "[ERROR] 'channel' is not specified" - exit 1 - fi - - echo "${ROOT_DIR}/bundle/${channel}/$(getPackageName)" -} - -installOPM() { - OPM_BINARY=$(command -v opm) || true - if [[ ! -x $OPM_BINARY ]]; then - OPM_TEMP_DIR="$(mktemp -q -d -t "OPM_XXXXXX" 2>/dev/null || mktemp -q -d)" - pushd "${OPM_TEMP_DIR}" || exit - - echo "[INFO] Downloading 'opm' cli tool..." - curl -sLo opm "$(curl -sL https://api.github.com/repos/operator-framework/operator-registry/releases/33432389 | jq -r '[.assets[] | select(.name == "linux-amd64-opm")] | first | .browser_download_url')" - export OPM_BINARY="${OPM_TEMP_DIR}/opm" - chmod +x "${OPM_BINARY}" - echo "[INFO] Downloading completed!" - echo "[INFO] 'opm' binary path: ${OPM_BINARY}" - ${OPM_BINARY} version - popd || exit - fi -} diff --git a/olm/prepare-community-operators-update.sh b/olm/prepare-community-operators-update.sh index 7e37b7687..96f40809f 100755 --- a/olm/prepare-community-operators-update.sh +++ b/olm/prepare-community-operators-update.sh @@ -22,7 +22,8 @@ CURRENT_DIR=$(pwd) SCRIPT=$(readlink -f "${BASH_SOURCE[0]}") BASE_DIR=$(dirname "$(dirname "$SCRIPT")") STABLE_CHANNELS=("stable") -source "${BASE_DIR}/olm/check-yq.sh" +OPERATOR_REPO=$(dirname "$BASE_DIR") +source "${OPERATOR_REPO}/.github/bin/common.sh" base_branch="main" GITHUB_USER="che-bot" @@ -54,12 +55,6 @@ Options: " } -. ${BASE_DIR}/olm/olm.sh -installOPM - -# $BASE_DIR is set to {OPERATOR_DIR}/olm -OPERATOR_REPO=$(dirname "$BASE_DIR") -source ${OPERATOR_REPO}/.github/bin/common.sh getLatestStableVersions INDEX_IMAGE="quay.io/eclipse/eclipse-che-openshift-opm-catalog:test" @@ -118,7 +113,7 @@ do > "${folderToUpdate}/${LAST_PACKAGE_VERSION}/manifests/eclipse-che.v${LAST_PACKAGE_VERSION}.clusterserviceversion.yaml" echo " - Update the CRD files" - cp "${OPERATOR_REPO}/bundle/$channel/eclipse-che-preview-openshift/manifests/org_v1_che_crd.yaml" "${folderToUpdate}/${LAST_PACKAGE_VERSION}/manifests/checlusters.org.eclipse.che.crd.yaml" + cp "${OPERATOR_REPO}/bundle/$channel/eclipse-che-preview-openshift/manifests/org.eclipse.che_checlusters.yaml" "${folderToUpdate}/${LAST_PACKAGE_VERSION}/manifests/org.eclipse.che_checlusters.yaml" echo cp ${OPERATOR_REPO}/bundle/$channel/eclipse-che-preview-openshift/metadata/* "${folderToUpdate}/${LAST_PACKAGE_VERSION}/metadata" @@ -164,6 +159,6 @@ done cd "${CURRENT_DIR}" echo -echo "Generated pull request: +echo "Generated pull request: https://github.com/redhat-openshift-ecosystem/community-operators-prod/pulls/che-incubator-bot " diff --git a/olm/release-olm-files.sh b/olm/release-olm-files.sh index 2e75365c3..34f742d3e 100755 --- a/olm/release-olm-files.sh +++ b/olm/release-olm-files.sh @@ -22,9 +22,9 @@ while [[ "$#" -gt 0 ]]; do done OPERATOR_DIR=$(dirname $(dirname $(readlink -f "${BASH_SOURCE[0]}"))) -BASE_DIR="${OPERATOR_DIR}/olm" +source "${OPERATOR_REPO}/.github/bin/common.sh" -source ${BASE_DIR}/check-yq.sh +BASE_DIR="${OPERATOR_DIR}/olm" export LAST_RELEASE_VERSION @@ -55,8 +55,6 @@ if [[ -z "$RELEASE" ]] || [[ -z "$CHANNEL" ]]; then exit 1 fi - -source ${BASE_DIR}/olm.sh echo "[INFO] Creating release '${RELEASE}'" NEXT_BUNDLE_PATH=$(getBundlePath "next") @@ -67,13 +65,12 @@ echo "[INFO] Last package next version: ${lastPackageNextVersion}" STABLE_BUNDLE_PATH=$(getBundlePath $CHANNEL) RELEASE_CSV="${STABLE_BUNDLE_PATH}/manifests/che-operator.clusterserviceversion.yaml" -RELEASE_CHE_CRD="${STABLE_BUNDLE_PATH}/manifests/org_v1_che_crd.yaml" +RELEASE_CHE_CRD="${STABLE_BUNDLE_PATH}/manifests/org.eclipse.che_checlusters.yaml" MANAGER_YAML="${OPERATOR_DIR}/config/manager/manager.yaml" setLatestReleasedVersion downloadLatestReleasedBundleCRCRD -packageName=$(getPackageName) echo "[INFO] Will create release '${RELEASE}' from next version ${lastPackageNextVersion}'" @@ -83,14 +80,14 @@ sed \ -e 's|quay.io/eclipse/che-dashboard:next|quay.io/eclipse/che-dashboard:'${RELEASE}'|' \ -e 's|"devfileRegistryImage": *"quay.io/eclipse/che-devfile-registry:next"|"devfileRegistryImage": ""|' \ -e 's|"pluginRegistryImage": *"quay.io/eclipse/che-plugin-registry:next"|"pluginRegistryImage": ""|' \ --e "/^ replaces: ${packageName}.v.*/d" \ +-e "/^ replaces: ${ECLIPSE_CHE_PACKAGE_NAME}.v.*/d" \ -e "s/^ version: ${lastPackageNextVersion}/ version: ${RELEASE}/" \ -e "s/: next/: ${RELEASE}/" \ -e "s/:next/:${RELEASE}/" \ -e "s/${lastPackageNextVersion}/${RELEASE}/" \ -e "s/createdAt:.*$/createdAt: \"$(date -u +%FT%TZ)\"/" "${LAST_NEXT_CSV}" > "${RELEASE_CSV}" -cp "${NEXT_BUNDLE_PATH}/manifests/org_v1_che_crd.yaml" "${RELEASE_CHE_CRD}" +cp "${NEXT_BUNDLE_PATH}/manifests/org.eclipse.che_checlusters.yaml" "${RELEASE_CHE_CRD}" cp -rf "${NEXT_BUNDLE_PATH}/bundle.Dockerfile" "${STABLE_BUNDLE_PATH}" cp -rf "${NEXT_BUNDLE_PATH}/metadata" "${STABLE_BUNDLE_PATH}" cp -rf "${NEXT_BUNDLE_PATH}/tests" "${STABLE_BUNDLE_PATH}" diff --git a/olm/testCatalog.sh b/olm/testCatalog.sh index 8f1e9d2d6..9c474587b 100755 --- a/olm/testCatalog.sh +++ b/olm/testCatalog.sh @@ -19,7 +19,6 @@ if [ -z "${OPERATOR_REPO}" ]; then SCRIPT=$(readlink -f "${BASH_SOURCE[0]}") OPERATOR_REPO=$(dirname "$(dirname "$SCRIPT")") fi -source "${OPERATOR_REPO}/olm/olm.sh" source "${OPERATOR_REPO}/.github/bin/common.sh" init() { @@ -49,7 +48,7 @@ usage () { echo "OPTIONS:" echo -e "\t-i,--catalog-image Catalog image" echo -e "\t-c,--channel=next|stable [default: next] Olm channel to deploy Eclipse Che from" - echo -e "\t-n,--namespace [default: eclipse-che] Kubernetes namepsace to deploy Eclipse Che into" + echo -e "\t-n,--namespace [default: eclipse-che] Kubernetes namespace to deploy Eclipse Che into" echo echo "Example:" echo -e "\t$0 -i quay.io/eclipse/eclipse-che-openshift-opm-catalog:next" @@ -58,28 +57,25 @@ usage () { } run() { - createNamespace ${NAMESPACE} + deployDevWorkspaceOperator "${CHANNEL}" - deployDevWorkspaceOperator ${CHANNEL} + createNamespace "${NAMESPACE}" + createCatalogSource "${ECLIPSE_CHE_CATALOG_SOURCE_NAME}" "${CATALOG_IMAGE}" - local customCatalogSource=$(getCustomCatalogSourceName) - createCatalogSource "${customCatalogSource}" "${CATALOG_IMAGE}" - - local bundles=$(getCatalogSourceBundles "${customCatalogSource}") + local bundles=$(getCatalogSourceBundles "${ECLIPSE_CHE_CATALOG_SOURCE_NAME}") fetchLatestCSVInfo "${CHANNEL}" "${bundles}" forcePullingOlmImages "${LATEST_CSV_BUNDLE_IMAGE}" - local subscription=$(getSubscriptionName) - createSubscription "${subscription}" $(getPackageName) ${CHANNEL} "${customCatalogSource}" "Manual" - approveInstallPlan "${subscription}" + createSubscription "${ECLIPSE_CHE_SUBSCRIPTION_NAME}" "${ECLIPSE_CHE_PACKAGE_NAME}" "${CHANNEL}" "${ECLIPSE_CHE_CATALOG_SOURCE_NAME}" "Manual" + approveInstallPlan "${ECLIPSE_CHE_SUBSCRIPTION_NAME}" sleep 10s - echo "$(getCheClusterCRFromExistedCSV)" | oc apply -n "${NAMESPACE}" -f - - waitEclipseCheDeployed $(getCheVersionFromExistedCSV) + getCheClusterCRFromExistedCSV | oc apply -n "${NAMESPACE}" -f - + waitEclipseCheDeployed "$(getCheVersionFromExistedCSV)" } -init $@ +init "$@" run echo "[INFO] Done" diff --git a/olm/testUpdate.sh b/olm/testUpdate.sh index 1d051b418..777611d05 100755 --- a/olm/testUpdate.sh +++ b/olm/testUpdate.sh @@ -19,12 +19,11 @@ if [ -z "${OPERATOR_REPO}" ]; then SCRIPT=$(readlink -f "${BASH_SOURCE[0]}") OPERATOR_REPO=$(dirname "$(dirname "$SCRIPT")") fi -source "${OPERATOR_REPO}"/olm/olm.sh -source "${OPERATOR_REPO}"/.github/bin/common.sh +source "${OPERATOR_REPO}/.github/bin/common.sh" init() { NAMESPACE="eclipse-che" - unset CHANNEL + CHANNEL="next" unset CATALOG_IMAGE while [[ "$#" -gt 0 ]]; do @@ -44,11 +43,11 @@ usage () { echo "Deploy and update Eclipse Che from a custom catalog." echo echo "Usage:" - echo -e "\t$0 -i CATALOG_IMAGE -c CHANNEL [-n NAMESPACE]" + echo -e "\t$0 -i CATALOG_IMAGE [-c CHANNEL] [-n NAMESPACE]" echo echo "OPTIONS:" echo -e "\t-i,--catalog-image Catalog image" - echo -e "\t-c,--channel Olm channel to deploy Eclipse Che from" + echo -e "\t-c,--channel [default: next] Olm channel to deploy Eclipse Che from" echo -e "\t-n,--namespace [default: eclipse-che] Kubernetes namepsace to deploy Eclipse Che into" echo echo "Example:" @@ -57,14 +56,12 @@ usage () { } run() { + deployDevWorkspaceOperator "${CHANNEL}" + createNamespace "${NAMESPACE}" + createCatalogSource "${ECLIPSE_CHE_CATALOG_SOURCE_NAME}" "${CATALOG_IMAGE}" - deployDevWorkspaceOperator ${CHANNEL} - - local customCatalogSource=$(getCustomCatalogSourceName) - createCatalogSource "${customCatalogSource}" "${CATALOG_IMAGE}" - - local bundles=$(getCatalogSourceBundles "${customCatalogSource}") + local bundles=$(getCatalogSourceBundles "${ECLIPSE_CHE_CATALOG_SOURCE_NAME}") fetchPreviousCSVInfo "${CHANNEL}" "${bundles}" fetchLatestCSVInfo "${CHANNEL}" "${bundles}" @@ -77,17 +74,16 @@ run() { forcePullingOlmImages "${PREVIOUS_CSV_BUNDLE_IMAGE}" forcePullingOlmImages "${LATEST_CSV_BUNDLE_IMAGE}" - local subscription=$(getSubscriptionName) - createSubscription "${subscription}" $(getPackageName) "${CHANNEL}" "${customCatalogSource}" "Manual" "${PREVIOUS_CSV_NAME}" - approveInstallPlan "${subscription}" + createSubscription "${ECLIPSE_CHE_SUBSCRIPTION_NAME}" "${ECLIPSE_CHE_PACKAGE_NAME}" "${CHANNEL}" "${ECLIPSE_CHE_CATALOG_SOURCE_NAME}" "Manual" "${PREVIOUS_CSV_NAME}" + approveInstallPlan "${ECLIPSE_CHE_SUBSCRIPTION_NAME}" sleep 10s - echo "$(getCheClusterCRFromExistedCSV)" | oc apply -n "${NAMESPACE}" -f - - waitEclipseCheDeployed $(getCheVersionFromExistedCSV) + getCheClusterCRFromExistedCSV | oc apply -n "${NAMESPACE}" -f - + waitEclipseCheDeployed "$(getCheVersionFromExistedCSV)" - approveInstallPlan "${subscription}" - waitEclipseCheDeployed $(getCheVersionFromExistedCSV) + approveInstallPlan "${ECLIPSE_CHE_SUBSCRIPTION_NAME}" + waitEclipseCheDeployed "$(getCheVersionFromExistedCSV)" } init "$@" diff --git a/pkg/deploy/data_types.go b/pkg/common/chetypes/types.go similarity index 88% rename from pkg/deploy/data_types.go rename to pkg/common/chetypes/types.go index f4ef11ea9..57e31b8f8 100644 --- a/pkg/deploy/data_types.go +++ b/pkg/common/chetypes/types.go @@ -10,10 +10,10 @@ // Red Hat, Inc. - initial API and implementation // -package deploy +package chetypes import ( - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/discovery" "sigs.k8s.io/controller-runtime/pkg/client" @@ -26,10 +26,11 @@ type ProvisioningStatus struct { } type DeployContext struct { - CheCluster *orgv1.CheCluster + CheCluster *chev2.CheCluster ClusterAPI ClusterAPI Proxy *Proxy IsSelfSignedCertificate bool + CheHost string } type ClusterAPI struct { diff --git a/pkg/common/constants/constants.go b/pkg/common/constants/constants.go new file mode 100644 index 000000000..2f9115d18 --- /dev/null +++ b/pkg/common/constants/constants.go @@ -0,0 +1,129 @@ +// +// Copyright (c) 2019-2022 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package constants + +const ( + // PostgresSQL + DefaultPostgresUser = "pgche" + DefaultPostgresHostName = "postgres" + DefaultPostgresPort = "5432" + DefaultPostgresDb = "dbche" + DefaultPostgresMemoryLimit = "1024Mi" + DefaultPostgresMemoryRequest = "512Mi" + DefaultPostgresCpuLimit = "500m" + DefaultPostgresCpuRequest = "100m" + DefaultPostgresCredentialsSecret = "postgres-credentials" + DefaultPostgresVolumeClaimName = "postgres-data" + DefaultPostgresPvcClaimSize = "1Gi" + + // Dashboard + DefaultDashboardMemoryLimit = "256Mi" + DefaultDashboardMemoryRequest = "32Mi" + DefaultDashboardCpuLimit = "500m" + DefaultDashboardCpuRequest = "100m" + + // PluginRegistry + DefaultPluginRegistryMemoryLimit = "256Mi" + DefaultPluginRegistryMemoryRequest = "32Mi" + DefaultPluginRegistryCpuLimit = "500m" + DefaultPluginRegistryCpuRequest = "100m" + + // DevfileRegistry + DefaultDevfileRegistryMemoryLimit = "256Mi" + DefaultDevfileRegistryMemoryRequest = "32Mi" + DefaultDevfileRegistryCpuLimit = "500m" + DefaultDevfileRegistryCpuRequest = "100m" + + // Server + DefaultServerMemoryLimit = "1024Mi" + DefaultServerMemoryRequest = "512Mi" + DefaultServerCpuLimit = "1" + DefaultServerCpuRequest = "100m" + DefaultServerLogLevel = "INFO" + DefaultServerMetricsPort = int32(8087) + DefaultServerDebugPort = int32(8000) + DefaultServerTrustStoreConfigMapName = "ca-certs" + DefaultProxyCredentialsSecret = "proxy-credentials" + DefaultGitSelfSignedCertsConfigMapName = "che-git-self-signed-cert" + DefaultJavaOpts = "-XX:MaxRAMPercentage=85.0" + DefaultSecurityContextFsGroup = 1724 + DefaultSecurityContextRunAsUser = 1724 + DefaultCheServiceAccountName = "che" + + // OAuth + BitBucketOAuthConfigMountPath = "/che-conf/oauth/bitbucket" + BitBucketOAuthConfigPrivateKeyFileName = "private.key" + BitBucketOAuthConfigConsumerKeyFileName = "consumer.key" + GitHubOAuthConfigMountPath = "/che-conf/oauth/github" + GitHubOAuthConfigClientIdFileName = "id" + GitHubOAuthConfigClientSecretFileName = "secret" + GitLabOAuthConfigMountPath = "/che-conf/oauth/gitlab" + GitLabOAuthConfigClientIdFileName = "id" + GitLabOAuthConfigClientSecretFileName = "secret" + OAuthScmConfiguration = "oauth-scm-configuration" + + // Labels + KubernetesComponentLabelKey = "app.kubernetes.io/component" + KubernetesPartOfLabelKey = "app.kubernetes.io/part-of" + KubernetesManagedByLabelKey = "app.kubernetes.io/managed-by" + KubernetesInstanceLabelKey = "app.kubernetes.io/instance" + KubernetesNameLabelKey = "app.kubernetes.io/name" + + // Annotations + CheEclipseOrgMountPath = "che.eclipse.org/mount-path" + CheEclipseOrgMountAs = "che.eclipse.org/mount-as" + CheEclipseOrgEnvName = "che.eclipse.org/env-name" + CheEclipseOrgNamespace = "che.eclipse.org/namespace" + CheEclipseOrgGithubOAuthCredentials = "che.eclipse.org/github-oauth-credentials" + CheEclipseOrgOAuthScmServer = "che.eclipse.org/oauth-scm-server" + CheEclipseOrgScmServerEndpoint = "che.eclipse.org/scm-server-endpoint" + CheEclipseOrgHash256 = "che.eclipse.org/hash256" + CheEclipseOrgManagedAnnotationsDigest = "che.eclipse.org/managed-annotations-digest" + + // Workspaces + DefaultPvcStrategy = "common" + DefaultPvcClaimSize = "10Gi" + DefaultWorkspaceJavaOpts = "-XX:MaxRAM=150m -XX:MaxRAMFraction=2 -XX:+UseParallelGC " + + "-XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 " + + "-Dsun.zip.disableMemoryMapping=true " + + "-Xms20m -Djava.security.egd=file:/dev/./urandom" + + // Ingress + DefaultSelfSignedCertificateSecretName = "self-signed-certificate" + DefaultCheTLSSecretName = "che-tls" + DefaultIngressClass = "nginx" + + // ImagePuller + KubernetesImagePullerOperatorCSV = "kubernetes-imagepuller-operator.v0.0.9" + + // components name + DevfileRegistryName = "devfile-registry" + PluginRegistryName = "plugin-registry" + PostgresName = "postgres" + DevWorkspaceController = "devworkspace-controller" + GatewayContainerName = "gateway" + GatewayConfigSideCarContainerName = "configbump" + GatewayAuthenticationContainerName = "oauth-proxy" + GatewayAuthorizationContainerName = "kube-rbac-proxy" + + // + CheEclipseOrg = "che.eclipse.org" + InstallOrUpdateFailed = "InstallOrUpdateFailed" +) + +var ( + DefaultSingleHostGatewayConfigMapLabels = map[string]string{ + "app": "che", + "component": "che-gateway-config", + } +) diff --git a/pkg/util/k8s_helpers.go b/pkg/common/k8s-helper/k8s_helper.go similarity index 52% rename from pkg/util/k8s_helpers.go rename to pkg/common/k8s-helper/k8s_helper.go index 1851425a0..77395fb2b 100644 --- a/pkg/util/k8s_helpers.go +++ b/pkg/common/k8s-helper/k8s_helper.go @@ -9,7 +9,7 @@ // Contributors: // Red Hat, Inc. - initial API and implementation // -package util +package k8shelper import ( "bytes" @@ -17,71 +17,61 @@ import ( "fmt" "io" - v1 "github.com/eclipse-che/che-operator/api/v1" + "github.com/eclipse-che/che-operator/pkg/common/test" + "k8s.io/client-go/kubernetes/fake" + "github.com/sirupsen/logrus" - authorizationv1 "k8s.io/api/authorization/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/tools/remotecommand" "sigs.k8s.io/controller-runtime/pkg/client/config" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/predicate" ) -type k8s struct { +type K8sHelper struct { clientset kubernetes.Interface } var ( - K8sclient = GetK8Client() + k8sHelper *K8sHelper ) -func GetK8Client() *k8s { - tests := IsTestMode() - if !tests { - cfg, err := config.GetConfig() - if err != nil { - logrus.Errorf(err.Error()) - } - client := k8s{} - client.clientset, err = kubernetes.NewForConfig(cfg) - - if err != nil { - logrus.Errorf(err.Error()) - return nil - } - return &client +func New() *K8sHelper { + if k8sHelper != nil { + return k8sHelper } - return nil + + if test.IsTestMode() { + return initializeForTesting() + } + + return initialize() } -func (cl *k8s) ExecIntoPod( - cr *v1.CheCluster, +func (cl *K8sHelper) GetClientset() kubernetes.Interface { + return cl.clientset +} + +func (cl *K8sHelper) ExecIntoPod( deploymentName string, - getCommand func(*v1.CheCluster) (string, error), - reason string) (string, error) { - - command, err := getCommand(cr) + command string, + reason string, + namespace string) (string, error) { + pod, err := cl.GetDeploymentPod(deploymentName, namespace) if err != nil { return "", err } - pod, err := cl.GetDeploymentPod(deploymentName, cr.Namespace) - if err != nil { - return "", err - } - - return cl.DoExecIntoPod(cr.Namespace, pod, command, reason) + return cl.DoExecIntoPod(namespace, pod, command, reason) } -func (cl *k8s) DoExecIntoPod(namespace string, podName string, command string, reason string) (string, error) { +func (cl *K8sHelper) DoExecIntoPod(namespace string, podName string, command string, reason string) (string, error) { var stdin io.Reader return cl.DoExecIntoPodWithStdin(namespace, podName, command, stdin, reason) } -func (cl *k8s) DoExecIntoPodWithStdin(namespace string, podName string, command string, stdin io.Reader, reason string) (string, error) { +func (cl *K8sHelper) DoExecIntoPodWithStdin(namespace string, podName string, command string, stdin io.Reader, reason string) (string, error) { if reason != "" { logrus.Infof("Running exec for '%s' in the pod '%s'", reason, podName) } @@ -103,7 +93,7 @@ func (cl *k8s) DoExecIntoPodWithStdin(namespace string, podName string, command } //GetDeploymentPod queries all pods is a selected namespace by LabelSelector -func (cl *k8s) GetDeploymentPod(name string, ns string) (podName string, err error) { +func (cl *K8sHelper) GetDeploymentPod(name string, ns string) (podName string, err error) { api := cl.clientset.CoreV1() listOptions := metav1.ListOptions{ LabelSelector: "component=" + name, @@ -120,7 +110,7 @@ func (cl *k8s) GetDeploymentPod(name string, ns string) (podName string, err err return podName, nil } -func (cl *k8s) GetPodsByComponent(name string, ns string) []string { +func (cl *K8sHelper) GetPodsByComponent(name string, ns string) []string { names := []string{} api := cl.clientset.CoreV1() listOptions := metav1.ListOptions{ @@ -134,17 +124,7 @@ func (cl *k8s) GetPodsByComponent(name string, ns string) []string { return names } -// Reads 'user' and 'password' from the given secret -func (cl *k8s) ReadSecret(name string, ns string) (user string, password string, err error) { - secret, err := cl.clientset.CoreV1().Secrets(ns).Get(context.TODO(), name, metav1.GetOptions{}) - if err != nil { - return "", "", err - } - return string(secret.Data["user"]), string(secret.Data["password"]), nil -} - -func (cl *k8s) RunExec(command []string, podName, namespace string, stdin io.Reader) (string, string, error) { - +func (cl *K8sHelper) RunExec(command []string, podName, namespace string, stdin io.Reader) (string, string, error) { req := cl.clientset.CoreV1().RESTClient().Post(). Resource("pods"). Name(podName). @@ -176,34 +156,28 @@ func (cl *k8s) RunExec(command []string, podName, namespace string, stdin io.Rea return stdout.String(), stderr.String(), err } -func (cl *k8s) IsResourceOperationPermitted(resourceAttr *authorizationv1.ResourceAttributes) (ok bool, err error) { - lsar := &authorizationv1.SelfSubjectAccessReview{ - Spec: authorizationv1.SelfSubjectAccessReviewSpec{ - ResourceAttributes: resourceAttr, - }, +func initializeForTesting() *K8sHelper { + k8sHelper = &K8sHelper{ + clientset: fake.NewSimpleClientset(), } - ssar, err := cl.clientset.AuthorizationV1().SelfSubjectAccessReviews().Create(context.TODO(), lsar, metav1.CreateOptions{}) + return k8sHelper +} + +func initialize() *K8sHelper { + cfg, err := config.GetConfig() if err != nil { - return false, err + logrus.Fatalf("Failed to initialized Kubernetes client: %v", err) } - return ssar.Status.Allowed, nil -} - -func InNamespaceEventFilter(namespace string) predicate.Predicate { - return predicate.Funcs{ - CreateFunc: func(ce event.CreateEvent) bool { - return namespace == ce.Object.GetNamespace() - }, - DeleteFunc: func(de event.DeleteEvent) bool { - return namespace == de.Object.GetNamespace() - }, - UpdateFunc: func(ue event.UpdateEvent) bool { - return namespace == ue.ObjectOld.GetNamespace() - }, - GenericFunc: func(ge event.GenericEvent) bool { - return namespace == ge.Object.GetNamespace() - }, + clientSet, err := kubernetes.NewForConfig(cfg) + if err != nil { + logrus.Fatalf("Failed to initialized Kubernetes client: %v", err) } + + k8sHelper = &K8sHelper{ + clientset: clientSet, + } + + return k8sHelper } diff --git a/pkg/common/operator-defaults/defaults.go b/pkg/common/operator-defaults/defaults.go new file mode 100644 index 000000000..2ce76ccc4 --- /dev/null +++ b/pkg/common/operator-defaults/defaults.go @@ -0,0 +1,394 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// +package defaults + +import ( + "fmt" + "os" + "strings" + + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + chev2 "github.com/eclipse-che/che-operator/api/v2" + util "github.com/eclipse-che/che-operator/pkg/common/utils" + "github.com/sirupsen/logrus" + appsv1 "k8s.io/api/apps/v1" +) + +var ( + defaultCheVersion string + defaultCheFlavor string + defaultCheServerImage string + defaultDashboardImage string + defaultDevworkspaceControllerImage string + defaultPluginRegistryImage string + defaultDevfileRegistryImage string + defaultCheTLSSecretsCreationJobImage string + defaultPostgresImage string + defaultPostgres13Image string + defaultSingleHostGatewayImage string + defaultSingleHostGatewayConfigSidecarImage string + defaultGatewayAuthenticationSidecarImage string + defaultGatewayAuthorizationSidecarImage string + defaultGatewayHeaderProxySidecarImage string + defaultCheWorkspacePluginBrokerMetadataImage string + defaultCheWorkspacePluginBrokerArtifactsImage string + defaultCheServerSecureExposerJwtProxyImage string + defaultConsoleLinkName string + defaultConsoleLinkDisplayName string + defaultConsoleLinkSection string + defaultsConsoleLinkImage string + + initialized = false +) + +func Initialize(operatorDeploymentFilePath string) { + if operatorDeploymentFilePath == "" { + InitializeFromEnv() + } else { + InitializeFromFile(operatorDeploymentFilePath) + } + +} + +func InitializeFromFile(operatorDeploymentFilePath string) { + operatorDeployment := &appsv1.Deployment{} + if err := util.ReadObjectInto(operatorDeploymentFilePath, operatorDeployment); err != nil { + logrus.Fatalf("Failed to read operator deployment from '%s', cause: %v", operatorDeploymentFilePath, err) + } + + containers := operatorDeployment.Spec.Template.Spec.Containers + if len(containers) == 0 { + logrus.Fatalf("Containers not found in operator deployment '%s'", operatorDeploymentFilePath) + } + + defaultCheVersion = util.GetEnv(containers[0].Env, "CHE_VERSION") + defaultCheFlavor = util.GetEnv(containers[0].Env, "CHE_FLAVOR") + defaultConsoleLinkDisplayName = util.GetEnv(containers[0].Env, "CONSOLE_LINK_DISPLAY_NAME") + defaultConsoleLinkName = util.GetEnv(containers[0].Env, "CONSOLE_LINK_NAME") + defaultConsoleLinkSection = util.GetEnv(containers[0].Env, "CONSOLE_LINK_SECTION") + defaultsConsoleLinkImage = util.GetEnv(containers[0].Env, "CONSOLE_LINK_IMAGE") + + defaultCheServerImage = util.GetEnv(containers[0].Env, util.GetArchitectureDependentEnv("RELATED_IMAGE_che_server")) + defaultDashboardImage = util.GetEnv(containers[0].Env, util.GetArchitectureDependentEnv("RELATED_IMAGE_dashboard")) + defaultDevworkspaceControllerImage = util.GetEnv(containers[0].Env, util.GetArchitectureDependentEnv("RELATED_IMAGE_devworkspace_controller")) + defaultPluginRegistryImage = util.GetEnv(containers[0].Env, util.GetArchitectureDependentEnv("RELATED_IMAGE_plugin_registry")) + defaultDevfileRegistryImage = util.GetEnv(containers[0].Env, util.GetArchitectureDependentEnv("RELATED_IMAGE_devfile_registry")) + defaultPostgresImage = util.GetEnv(containers[0].Env, util.GetArchitectureDependentEnv("RELATED_IMAGE_postgres")) + defaultPostgres13Image = util.GetEnv(containers[0].Env, util.GetArchitectureDependentEnv("RELATED_IMAGE_postgres_13_3")) + defaultSingleHostGatewayImage = util.GetEnv(containers[0].Env, util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway")) + defaultSingleHostGatewayConfigSidecarImage = util.GetEnv(containers[0].Env, util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway_config_sidecar")) + defaultGatewayAuthenticationSidecarImage = util.GetEnv(containers[0].Env, util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authentication_sidecar")) + defaultGatewayAuthorizationSidecarImage = util.GetEnv(containers[0].Env, util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authorization_sidecar")) + + // Don't get some k8s specific env + if !infrastructure.IsOpenShift() { + defaultCheTLSSecretsCreationJobImage = util.GetEnv(containers[0].Env, util.GetArchitectureDependentEnv("RELATED_IMAGE_che_tls_secrets_creation_job")) + defaultGatewayAuthenticationSidecarImage = util.GetEnv(containers[0].Env, util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authentication_sidecar_k8s")) + defaultGatewayAuthorizationSidecarImage = util.GetEnv(containers[0].Env, util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authorization_sidecar_k8s")) + } + + initialized = true +} + +func InitializeFromEnv() { + defaultCheVersion = ensureEnv("CHE_VERSION") + defaultCheFlavor = ensureEnv("CHE_FLAVOR") + defaultConsoleLinkDisplayName = ensureEnv("CONSOLE_LINK_DISPLAY_NAME") + defaultConsoleLinkName = ensureEnv("CONSOLE_LINK_NAME") + defaultConsoleLinkSection = ensureEnv("CONSOLE_LINK_SECTION") + defaultsConsoleLinkImage = ensureEnv("CONSOLE_LINK_IMAGE") + + defaultCheServerImage = ensureEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_che_server")) + defaultDashboardImage = ensureEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_dashboard")) + defaultDevworkspaceControllerImage = ensureEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_devworkspace_controller")) + defaultPluginRegistryImage = ensureEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_plugin_registry")) + defaultDevfileRegistryImage = ensureEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_devfile_registry")) + defaultPostgresImage = ensureEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_postgres")) + + // allow not to set env variable into a container + // while downstream is not migrated to PostgreSQL 13.3 yet + defaultPostgres13Image = os.Getenv(util.GetArchitectureDependentEnv("RELATED_IMAGE_postgres_13_3")) + + defaultSingleHostGatewayImage = ensureEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway")) + defaultSingleHostGatewayConfigSidecarImage = ensureEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway_config_sidecar")) + defaultGatewayAuthenticationSidecarImage = ensureEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authentication_sidecar")) + defaultGatewayAuthorizationSidecarImage = ensureEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authorization_sidecar")) + defaultGatewayHeaderProxySidecarImage = ensureEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_header_sidecar")) + + // Don't get some k8s specific env + if !infrastructure.IsOpenShift() { + defaultCheTLSSecretsCreationJobImage = ensureEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_che_tls_secrets_creation_job")) + defaultGatewayAuthenticationSidecarImage = ensureEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authentication_sidecar_k8s")) + defaultGatewayAuthorizationSidecarImage = ensureEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authorization_sidecar_k8s")) + } + + initialized = true +} + +func ensureEnv(name string) string { + value := os.Getenv(name) + if value == "" { + logrus.Fatalf("Failed to initialize default value: '%s'. Environment variable not found.", name) + } + + return value +} + +func GetCheServerImage(checluster *chev2.CheCluster) string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return PatchDefaultImageName(checluster, defaultCheServerImage) +} + +func GetCheTLSSecretsCreationJobImage() string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return defaultCheTLSSecretsCreationJobImage +} + +func GetCheVersion() string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return defaultCheVersion +} + +func GetPostgresImage(checluster *chev2.CheCluster) string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return PatchDefaultImageName(checluster, defaultPostgresImage) +} + +func GetPostgres13Image(checluster *chev2.CheCluster) string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return PatchDefaultImageName(checluster, defaultPostgres13Image) +} + +func GetDashboardImage(checluster *chev2.CheCluster) string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return PatchDefaultImageName(checluster, defaultDashboardImage) +} + +func GetDevworkspaceControllerImage(checluster *chev2.CheCluster) string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return PatchDefaultImageName(checluster, defaultDevworkspaceControllerImage) +} + +func GetPluginRegistryImage(checluster *chev2.CheCluster) string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return PatchDefaultImageName(checluster, defaultPluginRegistryImage) +} + +func GetDevfileRegistryImage(checluster *chev2.CheCluster) string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return PatchDefaultImageName(checluster, defaultDevfileRegistryImage) +} + +func GetCheWorkspacePluginBrokerMetadataImage(checluster *chev2.CheCluster) string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return PatchDefaultImageName(checluster, defaultCheWorkspacePluginBrokerMetadataImage) +} + +func GetCheWorkspacePluginBrokerArtifactsImage(checluster *chev2.CheCluster) string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return PatchDefaultImageName(checluster, defaultCheWorkspacePluginBrokerArtifactsImage) +} + +func GetCheServerSecureExposerJwtProxyImage(checluster *chev2.CheCluster) string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return PatchDefaultImageName(checluster, defaultCheServerSecureExposerJwtProxyImage) +} + +func GetGatewayImage(checluster *chev2.CheCluster) string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return PatchDefaultImageName(checluster, defaultSingleHostGatewayImage) +} + +func GetGatewayConfigSidecarImage(checluster *chev2.CheCluster) string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return PatchDefaultImageName(checluster, defaultSingleHostGatewayConfigSidecarImage) +} + +func GetGatewayAuthenticationSidecarImage(checluster *chev2.CheCluster) string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return PatchDefaultImageName(checluster, defaultGatewayAuthenticationSidecarImage) +} + +func GetGatewayAuthorizationSidecarImage(checluster *chev2.CheCluster) string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return PatchDefaultImageName(checluster, defaultGatewayAuthorizationSidecarImage) +} + +func GetGatewayHeaderProxySidecarImage(checluster *chev2.CheCluster) string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return PatchDefaultImageName(checluster, defaultGatewayHeaderProxySidecarImage) +} + +func GetCheFlavor() string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return defaultCheFlavor +} + +func IsComponentReadinessInitContainersConfigured() bool { + return os.Getenv("ADD_COMPONENT_READINESS_INIT_CONTAINERS") == "true" +} + +func GetConsoleLinkName() string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return defaultConsoleLinkName +} + +func GetConsoleLinkDisplayName() string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return defaultConsoleLinkDisplayName +} + +func GetConsoleLinkSection() string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return defaultConsoleLinkSection +} + +func GetConsoleLinkImage() string { + if !initialized { + logrus.Fatalf("Operator defaults are not initialized.") + } + + return defaultsConsoleLinkImage +} + +func getDefaultFromEnv(envName string) string { + value := os.Getenv(envName) + + if len(value) == 0 { + logrus.Fatalf("Failed to initialize default value: '%s'. Environment variable with default value was not found.", envName) + } + + return value +} + +func PatchDefaultImageName(checluster *chev2.CheCluster, imageName string) string { + if !checluster.IsAirGapMode() { + return imageName + } + + var hostname, organization string + if checluster.Spec.ContainerRegistry.Hostname != "" { + hostname = checluster.Spec.ContainerRegistry.Hostname + } else { + hostname = getHostnameFromImage(imageName) + } + + if checluster.Spec.ContainerRegistry.Organization != "" { + organization = checluster.Spec.ContainerRegistry.Organization + } else { + organization = getOrganizationFromImage(imageName) + } + + image := getImageNameFromFullImage(imageName) + return fmt.Sprintf("%s/%s/%s", hostname, organization, image) +} + +func getImageNameFromFullImage(image string) string { + imageParts := strings.Split(image, "/") + nameAndTag := "" + switch len(imageParts) { + case 1: + nameAndTag = imageParts[0] + case 2: + nameAndTag = imageParts[1] + case 3: + nameAndTag = imageParts[2] + } + return nameAndTag +} + +func getHostnameFromImage(image string) string { + imageParts := strings.Split(image, "/") + hostname := "" + switch len(imageParts) { + case 3: + hostname = imageParts[0] + default: + hostname = "docker.io" + } + return hostname +} + +func getOrganizationFromImage(image string) string { + imageParts := strings.Split(image, "/") + organization := "" + switch len(imageParts) { + case 2: + organization = imageParts[0] + case 3: + organization = imageParts[1] + } + return organization +} diff --git a/pkg/common/operator-defaults/defaults_test.go b/pkg/common/operator-defaults/defaults_test.go new file mode 100644 index 000000000..77ea99807 --- /dev/null +++ b/pkg/common/operator-defaults/defaults_test.go @@ -0,0 +1,85 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// +package defaults + +import ( + "fmt" + "testing" + + chev2 "github.com/eclipse-che/che-operator/api/v2" +) + +func TestCorrectImageName(t *testing.T) { + testCases := map[string]string{ + "docker.io/eclipse/che-operator:latest": "che-operator:latest", + "eclipse/che-operator:7.1.0": "che-operator:7.1.0", + "che-operator:7.2.0": "che-operator:7.2.0", + } + for k, v := range testCases { + t.Run(k, func(*testing.T) { + actual := getImageNameFromFullImage(k) + if actual != v { + t.Errorf("Expected %s but was %s", v, actual) + } + }) + } +} + +func TestCorrectAirGapPatchedImage(t *testing.T) { + type testcase struct { + image string + expected string + cr *chev2.CheCluster + } + + var ( + airGapRegistryHostname = "myregistry.org" + airGapRegistryOrganization = "myorg" + expectedAirGapPostgresUpstreamImage = makeAirGapImagePath(airGapRegistryHostname, airGapRegistryOrganization, getImageNameFromFullImage(defaultPostgresImage)) + expectedAirGapPostgresUpstreamImageOnlyOrgChanged = makeAirGapImagePath(getHostnameFromImage(defaultPostgresImage), airGapRegistryOrganization, getImageNameFromFullImage(defaultPostgresImage)) + ) + + upstream := &chev2.CheCluster{} + airGapUpstream := &chev2.CheCluster{ + Spec: chev2.CheClusterSpec{ + ContainerRegistry: chev2.CheClusterContainerRegistry{ + Hostname: airGapRegistryHostname, + Organization: airGapRegistryOrganization, + }, + }, + } + upstreamOnlyOrg := &chev2.CheCluster{ + Spec: chev2.CheClusterSpec{ + ContainerRegistry: chev2.CheClusterContainerRegistry{ + Organization: airGapRegistryOrganization, + }, + }, + } + + testCases := map[string]testcase{ + "default postgres": {image: defaultPostgresImage, expected: defaultPostgresImage, cr: upstream}, + "airgap postgres": {image: defaultPostgresImage, expected: expectedAirGapPostgresUpstreamImage, cr: airGapUpstream}, + "with only the org changed": {image: defaultPostgresImage, expected: expectedAirGapPostgresUpstreamImageOnlyOrgChanged, cr: upstreamOnlyOrg}, + } + for name, tc := range testCases { + t.Run(name, func(*testing.T) { + actual := PatchDefaultImageName(tc.cr, tc.image) + if actual != tc.expected { + t.Errorf("Expected %s but was %s", tc.expected, actual) + } + }) + } +} + +func makeAirGapImagePath(hostname, org, nameAndTag string) string { + return fmt.Sprintf("%s/%s/%s", hostname, org, nameAndTag) +} diff --git a/pkg/signal/signal_handler.go b/pkg/common/signal/signal_handler.go similarity index 96% rename from pkg/signal/signal_handler.go rename to pkg/common/signal/signal_handler.go index 09f68c647..b57e7777f 100644 --- a/pkg/signal/signal_handler.go +++ b/pkg/common/signal/signal_handler.go @@ -19,6 +19,7 @@ import ( "syscall" "time" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" "github.com/sirupsen/logrus" appsv1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/types" @@ -76,10 +77,7 @@ func printSignal(signal os.Signal) { } func GetTerminationGracePeriodSeconds(k8sClient client.Reader, namespace string) int64 { - cheFlavor := os.Getenv("CHE_FLAVOR") - if cheFlavor == "" { - cheFlavor = "che" - } + cheFlavor := defaults.GetCheFlavor() defaultTerminationGracePeriodSeconds := int64(20) deployment := &appsv1.Deployment{} diff --git a/pkg/common/test/utils.go b/pkg/common/test/utils.go new file mode 100644 index 000000000..f37364fa5 --- /dev/null +++ b/pkg/common/test/utils.go @@ -0,0 +1,195 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// +package test + +import ( + "context" + "os" + "testing" + + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + console "github.com/openshift/api/console/v1" + oauthv1 "github.com/openshift/api/oauth/v1" + routev1 "github.com/openshift/api/route/v1" + userv1 "github.com/openshift/api/user/v1" + operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + configv1 "github.com/openshift/api/config/v1" + fakeDiscovery "k8s.io/client-go/discovery/fake" + fakeclientset "k8s.io/client-go/kubernetes/fake" + "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +type TestExpectedResources struct { + MemoryLimit string + MemoryRequest string + CpuRequest string + CpuLimit string +} + +func CompareResources(actualDeployment *appsv1.Deployment, expected TestExpectedResources, t *testing.T) { + container := &actualDeployment.Spec.Template.Spec.Containers[0] + compareQuantity( + "Memory limits", + container.Resources.Limits.Memory(), + expected.MemoryLimit, + t, + ) + + compareQuantity( + "Memory requests", + container.Resources.Requests.Memory(), + expected.MemoryRequest, + t, + ) + + compareQuantity( + "CPU limits", + container.Resources.Limits.Cpu(), + expected.CpuLimit, + t, + ) + + compareQuantity( + "CPU requests", + container.Resources.Requests.Cpu(), + expected.CpuRequest, + t, + ) +} + +func ValidateSecurityContext(actualDeployment *appsv1.Deployment, t *testing.T) { + if actualDeployment.Spec.Template.Spec.Containers[0].SecurityContext.Capabilities.Drop[0] != "ALL" { + t.Error("Deployment doesn't contain 'Capabilities Drop ALL' in a SecurityContext") + } +} + +func compareQuantity(resource string, actualQuantity *resource.Quantity, expected string, t *testing.T) { + expectedQuantity := GetResourceQuantity(expected, expected) + if !actualQuantity.Equal(expectedQuantity) { + t.Errorf("%s: expected %s, actual %s", resource, expectedQuantity.String(), actualQuantity.String()) + } +} + +func ValidateContainData(actualData map[string]string, expectedData map[string]string, t *testing.T) { + for k, v := range expectedData { + actualValue, exists := actualData[k] + if exists { + if actualValue != v { + t.Errorf("Key '%s', actual: '%s', expected: '%s'", k, actualValue, v) + } + } else if v != "" { + t.Errorf("Key '%s' does not exists, expected value: '%s'", k, v) + } + } +} + +func FindVolume(volumes []corev1.Volume, name string) corev1.Volume { + for _, volume := range volumes { + if volume.Name == name { + return volume + } + } + + return corev1.Volume{} +} + +func FindVolumeMount(volumes []corev1.VolumeMount, name string) corev1.VolumeMount { + for _, volumeMount := range volumes { + if volumeMount.Name == name { + return volumeMount + } + } + + return corev1.VolumeMount{} +} + +func IsObjectExists(client client.Client, key types.NamespacedName, blueprint client.Object) bool { + err := client.Get(context.TODO(), key, blueprint) + if err != nil { + return false + } + + return true +} + +func GetResourceQuantity(value string, defaultValue string) resource.Quantity { + if value != "" { + return resource.MustParse(value) + } + return resource.MustParse(defaultValue) +} + +func EnableTestMode() { + os.Setenv("MOCK_API", "1") +} + +func IsTestMode() bool { + testMode := os.Getenv("MOCK_API") + return len(testMode) != 0 +} + +// Initialize DeployContext for tests +func GetDeployContext(cheCluster *chev2.CheCluster, initObjs []runtime.Object) *chetypes.DeployContext { + if cheCluster == nil { + // use a default checluster + cheCluster = &chev2.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eclipse-che", + Namespace: "eclipse-che", + }, + Status: chev2.CheClusterStatus{ + CheURL: "che-host", + }, + } + } + + scheme := scheme.Scheme + chev2.SchemeBuilder.AddToScheme(scheme) + scheme.AddKnownTypes(operatorsv1alpha1.SchemeGroupVersion, &operatorsv1alpha1.Subscription{}) + scheme.AddKnownTypes(crdv1.SchemeGroupVersion, &crdv1.CustomResourceDefinition{}) + scheme.AddKnownTypes(operatorsv1alpha1.SchemeGroupVersion, &operatorsv1alpha1.Subscription{}) + scheme.AddKnownTypes(oauthv1.SchemeGroupVersion, &oauthv1.OAuthClient{}) + scheme.AddKnownTypes(userv1.SchemeGroupVersion, &userv1.UserList{}, &userv1.User{}, &userv1.Identity{}) + scheme.AddKnownTypes(configv1.SchemeGroupVersion, &configv1.OAuth{}, &configv1.Proxy{}, &configv1.Console{}) + scheme.AddKnownTypes(routev1.GroupVersion, &routev1.Route{}) + scheme.AddKnownTypes(corev1.SchemeGroupVersion, &corev1.Secret{}) + scheme.AddKnownTypes(corev1.SchemeGroupVersion, &corev1.Secret{}) + scheme.AddKnownTypes(console.SchemeGroupVersion, &console.ConsoleLink{}) + + initObjs = append(initObjs, cheCluster) + cli := fake.NewFakeClientWithScheme(scheme, initObjs...) + clientSet := fakeclientset.NewSimpleClientset() + fakeDiscovery, _ := clientSet.Discovery().(*fakeDiscovery.FakeDiscovery) + + return &chetypes.DeployContext{ + CheCluster: cheCluster, + ClusterAPI: chetypes.ClusterAPI{ + Client: cli, + NonCachingClient: cli, + Scheme: scheme, + DiscoveryClient: fakeDiscovery, + }, + Proxy: &chetypes.Proxy{}, + CheHost: "che-host", + } +} diff --git a/pkg/util/process.go b/pkg/common/utils/process.go similarity index 98% rename from pkg/util/process.go rename to pkg/common/utils/process.go index 96645ba11..067a6e4d2 100644 --- a/pkg/util/process.go +++ b/pkg/common/utils/process.go @@ -10,7 +10,7 @@ // Red Hat, Inc. - initial API and implementation // -package util +package utils import ( "bytes" diff --git a/pkg/common/utils/utils.go b/pkg/common/utils/utils.go new file mode 100644 index 000000000..2279116f7 --- /dev/null +++ b/pkg/common/utils/utils.go @@ -0,0 +1,261 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// +package utils + +import ( + "crypto/sha256" + "encoding/base64" + "io/ioutil" + "math/rand" + "os" + "regexp" + "runtime" + "strings" + "time" + + "k8s.io/apimachinery/pkg/labels" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/discovery" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/yaml" +) + +func Contains(list []string, s string) bool { + for _, v := range list { + if v == s { + return true + } + } + return false +} + +func Remove(list []string, s string) []string { + for i, v := range list { + if v == s { + list = append(list[:i], list[i+1:]...) + } + } + return list +} + +func GeneratePassword(stringLength int) (passwd string) { + rand.Seed(time.Now().UnixNano()) + chars := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "abcdefghijklmnopqrstuvwxyz" + + "0123456789") + length := stringLength + buf := make([]rune, length) + for i := range buf { + buf[i] = chars[rand.Intn(len(chars))] + } + passwd = string(buf) + return passwd +} + +func IsK8SResourceServed(discoveryClient discovery.DiscoveryInterface, resourceName string) bool { + _, resourceList, err := discoveryClient.ServerGroupsAndResources() + if err != nil { + return false + } + + return hasAPIResourceNameInList(resourceName, resourceList) +} + +func hasAPIResourceNameInList(name string, resources []*metav1.APIResourceList) bool { + for _, l := range resources { + for _, r := range l.APIResources { + if r.Name == name { + return true + } + } + } + + return false +} + +func GetValue(value string, defaultValue string) string { + if value == "" { + value = defaultValue + } + return value +} + +func GetEnv(envs []corev1.EnvVar, name string) string { + for _, env := range envs { + if env.Name == name { + return env.Value + } + } + + return "" +} + +func GetEnvByRegExp(regExp string) []corev1.EnvVar { + var env []corev1.EnvVar + for _, e := range os.Environ() { + pair := strings.SplitN(e, "=", 2) + envName := pair[0] + rxp := regexp.MustCompile(regExp) + if rxp.MatchString(envName) { + envName = GetArchitectureDependentEnv(envName) + env = append(env, corev1.EnvVar{Name: envName, Value: pair[1]}) + } + } + return env +} + +// GetArchitectureDependentEnv returns environment variable dependending on architecture +// by adding "_" suffix. If variable is not set then the default will be return. +func GetArchitectureDependentEnv(env string) string { + archEnv := env + "_" + runtime.GOARCH + if _, ok := os.LookupEnv(archEnv); ok { + return archEnv + } + + return env +} + +// GetImageNameAndTag returns the image repository and tag name from the provided image +// Referenced from https://github.com/che-incubator/chectl/blob/main/src/util.ts +func GetImageNameAndTag(image string) (string, string) { + var imageName, imageTag string + if strings.Contains(image, "@") { + // Image is referenced via a digest + index := strings.Index(image, "@") + imageName = image[:index] + imageTag = image[index+1:] + } else { + // Image is referenced via a tag + lastColonIndex := strings.LastIndex(image, ":") + if lastColonIndex == -1 { + imageName = image + imageTag = "latest" + } else { + beforeLastColon := image[:lastColonIndex] + afterLastColon := image[lastColonIndex+1:] + if strings.Contains(afterLastColon, "/") { + // The colon is for registry port and not for a tag + imageName = image + imageTag = "latest" + } else { + // The colon separates image name from the tag + imageName = beforeLastColon + imageTag = afterLastColon + } + } + } + return imageName, imageTag +} + +func ReadObjectInto(yamlFile string, obj interface{}) error { + data, err := ioutil.ReadFile(yamlFile) + if err != nil { + return err + } + + err = yaml.Unmarshal(data, obj) + if err != nil { + return err + } + + return nil +} + +func ComputeHash256(data []byte) string { + hasher := sha256.New() + hasher.Write(data) + return base64.URLEncoding.EncodeToString(hasher.Sum(nil)) +} + +func GetPullPolicyFromDockerImage(dockerImage string) string { + tag := "" + parts := strings.Split(dockerImage, ":") + if len(parts) > 1 { + tag = parts[1] + } + if tag == "latest" || tag == "nightly" || tag == "next" { + return "Always" + } + return "IfNotPresent" +} + +func GetMap(value map[string]string, defaultValue map[string]string) map[string]string { + ret := value + if len(value) < 1 { + ret = defaultValue + } + + return ret +} + +func InNamespaceEventFilter(namespace string) predicate.Predicate { + return predicate.Funcs{ + CreateFunc: func(ce event.CreateEvent) bool { + return namespace == ce.Object.GetNamespace() + }, + DeleteFunc: func(de event.DeleteEvent) bool { + return namespace == de.Object.GetNamespace() + }, + UpdateFunc: func(ue event.UpdateEvent) bool { + return namespace == ue.ObjectOld.GetNamespace() + }, + GenericFunc: func(ge event.GenericEvent) bool { + return namespace == ge.Object.GetNamespace() + }, + } +} + +func ParseMap(src string) map[string]string { + if src == "" { + return nil + } + + m := map[string]string{} + for _, item := range strings.Split(src, ",") { + keyValuePair := strings.Split(item, "=") + if len(keyValuePair) == 1 { + continue + } + + key := strings.TrimSpace(keyValuePair[0]) + value := strings.TrimSpace(keyValuePair[1]) + if key != "" && value != "" { + m[keyValuePair[0]] = keyValuePair[1] + } + } + + return m +} + +func CloneMap(m map[string]string) map[string]string { + if m == nil { + return nil + } + + result := make(map[string]string) + for k, v := range m { + result[k] = v + } + return result +} + +// Converts label map into plain string +func FormatLabels(m map[string]string) string { + if len(m) == 0 { + return "" + } + + return labels.FormatLabels(m) +} diff --git a/pkg/util/util_test.go b/pkg/common/utils/utils_test.go similarity index 59% rename from pkg/util/util_test.go rename to pkg/common/utils/utils_test.go index d7e5243d1..3ff589b35 100644 --- a/pkg/util/util_test.go +++ b/pkg/common/utils/utils_test.go @@ -9,25 +9,23 @@ // Contributors: // Red Hat, Inc. - initial API and implementation // -package util +package utils import ( "reflect" "testing" - - orgv1 "github.com/eclipse-che/che-operator/api/v1" ) func TestGeneratePasswd(t *testing.T) { chars := 12 - passwd := GeneratePasswd(chars) + passwd := GeneratePassword(chars) expectedCharsNumber := 12 if !reflect.DeepEqual(len(passwd), expectedCharsNumber) { t.Errorf("Test failed. Expected %v chars, got %v chars", expectedCharsNumber, len(passwd)) } - passwd1 := GeneratePasswd(12) + passwd1 := GeneratePassword(12) if reflect.DeepEqual(passwd, passwd1) { t.Errorf("Test failed. Passwords are identical, %s: %s", passwd, passwd1) } @@ -65,48 +63,3 @@ func TestGetImageNameAndTag(t *testing.T) { } } } - -func TestIsCheMultiUser(t *testing.T) { - var tests = []struct { - checluster *orgv1.CheCluster - expectedIsCheMultiUser bool - }{ - {&orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{}, - }, - }, true}, - {&orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - CustomCheProperties: map[string]string{}, - }, - }, - }, true}, - {&orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - CustomCheProperties: map[string]string{ - "CHE_MULTIUSER": "true", - }, - }, - }, - }, true}, - {&orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - CustomCheProperties: map[string]string{ - "CHE_MULTIUSER": "false", - }, - }, - }, - }, false}, - } - - for _, test := range tests { - actualIsCheMultiUser := IsCheMultiUser(test.checluster) - if actualIsCheMultiUser != test.expectedIsCheMultiUser { - t.Errorf("Test Failed. Expected '%t', but got '%t'", test.expectedIsCheMultiUser, actualIsCheMultiUser) - } - } -} diff --git a/pkg/deploy/checluster.go b/pkg/deploy/checluster.go index 6c48918e1..c66c8f772 100644 --- a/pkg/deploy/checluster.go +++ b/pkg/deploy/checluster.go @@ -14,12 +14,13 @@ package deploy import ( "context" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/types" ) // UpdateCheCRSpec - updates Che CR "spec" by field -func UpdateCheCRSpec(deployContext *DeployContext, field string, value string) error { +func UpdateCheCRSpec(deployContext *chetypes.DeployContext, field string, value string) error { err := deployContext.ClusterAPI.Client.Update(context.TODO(), deployContext.CheCluster) if err == nil { logrus.Infof("Custom resource spec %s updated with %s: %s", deployContext.CheCluster.Name, field, value) @@ -28,7 +29,7 @@ func UpdateCheCRSpec(deployContext *DeployContext, field string, value string) e return err } -func UpdateCheCRStatus(deployContext *DeployContext, field string, value string) (err error) { +func UpdateCheCRStatus(deployContext *chetypes.DeployContext, field string, value string) (err error) { err = deployContext.ClusterAPI.Client.Status().Update(context.TODO(), deployContext.CheCluster) if err == nil { logrus.Infof("Custom resource status %s updated with %s: %s", deployContext.CheCluster.Name, field, value) @@ -38,7 +39,7 @@ func UpdateCheCRStatus(deployContext *DeployContext, field string, value string) return err } -func SetStatusDetails(deployContext *DeployContext, reason string, message string, helpLink string) (err error) { +func SetStatusDetails(deployContext *chetypes.DeployContext, reason string, message string) (err error) { if reason != deployContext.CheCluster.Status.Reason { deployContext.CheCluster.Status.Reason = reason if err := UpdateCheCRStatus(deployContext, "status: Reason", reason); err != nil { @@ -51,16 +52,10 @@ func SetStatusDetails(deployContext *DeployContext, reason string, message strin return err } } - if helpLink != deployContext.CheCluster.Status.HelpLink { - deployContext.CheCluster.Status.HelpLink = helpLink - if err := UpdateCheCRStatus(deployContext, "status: HelpLink", message); err != nil { - return err - } - } return nil } -func ReloadCheClusterCR(deployContext *DeployContext) error { +func ReloadCheClusterCR(deployContext *chetypes.DeployContext) error { return deployContext.ClusterAPI.Client.Get( context.TODO(), types.NamespacedName{Name: deployContext.CheCluster.Name, Namespace: deployContext.CheCluster.Namespace}, diff --git a/pkg/deploy/checluster_test.go b/pkg/deploy/checluster_test.go index ada9ee8e3..1f48b67f9 100644 --- a/pkg/deploy/checluster_test.go +++ b/pkg/deploy/checluster_test.go @@ -12,7 +12,8 @@ package deploy import ( - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -21,7 +22,7 @@ import ( ) func TestReload(t *testing.T) { - cheCluster := &orgv1.CheCluster{ + cheCluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", @@ -29,10 +30,10 @@ func TestReload(t *testing.T) { }, } - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme, cheCluster) - cheCluster = &orgv1.CheCluster{ + cheCluster = &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", @@ -40,9 +41,9 @@ func TestReload(t *testing.T) { }, } - deployContext := &DeployContext{ + deployContext := &chetypes.DeployContext{ CheCluster: cheCluster, - ClusterAPI: ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, diff --git a/pkg/util/checluster_util.go b/pkg/deploy/checluster_util.go similarity index 87% rename from pkg/util/checluster_util.go rename to pkg/deploy/checluster_util.go index aa4de7c08..35e3fe8a6 100644 --- a/pkg/util/checluster_util.go +++ b/pkg/deploy/checluster_util.go @@ -9,21 +9,21 @@ // Contributors: // Red Hat, Inc. - initial API and implementation // -package util +package deploy import ( "context" "fmt" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" ) // Finds checluster custom resource in a given namespace. // If namespace is empty then checluster will be found in any namespace. -func FindCheClusterCRInNamespace(cl client.Client, namespace string) (*orgv1.CheCluster, int, error) { - cheClusters := &orgv1.CheClusterList{} +func FindCheClusterCRInNamespace(cl client.Client, namespace string) (*chev2.CheCluster, int, error) { + cheClusters := &chev2.CheClusterList{} listOptions := &client.ListOptions{Namespace: namespace} if err := cl.List(context.TODO(), cheClusters, listOptions); err != nil { return nil, -1, err @@ -33,7 +33,7 @@ func FindCheClusterCRInNamespace(cl client.Client, namespace string) (*orgv1.Che return nil, len(cheClusters.Items), fmt.Errorf("Expected one instance of CheCluster custom resources, but '%d' found.", len(cheClusters.Items)) } - checluster := &orgv1.CheCluster{} + checluster := &chev2.CheCluster{} namespacedName := types.NamespacedName{Namespace: cheClusters.Items[0].GetNamespace(), Name: cheClusters.Items[0].GetName()} err := cl.Get(context.TODO(), namespacedName, checluster) if err != nil { diff --git a/pkg/util/checluster_util_test.go b/pkg/deploy/checluster_util_test.go similarity index 79% rename from pkg/util/checluster_util_test.go rename to pkg/deploy/checluster_util_test.go index 1f615d8c4..125503c12 100644 --- a/pkg/util/checluster_util_test.go +++ b/pkg/deploy/checluster_util_test.go @@ -9,12 +9,12 @@ // Contributors: // Red Hat, Inc. - initial API and implementation // -package util +package deploy import ( "testing" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -36,7 +36,7 @@ func TestFindCheCRinNamespace(t *testing.T) { { name: "CR in 'eclipse-che' namespace", initObjects: []runtime.Object{ - &orgv1.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "eclipse-che"}}, + &chev2.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "eclipse-che"}}, }, watchNamespace: "eclipse-che", expectedNumber: 1, @@ -46,7 +46,7 @@ func TestFindCheCRinNamespace(t *testing.T) { { name: "CR in 'default' namespace", initObjects: []runtime.Object{ - &orgv1.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "default"}}, + &chev2.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "default"}}, }, watchNamespace: "eclipse-che", expectedNumber: 0, @@ -55,8 +55,8 @@ func TestFindCheCRinNamespace(t *testing.T) { { name: "several CR in 'eclipse-che' namespace", initObjects: []runtime.Object{ - &orgv1.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "eclipse-che"}}, - &orgv1.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "test-eclipse-che", Namespace: "eclipse-che"}}, + &chev2.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "eclipse-che"}}, + &chev2.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "test-eclipse-che", Namespace: "eclipse-che"}}, }, watchNamespace: "eclipse-che", expectedNumber: 2, @@ -65,8 +65,8 @@ func TestFindCheCRinNamespace(t *testing.T) { { name: "several CR in different namespaces", initObjects: []runtime.Object{ - &orgv1.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "eclipse-che"}}, - &orgv1.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "default"}}, + &chev2.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "eclipse-che"}}, + &chev2.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "default"}}, }, watchNamespace: "eclipse-che", expectedNumber: 1, @@ -76,7 +76,7 @@ func TestFindCheCRinNamespace(t *testing.T) { { name: "CR in 'eclipse-che' namespace, all-namespace mode", initObjects: []runtime.Object{ - &orgv1.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "eclipse-che"}}, + &chev2.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "eclipse-che"}}, }, watchNamespace: "", expectedNumber: 1, @@ -86,7 +86,7 @@ func TestFindCheCRinNamespace(t *testing.T) { { name: "CR in 'default' namespace, all-namespace mode", initObjects: []runtime.Object{ - &orgv1.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "default"}}, + &chev2.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "default"}}, }, watchNamespace: "", expectedNumber: 1, @@ -96,8 +96,8 @@ func TestFindCheCRinNamespace(t *testing.T) { { name: "several CR in 'eclipse-che' namespace, all-namespace mode", initObjects: []runtime.Object{ - &orgv1.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "eclipse-che"}}, - &orgv1.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "test-eclipse-che", Namespace: "eclipse-che"}}, + &chev2.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "eclipse-che"}}, + &chev2.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "test-eclipse-che", Namespace: "eclipse-che"}}, }, watchNamespace: "", expectedNumber: 2, @@ -106,8 +106,8 @@ func TestFindCheCRinNamespace(t *testing.T) { { name: "several CR in different namespaces, all-namespace mode", initObjects: []runtime.Object{ - &orgv1.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "eclipse-che"}}, - &orgv1.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "default"}}, + &chev2.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "eclipse-che"}}, + &chev2.CheCluster{ObjectMeta: metav1.ObjectMeta{Name: "eclipse-che", Namespace: "default"}}, }, watchNamespace: "", expectedNumber: 2, @@ -118,7 +118,7 @@ func TestFindCheCRinNamespace(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { scheme := scheme.Scheme - orgv1.SchemeBuilder.AddToScheme(scheme) + chev2.SchemeBuilder.AddToScheme(scheme) cli := fake.NewFakeClientWithScheme(scheme, testCase.initObjects...) checluster, num, err := FindCheClusterCRInNamespace(cli, testCase.watchNamespace) diff --git a/pkg/deploy/clusterrole.go b/pkg/deploy/clusterrole.go index 1cc5ee94a..e369a3c12 100644 --- a/pkg/deploy/clusterrole.go +++ b/pkg/deploy/clusterrole.go @@ -12,6 +12,9 @@ package deploy import ( + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" rbac "k8s.io/api/rbac/v1" @@ -24,7 +27,7 @@ var crDiffOpts = cmp.Options{ } func SyncClusterRoleToCluster( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, policyRule []rbac.PolicyRule) (bool, error) { @@ -32,8 +35,8 @@ func SyncClusterRoleToCluster( return Sync(deployContext, crSpec, crDiffOpts) } -func getClusterRoleSpec(deployContext *DeployContext, name string, policyRule []rbac.PolicyRule) *rbac.ClusterRole { - labels := GetLabels(deployContext.CheCluster, DefaultCheFlavor(deployContext.CheCluster)) +func getClusterRoleSpec(deployContext *chetypes.DeployContext, name string, policyRule []rbac.PolicyRule) *rbac.ClusterRole { + labels := GetLabels(defaults.GetCheFlavor()) clusterRole := &rbac.ClusterRole{ TypeMeta: metav1.TypeMeta{ Kind: "ClusterRole", @@ -43,7 +46,7 @@ func getClusterRoleSpec(deployContext *DeployContext, name string, policyRule [] Name: name, Labels: labels, Annotations: map[string]string{ - CheEclipseOrgNamespace: deployContext.CheCluster.Namespace, + constants.CheEclipseOrgNamespace: deployContext.CheCluster.Namespace, }, }, Rules: policyRule, diff --git a/pkg/deploy/clusterrole_test.go b/pkg/deploy/clusterrole_test.go index 5e0060130..5ef3034dd 100644 --- a/pkg/deploy/clusterrole_test.go +++ b/pkg/deploy/clusterrole_test.go @@ -14,7 +14,8 @@ package deploy import ( "context" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -25,17 +26,17 @@ import ( ) func TestSyncClusterRole(t *testing.T) { - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) rbacv1.SchemeBuilder.AddToScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme) - deployContext := &DeployContext{ - CheCluster: &orgv1.CheCluster{ + deployContext := &chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, }, - ClusterAPI: ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, diff --git a/pkg/deploy/clusterrolebinding.go b/pkg/deploy/clusterrolebinding.go index 16bd072dc..0328f4490 100644 --- a/pkg/deploy/clusterrolebinding.go +++ b/pkg/deploy/clusterrolebinding.go @@ -14,6 +14,9 @@ package deploy import ( "strings" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" rbac "k8s.io/api/rbac/v1" @@ -26,7 +29,7 @@ var crbDiffOpts = cmp.Options{ } func SyncClusterRoleBindingToCluster( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, serviceAccountName string, clusterRoleName string) (bool, error) { @@ -36,7 +39,7 @@ func SyncClusterRoleBindingToCluster( } func SyncClusterRoleBindingAndAddFinalizerToCluster( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, serviceAccountName string, clusterRoleName string) (bool, error) { @@ -46,7 +49,7 @@ func SyncClusterRoleBindingAndAddFinalizerToCluster( return SyncAndAddFinalizer(deployContext, crbSpec, crbDiffOpts, finalizer) } -func ReconcileClusterRoleBindingFinalizer(deployContext *DeployContext, name string) error { +func ReconcileClusterRoleBindingFinalizer(deployContext *chetypes.DeployContext, name string) error { if deployContext.CheCluster.DeletionTimestamp.IsZero() { return nil } @@ -55,11 +58,11 @@ func ReconcileClusterRoleBindingFinalizer(deployContext *DeployContext, name str return DeleteObjectWithFinalizer(deployContext, types.NamespacedName{Name: name}, &rbac.ClusterRoleBinding{}, finalizer) } -func GetLegacyUniqueClusterRoleBindingName(deployContext *DeployContext, serviceAccount string, clusterRole string) string { +func GetLegacyUniqueClusterRoleBindingName(deployContext *chetypes.DeployContext, serviceAccount string, clusterRole string) string { return deployContext.CheCluster.Namespace + "-" + serviceAccount + "-" + clusterRole } -func ReconcileLegacyClusterRoleBindingFinalizer(deployContext *DeployContext, name string) error { +func ReconcileLegacyClusterRoleBindingFinalizer(deployContext *chetypes.DeployContext, name string) error { if deployContext.CheCluster.DeletionTimestamp.IsZero() { return nil } @@ -69,12 +72,12 @@ func ReconcileLegacyClusterRoleBindingFinalizer(deployContext *DeployContext, na } func getClusterRoleBindingSpec( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, serviceAccountName string, roleName string) *rbac.ClusterRoleBinding { - labels := GetLabels(deployContext.CheCluster, DefaultCheFlavor(deployContext.CheCluster)) + labels := GetLabels(defaults.GetCheFlavor()) clusterRoleBinding := &rbac.ClusterRoleBinding{ TypeMeta: metav1.TypeMeta{ Kind: "ClusterRoleBinding", @@ -84,7 +87,7 @@ func getClusterRoleBindingSpec( Name: name, Labels: labels, Annotations: map[string]string{ - CheEclipseOrgNamespace: deployContext.CheCluster.Namespace, + constants.CheEclipseOrgNamespace: deployContext.CheCluster.Namespace, }, }, Subjects: []rbac.Subject{ diff --git a/pkg/deploy/clusterrolebinding_test.go b/pkg/deploy/clusterrolebinding_test.go index 87fa01ee9..c7b6b5ce4 100644 --- a/pkg/deploy/clusterrolebinding_test.go +++ b/pkg/deploy/clusterrolebinding_test.go @@ -15,8 +15,9 @@ import ( "context" "time" - orgv1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/pkg/util" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/utils" rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -27,16 +28,16 @@ import ( ) func TestSyncClusterRoleBindingToCluster(t *testing.T) { - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) rbacv1.SchemeBuilder.AddToScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme) - deployContext := &DeployContext{ - CheCluster: &orgv1.CheCluster{ + deployContext := &chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, }, - ClusterAPI: ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, @@ -72,17 +73,17 @@ func TestSyncClusterRoleBindingToCluster(t *testing.T) { } func TestSyncClusterRoleBindingAndAddFinalizerToCluster(t *testing.T) { - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) rbacv1.SchemeBuilder.AddToScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme) - deployContext := &DeployContext{ - CheCluster: &orgv1.CheCluster{ + deployContext := &chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, }, - ClusterAPI: ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, @@ -95,7 +96,7 @@ func TestSyncClusterRoleBindingAndAddFinalizerToCluster(t *testing.T) { t.Fatalf("Failed to sync crb: %v", err) } - if !util.ContainsString(deployContext.CheCluster.Finalizers, "test.crb.finalizers.che.eclipse.org") { + if !utils.Contains(deployContext.CheCluster.Finalizers, "test.crb.finalizers.che.eclipse.org") { t.Fatalf("Failed to add finalizer") } @@ -110,7 +111,7 @@ func TestSyncClusterRoleBindingAndAddFinalizerToCluster(t *testing.T) { if err != nil { t.Fatalf("CRD shouldn't be deleted: %v", err) } - if !util.ContainsString(deployContext.CheCluster.Finalizers, "test.crb.finalizers.che.eclipse.org") { + if !utils.Contains(deployContext.CheCluster.Finalizers, "test.crb.finalizers.che.eclipse.org") { t.Fatalf("Finalizer shouldn't be deleted") } @@ -126,7 +127,7 @@ func TestSyncClusterRoleBindingAndAddFinalizerToCluster(t *testing.T) { if err == nil { t.Fatalf("Failed to remove crb: %v", err) } - if util.ContainsString(deployContext.CheCluster.Finalizers, "test.crb.finalizers.che.eclipse.org") { + if utils.Contains(deployContext.CheCluster.Finalizers, "test.crb.finalizers.che.eclipse.org") { t.Fatalf("Failed to remove finalizer") } } diff --git a/pkg/deploy/configmap.go b/pkg/deploy/configmap.go index 8baa844e7..3674e768e 100644 --- a/pkg/deploy/configmap.go +++ b/pkg/deploy/configmap.go @@ -14,6 +14,7 @@ package deploy import ( "reflect" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" corev1 "k8s.io/api/core/v1" @@ -28,7 +29,7 @@ var ConfigMapDiffOpts = cmp.Options{ } func SyncConfigMapDataToCluster( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, data map[string]string, component string) (bool, error) { @@ -38,14 +39,14 @@ func SyncConfigMapDataToCluster( } func SyncConfigMapSpecToCluster( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, configMapSpec *corev1.ConfigMap) (bool, error) { return Sync(deployContext, configMapSpec, ConfigMapDiffOpts) } func GetConfigMapSpec( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, data map[string]string, component string) *corev1.ConfigMap { @@ -58,7 +59,7 @@ func GetConfigMapSpec( ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: deployContext.CheCluster.Namespace, - Labels: GetLabels(deployContext.CheCluster, component), + Labels: GetLabels(component), Annotations: map[string]string{}, }, Data: data, diff --git a/pkg/deploy/configmap_test.go b/pkg/deploy/configmap_test.go index e65733417..2f4a1b912 100644 --- a/pkg/deploy/configmap_test.go +++ b/pkg/deploy/configmap_test.go @@ -16,7 +16,8 @@ import ( "testing" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -25,17 +26,17 @@ import ( ) func TestSyncConfigMapDataToCluster(t *testing.T) { - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) corev1.SchemeBuilder.AddToScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme) - deployContext := &DeployContext{ - CheCluster: &orgv1.CheCluster{ + deployContext := &chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, }, - ClusterAPI: ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, @@ -75,17 +76,17 @@ func TestSyncConfigMapDataToCluster(t *testing.T) { } func TestSyncConfigMapSpecDataToCluster(t *testing.T) { - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) corev1.SchemeBuilder.AddToScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme) - deployContext := &DeployContext{ - CheCluster: &orgv1.CheCluster{ + deployContext := &chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, }, - ClusterAPI: ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, diff --git a/pkg/deploy/consolelink/consolelink.go b/pkg/deploy/consolelink/consolelink.go index c33e3ed2d..cc6d859ce 100644 --- a/pkg/deploy/consolelink/consolelink.go +++ b/pkg/deploy/consolelink/consolelink.go @@ -15,8 +15,12 @@ import ( "fmt" "strings" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" consolev1 "github.com/openshift/api/console/v1" "github.com/sirupsen/logrus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -37,10 +41,9 @@ func NewConsoleLinkReconciler() *ConsoleLinkReconciler { return &ConsoleLinkReconciler{} } -func (c *ConsoleLinkReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { - if !util.IsOpenShift4 || !util.HasK8SResourceObject(ctx.ClusterAPI.DiscoveryClient, ConsoleLinksResourceName) { - // console link is supported only on OpenShift >= 4.2 - logrus.Debug("Console link won't be created. Consolelinks is not supported by OpenShift cluster.") +func (c *ConsoleLinkReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { + if !infrastructure.IsOpenShift() || !utils.IsK8SResourceServed(ctx.ClusterAPI.DiscoveryClient, ConsoleLinksResourceName) { + logrus.Debug("Console link won't be created. Consolelinks is not supported by kubernetes cluster.") return reconcile.Result{}, true, nil } @@ -52,15 +55,15 @@ func (c *ConsoleLinkReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile. return reconcile.Result{}, true, nil } -func (c *ConsoleLinkReconciler) Finalize(ctx *deploy.DeployContext) bool { - if err := deploy.DeleteObjectWithFinalizer(ctx, client.ObjectKey{Name: deploy.DefaultConsoleLinkName()}, &consolev1.ConsoleLink{}, ConsoleLinkFinalizerName); err != nil { +func (c *ConsoleLinkReconciler) Finalize(ctx *chetypes.DeployContext) bool { + if err := deploy.DeleteObjectWithFinalizer(ctx, client.ObjectKey{Name: defaults.GetConsoleLinkName()}, &consolev1.ConsoleLink{}, ConsoleLinkFinalizerName); err != nil { logrus.Errorf("Error deleting finalizer: %v", err) return false } return true } -func (c *ConsoleLinkReconciler) createConsoleLink(ctx *deploy.DeployContext) (bool, error) { +func (c *ConsoleLinkReconciler) createConsoleLink(ctx *chetypes.DeployContext) (bool, error) { consoleLinkSpec := c.getConsoleLinkSpec(ctx) _, err := deploy.CreateIfNotExists(ctx, consoleLinkSpec) if err != nil { @@ -68,13 +71,13 @@ func (c *ConsoleLinkReconciler) createConsoleLink(ctx *deploy.DeployContext) (bo } consoleLink := &consolev1.ConsoleLink{} - exists, err := deploy.Get(ctx, client.ObjectKey{Name: deploy.DefaultConsoleLinkName()}, consoleLink) + exists, err := deploy.Get(ctx, client.ObjectKey{Name: defaults.GetConsoleLinkName()}, consoleLink) if !exists || err != nil { return false, err } // consolelink is for this specific instance of Eclipse Che - if strings.Index(consoleLink.Spec.Link.Href, ctx.CheCluster.GetCheHost()) != -1 { + if strings.Index(consoleLink.Spec.Link.Href, ctx.CheHost) != -1 { err = deploy.AppendFinalizer(ctx, ConsoleLinkFinalizerName) return err == nil, err } @@ -82,26 +85,26 @@ func (c *ConsoleLinkReconciler) createConsoleLink(ctx *deploy.DeployContext) (bo return true, nil } -func (c *ConsoleLinkReconciler) getConsoleLinkSpec(ctx *deploy.DeployContext) *consolev1.ConsoleLink { +func (c *ConsoleLinkReconciler) getConsoleLinkSpec(ctx *chetypes.DeployContext) *consolev1.ConsoleLink { consoleLink := &consolev1.ConsoleLink{ TypeMeta: metav1.TypeMeta{ Kind: "ConsoleLink", APIVersion: consolev1.SchemeGroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ - Name: deploy.DefaultConsoleLinkName(), + Name: defaults.GetConsoleLinkName(), Annotations: map[string]string{ - deploy.CheEclipseOrgNamespace: ctx.CheCluster.Namespace, + constants.CheEclipseOrgNamespace: ctx.CheCluster.Namespace, }, }, Spec: consolev1.ConsoleLinkSpec{ Link: consolev1.Link{ - Href: ctx.CheCluster.Status.CheURL, - Text: deploy.DefaultConsoleLinkDisplayName()}, + Href: "https://" + ctx.CheHost, + Text: defaults.GetConsoleLinkDisplayName()}, Location: consolev1.ApplicationMenu, ApplicationMenu: &consolev1.ApplicationMenuSpec{ - Section: deploy.DefaultConsoleLinkSection(), - ImageURL: fmt.Sprintf("https://%s%s", ctx.CheCluster.GetCheHost(), deploy.DefaultConsoleLinkImage()), + Section: defaults.GetConsoleLinkSection(), + ImageURL: fmt.Sprintf("https://%s%s", ctx.CheHost, defaults.GetConsoleLinkImage()), }, }, } diff --git a/pkg/deploy/consolelink/consolelink_test.go b/pkg/deploy/consolelink/consolelink_test.go index 7a90d1f34..8968b89e1 100644 --- a/pkg/deploy/consolelink/consolelink_test.go +++ b/pkg/deploy/consolelink/consolelink_test.go @@ -12,9 +12,11 @@ package consolelink import ( - orgv1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + chev2 "github.com/eclipse-che/che-operator/api/v2" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" + "github.com/eclipse-che/che-operator/pkg/common/utils" console "github.com/openshift/api/console/v1" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -26,18 +28,19 @@ import ( ) func TestReconcileConsoleLink(t *testing.T) { - cheCluster := &orgv1.CheCluster{ + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + + cheCluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, - Status: orgv1.CheClusterStatus{ + Status: chev2.CheClusterStatus{ CheURL: "https://che-host", }, } - util.IsOpenShift4 = true - ctx := deploy.GetTestDeployContext(cheCluster, []runtime.Object{}) + ctx := test.GetDeployContext(cheCluster, []runtime.Object{}) ctx.ClusterAPI.DiscoveryClient.(*fakeDiscovery.FakeDiscovery).Fake.Resources = []*metav1.APIResourceList{ { APIResources: []metav1.APIResource{ @@ -51,13 +54,13 @@ func TestReconcileConsoleLink(t *testing.T) { assert.True(t, done) assert.Nil(t, err) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: deploy.DefaultConsoleLinkName()}, &console.ConsoleLink{})) - assert.True(t, util.ContainsString(ctx.CheCluster.Finalizers, ConsoleLinkFinalizerName)) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: defaults.GetConsoleLinkName()}, &console.ConsoleLink{})) + assert.True(t, utils.Contains(ctx.CheCluster.Finalizers, ConsoleLinkFinalizerName)) // Initialize DeletionTimestamp => checluster is being deleted done = consolelink.Finalize(ctx) assert.True(t, done) - assert.False(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: deploy.DefaultConsoleLinkName()}, &console.ConsoleLink{})) - assert.False(t, util.ContainsString(ctx.CheCluster.Finalizers, ConsoleLinkFinalizerName)) + assert.False(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: defaults.GetConsoleLinkName()}, &console.ConsoleLink{})) + assert.False(t, utils.Contains(ctx.CheCluster.Finalizers, ConsoleLinkFinalizerName)) } diff --git a/pkg/deploy/consolelink/init_test.go b/pkg/deploy/consolelink/init_test.go index 8390ba322..918a1f0de 100644 --- a/pkg/deploy/consolelink/init_test.go +++ b/pkg/deploy/consolelink/init_test.go @@ -11,11 +11,15 @@ // package consolelink -import "github.com/eclipse-che/che-operator/pkg/deploy" +import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" +) func init() { - err := deploy.InitTestDefaultsFromDeployment("../../../config/manager/manager.yaml") - if err != nil { - panic(err) - } + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../../../config/manager/manager.yaml") } diff --git a/pkg/deploy/dashboard/dashboard.go b/pkg/deploy/dashboard/dashboard.go index d1b3c1f03..b19340ee3 100644 --- a/pkg/deploy/dashboard/dashboard.go +++ b/pkg/deploy/dashboard/dashboard.go @@ -18,10 +18,12 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" "github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy/expose" "github.com/eclipse-che/che-operator/pkg/deploy/gateway" - "github.com/eclipse-che/che-operator/pkg/util" "github.com/sirupsen/logrus" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/types" @@ -43,11 +45,11 @@ func NewDashboardReconciler() *DashboardReconciler { return &DashboardReconciler{} } -func (d *DashboardReconciler) getComponentName(ctx *deploy.DeployContext) string { - return deploy.DefaultCheFlavor(ctx.CheCluster) + "-dashboard" +func (d *DashboardReconciler) getComponentName(ctx *chetypes.DeployContext) string { + return defaults.GetCheFlavor() + "-dashboard" } -func (d *DashboardReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { +func (d *DashboardReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { // Create a new dashboard service done, err := deploy.SyncServiceToCluster(ctx, d.getComponentName(ctx), []string{"http"}, []int32{8080}, d.getComponentName(ctx)) if !done { @@ -55,7 +57,7 @@ func (d *DashboardReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Re } // Expose dashboard service with route or ingress - _, done, err = expose.ExposeWithHostPath(ctx, d.getComponentName(ctx), ctx.CheCluster.GetCheHost(), + _, done, err = expose.ExposeWithHostPath(ctx, d.getComponentName(ctx), ctx.CheHost, exposePath, d.createGatewayConfig(ctx), ) @@ -98,7 +100,7 @@ func (d *DashboardReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Re return reconcile.Result{}, true, nil } -func (d *DashboardReconciler) Finalize(ctx *deploy.DeployContext) bool { +func (d *DashboardReconciler) Finalize(ctx *chetypes.DeployContext) bool { done := true if _, err := deploy.Delete(ctx, types.NamespacedName{Name: d.getClusterRoleName(ctx)}, &rbacv1.ClusterRole{}); err != nil { done = false @@ -117,14 +119,14 @@ func (d *DashboardReconciler) Finalize(ctx *deploy.DeployContext) bool { return done } -func (d *DashboardReconciler) createGatewayConfig(ctx *deploy.DeployContext) *gateway.TraefikConfig { +func (d *DashboardReconciler) createGatewayConfig(ctx *chetypes.DeployContext) *gateway.TraefikConfig { cfg := gateway.CreateCommonTraefikConfig( d.getComponentName(ctx), fmt.Sprintf("Path(`/`, `/f`) || PathPrefix(`%s`)", exposePath), 10, "http://"+d.getComponentName(ctx)+":8080", []string{}) - if util.IsOpenShift { + if infrastructure.IsOpenShift() { cfg.AddAuthHeaderRewrite(d.getComponentName(ctx)) } return cfg diff --git a/pkg/deploy/dashboard/dashboard_deployment_test.go b/pkg/deploy/dashboard/dashboard_deployment_test.go index 702165bc5..0f859c775 100644 --- a/pkg/deploy/dashboard/dashboard_deployment_test.go +++ b/pkg/deploy/dashboard/dashboard_deployment_test.go @@ -15,17 +15,20 @@ package dashboard import ( "os" + "k8s.io/apimachinery/pkg/api/resource" + + "github.com/devfile/devworkspace-operator/pkg/infrastructure" configv1 "github.com/openshift/api/config/v1" "github.com/stretchr/testify/assert" "github.com/google/go-cmp/cmp" corev1 "k8s.io/api/core/v1" - "github.com/eclipse-che/che-operator/pkg/util" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" - "github.com/eclipse-che/che-operator/pkg/deploy" - - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -36,13 +39,13 @@ import ( ) func TestDashboardDeploymentSecurityContext(t *testing.T) { - ctx := deploy.GetTestDeployContext(nil, []runtime.Object{}) + ctx := test.GetDeployContext(nil, []runtime.Object{}) dashboard := NewDashboardReconciler() deployment, err := dashboard.getDashboardDeploymentSpec(ctx) assert.Nil(t, err) - util.ValidateSecurityContext(deployment, t) + test.ValidateSecurityContext(deployment, t) } func TestDashboardDeploymentResources(t *testing.T) { @@ -53,18 +56,18 @@ func TestDashboardDeploymentResources(t *testing.T) { memoryRequest string cpuRequest string cpuLimit string - cheCluster *orgv1.CheCluster + cheCluster *chev2.CheCluster } testCases := []resourcesTestCase{ { name: "Test default limits", initObjects: []runtime.Object{}, - memoryLimit: deploy.DefaultDashboardMemoryLimit, - memoryRequest: deploy.DefaultDashboardMemoryRequest, - cpuLimit: deploy.DefaultDashboardCpuLimit, - cpuRequest: deploy.DefaultDashboardCpuRequest, - cheCluster: &orgv1.CheCluster{ + memoryLimit: constants.DefaultDashboardMemoryLimit, + memoryRequest: constants.DefaultDashboardMemoryRequest, + cpuLimit: constants.DefaultDashboardCpuLimit, + cpuRequest: constants.DefaultDashboardCpuRequest, + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", @@ -78,17 +81,32 @@ func TestDashboardDeploymentResources(t *testing.T) { cpuRequest: "150m", memoryLimit: "250Mi", memoryRequest: "150Mi", - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - DashboardCpuLimit: "250m", - DashboardCpuRequest: "150m", - DashboardMemoryLimit: "250Mi", - DashboardMemoryRequest: "150Mi", + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + Dashboard: chev2.Dashboard{ + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: defaults.GetCheFlavor() + "-dashboard", + Resources: chev2.ResourceRequirements{ + Requests: chev2.ResourceList{ + Memory: resource.MustParse("150Mi"), + Cpu: resource.MustParse("150m"), + }, + Limits: chev2.ResourceList{ + Memory: resource.MustParse("250Mi"), + Cpu: resource.MustParse("250m"), + }, + }, + }, + }, + }, + }, }, }, }, @@ -98,13 +116,13 @@ func TestDashboardDeploymentResources(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true))) - ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{}) + ctx := test.GetDeployContext(testCase.cheCluster, []runtime.Object{}) dashboard := NewDashboardReconciler() deployment, err := dashboard.getDashboardDeploymentSpec(ctx) assert.Nil(t, err) - util.CompareResources(deployment, - util.TestExpectedResources{ + test.CompareResources(deployment, + test.TestExpectedResources{ MemoryLimit: testCase.memoryLimit, MemoryRequest: testCase.memoryRequest, CpuRequest: testCase.cpuRequest, @@ -116,11 +134,13 @@ func TestDashboardDeploymentResources(t *testing.T) { } func TestDashboardDeploymentEnvVars(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + type resourcesTestCase struct { name string initObjects []runtime.Object envVars []corev1.EnvVar - cheCluster *orgv1.CheCluster + cheCluster *chev2.CheCluster } testCases := []resourcesTestCase{ { @@ -151,12 +171,12 @@ func TestDashboardDeploymentEnvVars(t *testing.T) { Name: "OPENSHIFT_CONSOLE_URL", }, }, - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, - Status: orgv1.CheClusterStatus{ + Status: chev2.CheClusterStatus{ CheURL: "https://che-host", }, }, @@ -200,12 +220,12 @@ func TestDashboardDeploymentEnvVars(t *testing.T) { Value: "https://console-openshift-console.apps.my-host/", }, }, - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, - Status: orgv1.CheClusterStatus{ + Status: chev2.CheClusterStatus{ CheURL: "https://che-host", }, }, @@ -215,10 +235,7 @@ func TestDashboardDeploymentEnvVars(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true))) - ctx := deploy.GetTestDeployContext(testCase.cheCluster, testCase.initObjects) - - util.IsOpenShift = true - util.IsOpenShift4 = true + ctx := test.GetDeployContext(testCase.cheCluster, testCase.initObjects) dashboard := NewDashboardReconciler() deployment, err := dashboard.getDashboardDeploymentSpec(ctx) @@ -231,12 +248,14 @@ func TestDashboardDeploymentEnvVars(t *testing.T) { } func TestDashboardDeploymentVolumes(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + type resourcesTestCase struct { name string initObjects []runtime.Object volumes []corev1.Volume volumeMounts []corev1.VolumeMount - cheCluster *orgv1.CheCluster + cheCluster *chev2.CheCluster } testCases := []resourcesTestCase{ { @@ -258,7 +277,7 @@ func TestDashboardDeploymentVolumes(t *testing.T) { volumeMounts: []corev1.VolumeMount{ {Name: "che-custom-ca", MountPath: "/public-certs/custom"}, }, - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", @@ -270,7 +289,7 @@ func TestDashboardDeploymentVolumes(t *testing.T) { initObjects: []runtime.Object{ &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: deploy.CheTLSSelfSignedCertificateSecretName, + Name: constants.DefaultSelfSignedCertificateSecretName, Namespace: "eclipse-che", }, }, @@ -304,7 +323,7 @@ func TestDashboardDeploymentVolumes(t *testing.T) { {Name: "che-custom-ca", MountPath: "/public-certs/custom"}, {Name: "che-self-signed-ca", MountPath: "/public-certs/che-self-signed"}, }, - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", @@ -316,7 +335,7 @@ func TestDashboardDeploymentVolumes(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true))) - ctx := deploy.GetTestDeployContext(testCase.cheCluster, testCase.initObjects) + ctx := test.GetDeployContext(testCase.cheCluster, testCase.initObjects) dashboard := NewDashboardReconciler() deployment, err := dashboard.getDashboardDeploymentSpec(ctx) diff --git a/pkg/deploy/dashboard/dashboard_test.go b/pkg/deploy/dashboard/dashboard_test.go index cf8c40e99..a29d36fc7 100644 --- a/pkg/deploy/dashboard/dashboard_test.go +++ b/pkg/deploy/dashboard/dashboard_test.go @@ -13,8 +13,9 @@ package dashboard import ( - "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/test" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/stretchr/testify/assert" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/runtime" @@ -29,59 +30,59 @@ import ( const Namespace = "eclipse-che" func TestDashboardOpenShift(t *testing.T) { - util.IsOpenShift = true + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) - ctx := deploy.GetTestDeployContext(nil, []runtime.Object{}) + ctx := test.GetDeployContext(nil, []runtime.Object{}) dashboard := NewDashboardReconciler() _, done, err := dashboard.Reconcile(ctx) assert.True(t, done) assert.Nil(t, err) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &corev1.Service{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: DashboardSA, Namespace: "eclipse-che"}, &corev1.ServiceAccount{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleBindingName(ctx)}, &rbacv1.ClusterRoleBinding{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleName(ctx)}, &rbacv1.ClusterRole{})) - assert.True(t, util.ContainsString(ctx.CheCluster.Finalizers, ClusterPermissionsDashboardFinalizer)) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &corev1.Service{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: DashboardSA, Namespace: "eclipse-che"}, &corev1.ServiceAccount{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleBindingName(ctx)}, &rbacv1.ClusterRoleBinding{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleName(ctx)}, &rbacv1.ClusterRole{})) + assert.True(t, utils.Contains(ctx.CheCluster.Finalizers, ClusterPermissionsDashboardFinalizer)) } func TestDashboardKubernetes(t *testing.T) { - util.IsOpenShift = false + infrastructure.InitializeForTesting(infrastructure.Kubernetes) - ctx := deploy.GetTestDeployContext(nil, []runtime.Object{}) + ctx := test.GetDeployContext(nil, []runtime.Object{}) dashboard := NewDashboardReconciler() _, done, err := dashboard.Reconcile(ctx) assert.True(t, done) assert.Nil(t, err) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &corev1.Service{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: DashboardSA, Namespace: "eclipse-che"}, &corev1.ServiceAccount{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleBindingName(ctx)}, &rbacv1.ClusterRoleBinding{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleName(ctx)}, &rbacv1.ClusterRole{})) - assert.True(t, util.ContainsString(ctx.CheCluster.Finalizers, ClusterPermissionsDashboardFinalizer)) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &corev1.Service{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: DashboardSA, Namespace: "eclipse-che"}, &corev1.ServiceAccount{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleBindingName(ctx)}, &rbacv1.ClusterRoleBinding{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleName(ctx)}, &rbacv1.ClusterRole{})) + assert.True(t, utils.Contains(ctx.CheCluster.Finalizers, ClusterPermissionsDashboardFinalizer)) } func TestDashboardClusterRBACFinalizerOnKubernetes(t *testing.T) { - util.IsOpenShift = false + infrastructure.InitializeForTesting(infrastructure.Kubernetes) - ctx := deploy.GetTestDeployContext(nil, []runtime.Object{}) + ctx := test.GetDeployContext(nil, []runtime.Object{}) dashboard := NewDashboardReconciler() _, done, err := dashboard.Reconcile(ctx) assert.True(t, done) assert.Nil(t, err) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleBindingName(ctx)}, &rbacv1.ClusterRoleBinding{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleName(ctx)}, &rbacv1.ClusterRole{})) - assert.True(t, util.ContainsString(ctx.CheCluster.Finalizers, ClusterPermissionsDashboardFinalizer)) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleBindingName(ctx)}, &rbacv1.ClusterRoleBinding{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleName(ctx)}, &rbacv1.ClusterRole{})) + assert.True(t, utils.Contains(ctx.CheCluster.Finalizers, ClusterPermissionsDashboardFinalizer)) done = dashboard.Finalize(ctx) assert.True(t, done) - assert.False(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleBindingName(ctx)}, &rbacv1.ClusterRoleBinding{})) - assert.False(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleName(ctx)}, &rbacv1.ClusterRole{})) - assert.False(t, util.ContainsString(ctx.CheCluster.Finalizers, ClusterPermissionsDashboardFinalizer)) + assert.False(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleBindingName(ctx)}, &rbacv1.ClusterRoleBinding{})) + assert.False(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleName(ctx)}, &rbacv1.ClusterRole{})) + assert.False(t, utils.Contains(ctx.CheCluster.Finalizers, ClusterPermissionsDashboardFinalizer)) } diff --git a/pkg/deploy/dashboard/deployment_dashboard.go b/pkg/deploy/dashboard/deployment_dashboard.go index 47ba17666..43a843cad 100644 --- a/pkg/deploy/dashboard/deployment_dashboard.go +++ b/pkg/deploy/dashboard/deployment_dashboard.go @@ -15,14 +15,19 @@ package dashboard import ( "context" "fmt" - "strconv" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" configv1 "github.com/openshift/api/config/v1" + "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/pointer" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy/tls" - "github.com/eclipse-che/che-operator/pkg/util" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -32,7 +37,7 @@ import ( const CHE_SELF_SIGNED_MOUNT_PATH = "/public-certs/che-self-signed" const CHE_CUSTOM_CERTS_MOUNT_PATH = "/public-certs/custom" -func (d *DashboardReconciler) getDashboardDeploymentSpec(ctx *deploy.DeployContext) (*appsv1.Deployment, error) { +func (d *DashboardReconciler) getDashboardDeploymentSpec(ctx *chetypes.DeployContext) (*appsv1.Deployment, error) { var volumes []corev1.Volume var volumeMounts []corev1.VolumeMount var envVars []corev1.EnvVar @@ -52,10 +57,10 @@ func (d *DashboardReconciler) getDashboardDeploymentSpec(ctx *deploy.DeployConte // CHE_HOST is here for backward compatibility. Replaced with CHE_URL corev1.EnvVar{ Name: "CHE_HOST", - Value: ctx.CheCluster.Status.CheURL}, + Value: "https://" + ctx.CheHost}, corev1.EnvVar{ Name: "CHE_URL", - Value: ctx.CheCluster.Status.CheURL}, + Value: "https://" + ctx.CheHost}, corev1.EnvVar{ Name: "CHECLUSTER_CR_NAMESPACE", Value: ctx.CheCluster.Namespace}, @@ -70,7 +75,7 @@ func (d *DashboardReconciler) getDashboardDeploymentSpec(ctx *deploy.DeployConte Value: fmt.Sprintf("http://%s.%s.svc:8080/api", deploy.CheServiceName, ctx.CheCluster.Namespace)}, ) - if util.IsOpenShift { + if infrastructure.IsOpenShift() { envVars = append(envVars, corev1.EnvVar{ Name: "OPENSHIFT_CONSOLE_URL", @@ -78,10 +83,10 @@ func (d *DashboardReconciler) getDashboardDeploymentSpec(ctx *deploy.DeployConte } terminationGracePeriodSeconds := int64(30) - labels, labelsSelector := deploy.GetLabelsAndSelector(ctx.CheCluster, d.getComponentName(ctx)) + labels, labelsSelector := deploy.GetLabelsAndSelector(d.getComponentName(ctx)) - dashboardImageAndTag := util.GetValue(ctx.CheCluster.Spec.Server.DashboardImage, deploy.DefaultDashboardImage(ctx.CheCluster)) - pullPolicy := corev1.PullPolicy(util.GetValue(ctx.CheCluster.Spec.Server.DashboardImagePullPolicy, deploy.DefaultPullPolicyFromDockerImage(dashboardImageAndTag))) + image := defaults.GetDashboardImage(ctx.CheCluster) + pullPolicy := corev1.PullPolicy(utils.GetPullPolicyFromDockerImage(image)) deployment := &appsv1.Deployment{ TypeMeta: metav1.TypeMeta{ @@ -108,7 +113,7 @@ func (d *DashboardReconciler) getDashboardDeploymentSpec(ctx *deploy.DeployConte { Name: d.getComponentName(ctx), ImagePullPolicy: pullPolicy, - Image: dashboardImageAndTag, + Image: image, Ports: []corev1.ContainerPort{ { Name: "http", @@ -118,20 +123,12 @@ func (d *DashboardReconciler) getDashboardDeploymentSpec(ctx *deploy.DeployConte }, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ - corev1.ResourceMemory: util.GetResourceQuantity( - ctx.CheCluster.Spec.Server.DashboardMemoryRequest, - deploy.DefaultDashboardMemoryRequest), - corev1.ResourceCPU: util.GetResourceQuantity( - ctx.CheCluster.Spec.Server.DashboardCpuRequest, - deploy.DefaultDashboardCpuRequest), + corev1.ResourceMemory: resource.MustParse(constants.DefaultDashboardMemoryRequest), + corev1.ResourceCPU: resource.MustParse(constants.DefaultDashboardCpuRequest), }, Limits: corev1.ResourceList{ - corev1.ResourceMemory: util.GetResourceQuantity( - ctx.CheCluster.Spec.Server.DashboardMemoryLimit, - deploy.DefaultDashboardMemoryLimit), - corev1.ResourceCPU: util.GetResourceQuantity( - ctx.CheCluster.Spec.Server.DashboardCpuLimit, - deploy.DefaultDashboardCpuLimit), + corev1.ResourceMemory: resource.MustParse(constants.DefaultDashboardMemoryLimit), + corev1.ResourceCPU: resource.MustParse(constants.DefaultDashboardCpuLimit), }, }, SecurityContext: &corev1.SecurityContext{ @@ -185,25 +182,18 @@ func (d *DashboardReconciler) getDashboardDeploymentSpec(ctx *deploy.DeployConte }, } - if !util.IsOpenShift { - runAsUser, err := strconv.ParseInt(util.GetValue(ctx.CheCluster.Spec.K8s.SecurityContextRunAsUser, deploy.DefaultSecurityContextRunAsUser), 10, 64) - if err != nil { - return nil, err - } - fsGroup, err := strconv.ParseInt(util.GetValue(ctx.CheCluster.Spec.K8s.SecurityContextFsGroup, deploy.DefaultSecurityContextFsGroup), 10, 64) - if err != nil { - return nil, err - } + if !infrastructure.IsOpenShift() { deployment.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{ - RunAsUser: &runAsUser, - FSGroup: &fsGroup, + RunAsUser: pointer.Int64Ptr(constants.DefaultSecurityContextRunAsUser), + FSGroup: pointer.Int64Ptr(constants.DefaultSecurityContextFsGroup), } } + deploy.CustomizeDeployment(deployment, &ctx.CheCluster.Spec.Components.Dashboard.Deployment, true) return deployment, nil } -func (d *DashboardReconciler) evaluateOpenShiftConsoleURL(ctx *deploy.DeployContext) string { +func (d *DashboardReconciler) evaluateOpenShiftConsoleURL(ctx *chetypes.DeployContext) string { console := &configv1.Console{} err := ctx.ClusterAPI.NonCachingClient.Get(context.TODO(), types.NamespacedName{ @@ -224,7 +214,7 @@ func (d *DashboardReconciler) provisionCheSelfSignedCA(volumes []corev1.Volume, Name: "che-self-signed-ca", VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: deploy.CheTLSSelfSignedCertificateSecretName, + SecretName: constants.DefaultSelfSignedCertificateSecretName, Items: []corev1.KeyToPath{ { Key: "ca.crt", diff --git a/pkg/deploy/dashboard/init_test.go b/pkg/deploy/dashboard/init_test.go index 19b3ae96b..df9bfa9fe 100644 --- a/pkg/deploy/dashboard/init_test.go +++ b/pkg/deploy/dashboard/init_test.go @@ -12,11 +12,15 @@ package dashboard -import "github.com/eclipse-che/che-operator/pkg/deploy" +import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" +) func init() { - err := deploy.InitTestDefaultsFromDeployment("../../../config/manager/manager.yaml") - if err != nil { - panic(err) - } + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../../../config/manager/manager.yaml") } diff --git a/pkg/deploy/dashboard/rbac.go b/pkg/deploy/dashboard/rbac.go index 65f14c040..c4b6a6c28 100644 --- a/pkg/deploy/dashboard/rbac.go +++ b/pkg/deploy/dashboard/rbac.go @@ -15,9 +15,8 @@ package dashboard import ( "fmt" - "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" - + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" rbacv1 "k8s.io/api/rbac/v1" ) @@ -51,7 +50,7 @@ func GetPrivilegedPoliciesRulesForKubernetes() []rbacv1.PolicyRule { }, } - if !util.IsOpenShift { + if !infrastructure.IsOpenShift() { rules = append(rules, // on Kubernetes, Dashboard stores user preferences in secrets with SA // until native auth is not implemented there as well @@ -65,10 +64,10 @@ func GetPrivilegedPoliciesRulesForKubernetes() []rbacv1.PolicyRule { return rules } -func (d *DashboardReconciler) getClusterRoleName(ctx *deploy.DeployContext) string { +func (d *DashboardReconciler) getClusterRoleName(ctx *chetypes.DeployContext) string { return fmt.Sprintf(DashboardSAClusterRoleTemplate, ctx.CheCluster.Namespace) } -func (d *DashboardReconciler) getClusterRoleBindingName(ctx *deploy.DeployContext) string { +func (d *DashboardReconciler) getClusterRoleBindingName(ctx *chetypes.DeployContext) string { return fmt.Sprintf(DashboardSAClusterRoleBindingTemplate, ctx.CheCluster.Namespace) } diff --git a/pkg/deploy/defaults.go b/pkg/deploy/defaults.go deleted file mode 100644 index 2daaa8330..000000000 --- a/pkg/deploy/defaults.go +++ /dev/null @@ -1,464 +0,0 @@ -// -// Copyright (c) 2019-2021 Red Hat, Inc. -// This program and the accompanying materials are made -// available under the terms of the Eclipse Public License 2.0 -// which is available at https://www.eclipse.org/legal/epl-2.0/ -// -// SPDX-License-Identifier: EPL-2.0 -// -// Contributors: -// Red Hat, Inc. - initial API and implementation -// -package deploy - -import ( - "fmt" - "io/ioutil" - "os" - "strings" - - "github.com/eclipse-che/che-operator/pkg/util" - "github.com/sirupsen/logrus" - appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/apps/v1" - "k8s.io/client-go/kubernetes/scheme" - - orgv1 "github.com/eclipse-che/che-operator/api/v1" -) - -var ( - defaultCheServerImage string - defaultCheVersion string - defaultDashboardImage string - defaultDevworkspaceControllerImage string - defaultPluginRegistryImage string - defaultDevfileRegistryImage string - defaultCheTLSSecretsCreationJobImage string - defaultPvcJobsImage string - defaultPostgresImage string - defaultPostgres13Image string - defaultSingleHostGatewayImage string - defaultSingleHostGatewayConfigSidecarImage string - defaultGatewayAuthenticationSidecarImage string - defaultGatewayAuthorizationSidecarImage string - defaultGatewayHeaderProxySidecarImage string - - defaultCheWorkspacePluginBrokerMetadataImage string - defaultCheWorkspacePluginBrokerArtifactsImage string - defaultCheServerSecureExposerJwtProxyImage string - DefaultSingleHostGatewayConfigMapLabels = map[string]string{ - "app": "che", - "component": "che-gateway-config", - } -) - -const ( - DefaultChePostgresUser = "pgche" - DefaultChePostgresHostName = "postgres" - DefaultChePostgresPort = "5432" - DefaultChePostgresDb = "dbche" - DefaultPvcStrategy = "common" - DefaultPvcClaimSize = "10Gi" - DefaultIngressClass = "nginx" - DefaultChePostgresCredentialsSecret = "postgres-credentials" - - DefaultCheLogLevel = "INFO" - DefaultCheDebug = "false" - DefaultCheMetricsPort = int32(8087) - DefaultCheDebugPort = int32(8000) - DefaultPostgresVolumeClaimName = "postgres-data" - DefaultPostgresPvcClaimSize = "1Gi" - - DefaultJavaOpts = "-XX:MaxRAMPercentage=85.0" - DefaultWorkspaceJavaOpts = "-XX:MaxRAM=150m -XX:MaxRAMFraction=2 -XX:+UseParallelGC " + - "-XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 " + - "-Dsun.zip.disableMemoryMapping=true " + - "-Xms20m -Djava.security.egd=file:/dev/./urandom" - DefaultSecurityContextFsGroup = "1724" - DefaultSecurityContextRunAsUser = "1724" - - KubernetesImagePullerOperatorCSV = "kubernetes-imagepuller-operator.v0.0.9" - - ServerExposureStrategy = "single-host" - GatewaySingleHostExposureType = "gateway" - - // kubernetes default labels - KubernetesComponentLabelKey = "app.kubernetes.io/component" - KubernetesPartOfLabelKey = "app.kubernetes.io/part-of" - KubernetesManagedByLabelKey = "app.kubernetes.io/managed-by" - KubernetesInstanceLabelKey = "app.kubernetes.io/instance" - KubernetesNameLabelKey = "app.kubernetes.io/name" - - CheEclipseOrg = "che.eclipse.org" - OAuthScmConfiguration = "oauth-scm-configuration" - - // che.eclipse.org annotations - CheEclipseOrgMountPath = "che.eclipse.org/mount-path" - CheEclipseOrgMountAs = "che.eclipse.org/mount-as" - CheEclipseOrgEnvName = "che.eclipse.org/env-name" - CheEclipseOrgNamespace = "che.eclipse.org/namespace" - CheEclipseOrgGithubOAuthCredentials = "che.eclipse.org/github-oauth-credentials" - CheEclipseOrgOAuthScmServer = "che.eclipse.org/oauth-scm-server" - CheEclipseOrgScmServerEndpoint = "che.eclipse.org/scm-server-endpoint" - CheEclipseOrgHash256 = "che.eclipse.org/hash256" - CheEclipseOrgManagedAnnotationsDigest = "che.eclipse.org/managed-annotations-digest" - - // components - DevfileRegistryName = "devfile-registry" - PluginRegistryName = "plugin-registry" - PostgresName = "postgres" - - // CheServiceAccountName - service account name for che-server. - CheServiceAccountName = "che" - - // Name of the secret that holds self-signed certificate for git connections - GitSelfSignedCertsConfigMapName = "che-git-self-signed-cert" - - CheTLSSelfSignedCertificateSecretName = "self-signed-certificate" - DefaultCheTLSSecretName = "che-tls" - - // limits - DefaultDashboardMemoryLimit = "256Mi" - DefaultDashboardMemoryRequest = "32Mi" - DefaultDashboardCpuLimit = "500m" - DefaultDashboardCpuRequest = "100m" - - DefaultPluginRegistryMemoryLimit = "256Mi" - DefaultPluginRegistryMemoryRequest = "32Mi" - DefaultPluginRegistryCpuLimit = "500m" - DefaultPluginRegistryCpuRequest = "100m" - - DefaultDevfileRegistryMemoryLimit = "256Mi" - DefaultDevfileRegistryMemoryRequest = "32Mi" - DefaultDevfileRegistryCpuLimit = "500m" - DefaultDevfileRegistryCpuRequest = "100m" - - DefaultServerMemoryLimit = "1024Mi" - DefaultServerMemoryRequest = "512Mi" - DefaultServerCpuLimit = "1" - DefaultServerCpuRequest = "100m" - - DefaultIdentityProviderMemoryLimit = "1536Mi" - DefaultIdentityProviderMemoryRequest = "1024Mi" - DefaultIdentityProviderCpuLimit = "2" - DefaultIdentityProviderCpuRequest = "100m" - - DefaultPostgresMemoryLimit = "1024Mi" - DefaultPostgresMemoryRequest = "512Mi" - DefaultPostgresCpuLimit = "500m" - DefaultPostgresCpuRequest = "100m" - - BitBucketOAuthConfigMountPath = "/che-conf/oauth/bitbucket" - BitBucketOAuthConfigPrivateKeyFileName = "private.key" - BitBucketOAuthConfigConsumerKeyFileName = "consumer.key" - - GitHubOAuthConfigMountPath = "/che-conf/oauth/github" - GitHubOAuthConfigClientIdFileName = "id" - GitHubOAuthConfigClientSecretFileName = "secret" - - GitLabOAuthConfigMountPath = "/che-conf/oauth/gitlab" - GitLabOAuthConfigClientIdFileName = "id" - GitLabOAuthConfigClientSecretFileName = "secret" - - InstallOrUpdateFailed = "InstallOrUpdateFailed" - DefaultServerTrustStoreConfigMapName = "ca-certs" -) - -func InitDefaults(defaultsPath string) { - if defaultsPath == "" { - InitDefaultsFromEnv() - } else { - InitDefaultsFromFile(defaultsPath) - } -} - -func InitDefaultsFromFile(defaultsPath string) { - operatorDeployment := getDefaultsFromFile(defaultsPath) - - defaultCheVersion = util.GetDeploymentEnv(operatorDeployment, "CHE_VERSION") - defaultCheServerImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_che_server")) - defaultDashboardImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_dashboard")) - defaultDevworkspaceControllerImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_devworkspace_controller")) - defaultPluginRegistryImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_plugin_registry")) - defaultDevfileRegistryImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_devfile_registry")) - defaultPvcJobsImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_pvc_jobs")) - defaultPostgresImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_postgres")) - defaultPostgres13Image = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_postgres_13_3")) - defaultSingleHostGatewayImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway")) - defaultSingleHostGatewayConfigSidecarImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway_config_sidecar")) - defaultGatewayAuthenticationSidecarImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authentication_sidecar")) - defaultGatewayAuthorizationSidecarImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authorization_sidecar")) - - // Don't get some k8s specific env - if !util.IsOpenShift { - defaultCheTLSSecretsCreationJobImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_che_tls_secrets_creation_job")) - defaultGatewayAuthenticationSidecarImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authentication_sidecar_k8s")) - defaultGatewayAuthorizationSidecarImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authorization_sidecar_k8s")) - } -} - -func getDefaultsFromFile(defaultsPath string) *v1.Deployment { - bytes, err := ioutil.ReadFile(defaultsPath) - if err != nil { - logrus.Fatalf("Unable to read file with defaults by path %s", defaultsPath) - } - - decode := scheme.Codecs.UniversalDeserializer().Decode - obj, _, err := decode(bytes, nil, nil) - if err != nil { - logrus.Fatalf(fmt.Sprintf("Error while decoding YAML object with defaults. Err was: %s", err)) - } - - deployment, ok := obj.(*v1.Deployment) - if ok { - return deployment - } - logrus.Fatalf("File %s doesn't contains real deployment.", defaultsPath) - return nil -} - -func getDefaultFromEnv(envName string) string { - value := os.Getenv(envName) - - if len(value) == 0 { - logrus.Fatalf("Failed to initialize default value: '%s'. Environment variable with default value was not found.", envName) - } - - return value -} - -func IsComponentReadinessInitContainersConfigured(cr *orgv1.CheCluster) bool { - return os.Getenv("ADD_COMPONENT_READINESS_INIT_CONTAINERS") == "true" -} - -func DefaultCheFlavor(cr *orgv1.CheCluster) string { - return getDefaultFromEnv("CHE_FLAVOR") -} - -func DefaultConsoleLinkName() string { - return getDefaultFromEnv("CONSOLE_LINK_NAME") -} - -func DefaultConsoleLinkDisplayName() string { - return getDefaultFromEnv("CONSOLE_LINK_DISPLAY_NAME") -} - -func DefaultConsoleLinkSection() string { - return getDefaultFromEnv("CONSOLE_LINK_SECTION") -} - -func DefaultConsoleLinkImage() string { - return getDefaultFromEnv("CONSOLE_LINK_IMAGE") -} - -func DefaultCheVersion() string { - return defaultCheVersion -} - -func DefaultCheServerImage(cr *orgv1.CheCluster) string { - return PatchDefaultImageName(cr, defaultCheServerImage) -} - -func DefaultCheTLSSecretsCreationJobImage() string { - return defaultCheTLSSecretsCreationJobImage -} - -func DefaultPvcJobsImage(cr *orgv1.CheCluster) string { - return PatchDefaultImageName(cr, defaultPvcJobsImage) -} - -func DefaultPostgresImage(cr *orgv1.CheCluster) string { - return PatchDefaultImageName(cr, defaultPostgresImage) -} - -func DefaultPostgres13Image(cr *orgv1.CheCluster) string { - // it might be empty value until it propertly downstreamed - if defaultPostgres13Image == "" { - return defaultPostgres13Image - } - return PatchDefaultImageName(cr, defaultPostgres13Image) -} - -func DefaultDashboardImage(cr *orgv1.CheCluster) string { - return PatchDefaultImageName(cr, defaultDashboardImage) -} - -func DefaultDevworkspaceControllerImage(cr *orgv1.CheCluster) string { - return PatchDefaultImageName(cr, defaultDevworkspaceControllerImage) -} - -func DefaultPluginRegistryImage(cr *orgv1.CheCluster) string { - return PatchDefaultImageName(cr, defaultPluginRegistryImage) -} - -func DefaultDevfileRegistryImage(cr *orgv1.CheCluster) string { - return PatchDefaultImageName(cr, defaultDevfileRegistryImage) -} - -func DefaultCheWorkspacePluginBrokerMetadataImage(cr *orgv1.CheCluster) string { - return PatchDefaultImageName(cr, defaultCheWorkspacePluginBrokerMetadataImage) -} - -func DefaultCheWorkspacePluginBrokerArtifactsImage(cr *orgv1.CheCluster) string { - return PatchDefaultImageName(cr, defaultCheWorkspacePluginBrokerArtifactsImage) -} - -func DefaultCheServerSecureExposerJwtProxyImage(cr *orgv1.CheCluster) string { - return PatchDefaultImageName(cr, defaultCheServerSecureExposerJwtProxyImage) -} - -func DefaultSingleHostGatewayImage(cr *orgv1.CheCluster) string { - return PatchDefaultImageName(cr, defaultSingleHostGatewayImage) -} - -func DefaultSingleHostGatewayConfigSidecarImage(cr *orgv1.CheCluster) string { - return PatchDefaultImageName(cr, defaultSingleHostGatewayConfigSidecarImage) -} - -func DefaultGatewayAuthenticationSidecarImage(cr *orgv1.CheCluster) string { - return PatchDefaultImageName(cr, defaultGatewayAuthenticationSidecarImage) -} - -func DefaultGatewayAuthorizationSidecarImage(cr *orgv1.CheCluster) string { - return PatchDefaultImageName(cr, defaultGatewayAuthorizationSidecarImage) -} - -func DefaultGatewayHeaderProxySidecarImage(cr *orgv1.CheCluster) string { - return PatchDefaultImageName(cr, defaultGatewayHeaderProxySidecarImage) -} - -func DefaultKubernetesImagePullerOperatorCSV() string { - return KubernetesImagePullerOperatorCSV -} - -func DefaultPullPolicyFromDockerImage(dockerImage string) string { - tag := "latest" - parts := strings.Split(dockerImage, ":") - if len(parts) > 1 { - tag = parts[1] - } - if tag == "latest" || tag == "nightly" || tag == "next" { - return "Always" - } - return "IfNotPresent" -} - -// GetWorkspaceNamespaceDefault - returns workspace namespace default strategy, which points on the namespaces used for workspaces execution. -func GetWorkspaceNamespaceDefault(cr *orgv1.CheCluster) string { - if cr.Spec.Server.CustomCheProperties != nil { - k8sNamespaceDefault := cr.Spec.Server.CustomCheProperties["CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT"] - if k8sNamespaceDefault != "" { - return k8sNamespaceDefault - } - } - - workspaceNamespaceDefault := cr.Namespace - if util.IsOpenShift { - workspaceNamespaceDefault = "-" + DefaultCheFlavor(cr) - } - return util.GetValue(cr.Spec.Server.WorkspaceNamespaceDefault, workspaceNamespaceDefault) -} - -func PatchDefaultImageName(cr *orgv1.CheCluster, imageName string) string { - if !cr.IsAirGapMode() { - return imageName - } - var hostname, organization string - if cr.Spec.Server.AirGapContainerRegistryHostname != "" { - hostname = cr.Spec.Server.AirGapContainerRegistryHostname - } else { - hostname = getHostnameFromImage(imageName) - } - if cr.Spec.Server.AirGapContainerRegistryOrganization != "" { - organization = cr.Spec.Server.AirGapContainerRegistryOrganization - } else { - organization = getOrganizationFromImage(imageName) - } - image := getImageNameFromFullImage(imageName) - return fmt.Sprintf("%s/%s/%s", hostname, organization, image) -} - -func getImageNameFromFullImage(image string) string { - imageParts := strings.Split(image, "/") - nameAndTag := "" - switch len(imageParts) { - case 1: - nameAndTag = imageParts[0] - case 2: - nameAndTag = imageParts[1] - case 3: - nameAndTag = imageParts[2] - } - return nameAndTag -} - -func getHostnameFromImage(image string) string { - imageParts := strings.Split(image, "/") - hostname := "" - switch len(imageParts) { - case 3: - hostname = imageParts[0] - default: - hostname = "docker.io" - } - return hostname -} - -func getOrganizationFromImage(image string) string { - imageParts := strings.Split(image, "/") - organization := "" - switch len(imageParts) { - case 2: - organization = imageParts[0] - case 3: - organization = imageParts[1] - } - return organization -} - -func InitDefaultsFromEnv() { - defaultCheVersion = getDefaultFromEnv("CHE_VERSION") - defaultCheServerImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_che_server")) - defaultDashboardImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_dashboard")) - defaultDevworkspaceControllerImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_devworkspace_controller")) - defaultPluginRegistryImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_plugin_registry")) - defaultDevfileRegistryImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_devfile_registry")) - defaultPvcJobsImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_pvc_jobs")) - defaultPostgresImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_postgres")) - - // allow not to set env variable into a container - // while downstream is not migrated to PostgreSQL 13.3 yet - defaultPostgres13Image = os.Getenv(util.GetArchitectureDependentEnv("RELATED_IMAGE_postgres_13_3")) - - defaultSingleHostGatewayImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway")) - defaultSingleHostGatewayConfigSidecarImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway_config_sidecar")) - defaultGatewayAuthenticationSidecarImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authentication_sidecar")) - defaultGatewayAuthorizationSidecarImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authorization_sidecar")) - defaultGatewayHeaderProxySidecarImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_header_sidecar")) - - // Don't get some k8s specific env - if !util.IsOpenShift { - defaultCheTLSSecretsCreationJobImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_che_tls_secrets_creation_job")) - defaultGatewayAuthenticationSidecarImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authentication_sidecar_k8s")) - defaultGatewayAuthorizationSidecarImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authorization_sidecar_k8s")) - } -} - -func InitTestDefaultsFromDeployment(deploymentFile string) error { - operator := &appsv1.Deployment{} - err := util.ReadObject(deploymentFile, operator) - if err != nil { - return err - } - - for _, env := range operator.Spec.Template.Spec.Containers[0].Env { - err = os.Setenv(env.Name, env.Value) - if err != nil { - return err - } - } - - os.Setenv("MOCK_API", "1") - InitDefaultsFromEnv() - return nil -} diff --git a/pkg/deploy/defaults_test.go b/pkg/deploy/defaults_test.go deleted file mode 100644 index 6f04e04e1..000000000 --- a/pkg/deploy/defaults_test.go +++ /dev/null @@ -1,182 +0,0 @@ -// -// Copyright (c) 2019-2021 Red Hat, Inc. -// This program and the accompanying materials are made -// available under the terms of the Eclipse Public License 2.0 -// which is available at https://www.eclipse.org/legal/epl-2.0/ -// -// SPDX-License-Identifier: EPL-2.0 -// -// Contributors: -// Red Hat, Inc. - initial API and implementation -// -package deploy - -import ( - "fmt" - "os" - "testing" - - "github.com/eclipse-che/che-operator/pkg/util" - - orgv1 "github.com/eclipse-che/che-operator/api/v1" -) - -func TestDefaultFromEnv(t *testing.T) { - - cheVersionTest := os.Getenv("CHE_VERSION") - - cheServerImageTest := os.Getenv(util.GetArchitectureDependentEnv("RELATED_IMAGE_che_server")) - dashboardImageTest := os.Getenv(util.GetArchitectureDependentEnv("RELATED_IMAGE_dashboard")) - pluginRegistryImageTest := os.Getenv(util.GetArchitectureDependentEnv("RELATED_IMAGE_plugin_registry")) - devfileRegistryImageTest := os.Getenv(util.GetArchitectureDependentEnv("RELATED_IMAGE_devfile_registry")) - pvcJobsImageTest := os.Getenv(util.GetArchitectureDependentEnv("RELATED_IMAGE_pvc_jobs")) - postgresImageTest := os.Getenv(util.GetArchitectureDependentEnv("RELATED_IMAGE_postgres")) - postgres13ImageTest := os.Getenv(util.GetArchitectureDependentEnv("RELATED_IMAGE_postgres_13_3")) - - if DefaultCheVersion() != cheVersionTest { - t.Errorf("Expected %s but was %s", cheVersionTest, DefaultCheVersion()) - } - - cheCluster := &orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{}, - }, - } - - if DefaultCheServerImage(cheCluster) != cheServerImageTest { - t.Errorf("Expected %s but was %s", cheServerImageTest, DefaultCheServerImage(cheCluster)) - } - - if DefaultDashboardImage(cheCluster) != dashboardImageTest { - t.Errorf("Expected %s but was %s", dashboardImageTest, DefaultDashboardImage(cheCluster)) - } - - if DefaultPluginRegistryImage(cheCluster) != pluginRegistryImageTest { - t.Errorf("Expected %s but was %s", pluginRegistryImageTest, DefaultPluginRegistryImage(cheCluster)) - } - - if DefaultDevfileRegistryImage(cheCluster) != devfileRegistryImageTest { - t.Errorf("Expected %s but was %s", devfileRegistryImageTest, DefaultDevfileRegistryImage(cheCluster)) - } - - if DefaultPvcJobsImage(cheCluster) != pvcJobsImageTest { - t.Errorf("Expected %s but was %s", pvcJobsImageTest, DefaultPvcJobsImage(cheCluster)) - } - - if DefaultPostgresImage(cheCluster) != postgresImageTest { - t.Errorf("Expected %s but was %s", postgresImageTest, DefaultPostgresImage(cheCluster)) - } - - if DefaultPostgres13Image(cheCluster) != postgres13ImageTest { - t.Errorf("Expected %s but was %s", postgres13ImageTest, DefaultPostgres13Image(cheCluster)) - } -} - -func TestCorrectImageName(t *testing.T) { - testCases := map[string]string{ - "docker.io/eclipse/che-operator:latest": "che-operator:latest", - "eclipse/che-operator:7.1.0": "che-operator:7.1.0", - "che-operator:7.2.0": "che-operator:7.2.0", - } - for k, v := range testCases { - t.Run(k, func(*testing.T) { - actual := getImageNameFromFullImage(k) - if actual != v { - t.Errorf("Expected %s but was %s", v, actual) - } - }) - } -} - -func TestCorrectAirGapPatchedImage(t *testing.T) { - type testcase struct { - image string - expected string - cr *orgv1.CheCluster - } - - var ( - airGapRegistryHostname = "myregistry.org" - airGapRegistryOrganization = "myorg" - expectedAirGapPostgresUpstreamImage = makeAirGapImagePath(airGapRegistryHostname, airGapRegistryOrganization, getImageNameFromFullImage(defaultPostgresImage)) - expectedAirGapPostgresUpstreamImageOnlyOrgChanged = makeAirGapImagePath(getHostnameFromImage(defaultPostgresImage), airGapRegistryOrganization, getImageNameFromFullImage(defaultPostgresImage)) - expectedAirGapDevspacesPluginRegistryOnlyOrgChanged = makeAirGapImagePath(getHostnameFromImage(defaultPluginRegistryImage), airGapRegistryOrganization, getImageNameFromFullImage(defaultPluginRegistryImage)) - expectedAirGapDevspacesPostgresImage = makeAirGapImagePath(airGapRegistryHostname, airGapRegistryOrganization, getImageNameFromFullImage(defaultPostgresImage)) - expectedAirGapDevspacesDevfileRegistryImageOnlyHostnameChanged = makeAirGapImagePath(airGapRegistryHostname, getOrganizationFromImage(defaultDevfileRegistryImage), getImageNameFromFullImage(defaultDevfileRegistryImage)) - ) - - upstream := &orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{}, - }, - } - devspaces := &orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - CheFlavor: "devspaces", - }, - }, - } - airGapUpstream := &orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - AirGapContainerRegistryHostname: airGapRegistryHostname, - AirGapContainerRegistryOrganization: airGapRegistryOrganization, - }, - }, - } - airGapDevspaces := &orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - AirGapContainerRegistryHostname: airGapRegistryHostname, - AirGapContainerRegistryOrganization: airGapRegistryOrganization, - CheFlavor: "devspaces", - }, - }, - } - upstreamOnlyOrg := &orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - AirGapContainerRegistryOrganization: airGapRegistryOrganization, - }, - }, - } - devspacesOnlyOrg := &orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - AirGapContainerRegistryOrganization: airGapRegistryOrganization, - CheFlavor: "devspaces", - }, - }, - } - devspacesOnlyHostname := &orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - AirGapContainerRegistryHostname: airGapRegistryHostname, - CheFlavor: "devspaces", - }, - }, - } - - testCases := map[string]testcase{ - "default postgres": {image: defaultPostgresImage, expected: defaultPostgresImage, cr: upstream}, - "airgap postgres": {image: defaultPostgresImage, expected: expectedAirGapPostgresUpstreamImage, cr: airGapUpstream}, - "with only the org changed": {image: defaultPostgresImage, expected: expectedAirGapPostgresUpstreamImageOnlyOrgChanged, cr: upstreamOnlyOrg}, - "devspaces plugin registry with only the org changed": {image: defaultPluginRegistryImage, expected: expectedAirGapDevspacesPluginRegistryOnlyOrgChanged, cr: devspacesOnlyOrg}, - "devspaces postgres": {image: defaultPostgresImage, expected: defaultPostgresImage, cr: devspaces}, - "devspaces airgap postgres": {image: defaultPostgresImage, expected: expectedAirGapDevspacesPostgresImage, cr: airGapDevspaces}, - "devspaces airgap with only hostname defined": {image: defaultDevfileRegistryImage, expected: expectedAirGapDevspacesDevfileRegistryImageOnlyHostnameChanged, cr: devspacesOnlyHostname}, - } - for name, tc := range testCases { - t.Run(name, func(*testing.T) { - actual := PatchDefaultImageName(tc.cr, tc.image) - if actual != tc.expected { - t.Errorf("Expected %s but was %s", tc.expected, actual) - } - }) - } -} - -func makeAirGapImagePath(hostname, org, nameAndTag string) string { - return fmt.Sprintf("%s/%s/%s", hostname, org, nameAndTag) -} diff --git a/pkg/deploy/deployment.go b/pkg/deploy/deployment.go index 8340d9ae8..d168029e6 100644 --- a/pkg/deploy/deployment.go +++ b/pkg/deploy/deployment.go @@ -19,9 +19,15 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/selection" + "k8s.io/utils/pointer" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/eclipse-che/che-operator/pkg/util" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + "github.com/eclipse-che/che-operator/pkg/common/test" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/sirupsen/logrus" @@ -45,7 +51,7 @@ var DefaultDeploymentDiffOpts = cmp.Options{ } func SyncDeploymentSpecToCluster( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, deploymentSpec *appsv1.Deployment, deploymentDiffOpts cmp.Options) (bool, error) { @@ -72,7 +78,7 @@ func SyncDeploymentSpecToCluster( } // always return true for tests - if util.IsTestMode() { + if test.IsTestMode() { return true, nil } @@ -90,15 +96,76 @@ func SyncDeploymentSpecToCluster( return provisioned, nil } +func CustomizeDeployment(deployment *appsv1.Deployment, customization *chev2.Deployment, customizeSecurityContext bool) error { + if customization == nil || len(customization.Containers) == 0 { + return nil + } + + for index, _ := range deployment.Spec.Template.Spec.Containers { + container := &deployment.Spec.Template.Spec.Containers[index] + + customizationContainer := findCustomizationContainer(container.Name, customization.Containers) + if customizationContainer == nil { + break + } + + container.Image = utils.GetValue(customizationContainer.Image, container.Image) + if customizationContainer.ImagePullPolicy != "" { + container.ImagePullPolicy = customizationContainer.ImagePullPolicy + } else { + container.ImagePullPolicy = corev1.PullPolicy(utils.GetPullPolicyFromDockerImage(container.Image)) + } + + container.Resources = corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceMemory: getQuantity(customizationContainer.Resources.Requests.Memory, container.Resources.Requests[corev1.ResourceMemory]), + corev1.ResourceCPU: getQuantity(customizationContainer.Resources.Requests.Cpu, container.Resources.Requests[corev1.ResourceCPU]), + }, + Limits: corev1.ResourceList{ + corev1.ResourceMemory: getQuantity(customizationContainer.Resources.Limits.Memory, container.Resources.Limits[corev1.ResourceMemory]), + corev1.ResourceCPU: getQuantity(customizationContainer.Resources.Limits.Cpu, container.Resources.Limits[corev1.ResourceCPU]), + }, + } + } + + if customizeSecurityContext && !infrastructure.IsOpenShift() { + if customization.SecurityContext.FsGroup != nil { + deployment.Spec.Template.Spec.SecurityContext.FSGroup = pointer.Int64Ptr(*customization.SecurityContext.FsGroup) + } + if customization.SecurityContext.RunAsUser != nil { + deployment.Spec.Template.Spec.SecurityContext.RunAsUser = pointer.Int64Ptr(*customization.SecurityContext.RunAsUser) + } + } + + return nil +} + +func getQuantity(value resource.Quantity, defaultValue resource.Quantity) resource.Quantity { + if !value.IsZero() { + return value + } + return defaultValue +} + +func findCustomizationContainer(origContainerName string, custContainers []chev2.Container) *chev2.Container { + for _, c := range custContainers { + if c.Name == origContainerName { + return &c + } + } + + return nil +} + // MountSecrets mounts secrets into a container as a file or as environment variable. // Secrets are selected by the following labels: // - app.kubernetes.io/part-of=che.eclipse.org // - app.kubernetes.io/component=-secret -func MountSecrets(specDeployment *appsv1.Deployment, deployContext *DeployContext) error { +func MountSecrets(specDeployment *appsv1.Deployment, deployContext *chetypes.DeployContext) error { secrets := &corev1.SecretList{} - kubernetesPartOfLabelSelectorRequirement, _ := labels.NewRequirement(KubernetesPartOfLabelKey, selection.Equals, []string{CheEclipseOrg}) - kubernetesComponentLabelSelectorRequirement, _ := labels.NewRequirement(KubernetesComponentLabelKey, selection.Equals, []string{specDeployment.Name + "-secret"}) + kubernetesPartOfLabelSelectorRequirement, _ := labels.NewRequirement(constants.KubernetesPartOfLabelKey, selection.Equals, []string{constants.CheEclipseOrg}) + kubernetesComponentLabelSelectorRequirement, _ := labels.NewRequirement(constants.KubernetesComponentLabelKey, selection.Equals, []string{specDeployment.Name + "-secret"}) listOptions := &client.ListOptions{ LabelSelector: labels.NewSelector().Add(*kubernetesPartOfLabelSelectorRequirement).Add(*kubernetesComponentLabelSelectorRequirement), @@ -114,7 +181,7 @@ func MountSecrets(specDeployment *appsv1.Deployment, deployContext *DeployContex container := &specDeployment.Spec.Template.Spec.Containers[0] for _, secretObj := range secrets.Items { - switch secretObj.Annotations[CheEclipseOrgMountAs] { + switch secretObj.Annotations[constants.CheEclipseOrgMountAs] { case "file": voluseSource := corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ @@ -129,7 +196,7 @@ func MountSecrets(specDeployment *appsv1.Deployment, deployContext *DeployContex volumeMount := corev1.VolumeMount{ Name: secretObj.Name, - MountPath: secretObj.Annotations[CheEclipseOrgMountPath], + MountPath: secretObj.Annotations[constants.CheEclipseOrgMountPath], } specDeployment.Spec.Template.Spec.Volumes = append(specDeployment.Spec.Template.Spec.Volumes, volume) @@ -157,15 +224,15 @@ func MountSecrets(specDeployment *appsv1.Deployment, deployContext *DeployContex var envName string // check if evn name defined explicitly - envNameAnnotation := CheEclipseOrg + "/" + key + "_env-name" + envNameAnnotation := constants.CheEclipseOrg + "/" + key + "_env-name" envName, envNameExists := secretObj.Annotations[envNameAnnotation] if !envNameExists { // check if there is only one env name to mount - envName, envNameExists = secretObj.Annotations[CheEclipseOrgEnvName] + envName, envNameExists = secretObj.Annotations[constants.CheEclipseOrgEnvName] if len(secret.Data) > 1 { return fmt.Errorf("There are more than one environment variable to mount. Use annotation '%s' to specify a name", envNameAnnotation) } else if !envNameExists { - return fmt.Errorf("Environment name to mount secret key not found. Use annotation '%s' to specify a name", CheEclipseOrgEnvName) + return fmt.Errorf("Environment name to mount secret key not found. Use annotation '%s' to specify a name", constants.CheEclipseOrgEnvName) } } @@ -192,11 +259,11 @@ func MountSecrets(specDeployment *appsv1.Deployment, deployContext *DeployContex // Configmaps are selected by the following labels: // - app.kubernetes.io/part-of=che.eclipse.org // - app.kubernetes.io/component=-configmap -func MountConfigMaps(specDeployment *appsv1.Deployment, deployContext *DeployContext) error { +func MountConfigMaps(specDeployment *appsv1.Deployment, deployContext *chetypes.DeployContext) error { configmaps := &corev1.ConfigMapList{} - kubernetesPartOfLabelSelectorRequirement, _ := labels.NewRequirement(KubernetesPartOfLabelKey, selection.Equals, []string{CheEclipseOrg}) - kubernetesComponentLabelSelectorRequirement, _ := labels.NewRequirement(KubernetesComponentLabelKey, selection.Equals, []string{specDeployment.Name + "-configmap"}) + kubernetesPartOfLabelSelectorRequirement, _ := labels.NewRequirement(constants.KubernetesPartOfLabelKey, selection.Equals, []string{constants.CheEclipseOrg}) + kubernetesComponentLabelSelectorRequirement, _ := labels.NewRequirement(constants.KubernetesComponentLabelKey, selection.Equals, []string{specDeployment.Name + "-configmap"}) listOptions := &client.ListOptions{ LabelSelector: labels.NewSelector().Add(*kubernetesPartOfLabelSelectorRequirement).Add(*kubernetesComponentLabelSelectorRequirement), @@ -212,7 +279,7 @@ func MountConfigMaps(specDeployment *appsv1.Deployment, deployContext *DeployCon container := &specDeployment.Spec.Template.Spec.Containers[0] for _, configMapObj := range configmaps.Items { - switch configMapObj.Annotations[CheEclipseOrgMountAs] { + switch configMapObj.Annotations[constants.CheEclipseOrgMountAs] { case "file": voluseSource := corev1.VolumeSource{ ConfigMap: &corev1.ConfigMapVolumeSource{ @@ -229,7 +296,7 @@ func MountConfigMaps(specDeployment *appsv1.Deployment, deployContext *DeployCon volumeMount := corev1.VolumeMount{ Name: configMapObj.Name, - MountPath: configMapObj.Annotations[CheEclipseOrgMountPath], + MountPath: configMapObj.Annotations[constants.CheEclipseOrgMountPath], } specDeployment.Spec.Template.Spec.Volumes = append(specDeployment.Spec.Template.Spec.Volumes, volume) @@ -257,15 +324,15 @@ func MountConfigMaps(specDeployment *appsv1.Deployment, deployContext *DeployCon var envName string // check if evn name defined explicitly - envNameAnnotation := CheEclipseOrg + "/" + key + "_env-name" + envNameAnnotation := constants.CheEclipseOrg + "/" + key + "_env-name" envName, envNameExists := configMapObj.Annotations[envNameAnnotation] if !envNameExists { // check if there is only one env name to mount - envName, envNameExists = configMapObj.Annotations[CheEclipseOrgEnvName] + envName, envNameExists = configMapObj.Annotations[constants.CheEclipseOrgEnvName] if len(configmap.Data) > 1 { return fmt.Errorf("There are more than one environment variable to mount. Use annotation '%s' to specify a name", envNameAnnotation) } else if !envNameExists { - return fmt.Errorf("Environment name to mount configmap key not found. Use annotation '%s' to specify a name", CheEclipseOrgEnvName) + return fmt.Errorf("Environment name to mount configmap key not found. Use annotation '%s' to specify a name", constants.CheEclipseOrgEnvName) } } diff --git a/pkg/deploy/deployment_test.go b/pkg/deploy/deployment_test.go index 9c7e8778a..5508bd65c 100644 --- a/pkg/deploy/deployment_test.go +++ b/pkg/deploy/deployment_test.go @@ -16,10 +16,15 @@ import ( "os" "reflect" + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/api/resource" + "github.com/google/go-cmp/cmp" - orgv1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/pkg/util" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + "github.com/eclipse-che/che-operator/pkg/common/utils" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -121,12 +126,12 @@ func TestMountSecret(t *testing.T) { Name: "test-volume", Namespace: "eclipse-che", Labels: map[string]string{ - KubernetesPartOfLabelKey: CheEclipseOrg, - KubernetesComponentLabelKey: "che-secret", // corresponds to deployment name + constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg, + constants.KubernetesComponentLabelKey: "che-secret", // corresponds to deployment name }, Annotations: map[string]string{ - CheEclipseOrgMountAs: "file", - CheEclipseOrgMountPath: "/test-path", + constants.CheEclipseOrgMountAs: "file", + constants.CheEclipseOrgMountPath: "/test-path", }, }, Data: map[string][]byte{ @@ -189,12 +194,12 @@ func TestMountSecret(t *testing.T) { Name: "test-envs", Namespace: "eclipse-che", Labels: map[string]string{ - KubernetesPartOfLabelKey: CheEclipseOrg, - KubernetesComponentLabelKey: "che-secret", // corresponds to deployment name + constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg, + constants.KubernetesComponentLabelKey: "che-secret", // corresponds to deployment name }, Annotations: map[string]string{ - CheEclipseOrgMountAs: "env", - CheEclipseOrgEnvName: "ENV_A", + constants.CheEclipseOrgMountAs: "env", + constants.CheEclipseOrgEnvName: "ENV_A", }, }, Data: map[string][]byte{ @@ -279,14 +284,14 @@ func TestMountSecret(t *testing.T) { Name: "test-envs", Namespace: "eclipse-che", Labels: map[string]string{ - KubernetesPartOfLabelKey: CheEclipseOrg, - KubernetesComponentLabelKey: "che-secret", // corresponds to deployment name + constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg, + constants.KubernetesComponentLabelKey: "che-secret", // corresponds to deployment name }, Annotations: map[string]string{ - CheEclipseOrgMountAs: "env", - CheEclipseOrg + "/a_env-name": "ENV_A", - CheEclipseOrg + "/b_env-name": "ENV_B", - CheEclipseOrg + "/c_env-name": "ENV_C", + constants.CheEclipseOrgMountAs: "env", + constants.CheEclipseOrg + "/a_env-name": "ENV_A", + constants.CheEclipseOrg + "/b_env-name": "ENV_B", + constants.CheEclipseOrg + "/c_env-name": "ENV_C", }, }, Data: map[string][]byte{ @@ -302,17 +307,17 @@ func TestMountSecret(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true))) - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) testCase.initObjects = append(testCase.initObjects, testCase.initDeployment) cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...) - deployContext := &DeployContext{ - CheCluster: &orgv1.CheCluster{ + deployContext := &chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, }, - ClusterAPI: ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, @@ -399,12 +404,12 @@ func TestMountConfigMaps(t *testing.T) { Name: "test-volume", Namespace: "eclipse-che", Labels: map[string]string{ - KubernetesPartOfLabelKey: CheEclipseOrg, - KubernetesComponentLabelKey: "che-configmap", // corresponds to deployment name + constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg, + constants.KubernetesComponentLabelKey: "che-configmap", // corresponds to deployment name }, Annotations: map[string]string{ - CheEclipseOrgMountAs: "file", - CheEclipseOrgMountPath: "/test-path", + constants.CheEclipseOrgMountAs: "file", + constants.CheEclipseOrgMountPath: "/test-path", }, }, Data: map[string]string{ @@ -467,12 +472,12 @@ func TestMountConfigMaps(t *testing.T) { Name: "test-envs", Namespace: "eclipse-che", Labels: map[string]string{ - KubernetesPartOfLabelKey: CheEclipseOrg, - KubernetesComponentLabelKey: "che-configmap", // corresponds to deployment name + constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg, + constants.KubernetesComponentLabelKey: "che-configmap", // corresponds to deployment name }, Annotations: map[string]string{ - CheEclipseOrgMountAs: "env", - CheEclipseOrgEnvName: "ENV_A", + constants.CheEclipseOrgMountAs: "env", + constants.CheEclipseOrgEnvName: "ENV_A", }, }, Data: map[string]string{ @@ -557,14 +562,14 @@ func TestMountConfigMaps(t *testing.T) { Name: "test-envs", Namespace: "eclipse-che", Labels: map[string]string{ - KubernetesPartOfLabelKey: CheEclipseOrg, - KubernetesComponentLabelKey: "che-configmap", // corresponds to deployment name + constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg, + constants.KubernetesComponentLabelKey: "che-configmap", // corresponds to deployment name }, Annotations: map[string]string{ - CheEclipseOrgMountAs: "env", - CheEclipseOrg + "/a_env-name": "ENV_A", - CheEclipseOrg + "/b_env-name": "ENV_B", - CheEclipseOrg + "/c_env-name": "ENV_C", + constants.CheEclipseOrgMountAs: "env", + constants.CheEclipseOrg + "/a_env-name": "ENV_A", + constants.CheEclipseOrg + "/b_env-name": "ENV_B", + constants.CheEclipseOrg + "/c_env-name": "ENV_C", }, }, Data: map[string]string{ @@ -580,17 +585,17 @@ func TestMountConfigMaps(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true))) - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) testCase.initObjects = append(testCase.initObjects, testCase.initDeployment) cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...) - deployContext := &DeployContext{ - CheCluster: &orgv1.CheCluster{ + deployContext := &chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, }, - ClusterAPI: ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, @@ -610,21 +615,21 @@ func TestMountConfigMaps(t *testing.T) { } func TestSyncEnvVarDeploymentToCluster(t *testing.T) { - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme) - deployContext := &DeployContext{ - CheCluster: &orgv1.CheCluster{ + deployContext := &chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, }, - ClusterAPI: ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, }, - Proxy: &Proxy{}, + Proxy: &chetypes.Proxy{}, } // initial sync @@ -659,8 +664,167 @@ func TestSyncEnvVarDeploymentToCluster(t *testing.T) { } // check env var - cmRevision := util.FindEnv(actual.Spec.Template.Spec.Containers[0].Env, "test-name") - if cmRevision == nil || cmRevision.Value != "test-value" { + value := utils.GetEnv(actual.Spec.Template.Spec.Containers[0].Env, "test-name") + if value != "test-value" { t.Fatalf("Failed to sync deployment") } } + +func TestCustomizeDeploymentShouldNotUpdateResources(t *testing.T) { + deployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "test", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("100Mi"), + corev1.ResourceCPU: resource.MustParse("1"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), + corev1.ResourceCPU: resource.MustParse("2"), + }, + }, + }, + }, + }, + }, + }, + } + + customizationDeployment := &chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: "test", + }, + }, + } + + err := CustomizeDeployment(deployment, customizationDeployment, false) + assert.Nil(t, err) + assert.Equal(t, "1", deployment.Spec.Template.Spec.Containers[0].Resources.Requests.Cpu().String()) + assert.Equal(t, "100Mi", deployment.Spec.Template.Spec.Containers[0].Resources.Requests.Memory().String()) + assert.Equal(t, "2", deployment.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu().String()) + assert.Equal(t, "200Mi", deployment.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().String()) +} + +func TestCustomizeDeploymentImagePullPolicy(t *testing.T) { + type testCase struct { + name string + initDeployment *appsv1.Deployment + customizationDeployment *chev2.Deployment + expectedImagePullPolicy corev1.PullPolicy + } + + testCases := []testCase{ + { + name: "Should use ImagePullPolicy set explicitly", + initDeployment: &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "test", + Image: "test/test:test", + ImagePullPolicy: corev1.PullIfNotPresent, + }, + }, + }, + }, + }, + }, + customizationDeployment: &chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: "test", + ImagePullPolicy: corev1.PullNever, + }, + }, + }, + expectedImagePullPolicy: corev1.PullNever, + }, + { + name: "Should update ImagePullPolicy to Always for next tag", + initDeployment: &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "test", + Image: "test/test:test", + ImagePullPolicy: corev1.PullIfNotPresent, + }, + }, + }, + }, + }, + }, + customizationDeployment: &chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: "test", + Image: "test/test:next", + }, + }, + }, + expectedImagePullPolicy: corev1.PullAlways, + }, + { + name: "Should not update ImagePullPolicy", + initDeployment: &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "test", + Image: "test/test:v2.5.0", + ImagePullPolicy: corev1.PullIfNotPresent, + }, + }, + }, + }, + }, + }, + customizationDeployment: &chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: "test", + }, + }, + }, + expectedImagePullPolicy: corev1.PullIfNotPresent, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true))) + + err := CustomizeDeployment(testCase.initDeployment, testCase.customizationDeployment, false) + assert.Nil(t, err) + assert.Equal(t, testCase.expectedImagePullPolicy, testCase.initDeployment.Spec.Template.Spec.Containers[0].ImagePullPolicy) + }) + } +} diff --git a/pkg/deploy/dev-workspace/cache.go b/pkg/deploy/dev-workspace/cache.go index 856490ebf..c7eee5c69 100644 --- a/pkg/deploy/dev-workspace/cache.go +++ b/pkg/deploy/dev-workspace/cache.go @@ -14,8 +14,8 @@ package devworkspace import ( "io/ioutil" - "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" + "github.com/eclipse-che/che-operator/pkg/common/constants" + "github.com/eclipse-che/che-operator/pkg/common/utils" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "sigs.k8s.io/yaml" @@ -44,7 +44,7 @@ func readK8SUnstructured(yamlFile string, into *unstructured.Unstructured) error into.SetAnnotations(annotations) } - into.GetAnnotations()[deploy.CheEclipseOrgHash256] = hash + into.GetAnnotations()[constants.CheEclipseOrgHash256] = hash return nil } @@ -61,7 +61,7 @@ func readK8SObject(yamlFile string, into metav1.Object) error { into.SetAnnotations(annotations) } - into.GetAnnotations()[deploy.CheEclipseOrgHash256] = hash + into.GetAnnotations()[constants.CheEclipseOrgHash256] = hash return nil } @@ -75,7 +75,7 @@ func readObj(yamlFile string, into interface{}) (string, error) { cachedFile = &DevWorkspaceCachedObj{ data, - util.ComputeHash256(data), + utils.ComputeHash256(data), } cachedObjFiles[yamlFile] = cachedFile } diff --git a/pkg/deploy/dev-workspace/dev_workspace.go b/pkg/deploy/dev-workspace/dev_workspace.go index 4e1bf5ceb..d0bcd3ba3 100644 --- a/pkg/deploy/dev-workspace/dev_workspace.go +++ b/pkg/deploy/dev-workspace/dev_workspace.go @@ -18,8 +18,11 @@ import ( admissionregistrationv1 "k8s.io/api/admissionregistration/v1" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + "github.com/eclipse-che/che-operator/pkg/common/test" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" @@ -58,18 +61,13 @@ func NewDevWorkspaceReconciler() *DevWorkspaceReconciler { return &DevWorkspaceReconciler{} } -func (d *DevWorkspaceReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { - if util.IsOpenShift && !util.IsOpenShift4 { - // OpenShift 3.x is not supported - return reconcile.Result{}, true, nil - } - +func (d *DevWorkspaceReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { if isDevWorkspaceOperatorCSVExists(ctx) { // Do nothing if DevWorkspace has been already deployed via OLM return reconcile.Result{}, true, nil } - if util.IsOpenShift { + if infrastructure.IsOpenShift() { wtoInstalled, err := isWebTerminalSubscriptionExist(ctx) if err != nil { return reconcile.Result{Requeue: true}, false, err @@ -91,7 +89,7 @@ func (d *DevWorkspaceReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile return reconcile.Result{Requeue: true}, false, err } - namespaceOwnershipAnnotation := namespace.GetAnnotations()[deploy.CheEclipseOrgNamespace] + namespaceOwnershipAnnotation := namespace.GetAnnotations()[constants.CheEclipseOrgNamespace] if namespaceOwnershipAnnotation == "" { // don't manage DWO if namespace is create by someone else not but not Che Operator return reconcile.Result{}, true, nil @@ -107,7 +105,7 @@ func (d *DevWorkspaceReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile // Don't take a control over DWO if CheCluster in another CR is handling it return reconcile.Result{}, true, nil } - namespace.GetAnnotations()[deploy.CheEclipseOrgNamespace] = ctx.CheCluster.Namespace + namespace.GetAnnotations()[constants.CheEclipseOrgNamespace] = ctx.CheCluster.Namespace _, err = deploy.Sync(ctx, namespace) if err != nil { return reconcile.Result{Requeue: true}, false, err @@ -129,7 +127,7 @@ func (d *DevWorkspaceReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile } } - if !util.IsTestMode() { + if !test.IsTestMode() { if !devWorkspaceWebHookExistedBeforeSync { // we need to restart Che Operator to switch devworkspace controller mode restartCheOperator() @@ -139,6 +137,6 @@ func (d *DevWorkspaceReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile return reconcile.Result{}, true, nil } -func (d *DevWorkspaceReconciler) Finalize(ctx *deploy.DeployContext) bool { +func (d *DevWorkspaceReconciler) Finalize(ctx *chetypes.DeployContext) bool { return true } diff --git a/pkg/deploy/dev-workspace/dev_workspace_syncer.go b/pkg/deploy/dev-workspace/dev_workspace_syncer.go index 047287cc5..131ed74bd 100644 --- a/pkg/deploy/dev-workspace/dev_workspace_syncer.go +++ b/pkg/deploy/dev-workspace/dev_workspace_syncer.go @@ -15,8 +15,12 @@ import ( "context" "strings" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -28,8 +32,13 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) +const ( + OpenshiftDevWorkspaceTemplatesPath = "/tmp/devworkspace-operator/templates/deployment/openshift/objects" + KubernetesDevWorkspaceTemplatesPath = "/tmp/devworkspace-operator/templates/deployment/kubernetes/objects" +) + var ( - syncItems = []func(*deploy.DeployContext) (bool, error){ + syncItems = []func(*chetypes.DeployContext) (bool, error){ syncDwService, syncDwMetricService, syncDwServiceAccount, @@ -50,142 +59,134 @@ var ( syncDwConfigCRD, syncDwDeployment, } - - DevWorkspaceTemplates = DevWorkspaceTemplatesPath() - - OpenshiftDevWorkspaceTemplatesPath = "/tmp/devworkspace-operator/templates/deployment/openshift/objects" - KubernetesDevWorkspaceTemplatesPath = "/tmp/devworkspace-operator/templates/deployment/kubernetes/objects" - - DevWorkspaceServiceAccountFile = DevWorkspaceTemplates + "/devworkspace-controller-serviceaccount.ServiceAccount.yaml" - - DevWorkspaceRoleFile = DevWorkspaceTemplates + "/devworkspace-controller-leader-election-role.Role.yaml" - DevWorkspaceRoleBindingFile = DevWorkspaceTemplates + "/devworkspace-controller-leader-election-rolebinding.RoleBinding.yaml" - - DevWorkspaceClusterRoleFile = DevWorkspaceTemplates + "/devworkspace-controller-role.ClusterRole.yaml" - DevWorkspaceProxyClusterRoleFile = DevWorkspaceTemplates + "/devworkspace-controller-proxy-role.ClusterRole.yaml" - DevWorkspaceMetricsReaderClusterRoleFile = DevWorkspaceTemplates + "/devworkspace-controller-metrics-reader.ClusterRole.yaml" - DevWorkspaceViewWorkspacesClusterRoleFile = DevWorkspaceTemplates + "/devworkspace-controller-view-workspaces.ClusterRole.yaml" - DevWorkspaceEditWorkspacesClusterRoleFile = DevWorkspaceTemplates + "/devworkspace-controller-edit-workspaces.ClusterRole.yaml" - - DevWorkspaceClusterRoleBindingFile = DevWorkspaceTemplates + "/devworkspace-controller-rolebinding.ClusterRoleBinding.yaml" - DevWorkspaceProxyClusterRoleBindingFile = DevWorkspaceTemplates + "/devworkspace-controller-proxy-rolebinding.ClusterRoleBinding.yaml" - - DevWorkspaceWorkspaceRoutingCRDFile = DevWorkspaceTemplates + "/devworkspaceroutings.controller.devfile.io.CustomResourceDefinition.yaml" - DevWorkspaceTemplatesCRDFile = DevWorkspaceTemplates + "/devworkspacetemplates.workspace.devfile.io.CustomResourceDefinition.yaml" - DevWorkspaceCRDFile = DevWorkspaceTemplates + "/devworkspaces.workspace.devfile.io.CustomResourceDefinition.yaml" - DevWorkspaceOperatorConfigCRDFile = DevWorkspaceTemplates + "/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml" - - DevWorkspaceServiceFile = DevWorkspaceTemplates + "/devworkspace-controller-manager-service.Service.yaml" - DevWorkspaceMetricsServiceFile = DevWorkspaceTemplates + "/devworkspace-controller-metrics.Service.yaml" - DevWorkspaceDeploymentFile = DevWorkspaceTemplates + "/devworkspace-controller-manager.Deployment.yaml" - - DevWorkspaceIssuerFile = DevWorkspaceTemplates + "/devworkspace-controller-selfsigned-issuer.Issuer.yaml" - DevWorkspaceCertificateFile = DevWorkspaceTemplates + "/devworkspace-controller-serving-cert.Certificate.yaml" ) -func syncDwServiceAccount(deployContext *deploy.DeployContext) (bool, error) { - return readAndSyncObject(deployContext, DevWorkspaceServiceAccountFile, &corev1.ServiceAccount{}, DevWorkspaceNamespace) +func syncDwServiceAccount(deployContext *chetypes.DeployContext) (bool, error) { + path := devWorkspaceTemplatesPath() + "/devworkspace-controller-serviceaccount.ServiceAccount.yaml" + return readAndSyncObject(deployContext, path, &corev1.ServiceAccount{}, DevWorkspaceNamespace) } -func syncDwService(deployContext *deploy.DeployContext) (bool, error) { - return readAndSyncObject(deployContext, DevWorkspaceServiceFile, &corev1.Service{}, DevWorkspaceNamespace) +func syncDwService(deployContext *chetypes.DeployContext) (bool, error) { + path := devWorkspaceTemplatesPath() + "/devworkspace-controller-manager-service.Service.yaml" + return readAndSyncObject(deployContext, path, &corev1.Service{}, DevWorkspaceNamespace) } -func syncDwMetricService(deployContext *deploy.DeployContext) (bool, error) { - return readAndSyncObject(deployContext, DevWorkspaceMetricsServiceFile, &corev1.Service{}, DevWorkspaceNamespace) +func syncDwMetricService(deployContext *chetypes.DeployContext) (bool, error) { + filePath := devWorkspaceTemplatesPath() + "/devworkspace-controller-metrics.Service.yaml" + return readAndSyncObject(deployContext, filePath, &corev1.Service{}, DevWorkspaceNamespace) } -func syncDwRole(deployContext *deploy.DeployContext) (bool, error) { - return readAndSyncObject(deployContext, DevWorkspaceRoleFile, &rbacv1.Role{}, DevWorkspaceNamespace) +func syncDwRole(deployContext *chetypes.DeployContext) (bool, error) { + path := devWorkspaceTemplatesPath() + "/devworkspace-controller-leader-election-role.Role.yaml" + return readAndSyncObject(deployContext, path, &rbacv1.Role{}, DevWorkspaceNamespace) } -func syncDwRoleBinding(deployContext *deploy.DeployContext) (bool, error) { - return readAndSyncObject(deployContext, DevWorkspaceRoleBindingFile, &rbacv1.RoleBinding{}, DevWorkspaceNamespace) +func syncDwRoleBinding(deployContext *chetypes.DeployContext) (bool, error) { + path := devWorkspaceTemplatesPath() + "/devworkspace-controller-leader-election-rolebinding.RoleBinding.yaml" + return readAndSyncObject(deployContext, path, &rbacv1.RoleBinding{}, DevWorkspaceNamespace) } -func syncDwClusterRoleBinding(deployContext *deploy.DeployContext) (bool, error) { - return readAndSyncObject(deployContext, DevWorkspaceClusterRoleBindingFile, &rbacv1.ClusterRoleBinding{}, "") +func syncDwClusterRoleBinding(deployContext *chetypes.DeployContext) (bool, error) { + path := devWorkspaceTemplatesPath() + "/devworkspace-controller-rolebinding.ClusterRoleBinding.yaml" + return readAndSyncObject(deployContext, path, &rbacv1.ClusterRoleBinding{}, "") } -func syncDwProxyClusterRoleBinding(deployContext *deploy.DeployContext) (bool, error) { - return readAndSyncObject(deployContext, DevWorkspaceProxyClusterRoleBindingFile, &rbacv1.ClusterRoleBinding{}, "") +func syncDwProxyClusterRoleBinding(deployContext *chetypes.DeployContext) (bool, error) { + path := devWorkspaceTemplatesPath() + "/devworkspace-controller-proxy-rolebinding.ClusterRoleBinding.yaml" + return readAndSyncObject(deployContext, path, &rbacv1.ClusterRoleBinding{}, "") } -func syncDwClusterRole(deployContext *deploy.DeployContext) (bool, error) { - return readAndSyncObject(deployContext, DevWorkspaceClusterRoleFile, &rbacv1.ClusterRole{}, "") +func syncDwClusterRole(deployContext *chetypes.DeployContext) (bool, error) { + path := devWorkspaceTemplatesPath() + "/devworkspace-controller-role.ClusterRole.yaml" + return readAndSyncObject(deployContext, path, &rbacv1.ClusterRole{}, "") } -func syncDwMetricsClusterRole(deployContext *deploy.DeployContext) (bool, error) { - return readAndSyncObject(deployContext, DevWorkspaceMetricsReaderClusterRoleFile, &rbacv1.ClusterRole{}, "") +func syncDwMetricsClusterRole(deployContext *chetypes.DeployContext) (bool, error) { + path := devWorkspaceTemplatesPath() + "/devworkspace-controller-metrics-reader.ClusterRole.yaml" + return readAndSyncObject(deployContext, path, &rbacv1.ClusterRole{}, "") } -func syncDwProxyClusterRole(deployContext *deploy.DeployContext) (bool, error) { - return readAndSyncObject(deployContext, DevWorkspaceProxyClusterRoleFile, &rbacv1.ClusterRole{}, "") +func syncDwProxyClusterRole(deployContext *chetypes.DeployContext) (bool, error) { + path := devWorkspaceTemplatesPath() + "/devworkspace-controller-proxy-role.ClusterRole.yaml" + return readAndSyncObject(deployContext, path, &rbacv1.ClusterRole{}, "") } -func syncDwViewWorkspacesClusterRole(deployContext *deploy.DeployContext) (bool, error) { - return readAndSyncObject(deployContext, DevWorkspaceViewWorkspacesClusterRoleFile, &rbacv1.ClusterRole{}, "") +func syncDwViewWorkspacesClusterRole(deployContext *chetypes.DeployContext) (bool, error) { + path := devWorkspaceTemplatesPath() + "/devworkspace-controller-view-workspaces.ClusterRole.yaml" + return readAndSyncObject(deployContext, path, &rbacv1.ClusterRole{}, "") } -func syncDwEditWorkspacesClusterRole(deployContext *deploy.DeployContext) (bool, error) { - return readAndSyncObject(deployContext, DevWorkspaceEditWorkspacesClusterRoleFile, &rbacv1.ClusterRole{}, "") +func syncDwEditWorkspacesClusterRole(deployContext *chetypes.DeployContext) (bool, error) { + path := devWorkspaceTemplatesPath() + "/devworkspace-controller-edit-workspaces.ClusterRole.yaml" + return readAndSyncObject(deployContext, path, &rbacv1.ClusterRole{}, "") } -func syncDwWorkspaceRoutingCRD(deployContext *deploy.DeployContext) (bool, error) { - return readAndSyncObject(deployContext, DevWorkspaceWorkspaceRoutingCRDFile, &apiextensionsv1.CustomResourceDefinition{}, "") +func syncDwWorkspaceRoutingCRD(deployContext *chetypes.DeployContext) (bool, error) { + path := devWorkspaceTemplatesPath() + "/devworkspaceroutings.controller.devfile.io.CustomResourceDefinition.yaml" + return readAndSyncObject(deployContext, path, &apiextensionsv1.CustomResourceDefinition{}, "") } -func syncDwTemplatesCRD(deployContext *deploy.DeployContext) (bool, error) { - return readAndSyncObject(deployContext, DevWorkspaceTemplatesCRDFile, &apiextensionsv1.CustomResourceDefinition{}, "") +func syncDwTemplatesCRD(deployContext *chetypes.DeployContext) (bool, error) { + path := devWorkspaceTemplatesPath() + "/devworkspacetemplates.workspace.devfile.io.CustomResourceDefinition.yaml" + return readAndSyncObject(deployContext, path, &apiextensionsv1.CustomResourceDefinition{}, "") } -func syncDwCRD(deployContext *deploy.DeployContext) (bool, error) { - return readAndSyncObject(deployContext, DevWorkspaceCRDFile, &apiextensionsv1.CustomResourceDefinition{}, "") +func syncDwCRD(deployContext *chetypes.DeployContext) (bool, error) { + path := devWorkspaceTemplatesPath() + "/devworkspaces.workspace.devfile.io.CustomResourceDefinition.yaml" + return readAndSyncObject(deployContext, path, &apiextensionsv1.CustomResourceDefinition{}, "") } -func syncDwConfigCRD(deployContext *deploy.DeployContext) (bool, error) { - return readAndSyncObject(deployContext, DevWorkspaceOperatorConfigCRDFile, &apiextensionsv1.CustomResourceDefinition{}, "") +func syncDwConfigCRD(deployContext *chetypes.DeployContext) (bool, error) { + path := devWorkspaceTemplatesPath() + "/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml" + return readAndSyncObject(deployContext, path, &apiextensionsv1.CustomResourceDefinition{}, "") } -func syncDwIssuer(deployContext *deploy.DeployContext) (bool, error) { - if !util.IsOpenShift { +func syncDwIssuer(deployContext *chetypes.DeployContext) (bool, error) { + if !infrastructure.IsOpenShift() { // We're using unstructured to not require a direct dependency on the cert-manager // This will cause a failure if cert-manager is not installed, which we're ok with // Also, our Sync functionality requires the scheme to have the type we want to persist registered. // In case of cert-manager objects, we don't want that because we would have to depend // on cert manager, which would require us to also update operator-sdk version because cert-manager // uses extension/v1 objects. So, we have to go the unstructured way here... - return readAndSyncUnstructured(deployContext, DevWorkspaceIssuerFile) + path := devWorkspaceTemplatesPath() + "/devworkspace-controller-selfsigned-issuer.Issuer.yaml" + return readAndSyncUnstructured(deployContext, path) } return true, nil } -func syncDwCertificate(deployContext *deploy.DeployContext) (bool, error) { - if !util.IsOpenShift { +func syncDwCertificate(deployContext *chetypes.DeployContext) (bool, error) { + if !infrastructure.IsOpenShift() { // We're using unstructured to not require a direct dependency on the cert-manager // This will cause a failure if cert-manager is not installed, which we're ok with // Also, our Sync functionality requires the scheme to have the type we want to persist registered. // In case of cert-manager objects, we don't want that because we would have to depend // on cert manager, which would require us to also update operator-sdk version because cert-manager // uses extension/v1 objects. So, we have to go the unstructured way here... - return readAndSyncUnstructured(deployContext, DevWorkspaceCertificateFile) + path := devWorkspaceTemplatesPath() + "/devworkspace-controller-serving-cert.Certificate.yaml" + return readAndSyncUnstructured(deployContext, path) } return true, nil } -func syncDwDeployment(deployContext *deploy.DeployContext) (bool, error) { +func syncDwDeployment(deployContext *chetypes.DeployContext) (bool, error) { + path := devWorkspaceTemplatesPath() + "/devworkspace-controller-manager.Deployment.yaml" deployment := &appsv1.Deployment{} - err := readK8SObject(DevWorkspaceDeploymentFile, deployment) + err := readK8SObject(path, deployment) if err != nil { return false, err } - devworkspaceControllerImage := util.GetValue(deployContext.CheCluster.Spec.DevWorkspace.ControllerImage, deploy.DefaultDevworkspaceControllerImage(deployContext.CheCluster)) + devworkspaceControllerImage := defaults.GetDevworkspaceControllerImage(deployContext.CheCluster) + if len(deployContext.CheCluster.Spec.Components.DevWorkspace.Deployment.Containers) != 0 { + devworkspaceControllerImage = utils.GetValue(deployContext.CheCluster.Spec.Components.DevWorkspace.Deployment.Containers[0].Image, devworkspaceControllerImage) + } + for contIdx, container := range deployment.Spec.Template.Spec.Containers { if container.Name == "devworkspace-controller" { deployment.Spec.Template.Spec.Containers[contIdx].Image = devworkspaceControllerImage } else { - deployment.Spec.Template.Spec.Containers[contIdx].Image = deploy.PatchDefaultImageName( + deployment.Spec.Template.Spec.Containers[contIdx].Image = defaults.PatchDefaultImageName( deployContext.CheCluster, deployment.Spec.Template.Spec.Containers[contIdx].Image, ) @@ -195,7 +196,7 @@ func syncDwDeployment(deployContext *deploy.DeployContext) (bool, error) { if env.Name == "RELATED_IMAGE_devworkspace_webhook_server" { deployment.Spec.Template.Spec.Containers[contIdx].Env[envIdx].Value = devworkspaceControllerImage } else if strings.HasPrefix(env.Name, "RELATED_IMAGE_") { - deployment.Spec.Template.Spec.Containers[contIdx].Env[envIdx].Value = deploy.PatchDefaultImageName(deployContext.CheCluster, env.Value) + deployment.Spec.Template.Spec.Containers[contIdx].Env[envIdx].Value = defaults.PatchDefaultImageName(deployContext.CheCluster, env.Value) } } } @@ -203,7 +204,7 @@ func syncDwDeployment(deployContext *deploy.DeployContext) (bool, error) { return syncObject(deployContext, deployment, DevWorkspaceNamespace) } -func readAndSyncObject(deployContext *deploy.DeployContext, yamlFile string, obj client.Object, namespace string) (bool, error) { +func readAndSyncObject(deployContext *chetypes.DeployContext, yamlFile string, obj client.Object, namespace string) (bool, error) { err := readK8SObject(yamlFile, obj) if err != nil { return false, err @@ -212,7 +213,7 @@ func readAndSyncObject(deployContext *deploy.DeployContext, yamlFile string, obj return syncObject(deployContext, obj, namespace) } -func readAndSyncUnstructured(deployContext *deploy.DeployContext, yamlFile string) (bool, error) { +func readAndSyncUnstructured(deployContext *chetypes.DeployContext, yamlFile string) (bool, error) { obj := &unstructured.Unstructured{} err := readK8SUnstructured(yamlFile, obj) if err != nil { @@ -222,7 +223,7 @@ func readAndSyncUnstructured(deployContext *deploy.DeployContext, yamlFile strin return createUnstructured(deployContext, obj) } -func createUnstructured(deployContext *deploy.DeployContext, obj *unstructured.Unstructured) (bool, error) { +func createUnstructured(deployContext *chetypes.DeployContext, obj *unstructured.Unstructured) (bool, error) { check := &unstructured.Unstructured{} check.SetGroupVersionKind(obj.GroupVersionKind()) @@ -248,7 +249,7 @@ func createUnstructured(deployContext *deploy.DeployContext, obj *unstructured.U return true, nil } -func syncObject(deployContext *deploy.DeployContext, obj2sync client.Object, namespace string) (bool, error) { +func syncObject(deployContext *chetypes.DeployContext, obj2sync client.Object, namespace string) (bool, error) { obj2sync.SetNamespace(namespace) actual, err := deployContext.ClusterAPI.Scheme.New(obj2sync.GetObjectKind().GroupVersionKind()) @@ -263,16 +264,16 @@ func syncObject(deployContext *deploy.DeployContext, obj2sync client.Object, nam } // sync objects if it does not exists or has outdated hash - if !exists || actual.(metav1.Object).GetAnnotations()[deploy.CheEclipseOrgHash256] != obj2sync.GetAnnotations()[deploy.CheEclipseOrgHash256] { - obj2sync.GetAnnotations()[deploy.CheEclipseOrgNamespace] = deployContext.CheCluster.Namespace + if !exists || actual.(metav1.Object).GetAnnotations()[constants.CheEclipseOrgHash256] != obj2sync.GetAnnotations()[constants.CheEclipseOrgHash256] { + obj2sync.GetAnnotations()[constants.CheEclipseOrgNamespace] = deployContext.CheCluster.Namespace return deploy.Sync(deployContext, obj2sync) } return true, nil } -func DevWorkspaceTemplatesPath() string { - if util.IsOpenShift { +func devWorkspaceTemplatesPath() string { + if infrastructure.IsOpenShift() { return OpenshiftDevWorkspaceTemplatesPath } return KubernetesDevWorkspaceTemplatesPath diff --git a/pkg/deploy/dev-workspace/dev_workspace_syncer_test.go b/pkg/deploy/dev-workspace/dev_workspace_syncer_test.go index d8a5f26c1..2e1b0d1dc 100644 --- a/pkg/deploy/dev-workspace/dev_workspace_syncer_test.go +++ b/pkg/deploy/dev-workspace/dev_workspace_syncer_test.go @@ -14,7 +14,9 @@ package devworkspace import ( "testing" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/constants" + "github.com/eclipse-che/che-operator/pkg/common/test" "github.com/eclipse-che/che-operator/pkg/deploy" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" @@ -23,10 +25,10 @@ import ( ) func TestShouldSyncNewObject(t *testing.T) { - deployContext := deploy.GetTestDeployContext(nil, []runtime.Object{}) + deployContext := test.GetDeployContext(nil, []runtime.Object{}) newObject := deploy.GetConfigMapSpec(deployContext, "test", map[string]string{}, "test") - newObject.GetAnnotations()[deploy.CheEclipseOrgHash256] = "hash" + newObject.GetAnnotations()[constants.CheEclipseOrgHash256] = "hash" // tries to sync a new object isDone, err := syncObject(deployContext, newObject, "eclipse-che") @@ -39,17 +41,17 @@ func TestShouldSyncNewObject(t *testing.T) { assert.NoError(t, err, "failed to get configmap") assert.True(t, exists, "configmap is not found") - assert.Equal(t, "eclipse-che", actual.GetAnnotations()[deploy.CheEclipseOrgNamespace], "hash annotation mismatch") - assert.Equal(t, "hash", actual.GetAnnotations()[deploy.CheEclipseOrgHash256], "namespace annotation mismatch") + assert.Equal(t, "eclipse-che", actual.GetAnnotations()[constants.CheEclipseOrgNamespace], "hash annotation mismatch") + assert.Equal(t, "hash", actual.GetAnnotations()[constants.CheEclipseOrgHash256], "namespace annotation mismatch") } func TestShouldSyncObjectIfItWasCreatedBySameOriginHashDifferent(t *testing.T) { - deployContext := deploy.GetTestDeployContext(nil, []runtime.Object{}) + deployContext := test.GetDeployContext(nil, []runtime.Object{}) initialObject := deploy.GetConfigMapSpec(deployContext, "test", map[string]string{"a": "b"}, "test") initialObject.SetAnnotations(map[string]string{ - deploy.CheEclipseOrgHash256: "hash", - deploy.CheEclipseOrgNamespace: "eclipse-che", + constants.CheEclipseOrgHash256: "hash", + constants.CheEclipseOrgNamespace: "eclipse-che", }) isCreated, err := deploy.Create(deployContext, initialObject) assert.NoError(t, err) @@ -57,7 +59,7 @@ func TestShouldSyncObjectIfItWasCreatedBySameOriginHashDifferent(t *testing.T) { // creates initial object newObject := deploy.GetConfigMapSpec(deployContext, "test", map[string]string{"a": "c"}, "test") - newObject.GetAnnotations()[deploy.CheEclipseOrgHash256] = "newHash" + newObject.GetAnnotations()[constants.CheEclipseOrgHash256] = "newHash" // tries to sync object with a new _, err = syncObject(deployContext, newObject, "eclipse-che") @@ -70,41 +72,31 @@ func TestShouldSyncObjectIfItWasCreatedBySameOriginHashDifferent(t *testing.T) { assert.NoError(t, err, "failed to get configmap") assert.True(t, exists, "configmap is not found") - assert.Equal(t, "eclipse-che", actual.GetAnnotations()[deploy.CheEclipseOrgNamespace], "hash annotation mismatch") - assert.Equal(t, "newHash", actual.GetAnnotations()[deploy.CheEclipseOrgHash256], "namespace annotation mismatch") + assert.Equal(t, "eclipse-che", actual.GetAnnotations()[constants.CheEclipseOrgNamespace], "hash annotation mismatch") + assert.Equal(t, "newHash", actual.GetAnnotations()[constants.CheEclipseOrgHash256], "namespace annotation mismatch") assert.Equal(t, "c", actual.Data["a"], "data mismatch") } func TestShouldNotSyncObjectIfThereIsAnotherCheCluster(t *testing.T) { - cheCluster := &orgv1.CheCluster{ + cheCluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", Namespace: "eclipse-che-1", }, - Spec: orgv1.CheClusterSpec{ - DevWorkspace: orgv1.CheClusterSpecDevWorkspace{ - Enable: true, - }, - }, } - anotherCheCluster := &orgv1.CheCluster{ + anotherCheCluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", Namespace: "eclipse-che-2", }, - Spec: orgv1.CheClusterSpec{ - DevWorkspace: orgv1.CheClusterSpecDevWorkspace{ - Enable: true, - }, - }, } - deployContext := deploy.GetTestDeployContext(cheCluster, []runtime.Object{anotherCheCluster}) + deployContext := test.GetDeployContext(cheCluster, []runtime.Object{anotherCheCluster}) // creates initial object initialObject := deploy.GetConfigMapSpec(deployContext, "test", map[string]string{"a": "b"}, "test") initialObject.SetAnnotations(map[string]string{ - deploy.CheEclipseOrgHash256: "hash-2", - deploy.CheEclipseOrgNamespace: "eclipse-che-2", + constants.CheEclipseOrgHash256: "hash-2", + constants.CheEclipseOrgNamespace: "eclipse-che-2", }) isCreated, err := deploy.Create(deployContext, initialObject) assert.NoError(t, err) @@ -112,7 +104,7 @@ func TestShouldNotSyncObjectIfThereIsAnotherCheCluster(t *testing.T) { // tries to sync object with a new hash but different origin newObject := deploy.GetConfigMapSpec(deployContext, "test", map[string]string{"a": "c"}, "test") - newObject.GetAnnotations()[deploy.CheEclipseOrgHash256] = "newHash" + newObject.GetAnnotations()[constants.CheEclipseOrgHash256] = "newHash" isDone, err := syncObject(deployContext, newObject, "eclipse-che") assert.NoError(t, err, "Failed to sync object") assert.True(t, isDone, "Failed to sync object") @@ -123,19 +115,19 @@ func TestShouldNotSyncObjectIfThereIsAnotherCheCluster(t *testing.T) { assert.NoError(t, err, "failed to get configmap") assert.True(t, exists, "configmap is not found") - assert.Equal(t, "eclipse-che-2", actual.GetAnnotations()[deploy.CheEclipseOrgNamespace], "hash annotation mismatch") - assert.Equal(t, "hash-2", actual.GetAnnotations()[deploy.CheEclipseOrgHash256], "namespace annotation mismatch") + assert.Equal(t, "eclipse-che-2", actual.GetAnnotations()[constants.CheEclipseOrgNamespace], "hash annotation mismatch") + assert.Equal(t, "hash-2", actual.GetAnnotations()[constants.CheEclipseOrgHash256], "namespace annotation mismatch") assert.Equal(t, "b", actual.Data["a"], "data mismatch") } func TestShouldNotSyncObjectIfHashIsEqual(t *testing.T) { - deployContext := deploy.GetTestDeployContext(nil, []runtime.Object{}) + deployContext := test.GetDeployContext(nil, []runtime.Object{}) // creates initial object initialObject := deploy.GetConfigMapSpec(deployContext, "test", map[string]string{"a": "b"}, "test") initialObject.SetAnnotations(map[string]string{ - deploy.CheEclipseOrgHash256: "hash", - deploy.CheEclipseOrgNamespace: "eclipse-che", + constants.CheEclipseOrgHash256: "hash", + constants.CheEclipseOrgNamespace: "eclipse-che", }) isCreated, err := deploy.Create(deployContext, initialObject) assert.NoError(t, err) @@ -143,7 +135,7 @@ func TestShouldNotSyncObjectIfHashIsEqual(t *testing.T) { // tries to sync object with the same hash newObject := deploy.GetConfigMapSpec(deployContext, "test", map[string]string{"a": "c"}, "test") - newObject.GetAnnotations()[deploy.CheEclipseOrgHash256] = "hash" + newObject.GetAnnotations()[constants.CheEclipseOrgHash256] = "hash" isDone, err := syncObject(deployContext, newObject, "eclipse-che") assert.NoError(t, err, "Failed to sync object") @@ -155,13 +147,13 @@ func TestShouldNotSyncObjectIfHashIsEqual(t *testing.T) { assert.NoError(t, err, "failed to get configmap") assert.True(t, exists, "configmap is not found") - assert.Equal(t, "eclipse-che", actual.GetAnnotations()[deploy.CheEclipseOrgNamespace], "hash annotation mismatch") - assert.Equal(t, "hash", actual.GetAnnotations()[deploy.CheEclipseOrgHash256], "namespace annotation mismatch") + assert.Equal(t, "eclipse-che", actual.GetAnnotations()[constants.CheEclipseOrgNamespace], "hash annotation mismatch") + assert.Equal(t, "hash", actual.GetAnnotations()[constants.CheEclipseOrgHash256], "namespace annotation mismatch") assert.Equal(t, "b", actual.Data["a"], "data mismatch") } func TestShouldNotSyncObjectIfNamespaceIsNotManagedByChe(t *testing.T) { - deployContext := deploy.GetTestDeployContext(nil, []runtime.Object{}) + deployContext := test.GetDeployContext(nil, []runtime.Object{}) namespace := &corev1.Namespace{ TypeMeta: metav1.TypeMeta{ @@ -188,7 +180,7 @@ func TestShouldNotSyncObjectIfNamespaceIsNotManagedByChe(t *testing.T) { // creates initial object newObject := deploy.GetConfigMapSpec(deployContext, "test", map[string]string{"a": "c"}, "test") - newObject.GetAnnotations()[deploy.CheEclipseOrgHash256] = "newHash" + newObject.GetAnnotations()[constants.CheEclipseOrgHash256] = "newHash" // tries to sync object with a new _, err = syncObject(deployContext, newObject, DevWorkspaceNamespace) @@ -201,30 +193,25 @@ func TestShouldNotSyncObjectIfNamespaceIsNotManagedByChe(t *testing.T) { assert.NoError(t, err, "failed to get configmap") assert.True(t, exists, "configmap is not found") - assert.Equal(t, "", actual.GetAnnotations()[deploy.CheEclipseOrgNamespace], "hash annotation mismatch") - assert.Equal(t, "", actual.GetAnnotations()[deploy.CheEclipseOrgHash256], "namespace annotation mismatch") + assert.Equal(t, "", actual.GetAnnotations()[constants.CheEclipseOrgNamespace], "hash annotation mismatch") + assert.Equal(t, "", actual.GetAnnotations()[constants.CheEclipseOrgHash256], "namespace annotation mismatch") assert.Equal(t, "b", actual.Data["a"], "data mismatch") } func TestShouldSyncObjectIfItWasCreatedByAnotherOriginHashDifferent(t *testing.T) { - cheCluster := &orgv1.CheCluster{ + cheCluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - DevWorkspace: orgv1.CheClusterSpecDevWorkspace{ - Enable: true, - }, - }, } - deployContext := deploy.GetTestDeployContext(cheCluster, []runtime.Object{}) + deployContext := test.GetDeployContext(cheCluster, []runtime.Object{}) // creates initial object initialObject := deploy.GetConfigMapSpec(deployContext, "test", map[string]string{"a": "b"}, "test") initialObject.SetAnnotations(map[string]string{ - deploy.CheEclipseOrgHash256: "hash2", - deploy.CheEclipseOrgNamespace: "eclipse-che-2", + constants.CheEclipseOrgHash256: "hash2", + constants.CheEclipseOrgNamespace: "eclipse-che-2", }) isCreated, err := deploy.Create(deployContext, initialObject) assert.NoError(t, err) @@ -232,7 +219,7 @@ func TestShouldSyncObjectIfItWasCreatedByAnotherOriginHashDifferent(t *testing.T // tries to sync object with a new hash but different origin newObject := deploy.GetConfigMapSpec(deployContext, "test", map[string]string{"a": "c"}, "test") - newObject.GetAnnotations()[deploy.CheEclipseOrgHash256] = "hash" + newObject.GetAnnotations()[constants.CheEclipseOrgHash256] = "hash" _, err = syncObject(deployContext, newObject, "eclipse-che") assert.NoError(t, err, "Failed to sync object") @@ -244,7 +231,7 @@ func TestShouldSyncObjectIfItWasCreatedByAnotherOriginHashDifferent(t *testing.T assert.NoError(t, err, "failed to get configmap") assert.True(t, exists, "configmap is not found") - assert.Equal(t, "eclipse-che", actual.GetAnnotations()[deploy.CheEclipseOrgNamespace], "hash annotation mismatch") - assert.Equal(t, "hash", actual.GetAnnotations()[deploy.CheEclipseOrgHash256], "namespace annotation mismatch") + assert.Equal(t, "eclipse-che", actual.GetAnnotations()[constants.CheEclipseOrgNamespace], "hash annotation mismatch") + assert.Equal(t, "hash", actual.GetAnnotations()[constants.CheEclipseOrgHash256], "namespace annotation mismatch") assert.Equal(t, "c", actual.Data["a"], "data mismatch") } diff --git a/pkg/deploy/dev-workspace/dev_workspace_test.go b/pkg/deploy/dev-workspace/dev_workspace_test.go index 810004228..c49596778 100644 --- a/pkg/deploy/dev-workspace/dev_workspace_test.go +++ b/pkg/deploy/dev-workspace/dev_workspace_test.go @@ -22,9 +22,11 @@ import ( "testing" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/constants" + "github.com/eclipse-che/che-operator/pkg/common/test" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" "github.com/stretchr/testify/assert" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" @@ -42,62 +44,47 @@ const ( func TestReconcileDevWorkspace(t *testing.T) { type testCase struct { - name string - IsOpenShift bool - IsOpenShift4 bool - cheCluster *orgv1.CheCluster + name string + infrastructure infrastructure.Type + cheCluster *chev2.CheCluster } testCases := []testCase{ { name: "Reconcile DevWorkspace on OpenShift", - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - DevWorkspace: orgv1.CheClusterSpecDevWorkspace{ - Enable: true, - }, - }, }, - IsOpenShift: true, - IsOpenShift4: true, + infrastructure: infrastructure.OpenShiftv4, }, { name: "Reconcile DevWorkspace on K8S", - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - DevWorkspace: orgv1.CheClusterSpecDevWorkspace{ - Enable: true, + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + CheServer: chev2.CheServer{ + ExtraProperties: map[string]string{"CHE_INFRA_KUBERNETES_ENABLE__UNSUPPORTED__K8S": "true"}, + }, }, - Server: orgv1.CheClusterSpecServer{ - CustomCheProperties: map[string]string{"CHE_INFRA_KUBERNETES_ENABLE__UNSUPPORTED__K8S": "true"}, - }, - K8s: orgv1.CheClusterSpecK8SOnly{ - IngressDomain: "che.domain", + Networking: chev2.CheClusterSpecNetworking{ + Domain: "che.domain", }, }, }, - IsOpenShift: false, - IsOpenShift4: false, + infrastructure: infrastructure.Kubernetes, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - deployContext := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{}) + deployContext := test.GetDeployContext(testCase.cheCluster, []runtime.Object{}) - util.IsOpenShift = testCase.IsOpenShift - util.IsOpenShift4 = testCase.IsOpenShift4 - - // reread templates (workaround after setting IsOpenShift value) - DevWorkspaceTemplates = DevWorkspaceTemplatesPath() - DevWorkspaceIssuerFile = DevWorkspaceTemplates + "/devworkspace-controller-selfsigned-issuer.Issuer.yaml" - DevWorkspaceCertificateFile = DevWorkspaceTemplates + "/devworkspace-controller-serving-cert.Certificate.yaml" + infrastructure.InitializeForTesting(testCase.infrastructure) err := os.Setenv("ALLOW_DEVWORKSPACE_ENGINE", "true") assert.NoError(t, err) @@ -111,16 +98,11 @@ func TestReconcileDevWorkspace(t *testing.T) { } func TestShouldReconcileDevWorkspaceIfDevWorkspaceDeploymentExists(t *testing.T) { - cheCluster := &orgv1.CheCluster{ + cheCluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - DevWorkspace: orgv1.CheClusterSpecDevWorkspace{ - Enable: true, - }, - }, } devworkspaceDeployment := &appsv1.Deployment{ @@ -134,10 +116,9 @@ func TestShouldReconcileDevWorkspaceIfDevWorkspaceDeploymentExists(t *testing.T) }, } - deployContext := deploy.GetTestDeployContext(cheCluster, []runtime.Object{devworkspaceDeployment}) + deployContext := test.GetDeployContext(cheCluster, []runtime.Object{devworkspaceDeployment}) - util.IsOpenShift = true - util.IsOpenShift4 = true + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) err := os.Setenv("ALLOW_DEVWORKSPACE_ENGINE", "false") assert.NoError(t, err) @@ -149,15 +130,10 @@ func TestShouldReconcileDevWorkspaceIfDevWorkspaceDeploymentExists(t *testing.T) } func TestReconcileWhenWebTerminalSubscriptionExists(t *testing.T) { - cheCluster := &orgv1.CheCluster{ + cheCluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - DevWorkspace: orgv1.CheClusterSpecDevWorkspace{ - Enable: true, - }, - }, } subscription := &operatorsv1alpha1.Subscription{ ObjectMeta: metav1.ObjectMeta{ @@ -167,7 +143,7 @@ func TestReconcileWhenWebTerminalSubscriptionExists(t *testing.T) { Spec: &operatorsv1alpha1.SubscriptionSpec{}, } - deployContext := deploy.GetTestDeployContext(cheCluster, []runtime.Object{subscription}) + deployContext := test.GetDeployContext(cheCluster, []runtime.Object{subscription}) deployContext.ClusterAPI.Scheme.AddKnownTypes(operatorsv1alpha1.SchemeGroupVersion, &operatorsv1alpha1.Subscription{}) deployContext.ClusterAPI.Scheme.AddKnownTypes(admissionregistrationv1.SchemeGroupVersion, &admissionregistrationv1.MutatingWebhookConfiguration{}) deployContext.ClusterAPI.DiscoveryClient.(*fakeDiscovery.FakeDiscovery).Fake.Resources = []*metav1.APIResourceList{ @@ -177,8 +153,8 @@ func TestReconcileWhenWebTerminalSubscriptionExists(t *testing.T) { }, }, } - util.IsOpenShift = true - util.IsOpenShift4 = true + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) err := os.Setenv("ALLOW_DEVWORKSPACE_ENGINE", "true") assert.NoError(t, err) @@ -195,15 +171,10 @@ func TestReconcileWhenWebTerminalSubscriptionExists(t *testing.T) { } func TestReconcileDevWorkspaceCheckIfCSVExists(t *testing.T) { - cheCluster := &orgv1.CheCluster{ + cheCluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - DevWorkspace: orgv1.CheClusterSpecDevWorkspace{ - Enable: true, - }, - }, } devWorkspaceCSV := &operatorsv1alpha1.ClusterServiceVersion{ ObjectMeta: metav1.ObjectMeta{ @@ -213,7 +184,7 @@ func TestReconcileDevWorkspaceCheckIfCSVExists(t *testing.T) { Spec: operatorsv1alpha1.ClusterServiceVersionSpec{}, } - deployContext := deploy.GetTestDeployContext(cheCluster, []runtime.Object{}) + deployContext := test.GetDeployContext(cheCluster, []runtime.Object{}) deployContext.ClusterAPI.Scheme.AddKnownTypes(operatorsv1alpha1.SchemeGroupVersion, &operatorsv1alpha1.ClusterServiceVersion{}) deployContext.ClusterAPI.Scheme.AddKnownTypes(operatorsv1alpha1.SchemeGroupVersion, &operatorsv1alpha1.ClusterServiceVersionList{}) err := deployContext.ClusterAPI.Client.Create(context.TODO(), devWorkspaceCSV) @@ -228,8 +199,7 @@ func TestReconcileDevWorkspaceCheckIfCSVExists(t *testing.T) { }, } - util.IsOpenShift = true - util.IsOpenShift4 = true + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) err = os.Setenv("ALLOW_DEVWORKSPACE_ENGINE", "true") assert.NoError(t, err) @@ -244,15 +214,10 @@ func TestReconcileDevWorkspaceCheckIfCSVExists(t *testing.T) { } func TestReconcileDevWorkspaceIfUnmanagedDWONamespaceExists(t *testing.T) { - cheCluster := &orgv1.CheCluster{ + cheCluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - DevWorkspace: orgv1.CheClusterSpecDevWorkspace{ - Enable: true, - }, - }, } dwoNamespace := &corev1.Namespace{ @@ -261,12 +226,12 @@ func TestReconcileDevWorkspaceIfUnmanagedDWONamespaceExists(t *testing.T) { // no che annotations are there }, } - deployContext := deploy.GetTestDeployContext(cheCluster, []runtime.Object{}) + deployContext := test.GetDeployContext(cheCluster, []runtime.Object{}) err := deployContext.ClusterAPI.Client.Create(context.TODO(), dwoNamespace) assert.NoError(t, err) - util.IsOpenShift = true - util.IsOpenShift4 = true + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + err = os.Setenv("ALLOW_DEVWORKSPACE_ENGINE", "true") assert.NoError(t, err) @@ -281,27 +246,22 @@ func TestReconcileDevWorkspaceIfUnmanagedDWONamespaceExists(t *testing.T) { } func TestReconcileDevWorkspaceIfManagedDWONamespaceExists(t *testing.T) { - cheCluster := &orgv1.CheCluster{ + cheCluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - DevWorkspace: orgv1.CheClusterSpecDevWorkspace{ - Enable: true, - }, - }, } dwoNamespace := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: DevWorkspaceNamespace, Annotations: map[string]string{ - deploy.CheEclipseOrgNamespace: "eclipse-che", + constants.CheEclipseOrgNamespace: "eclipse-che", }, // no che annotations are there }, } - deployContext := deploy.GetTestDeployContext(cheCluster, []runtime.Object{}) + deployContext := test.GetDeployContext(cheCluster, []runtime.Object{}) err := deployContext.ClusterAPI.NonCachingClient.Create(context.TODO(), dwoNamespace) assert.NoError(t, err) @@ -309,8 +269,8 @@ func TestReconcileDevWorkspaceIfManagedDWONamespaceExists(t *testing.T) { types.NamespacedName{Name: DevWorkspaceNamespace, Namespace: DevWorkspaceNamespace}, &corev1.Namespace{}) - util.IsOpenShift = true - util.IsOpenShift4 = true + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + err = os.Setenv("ALLOW_DEVWORKSPACE_ENGINE", "true") assert.NoError(t, err) @@ -329,27 +289,22 @@ func TestReconcileDevWorkspaceIfManagedDWONamespaceExists(t *testing.T) { } func TestReconcileDevWorkspaceIfManagedDWOShouldBeTakenUnderControl(t *testing.T) { - cheCluster := &orgv1.CheCluster{ + cheCluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - DevWorkspace: orgv1.CheClusterSpecDevWorkspace{ - Enable: true, - }, - }, } dwoNamespace := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: DevWorkspaceNamespace, Annotations: map[string]string{ - deploy.CheEclipseOrgNamespace: "eclipse-che-removed", + constants.CheEclipseOrgNamespace: "eclipse-che-removed", }, // no che annotations are there }, } - deployContext := deploy.GetTestDeployContext(cheCluster, []runtime.Object{}) + deployContext := test.GetDeployContext(cheCluster, []runtime.Object{}) deployContext.ClusterAPI.Scheme.AddKnownTypes(crdv1.SchemeGroupVersion, &crdv1.CustomResourceDefinition{}) err := deployContext.ClusterAPI.NonCachingClient.Create(context.TODO(), dwoNamespace) assert.NoError(t, err) @@ -358,8 +313,8 @@ func TestReconcileDevWorkspaceIfManagedDWOShouldBeTakenUnderControl(t *testing.T types.NamespacedName{Name: DevWorkspaceNamespace, Namespace: DevWorkspaceNamespace}, &corev1.Namespace{}) - util.IsOpenShift = true - util.IsOpenShift4 = true + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + err = os.Setenv("ALLOW_DEVWORKSPACE_ENGINE", "true") assert.NoError(t, err) @@ -374,7 +329,7 @@ func TestReconcileDevWorkspaceIfManagedDWOShouldBeTakenUnderControl(t *testing.T types.NamespacedName{Name: DevWorkspaceNamespace}, dwoNamespace) assert.True(t, exists, "DevWorkspace Namespace does not exist") - assert.Equal(t, "eclipse-che", dwoNamespace.GetAnnotations()[deploy.CheEclipseOrgNamespace]) + assert.Equal(t, "eclipse-che", dwoNamespace.GetAnnotations()[constants.CheEclipseOrgNamespace]) // check that objects are sync exists, err = deploy.Get(deployContext, @@ -385,39 +340,29 @@ func TestReconcileDevWorkspaceIfManagedDWOShouldBeTakenUnderControl(t *testing.T } func TestReconcileDevWorkspaceIfManagedDWOShouldNotBeTakenUnderControl(t *testing.T) { - cheCluster := &orgv1.CheCluster{ + cheCluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "che-cluster", }, - Spec: orgv1.CheClusterSpec{ - DevWorkspace: orgv1.CheClusterSpecDevWorkspace{ - Enable: true, - }, - }, } - cheCluster2 := &orgv1.CheCluster{ + cheCluster2 := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che2", Name: "che-cluster2", }, - Spec: orgv1.CheClusterSpec{ - DevWorkspace: orgv1.CheClusterSpecDevWorkspace{ - Enable: true, - }, - }, } dwoNamespace := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: DevWorkspaceNamespace, Annotations: map[string]string{ - deploy.CheEclipseOrgNamespace: "eclipse-che2", + constants.CheEclipseOrgNamespace: "eclipse-che2", }, // no che annotations are there }, } - deployContext := deploy.GetTestDeployContext(cheCluster, []runtime.Object{}) + deployContext := test.GetDeployContext(cheCluster, []runtime.Object{}) deployContext.ClusterAPI.Scheme.AddKnownTypes(crdv1.SchemeGroupVersion, &crdv1.CustomResourceDefinition{}) err := deployContext.ClusterAPI.NonCachingClient.Create(context.TODO(), dwoNamespace) assert.NoError(t, err) @@ -428,8 +373,8 @@ func TestReconcileDevWorkspaceIfManagedDWOShouldNotBeTakenUnderControl(t *testin types.NamespacedName{Name: DevWorkspaceNamespace, Namespace: DevWorkspaceNamespace}, &corev1.Namespace{}) - util.IsOpenShift = true - util.IsOpenShift4 = true + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + err = os.Setenv("ALLOW_DEVWORKSPACE_ENGINE", "true") assert.NoError(t, err) @@ -444,7 +389,7 @@ func TestReconcileDevWorkspaceIfManagedDWOShouldNotBeTakenUnderControl(t *testin types.NamespacedName{Name: DevWorkspaceNamespace}, dwoNamespace) assert.True(t, exists, "DevWorkspace Namespace does not exist") - assert.Equal(t, "eclipse-che2", dwoNamespace.GetAnnotations()[deploy.CheEclipseOrgNamespace]) + assert.Equal(t, "eclipse-che2", dwoNamespace.GetAnnotations()[constants.CheEclipseOrgNamespace]) // check that objects are sync exists, err = deploy.Get(deployContext, diff --git a/pkg/deploy/dev-workspace/dev_workspace_utils.go b/pkg/deploy/dev-workspace/dev_workspace_utils.go index 05680a2a8..e89b28428 100644 --- a/pkg/deploy/dev-workspace/dev_workspace_utils.go +++ b/pkg/deploy/dev-workspace/dev_workspace_utils.go @@ -16,9 +16,11 @@ import ( "context" "strings" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" @@ -28,9 +30,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -func isDevWorkspaceOperatorCSVExists(deployContext *deploy.DeployContext) bool { +func isDevWorkspaceOperatorCSVExists(deployContext *chetypes.DeployContext) bool { // If clusterserviceversions resource doesn't exist in cluster DWO as well will not be present - if !util.HasK8SResourceObject(deployContext.ClusterAPI.DiscoveryClient, ClusterServiceVersionResourceName) { + if !utils.IsK8SResourceServed(deployContext.ClusterAPI.DiscoveryClient, ClusterServiceVersionResourceName) { return false } @@ -50,9 +52,9 @@ func isDevWorkspaceOperatorCSVExists(deployContext *deploy.DeployContext) bool { return false } -func isWebTerminalSubscriptionExist(deployContext *deploy.DeployContext) (bool, error) { +func isWebTerminalSubscriptionExist(deployContext *chetypes.DeployContext) (bool, error) { // If subscriptions resource doesn't exist in cluster WTO as well will not be present - if !util.HasK8SResourceObject(deployContext.ClusterAPI.DiscoveryClient, SubscriptionResourceName) { + if !utils.IsK8SResourceServed(deployContext.ClusterAPI.DiscoveryClient, SubscriptionResourceName) { return false, nil } @@ -74,12 +76,12 @@ func isWebTerminalSubscriptionExist(deployContext *deploy.DeployContext) (bool, return true, nil } -func createDwNamespace(deployContext *deploy.DeployContext) (bool, error) { +func createDwNamespace(deployContext *chetypes.DeployContext) (bool, error) { namespace := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: DevWorkspaceNamespace, Annotations: map[string]string{ - deploy.CheEclipseOrgNamespace: deployContext.CheCluster.Namespace, + constants.CheEclipseOrgNamespace: deployContext.CheCluster.Namespace, }, }, Spec: corev1.NamespaceSpec{}, @@ -88,8 +90,8 @@ func createDwNamespace(deployContext *deploy.DeployContext) (bool, error) { return deploy.CreateIfNotExists(deployContext, namespace) } -func isOnlyOneOperatorManagesDWResources(deployContext *deploy.DeployContext) (bool, error) { - cheClusters := &orgv1.CheClusterList{} +func isOnlyOneOperatorManagesDWResources(deployContext *chetypes.DeployContext) (bool, error) { + cheClusters := &chev2.CheClusterList{} err := deployContext.ClusterAPI.NonCachingClient.List(context.TODO(), cheClusters) if err != nil { return false, err diff --git a/pkg/deploy/dev-workspace/init_test.go b/pkg/deploy/dev-workspace/init_test.go index 15abfd64f..0e59a6937 100644 --- a/pkg/deploy/dev-workspace/init_test.go +++ b/pkg/deploy/dev-workspace/init_test.go @@ -11,11 +11,15 @@ // package devworkspace -import "github.com/eclipse-che/che-operator/pkg/deploy" +import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" +) func init() { - err := deploy.InitTestDefaultsFromDeployment("../../../config/manager/manager.yaml") - if err != nil { - panic(err) - } + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../../../config/manager/manager.yaml") } diff --git a/pkg/deploy/devfileregistry/devfileregistry.go b/pkg/deploy/devfileregistry/devfileregistry.go index 97d45f591..66d33478f 100644 --- a/pkg/deploy/devfileregistry/devfileregistry.go +++ b/pkg/deploy/devfileregistry/devfileregistry.go @@ -14,6 +14,8 @@ package devfileregistry import ( "fmt" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" "github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy/expose" "github.com/eclipse-che/che-operator/pkg/deploy/gateway" @@ -28,9 +30,11 @@ func NewDevfileRegistryReconciler() *DevfileRegistryReconciler { return &DevfileRegistryReconciler{} } -func (d *DevfileRegistryReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { - if ctx.CheCluster.Spec.Server.ExternalDevfileRegistry { - return reconcile.Result{}, true, nil +func (d *DevfileRegistryReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { + if ctx.CheCluster.Spec.Components.DevfileRegistry.DisableInternalRegistry { + ctx.CheCluster.Status.DevfileRegistryURL = "" + err := deploy.UpdateCheCRStatus(ctx, "DevfileRegistryURL", "") + return reconcile.Result{}, err == nil, err } done, err := d.syncService(ctx) @@ -61,35 +65,35 @@ func (d *DevfileRegistryReconciler) Reconcile(ctx *deploy.DeployContext) (reconc return reconcile.Result{}, true, nil } -func (d *DevfileRegistryReconciler) Finalize(ctx *deploy.DeployContext) bool { +func (d *DevfileRegistryReconciler) Finalize(ctx *chetypes.DeployContext) bool { return true } -func (d *DevfileRegistryReconciler) syncService(ctx *deploy.DeployContext) (bool, error) { +func (d *DevfileRegistryReconciler) syncService(ctx *chetypes.DeployContext) (bool, error) { return deploy.SyncServiceToCluster( ctx, - deploy.DevfileRegistryName, + constants.DevfileRegistryName, []string{"http"}, []int32{8080}, - deploy.DevfileRegistryName) + constants.DevfileRegistryName) } -func (d *DevfileRegistryReconciler) syncConfigMap(ctx *deploy.DeployContext) (bool, error) { +func (d *DevfileRegistryReconciler) syncConfigMap(ctx *chetypes.DeployContext) (bool, error) { data, err := d.getConfigMapData(ctx) if err != nil { return false, err } - return deploy.SyncConfigMapDataToCluster(ctx, deploy.DevfileRegistryName, data, deploy.DevfileRegistryName) + return deploy.SyncConfigMapDataToCluster(ctx, constants.DevfileRegistryName, data, constants.DevfileRegistryName) } -func (d *DevfileRegistryReconciler) exposeEndpoint(ctx *deploy.DeployContext) (string, bool, error) { +func (d *DevfileRegistryReconciler) exposeEndpoint(ctx *chetypes.DeployContext) (string, bool, error) { return expose.Expose( ctx, - deploy.DevfileRegistryName, + constants.DevfileRegistryName, d.createGatewayConfig()) } -func (d *DevfileRegistryReconciler) updateStatus(endpoint string, ctx *deploy.DeployContext) (bool, error) { +func (d *DevfileRegistryReconciler) updateStatus(endpoint string, ctx *chetypes.DeployContext) (bool, error) { devfileRegistryURL := "https://" + endpoint if devfileRegistryURL != ctx.CheCluster.Status.DevfileRegistryURL { ctx.CheCluster.Status.DevfileRegistryURL = devfileRegistryURL @@ -101,18 +105,18 @@ func (d *DevfileRegistryReconciler) updateStatus(endpoint string, ctx *deploy.De return true, nil } -func (d *DevfileRegistryReconciler) syncDeployment(ctx *deploy.DeployContext) (bool, error) { +func (d *DevfileRegistryReconciler) syncDeployment(ctx *chetypes.DeployContext) (bool, error) { spec := d.getDevfileRegistryDeploymentSpec(ctx) return deploy.SyncDeploymentSpecToCluster(ctx, spec, deploy.DefaultDeploymentDiffOpts) } func (d *DevfileRegistryReconciler) createGatewayConfig() *gateway.TraefikConfig { - pathPrefix := "/" + deploy.DevfileRegistryName + pathPrefix := "/" + constants.DevfileRegistryName cfg := gateway.CreateCommonTraefikConfig( - deploy.DevfileRegistryName, + constants.DevfileRegistryName, fmt.Sprintf("PathPrefix(`%s`)", pathPrefix), 10, - "http://"+deploy.DevfileRegistryName+":8080", + "http://"+constants.DevfileRegistryName+":8080", []string{pathPrefix}) return cfg diff --git a/pkg/deploy/devfileregistry/devfileregistry_configmap.go b/pkg/deploy/devfileregistry/devfileregistry_configmap.go index ba3187c85..d52660fef 100644 --- a/pkg/deploy/devfileregistry/devfileregistry_configmap.go +++ b/pkg/deploy/devfileregistry/devfileregistry_configmap.go @@ -15,7 +15,9 @@ import ( "encoding/json" "fmt" - "github.com/eclipse-che/che-operator/pkg/deploy" + "github.com/eclipse-che/che-operator/pkg/common/constants" + + "github.com/eclipse-che/che-operator/pkg/common/chetypes" ) type DevFileRegistryConfigMap struct { @@ -25,13 +27,13 @@ type DevFileRegistryConfigMap struct { CheDevfileRegistryInternalURL string `json:"CHE_DEVFILE_REGISTRY_INTERNAL_URL"` } -func (d *DevfileRegistryReconciler) getConfigMapData(ctx *deploy.DeployContext) (map[string]string, error) { +func (d *DevfileRegistryReconciler) getConfigMapData(ctx *chetypes.DeployContext) (map[string]string, error) { devfileRegistryEnv := make(map[string]string) data := &DevFileRegistryConfigMap{ - CheDevfileImagesRegistryURL: ctx.CheCluster.Spec.Server.AirGapContainerRegistryHostname, - CheDevfileImagesRegistryOrganization: ctx.CheCluster.Spec.Server.AirGapContainerRegistryOrganization, + CheDevfileImagesRegistryURL: ctx.CheCluster.Spec.ContainerRegistry.Hostname, + CheDevfileImagesRegistryOrganization: ctx.CheCluster.Spec.ContainerRegistry.Organization, CheDevfileRegistryURL: ctx.CheCluster.Status.DevfileRegistryURL, - CheDevfileRegistryInternalURL: fmt.Sprintf("http://%s.%s.svc:8080", deploy.DevfileRegistryName, ctx.CheCluster.Namespace), + CheDevfileRegistryInternalURL: fmt.Sprintf("http://%s.%s.svc:8080", constants.DevfileRegistryName, ctx.CheCluster.Namespace), } out, err := json.Marshal(data) diff --git a/pkg/deploy/devfileregistry/devfileregistry_deployment.go b/pkg/deploy/devfileregistry/devfileregistry_deployment.go index 01198dd70..15708b424 100644 --- a/pkg/deploy/devfileregistry/devfileregistry_deployment.go +++ b/pkg/deploy/devfileregistry/devfileregistry_deployment.go @@ -12,40 +12,36 @@ package devfileregistry import ( + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy/registry" - "github.com/eclipse-che/che-operator/pkg/util" appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" ) -func (d *DevfileRegistryReconciler) getDevfileRegistryDeploymentSpec(ctx *deploy.DeployContext) *appsv1.Deployment { +func (d *DevfileRegistryReconciler) getDevfileRegistryDeploymentSpec(ctx *chetypes.DeployContext) *appsv1.Deployment { registryType := "devfile" - registryImage := util.GetValue(ctx.CheCluster.Spec.Server.DevfileRegistryImage, deploy.DefaultDevfileRegistryImage(ctx.CheCluster)) - registryImagePullPolicy := v1.PullPolicy(util.GetValue(string(ctx.CheCluster.Spec.Server.DevfileRegistryPullPolicy), deploy.DefaultPullPolicyFromDockerImage(registryImage))) + registryImage := defaults.GetDevfileRegistryImage(ctx.CheCluster) + registryImagePullPolicy := v1.PullPolicy(utils.GetPullPolicyFromDockerImage(registryImage)) probePath := "/devfiles/" - devfileImagesEnv := util.GetEnvByRegExp("^.*devfile_registry_image.*$") + devfileImagesEnv := utils.GetEnvByRegExp("^.*devfile_registry_image.*$") resources := v1.ResourceRequirements{ Requests: v1.ResourceList{ - v1.ResourceMemory: util.GetResourceQuantity( - ctx.CheCluster.Spec.Server.DevfileRegistryMemoryRequest, - deploy.DefaultDevfileRegistryMemoryRequest), - v1.ResourceCPU: util.GetResourceQuantity( - ctx.CheCluster.Spec.Server.DevfileRegistryCpuRequest, - deploy.DefaultDevfileRegistryCpuRequest), + v1.ResourceMemory: resource.MustParse(constants.DefaultDevfileRegistryMemoryRequest), + v1.ResourceCPU: resource.MustParse(constants.DefaultDevfileRegistryCpuRequest), }, Limits: v1.ResourceList{ - v1.ResourceMemory: util.GetResourceQuantity( - ctx.CheCluster.Spec.Server.DevfileRegistryMemoryLimit, - deploy.DefaultDevfileRegistryMemoryLimit), - v1.ResourceCPU: util.GetResourceQuantity( - ctx.CheCluster.Spec.Server.DevfileRegistryCpuLimit, - deploy.DefaultDevfileRegistryCpuLimit), + v1.ResourceMemory: resource.MustParse(constants.DefaultDevfileRegistryMemoryLimit), + v1.ResourceCPU: resource.MustParse(constants.DefaultDevfileRegistryCpuLimit), }, } - return registry.GetSpecRegistryDeployment( + deployment := registry.GetSpecRegistryDeployment( ctx, registryType, registryImage, @@ -53,4 +49,6 @@ func (d *DevfileRegistryReconciler) getDevfileRegistryDeploymentSpec(ctx *deploy registryImagePullPolicy, resources, probePath) + deploy.CustomizeDeployment(deployment, &ctx.CheCluster.Spec.Components.DevfileRegistry.Deployment, false) + return deployment } diff --git a/pkg/deploy/devfileregistry/devfileregistry_deployment_test.go b/pkg/deploy/devfileregistry/devfileregistry_deployment_test.go index 14fccae8a..92a9af0a7 100644 --- a/pkg/deploy/devfileregistry/devfileregistry_deployment_test.go +++ b/pkg/deploy/devfileregistry/devfileregistry_deployment_test.go @@ -14,11 +14,12 @@ package devfileregistry import ( "os" - "github.com/eclipse-che/che-operator/pkg/util" + "k8s.io/apimachinery/pkg/api/resource" - "github.com/eclipse-che/che-operator/pkg/deploy" + "github.com/eclipse-che/che-operator/pkg/common/constants" + "github.com/eclipse-che/che-operator/pkg/common/test" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -35,18 +36,18 @@ func TestGetDevfileRegistryDeploymentSpec(t *testing.T) { memoryRequest string cpuRequest string cpuLimit string - cheCluster *orgv1.CheCluster + cheCluster *chev2.CheCluster } testCases := []testCase{ { name: "Test default limits", initObjects: []runtime.Object{}, - memoryLimit: deploy.DefaultDevfileRegistryMemoryLimit, - memoryRequest: deploy.DefaultDevfileRegistryMemoryRequest, - cpuLimit: deploy.DefaultDevfileRegistryCpuLimit, - cpuRequest: deploy.DefaultDevfileRegistryCpuRequest, - cheCluster: &orgv1.CheCluster{ + memoryLimit: constants.DefaultDevfileRegistryMemoryLimit, + memoryRequest: constants.DefaultDevfileRegistryMemoryRequest, + cpuLimit: constants.DefaultDevfileRegistryCpuLimit, + cpuRequest: constants.DefaultDevfileRegistryCpuRequest, + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", @@ -60,17 +61,32 @@ func TestGetDevfileRegistryDeploymentSpec(t *testing.T) { cpuRequest: "150m", memoryLimit: "250Mi", memoryRequest: "150Mi", - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - DevfileRegistryCpuLimit: "250m", - DevfileRegistryCpuRequest: "150m", - DevfileRegistryMemoryLimit: "250Mi", - DevfileRegistryMemoryRequest: "150Mi", + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + DevfileRegistry: chev2.DevfileRegistry{ + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: constants.DevfileRegistryName, + Resources: chev2.ResourceRequirements{ + Requests: chev2.ResourceList{ + Memory: resource.MustParse("150Mi"), + Cpu: resource.MustParse("150m"), + }, + Limits: chev2.ResourceList{ + Memory: resource.MustParse("250Mi"), + Cpu: resource.MustParse("250m"), + }, + }, + }, + }, + }, + }, }, }, }, @@ -80,13 +96,13 @@ func TestGetDevfileRegistryDeploymentSpec(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true))) - ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{}) + ctx := test.GetDeployContext(testCase.cheCluster, []runtime.Object{}) devfileregistry := NewDevfileRegistryReconciler() deployment := devfileregistry.getDevfileRegistryDeploymentSpec(ctx) - util.CompareResources(deployment, - util.TestExpectedResources{ + test.CompareResources(deployment, + test.TestExpectedResources{ MemoryLimit: testCase.memoryLimit, MemoryRequest: testCase.memoryRequest, CpuRequest: testCase.cpuRequest, @@ -94,7 +110,7 @@ func TestGetDevfileRegistryDeploymentSpec(t *testing.T) { }, t) - util.ValidateSecurityContext(deployment, t) + test.ValidateSecurityContext(deployment, t) }) } } diff --git a/pkg/deploy/devfileregistry/devfileregistry_test.go b/pkg/deploy/devfileregistry/devfileregistry_test.go index 34475c25f..277a92297 100644 --- a/pkg/deploy/devfileregistry/devfileregistry_test.go +++ b/pkg/deploy/devfileregistry/devfileregistry_test.go @@ -12,11 +12,9 @@ package devfileregistry import ( - "os" - - orgv1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/test" "github.com/stretchr/testify/assert" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -28,48 +26,38 @@ import ( ) func TestDevfileRegistryReconcile(t *testing.T) { - util.IsOpenShift = true - ctx := deploy.GetTestDeployContext(nil, []runtime.Object{}) + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + + ctx := test.GetDeployContext(nil, []runtime.Object{}) devfileregistry := NewDevfileRegistryReconciler() _, done, err := devfileregistry.Reconcile(ctx) assert.True(t, done) assert.Nil(t, err) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, &corev1.Service{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, &corev1.ConfigMap{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, &appsv1.Deployment{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, &corev1.Service{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, &corev1.ConfigMap{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, &appsv1.Deployment{})) assert.NotEmpty(t, ctx.CheCluster.Status.DevfileRegistryURL) } func TestShouldSetUpCorrectlyDevfileRegistryURL(t *testing.T) { type testCase struct { name string - isOpenShift bool - isOpenShift4 bool initObjects []runtime.Object - cheCluster *orgv1.CheCluster + cheCluster *chev2.CheCluster expectedDevfileRegistryURL string } testCases := []testCase{ { name: "Test Status.DevfileRegistryURL #1", - cheCluster: &orgv1.CheCluster{ - TypeMeta: metav1.TypeMeta{ - Kind: "CheCluster", - APIVersion: "org.eclipse.che/v1", - }, + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", - Name: os.Getenv("CHE_FLAVOR"), + Name: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ExternalDevfileRegistry: false, - }, - }, - Status: orgv1.CheClusterStatus{ + Status: chev2.CheClusterStatus{ CheURL: "https://che-host", }, }, @@ -77,51 +65,44 @@ func TestShouldSetUpCorrectlyDevfileRegistryURL(t *testing.T) { }, { name: "Test Status.DevfileRegistryURL #2", - cheCluster: &orgv1.CheCluster{ - TypeMeta: metav1.TypeMeta{ - Kind: "CheCluster", - APIVersion: "org.eclipse.che/v1", - }, + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", - Name: os.Getenv("CHE_FLAVOR"), + Name: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ExternalDevfileRegistry: false, - DevfileRegistryUrl: "https://devfile-registry.external.1", - ExternalDevfileRegistries: []orgv1.ExternalDevfileRegistries{ - {Url: "https://devfile-registry.external.2"}, + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + DevfileRegistry: chev2.DevfileRegistry{ + ExternalDevfileRegistries: []chev2.ExternalDevfileRegistry{ + {Url: "https://devfile-registry.external.2"}, + }, }, }, }, - Status: orgv1.CheClusterStatus{ + Status: chev2.CheClusterStatus{ CheURL: "https://che-host", }, }, expectedDevfileRegistryURL: "https://che-host/devfile-registry", }, { - name: "Test Status.DevfileRegistryURL #2", - cheCluster: &orgv1.CheCluster{ - TypeMeta: metav1.TypeMeta{ - Kind: "CheCluster", - APIVersion: "org.eclipse.che/v1", - }, + name: "Test Status.DevfileRegistryURL #3", + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", - Name: os.Getenv("CHE_FLAVOR"), + Name: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ExternalDevfileRegistry: true, - DevfileRegistryUrl: "https://devfile-registry.external.1", - ExternalDevfileRegistries: []orgv1.ExternalDevfileRegistries{ - {Url: "https://devfile-registry.external.2"}, + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + DevfileRegistry: chev2.DevfileRegistry{ + DisableInternalRegistry: true, + ExternalDevfileRegistries: []chev2.ExternalDevfileRegistry{ + {Url: "https://devfile-registry.external.2"}, + }, }, }, }, - Status: orgv1.CheClusterStatus{ + Status: chev2.CheClusterStatus{ CheURL: "https://che-host", }, }, @@ -131,10 +112,7 @@ func TestShouldSetUpCorrectlyDevfileRegistryURL(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{}) - - util.IsOpenShift = testCase.isOpenShift - util.IsOpenShift4 = testCase.isOpenShift4 + ctx := test.GetDeployContext(testCase.cheCluster, []runtime.Object{}) devfileregistry := NewDevfileRegistryReconciler() devfileregistry.Reconcile(ctx) diff --git a/pkg/deploy/devfileregistry/init_test.go b/pkg/deploy/devfileregistry/init_test.go index 4baca18fe..f40b289e4 100644 --- a/pkg/deploy/devfileregistry/init_test.go +++ b/pkg/deploy/devfileregistry/init_test.go @@ -11,11 +11,15 @@ // package devfileregistry -import "github.com/eclipse-che/che-operator/pkg/deploy" +import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" +) func init() { - err := deploy.InitTestDefaultsFromDeployment("../../../config/manager/manager.yaml") - if err != nil { - panic(err) - } + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../../../config/manager/manager.yaml") } diff --git a/pkg/deploy/expose/expose.go b/pkg/deploy/expose/expose.go index 41d8c5f9e..0518c99cb 100644 --- a/pkg/deploy/expose/expose.go +++ b/pkg/deploy/expose/expose.go @@ -14,11 +14,13 @@ package expose import ( "strings" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" routev1 "github.com/openshift/api/route/v1" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/test" "github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy/gateway" - "github.com/eclipse-che/che-operator/pkg/util" "github.com/sirupsen/logrus" networking "k8s.io/api/networking/v1" ) @@ -29,7 +31,7 @@ const ( //Expose exposes the specified component according to the configured exposure strategy rules func Expose( - deployContext *deploy.DeployContext, + deployContext *chetypes.DeployContext, componentName string, gatewayConfig *gateway.TraefikConfig) (endpointUrl string, done bool, err error) { //the host and path are empty and will be evaluated for the specified component + path @@ -40,7 +42,7 @@ func Expose( //Empty host or path will be evaluated according to the configured strategy rules. //Note: path may be prefixed according to the configured strategy rules. func ExposeWithHostPath( - deployContext *deploy.DeployContext, + deployContext *chetypes.DeployContext, component string, host string, path string, @@ -50,7 +52,7 @@ func ExposeWithHostPath( path = "/" + path } - if !util.IsOpenShift { + if !infrastructure.IsOpenShift() { return exposeWithGateway(deployContext, gatewayConfig, component, path, func() { if _, err = deploy.DeleteNamespacedObject(deployContext, component, &networking.Ingress{}); err != nil { logrus.Error(err) @@ -58,14 +60,14 @@ func ExposeWithHostPath( }) } else { return exposeWithGateway(deployContext, gatewayConfig, component, path, func() { - if _, err := deploy.DeleteNamespacedObject(deployContext, component, &routev1.Route{}); !util.IsTestMode() && err != nil { + if _, err := deploy.DeleteNamespacedObject(deployContext, component, &routev1.Route{}); !test.IsTestMode() && err != nil { logrus.Error(err) } }) } } -func exposeWithGateway(deployContext *deploy.DeployContext, +func exposeWithGateway(deployContext *chetypes.DeployContext, gatewayConfig *gateway.TraefikConfig, component string, path string, @@ -88,5 +90,5 @@ func exposeWithGateway(deployContext *deploy.DeployContext, if path == "" { path = "/" + component } - return deployContext.CheCluster.GetCheHost() + path, true, err + return deployContext.CheHost + path, true, err } diff --git a/pkg/deploy/finalizer.go b/pkg/deploy/finalizer.go index 8505f20d9..04790a84b 100644 --- a/pkg/deploy/finalizer.go +++ b/pkg/deploy/finalizer.go @@ -14,14 +14,19 @@ package deploy import ( "context" - "github.com/eclipse-che/che-operator/pkg/util" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/api/errors" "sigs.k8s.io/controller-runtime/pkg/client" ) -func AppendFinalizer(deployContext *DeployContext, finalizer string) error { - if !util.ContainsString(deployContext.CheCluster.ObjectMeta.Finalizers, finalizer) { +func AppendFinalizer(deployContext *chetypes.DeployContext, finalizer string) error { + if err := ReloadCheClusterCR(deployContext); err != nil { + return err + } + + if !utils.Contains(deployContext.CheCluster.ObjectMeta.Finalizers, finalizer) { for { deployContext.CheCluster.ObjectMeta.Finalizers = append(deployContext.CheCluster.ObjectMeta.Finalizers, finalizer) err := deployContext.ClusterAPI.Client.Update(context.TODO(), deployContext.CheCluster) @@ -42,10 +47,10 @@ func AppendFinalizer(deployContext *DeployContext, finalizer string) error { return nil } -func DeleteFinalizer(deployContext *DeployContext, finalizer string) error { - if util.ContainsString(deployContext.CheCluster.ObjectMeta.Finalizers, finalizer) { +func DeleteFinalizer(deployContext *chetypes.DeployContext, finalizer string) error { + if utils.Contains(deployContext.CheCluster.ObjectMeta.Finalizers, finalizer) { for { - deployContext.CheCluster.ObjectMeta.Finalizers = util.DoRemoveString(deployContext.CheCluster.ObjectMeta.Finalizers, finalizer) + deployContext.CheCluster.ObjectMeta.Finalizers = utils.Remove(deployContext.CheCluster.ObjectMeta.Finalizers, finalizer) err := deployContext.ClusterAPI.Client.Update(context.TODO(), deployContext.CheCluster) if err == nil { logrus.Infof("Deleted finalizer: %s", finalizer) @@ -64,7 +69,7 @@ func DeleteFinalizer(deployContext *DeployContext, finalizer string) error { return nil } -func DeleteObjectWithFinalizer(deployContext *DeployContext, key client.ObjectKey, objectMeta client.Object, finalizer string) error { +func DeleteObjectWithFinalizer(deployContext *chetypes.DeployContext, key client.ObjectKey, objectMeta client.Object, finalizer string) error { _, err := Delete(deployContext, key, objectMeta) if err != nil { // failed to delete, shouldn't us prevent from removing finalizer diff --git a/pkg/deploy/finalizer_test.go b/pkg/deploy/finalizer_test.go index 49f9de8e4..18aa64bea 100644 --- a/pkg/deploy/finalizer_test.go +++ b/pkg/deploy/finalizer_test.go @@ -14,8 +14,9 @@ package deploy import ( "os" - orgv1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/pkg/util" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/utils" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -31,19 +32,19 @@ const ( ) func TestAppendFinalizer(t *testing.T) { - cheCluster := &orgv1.CheCluster{ + cheCluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, } logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true))) - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme, cheCluster) - deployContext := &DeployContext{ + deployContext := &chetypes.DeployContext{ CheCluster: cheCluster, - ClusterAPI: ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, Scheme: scheme.Scheme, }, @@ -54,7 +55,7 @@ func TestAppendFinalizer(t *testing.T) { t.Fatalf("Failed to append finalizer: %v", err) } - if !util.ContainsString(deployContext.CheCluster.ObjectMeta.Finalizers, finalizer) { + if !utils.Contains(deployContext.CheCluster.ObjectMeta.Finalizers, finalizer) { t.Fatalf("Failed to append finalizer: %v", err) } @@ -70,7 +71,7 @@ func TestAppendFinalizer(t *testing.T) { } func TestDeleteFinalizer(t *testing.T) { - cheCluster := &orgv1.CheCluster{ + cheCluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", @@ -78,12 +79,12 @@ func TestDeleteFinalizer(t *testing.T) { }, } logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true))) - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme, cheCluster) - deployContext := &DeployContext{ + deployContext := &chetypes.DeployContext{ CheCluster: cheCluster, - ClusterAPI: ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, Scheme: scheme.Scheme, }, @@ -94,7 +95,7 @@ func TestDeleteFinalizer(t *testing.T) { t.Fatalf("Failed to append finalizer: %v", err) } - if util.ContainsString(deployContext.CheCluster.ObjectMeta.Finalizers, finalizer) { + if utils.Contains(deployContext.CheCluster.ObjectMeta.Finalizers, finalizer) { t.Fatalf("Failed to delete finalizer: %v", err) } } diff --git a/pkg/deploy/gateway/gateway.go b/pkg/deploy/gateway/gateway.go index cd5de7c3b..1e27c9a30 100644 --- a/pkg/deploy/gateway/gateway.go +++ b/pkg/deploy/gateway/gateway.go @@ -22,12 +22,17 @@ import ( "sigs.k8s.io/yaml" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" "github.com/sirupsen/logrus" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/eclipse-che/che-operator/pkg/deploy" - orgv1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/pkg/util" + chev2 "github.com/eclipse-che/che-operator/api/v2" "github.com/google/go-cmp/cmp/cmpopts" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -70,7 +75,7 @@ func NewGatewayReconciler() *GatewayReconciler { return &GatewayReconciler{} } -func (p *GatewayReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { +func (p *GatewayReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { err := SyncGatewayToCluster(ctx) if err != nil { return reconcile.Result{}, false, err @@ -79,16 +84,16 @@ func (p *GatewayReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Resu return reconcile.Result{}, true, nil } -func (p *GatewayReconciler) Finalize(ctx *deploy.DeployContext) bool { +func (p *GatewayReconciler) Finalize(ctx *chetypes.DeployContext) bool { return true } // SyncGatewayToCluster installs or deletes the gateway based on the custom resource configuration -func SyncGatewayToCluster(deployContext *deploy.DeployContext) error { +func SyncGatewayToCluster(deployContext *chetypes.DeployContext) error { return syncAll(deployContext) } -func syncAll(deployContext *deploy.DeployContext) error { +func syncAll(deployContext *chetypes.DeployContext) error { instance := deployContext.CheCluster sa := getGatewayServiceAccountSpec(instance) if _, err := deploy.Sync(deployContext, &sa, serviceAccountDiffOpts); err != nil { @@ -122,7 +127,7 @@ func syncAll(deployContext *deploy.DeployContext) error { return err } - if util.IsOpenShift { + if infrastructure.IsOpenShift() { if headerRewritePluginConfig, err := getGatewayHeaderRewritePluginConfigSpec(instance); err == nil { if _, err := deploy.Sync(deployContext, headerRewritePluginConfig, configMapDiffOpts); err != nil { return err @@ -165,7 +170,7 @@ func syncAll(deployContext *deploy.DeployContext) error { return nil } -func getGatewaySecretSpec(deployContext *deploy.DeployContext) (*corev1.Secret, error) { +func getGatewaySecretSpec(deployContext *chetypes.DeployContext) (*corev1.Secret, error) { secret := &corev1.Secret{} exists, err := deploy.GetNamespacedObject(deployContext, gatewayOauthSecretName, secret) if err == nil && exists { @@ -181,7 +186,7 @@ func getGatewaySecretSpec(deployContext *deploy.DeployContext) (*corev1.Secret, } } -func generateOauthSecretSpec(deployContext *deploy.DeployContext) *corev1.Secret { +func generateOauthSecretSpec(deployContext *chetypes.DeployContext) *corev1.Secret { return &corev1.Secret{ TypeMeta: metav1.TypeMeta{ APIVersion: corev1.SchemeGroupVersion.String(), @@ -190,7 +195,7 @@ func generateOauthSecretSpec(deployContext *deploy.DeployContext) *corev1.Secret ObjectMeta: metav1.ObjectMeta{ Name: gatewayOauthSecretName, Namespace: deployContext.CheCluster.Namespace, - Labels: deploy.GetLabels(deployContext.CheCluster, GatewayServiceName), + Labels: deploy.GetLabels(GatewayServiceName), }, Data: map[string][]byte{ "cookie_secret": generateRandomCookieSecret(), @@ -198,7 +203,7 @@ func generateOauthSecretSpec(deployContext *deploy.DeployContext) *corev1.Secret } } -func delete(clusterAPI deploy.ClusterAPI, obj metav1.Object) error { +func delete(clusterAPI chetypes.ClusterAPI, obj metav1.Object) error { key := client.ObjectKey{Name: obj.GetName(), Namespace: obj.GetNamespace()} ro := obj.(client.Object) if getErr := clusterAPI.Client.Get(context.TODO(), key, ro); getErr == nil { @@ -212,7 +217,7 @@ func delete(clusterAPI deploy.ClusterAPI, obj metav1.Object) error { return nil } -func DeleteGatewayRouteConfig(componentName string, deployContext *deploy.DeployContext) error { +func DeleteGatewayRouteConfig(componentName string, deployContext *chetypes.DeployContext) error { obj := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: GatewayConfigMapNamePrefix + componentName, @@ -225,7 +230,7 @@ func DeleteGatewayRouteConfig(componentName string, deployContext *deploy.Deploy // below functions declare the desired states of the various objects required for the gateway -func getGatewayServerConfigSpec(deployContext *deploy.DeployContext) (corev1.ConfigMap, error) { +func getGatewayServerConfigSpec(deployContext *chetypes.DeployContext) (corev1.ConfigMap, error) { cfg := CreateCommonTraefikConfig( serverComponentName, "PathPrefix(`/api`, `/swagger`, `/_app`)", @@ -233,21 +238,19 @@ func getGatewayServerConfigSpec(deployContext *deploy.DeployContext) (corev1.Con "http://"+deploy.CheServiceName+":8080", []string{}) - if util.IsOpenShift { + if infrastructure.IsOpenShift() { cfg.AddAuthHeaderRewrite(serverComponentName) // native user mode is currently only available on OpenShift but let's be defensive here so that // this doesn't break once we enable it on Kubernetes, too. Token check will have to work // differently on Kuberentes. - if util.IsOpenShift { - cfg.AddOpenShiftTokenCheck(serverComponentName) - } + cfg.AddOpenShiftTokenCheck(serverComponentName) } return GetConfigmapForGatewayConfig(deployContext, serverComponentName, cfg) } func GetConfigmapForGatewayConfig( - deployContext *deploy.DeployContext, + deployContext *chetypes.DeployContext, componentName string, gatewayConfig *TraefikConfig) (corev1.ConfigMap, error) { @@ -264,9 +267,9 @@ func GetConfigmapForGatewayConfig( ObjectMeta: metav1.ObjectMeta{ Name: GatewayConfigMapNamePrefix + componentName, Namespace: deployContext.CheCluster.Namespace, - Labels: util.MergeMaps( - deploy.GetLabels(deployContext.CheCluster, gatewayConfigComponentName), - util.GetMapValue(deployContext.CheCluster.Spec.Server.SingleHostGatewayConfigMapLabels, deploy.DefaultSingleHostGatewayConfigMapLabels)), + Labels: labels.Merge( + deploy.GetLabels(gatewayConfigComponentName), + utils.GetMap(deployContext.CheCluster.Spec.Networking.Auth.Gateway.ConfigLabels, constants.DefaultSingleHostGatewayConfigMapLabels)), }, Data: map[string]string{ componentName + ".yml": string(gatewayConfigContent), @@ -278,7 +281,7 @@ func GetConfigmapForGatewayConfig( return ret, nil } -func getGatewayServiceAccountSpec(instance *orgv1.CheCluster) corev1.ServiceAccount { +func getGatewayServiceAccountSpec(instance *chev2.CheCluster) corev1.ServiceAccount { return corev1.ServiceAccount{ TypeMeta: metav1.TypeMeta{ APIVersion: corev1.SchemeGroupVersion.String(), @@ -287,12 +290,12 @@ func getGatewayServiceAccountSpec(instance *orgv1.CheCluster) corev1.ServiceAcco ObjectMeta: metav1.ObjectMeta{ Name: GatewayServiceName, Namespace: instance.Namespace, - Labels: deploy.GetLabels(instance, GatewayServiceName), + Labels: deploy.GetLabels(GatewayServiceName), }, } } -func getGatewayRoleSpec(instance *orgv1.CheCluster) rbac.Role { +func getGatewayRoleSpec(instance *chev2.CheCluster) rbac.Role { return rbac.Role{ TypeMeta: metav1.TypeMeta{ APIVersion: rbac.SchemeGroupVersion.String(), @@ -301,7 +304,7 @@ func getGatewayRoleSpec(instance *orgv1.CheCluster) rbac.Role { ObjectMeta: metav1.ObjectMeta{ Name: GatewayServiceName, Namespace: instance.Namespace, - Labels: deploy.GetLabels(instance, GatewayServiceName), + Labels: deploy.GetLabels(GatewayServiceName), }, Rules: []rbac.PolicyRule{ { @@ -313,7 +316,7 @@ func getGatewayRoleSpec(instance *orgv1.CheCluster) rbac.Role { } } -func getGatewayRoleBindingSpec(instance *orgv1.CheCluster) rbac.RoleBinding { +func getGatewayRoleBindingSpec(instance *chev2.CheCluster) rbac.RoleBinding { return rbac.RoleBinding{ TypeMeta: metav1.TypeMeta{ APIVersion: rbac.SchemeGroupVersion.String(), @@ -322,7 +325,7 @@ func getGatewayRoleBindingSpec(instance *orgv1.CheCluster) rbac.RoleBinding { ObjectMeta: metav1.ObjectMeta{ Name: GatewayServiceName, Namespace: instance.Namespace, - Labels: deploy.GetLabels(instance, GatewayServiceName), + Labels: deploy.GetLabels(GatewayServiceName), }, RoleRef: rbac.RoleRef{ APIGroup: "rbac.authorization.k8s.io", @@ -339,19 +342,19 @@ func getGatewayRoleBindingSpec(instance *orgv1.CheCluster) rbac.RoleBinding { } func generateRandomCookieSecret() []byte { - return []byte(base64.StdEncoding.EncodeToString([]byte(util.GeneratePasswd(16)))) + return []byte(base64.StdEncoding.EncodeToString([]byte(utils.GeneratePassword(16)))) } -func getGatewayHeaderRewritePluginConfigSpec(instance *orgv1.CheCluster) (*corev1.ConfigMap, error) { +func getGatewayHeaderRewritePluginConfigSpec(instance *chev2.CheCluster) (*corev1.ConfigMap, error) { headerRewrite, err := ioutil.ReadFile("/tmp/header-rewrite-traefik-plugin/headerRewrite.go") if err != nil { - if !util.IsTestMode() { + if !test.IsTestMode() { return nil, err } } pluginMeta, err := ioutil.ReadFile("/tmp/header-rewrite-traefik-plugin/.traefik.yml") if err != nil { - if !util.IsTestMode() { + if !test.IsTestMode() { return nil, err } } @@ -364,7 +367,7 @@ func getGatewayHeaderRewritePluginConfigSpec(instance *orgv1.CheCluster) (*corev ObjectMeta: metav1.ObjectMeta{ Name: "che-gateway-config-header-rewrite-traefik-plugin", Namespace: instance.Namespace, - Labels: deploy.GetLabels(instance, GatewayServiceName), + Labels: deploy.GetLabels(GatewayServiceName), }, Data: map[string]string{ "headerRewrite.go": string(headerRewrite), @@ -373,7 +376,7 @@ func getGatewayHeaderRewritePluginConfigSpec(instance *orgv1.CheCluster) (*corev }, nil } -func getGatewayTraefikConfigSpec(instance *orgv1.CheCluster) corev1.ConfigMap { +func getGatewayTraefikConfigSpec(instance *chev2.CheCluster) corev1.ConfigMap { traefikPort := 8081 data := fmt.Sprintf(` entrypoints: @@ -395,7 +398,7 @@ providers: log: level: "INFO"`, traefikPort) - if util.IsOpenShift { + if infrastructure.IsOpenShift() { data += ` experimental: localPlugins: @@ -411,7 +414,7 @@ experimental: ObjectMeta: metav1.ObjectMeta{ Name: "che-gateway-config", Namespace: instance.Namespace, - Labels: deploy.GetLabels(instance, GatewayServiceName), + Labels: deploy.GetLabels(GatewayServiceName), }, Data: map[string]string{ "traefik.yml": data, @@ -419,12 +422,12 @@ experimental: } } -func getGatewayDeploymentSpec(ctx *deploy.DeployContext) appsv1.Deployment { +func getGatewayDeploymentSpec(ctx *chetypes.DeployContext) appsv1.Deployment { terminationGracePeriodSeconds := int64(10) - deployLabels, labelsSelector := deploy.GetLabelsAndSelector(ctx.CheCluster, GatewayServiceName) + deployLabels, labelsSelector := deploy.GetLabelsAndSelector(GatewayServiceName) - return appsv1.Deployment{ + deployment := appsv1.Deployment{ TypeMeta: metav1.TypeMeta{ APIVersion: appsv1.SchemeGroupVersion.String(), Kind: "Deployment", @@ -455,19 +458,22 @@ func getGatewayDeploymentSpec(ctx *deploy.DeployContext) appsv1.Deployment { }, }, } + + deploy.CustomizeDeployment(&deployment, &ctx.CheCluster.Spec.Networking.Auth.Gateway.Deployment, false) + return deployment } -func getContainersSpec(ctx *deploy.DeployContext) []corev1.Container { - configLabelsMap := util.GetMapValue(ctx.CheCluster.Spec.Server.SingleHostGatewayConfigMapLabels, deploy.DefaultSingleHostGatewayConfigMapLabels) - gatewayImage := util.GetValue(ctx.CheCluster.Spec.Server.SingleHostGatewayImage, deploy.DefaultSingleHostGatewayImage(ctx.CheCluster)) - configSidecarImage := util.GetValue(ctx.CheCluster.Spec.Server.SingleHostGatewayConfigSidecarImage, deploy.DefaultSingleHostGatewayConfigSidecarImage(ctx.CheCluster)) +func getContainersSpec(ctx *chetypes.DeployContext) []corev1.Container { + configLabelsMap := utils.GetMap(ctx.CheCluster.Spec.Networking.Auth.Gateway.ConfigLabels, constants.DefaultSingleHostGatewayConfigMapLabels) + gatewayImage := defaults.GetGatewayImage(ctx.CheCluster) + configSidecarImage := defaults.GetGatewayConfigSidecarImage(ctx.CheCluster) configLabels := labels.FormatLabels(configLabelsMap) containers := []corev1.Container{ { Name: "gateway", Image: gatewayImage, - ImagePullPolicy: corev1.PullAlways, + ImagePullPolicy: corev1.PullIfNotPresent, VolumeMounts: getTraefikContainerVolumeMounts(ctx.CheCluster), Resources: corev1.ResourceRequirements{ Limits: corev1.ResourceList{ @@ -483,7 +489,7 @@ func getContainersSpec(ctx *deploy.DeployContext) []corev1.Container { { Name: "configbump", Image: configSidecarImage, - ImagePullPolicy: corev1.PullAlways, + ImagePullPolicy: corev1.PullIfNotPresent, Resources: corev1.ResourceRequirements{ Limits: corev1.ResourceList{ corev1.ResourceMemory: resource.MustParse("256Mi"), @@ -529,7 +535,7 @@ func getContainersSpec(ctx *deploy.DeployContext) []corev1.Container { return containers } -func getTraefikContainerVolumeMounts(instance *orgv1.CheCluster) []corev1.VolumeMount { +func getTraefikContainerVolumeMounts(instance *chev2.CheCluster) []corev1.VolumeMount { mounts := []corev1.VolumeMount{ { Name: "static-config", @@ -540,7 +546,7 @@ func getTraefikContainerVolumeMounts(instance *orgv1.CheCluster) []corev1.Volume MountPath: "/dynamic-config", }, } - if util.IsOpenShift { + if infrastructure.IsOpenShift() { mounts = append(mounts, corev1.VolumeMount{ Name: "header-rewrite-traefik-plugin", MountPath: "/plugins-local/src/github.com/che-incubator/header-rewrite-traefik-plugin", @@ -550,7 +556,7 @@ func getTraefikContainerVolumeMounts(instance *orgv1.CheCluster) []corev1.Volume return mounts } -func getVolumesSpec(instance *orgv1.CheCluster) []corev1.Volume { +func getVolumesSpec(instance *chev2.CheCluster) []corev1.Volume { volumes := []corev1.Volume{ { Name: "static-config", @@ -574,7 +580,7 @@ func getVolumesSpec(instance *orgv1.CheCluster) []corev1.Volume { getOauthProxyConfigVolume(), getKubeRbacProxyConfigVolume()) - if util.IsOpenShift { + if infrastructure.IsOpenShift() { volumes = append(volumes, corev1.Volume{ Name: "header-rewrite-traefik-plugin", VolumeSource: corev1.VolumeSource{ @@ -590,7 +596,7 @@ func getVolumesSpec(instance *orgv1.CheCluster) []corev1.Volume { return volumes } -func getGatewayServiceSpec(instance *orgv1.CheCluster) corev1.Service { +func getGatewayServiceSpec(instance *chev2.CheCluster) corev1.Service { return corev1.Service{ TypeMeta: metav1.TypeMeta{ APIVersion: corev1.SchemeGroupVersion.String(), @@ -599,10 +605,10 @@ func getGatewayServiceSpec(instance *orgv1.CheCluster) corev1.Service { ObjectMeta: metav1.ObjectMeta{ Name: GatewayServiceName, Namespace: instance.Namespace, - Labels: deploy.GetLabels(instance, GatewayServiceName), + Labels: deploy.GetLabels(GatewayServiceName), }, Spec: corev1.ServiceSpec{ - Selector: deploy.GetLabels(instance, GatewayServiceName), + Selector: deploy.GetLabels(GatewayServiceName), SessionAffinity: corev1.ServiceAffinityNone, Type: corev1.ServiceTypeClusterIP, Ports: []corev1.ServicePort{ diff --git a/pkg/deploy/gateway/gateway_test.go b/pkg/deploy/gateway/gateway_test.go index ac3ff3221..bbf4f3890 100644 --- a/pkg/deploy/gateway/gateway_test.go +++ b/pkg/deploy/gateway/gateway_test.go @@ -17,13 +17,17 @@ import ( "strings" "testing" - orgv1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" "github.com/stretchr/testify/assert" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -31,23 +35,24 @@ import ( ) func TestSyncAllToCluster(t *testing.T) { - util.IsOpenShift = true - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) corev1.SchemeBuilder.AddToScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme) - deployContext := &deploy.DeployContext{ - CheCluster: &orgv1.CheCluster{ + deployContext := &chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, }, - ClusterAPI: deploy.ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, }, - Proxy: &deploy.Proxy{}, + Proxy: &chetypes.Proxy{}, } err := SyncGatewayToCluster(deployContext) @@ -75,23 +80,24 @@ func TestSyncAllToCluster(t *testing.T) { } func TestNativeUserGateway(t *testing.T) { - util.IsOpenShift = true - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) corev1.SchemeBuilder.AddToScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme) - deployContext := &deploy.DeployContext{ - CheCluster: &orgv1.CheCluster{ + deployContext := &chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, }, - ClusterAPI: deploy.ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, }, - Proxy: &deploy.Proxy{}, + Proxy: &chetypes.Proxy{}, } err := SyncGatewayToCluster(deployContext) @@ -138,16 +144,21 @@ func TestRandomCookieSecret(t *testing.T) { } func TestOauthProxyConfigUnauthorizedPaths(t *testing.T) { - util.IsOpenShift = true - util.IsOpenShift4 = true + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) t.Run("no skip auth", func(t *testing.T) { - ctx := deploy.GetTestDeployContext(&orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ExternalDevfileRegistry: true, - ExternalPluginRegistry: true, - }}}, nil) + ctx := test.GetDeployContext( + &chev2.CheCluster{ + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + PluginRegistry: chev2.PluginRegistry{ + DisableInternalRegistry: true, + }, + DevfileRegistry: chev2.DevfileRegistry{ + DisableInternalRegistry: true, + }, + }}, + }, nil) configmap := getGatewayOauthProxyConfigSpec(ctx, "blabol") config := configmap.Data["oauth-proxy.cfg"] @@ -157,12 +168,18 @@ func TestOauthProxyConfigUnauthorizedPaths(t *testing.T) { }) t.Run("no devfile-registry auth", func(t *testing.T) { - ctx := deploy.GetTestDeployContext(&orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ExternalDevfileRegistry: false, - ExternalPluginRegistry: true, - }}}, nil) + ctx := test.GetDeployContext( + &chev2.CheCluster{ + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + PluginRegistry: chev2.PluginRegistry{ + DisableInternalRegistry: true, + }, + DevfileRegistry: chev2.DevfileRegistry{ + DisableInternalRegistry: false, + }, + }}, + }, nil) configmap := getGatewayOauthProxyConfigSpec(ctx, "blabol") config := configmap.Data["oauth-proxy.cfg"] @@ -172,12 +189,18 @@ func TestOauthProxyConfigUnauthorizedPaths(t *testing.T) { }) t.Run("skip plugin-registry auth", func(t *testing.T) { - ctx := deploy.GetTestDeployContext(&orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ExternalDevfileRegistry: true, - ExternalPluginRegistry: false, - }}}, nil) + ctx := test.GetDeployContext( + &chev2.CheCluster{ + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + PluginRegistry: chev2.PluginRegistry{ + DisableInternalRegistry: false, + }, + DevfileRegistry: chev2.DevfileRegistry{ + DisableInternalRegistry: true, + }, + }}, + }, nil) configmap := getGatewayOauthProxyConfigSpec(ctx, "blabol") config := configmap.Data["oauth-proxy.cfg"] @@ -187,12 +210,18 @@ func TestOauthProxyConfigUnauthorizedPaths(t *testing.T) { }) t.Run("skip both registries auth", func(t *testing.T) { - ctx := deploy.GetTestDeployContext(&orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ExternalDevfileRegistry: false, - ExternalPluginRegistry: false, - }}}, nil) + ctx := test.GetDeployContext( + &chev2.CheCluster{ + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + PluginRegistry: chev2.PluginRegistry{ + DisableInternalRegistry: false, + }, + DevfileRegistry: chev2.DevfileRegistry{ + DisableInternalRegistry: false, + }, + }}, + }, nil) configmap := getGatewayOauthProxyConfigSpec(ctx, "blabol") config := configmap.Data["oauth-proxy.cfg"] @@ -202,10 +231,10 @@ func TestOauthProxyConfigUnauthorizedPaths(t *testing.T) { }) t.Run("skip '/healthz' path", func(t *testing.T) { - ctx := deploy.GetTestDeployContext(&orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{}}}, nil) - + ctx := test.GetDeployContext( + &chev2.CheCluster{ + Spec: chev2.CheClusterSpec{}, + }, nil) configmap := getGatewayOauthProxyConfigSpec(ctx, "blabol") config := configmap.Data["oauth-proxy.cfg"] assert.Contains(t, config, "/healthz$") @@ -213,40 +242,119 @@ func TestOauthProxyConfigUnauthorizedPaths(t *testing.T) { } func TestTokenValidityCheckOnOpenShiftNativeUser(t *testing.T) { - onOpenShift4(func() { - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) - corev1.SchemeBuilder.AddToScheme(scheme.Scheme) + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) - cm, err := getGatewayServerConfigSpec(&deploy.DeployContext{ - CheCluster: &orgv1.CheCluster{}, - ClusterAPI: deploy.ClusterAPI{ - Scheme: scheme.Scheme, - }, - }) - assert.NoError(t, err) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) + corev1.SchemeBuilder.AddToScheme(scheme.Scheme) - cfg := &TraefikConfig{} - - assert.NoError(t, yaml.Unmarshal([]byte(cm.Data["server.yml"]), cfg)) - - if assert.Contains(t, cfg.HTTP.Routers, "server") { - assert.Contains(t, cfg.HTTP.Routers["server"].Middlewares, "server-token-check") - } - if assert.Contains(t, cfg.HTTP.Middlewares, "server-token-check") && assert.NotNil(t, cfg.HTTP.Middlewares["server-token-check"].ForwardAuth) { - assert.Equal(t, "https://kubernetes.default.svc/apis/user.openshift.io/v1/users/~", cfg.HTTP.Middlewares["server-token-check"].ForwardAuth.Address) - } + cm, err := getGatewayServerConfigSpec(&chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{}, + ClusterAPI: chetypes.ClusterAPI{ + Scheme: scheme.Scheme, + }, }) + assert.NoError(t, err) + + cfg := &TraefikConfig{} + + assert.NoError(t, yaml.Unmarshal([]byte(cm.Data["server.yml"]), cfg)) + + if assert.Contains(t, cfg.HTTP.Routers, "server") { + assert.Contains(t, cfg.HTTP.Routers["server"].Middlewares, "server-token-check") + } + if assert.Contains(t, cfg.HTTP.Middlewares, "server-token-check") && assert.NotNil(t, cfg.HTTP.Middlewares["server-token-check"].ForwardAuth) { + assert.Equal(t, "https://kubernetes.default.svc/apis/user.openshift.io/v1/users/~", cfg.HTTP.Middlewares["server-token-check"].ForwardAuth.Address) + } } -func onOpenShift4(f func()) { - openshift := util.IsOpenShift - openshiftv4 := util.IsOpenShift4 +func TestCustomizeGatewayDeploymentAllImages(t *testing.T) { + checluster := &chev2.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eclipse-che", + Namespace: "eclipse-che", + }, + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + Auth: chev2.Auth{ + Gateway: chev2.Gateway{ + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: constants.GatewayContainerName, + Image: "gateway-image", + }, + { + Name: constants.GatewayConfigSideCarContainerName, + Image: "gateway-sidecar-image", + }, + { + Name: constants.GatewayAuthenticationContainerName, + Image: "gateway-authentication-image", + }, + { + Name: constants.GatewayAuthorizationContainerName, + Image: "gateway-authorization-image", + }, + }, + }, + }, + }, + }, + }, + } + ctx := test.GetDeployContext(checluster, []runtime.Object{}) - util.IsOpenShift = true - util.IsOpenShift4 = true + deployment := getGatewayDeploymentSpec(ctx) + containers := deployment.Spec.Template.Spec.Containers + assert.Equal(t, constants.GatewayContainerName, containers[0].Name) + assert.Equal(t, "gateway-image", containers[0].Image) - f() + assert.Equal(t, constants.GatewayConfigSideCarContainerName, containers[1].Name) + assert.Equal(t, "gateway-sidecar-image", containers[1].Image) - util.IsOpenShift = openshift - util.IsOpenShift4 = openshiftv4 + assert.Equal(t, constants.GatewayAuthenticationContainerName, containers[2].Name) + assert.Equal(t, "gateway-authentication-image", containers[2].Image) + + assert.Equal(t, constants.GatewayAuthorizationContainerName, containers[3].Name) + assert.Equal(t, "gateway-authorization-image", containers[3].Image) +} + +func TestCustomizeGatewayDeploymentSingleImage(t *testing.T) { + checluster := &chev2.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eclipse-che", + Namespace: "eclipse-che", + }, + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + Auth: chev2.Auth{ + Gateway: chev2.Gateway{ + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: constants.GatewayContainerName, + Image: "gateway-image", + }, + }, + }, + }, + }, + }, + }, + } + ctx := test.GetDeployContext(checluster, []runtime.Object{}) + + deployment := getGatewayDeploymentSpec(ctx) + containers := deployment.Spec.Template.Spec.Containers + assert.Equal(t, constants.GatewayContainerName, containers[0].Name) + assert.Equal(t, "gateway-image", containers[0].Image) + + assert.Equal(t, constants.GatewayConfigSideCarContainerName, containers[1].Name) + assert.Equal(t, defaults.GetGatewayConfigSidecarImage(checluster), containers[1].Image) + + assert.Equal(t, constants.GatewayAuthenticationContainerName, containers[2].Name) + assert.Equal(t, defaults.GetGatewayAuthenticationSidecarImage(checluster), containers[2].Image) + + assert.Equal(t, constants.GatewayAuthorizationContainerName, containers[3].Name) + assert.Equal(t, defaults.GetGatewayAuthorizationSidecarImage(checluster), containers[3].Image) } diff --git a/pkg/deploy/gateway/init_test.go b/pkg/deploy/gateway/init_test.go index 923ad9fe6..82eb6b118 100644 --- a/pkg/deploy/gateway/init_test.go +++ b/pkg/deploy/gateway/init_test.go @@ -11,11 +11,15 @@ // package gateway -import "github.com/eclipse-che/che-operator/pkg/deploy" +import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" +) func init() { - err := deploy.InitTestDefaultsFromDeployment("../../../config/manager/manager.yaml") - if err != nil { - panic(err) - } + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../../../config/manager/manager.yaml") } diff --git a/pkg/deploy/gateway/kube_rbac_proxy.go b/pkg/deploy/gateway/kube_rbac_proxy.go index 86cafec99..6fe165777 100644 --- a/pkg/deploy/gateway/kube_rbac_proxy.go +++ b/pkg/deploy/gateway/kube_rbac_proxy.go @@ -13,15 +13,15 @@ package gateway import ( - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func getGatewayKubeRbacProxyConfigSpec(instance *orgv1.CheCluster) corev1.ConfigMap { +func getGatewayKubeRbacProxyConfigSpec(instance *chev2.CheCluster) corev1.ConfigMap { return corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{ APIVersion: corev1.SchemeGroupVersion.String(), @@ -30,7 +30,7 @@ func getGatewayKubeRbacProxyConfigSpec(instance *orgv1.CheCluster) corev1.Config ObjectMeta: metav1.ObjectMeta{ Name: "che-gateway-config-kube-rbac-proxy", Namespace: instance.Namespace, - Labels: deploy.GetLabels(instance, GatewayServiceName), + Labels: deploy.GetLabels(GatewayServiceName), }, Data: map[string]string{ "authorization-config.yaml": ` @@ -47,12 +47,11 @@ authorization: } } -func getKubeRbacProxyContainerSpec(instance *orgv1.CheCluster) corev1.Container { - authzImage := util.GetValue(instance.Spec.Auth.GatewayAuthorizationSidecarImage, deploy.DefaultGatewayAuthorizationSidecarImage(instance)) +func getKubeRbacProxyContainerSpec(instance *chev2.CheCluster) corev1.Container { return corev1.Container{ Name: "kube-rbac-proxy", - Image: authzImage, - ImagePullPolicy: corev1.PullAlways, + Image: defaults.GetGatewayAuthorizationSidecarImage(instance), + ImagePullPolicy: corev1.PullIfNotPresent, Args: []string{ "--insecure-listen-address=0.0.0.0:8089", "--upstream=http://127.0.0.1:8090/ping", diff --git a/pkg/deploy/gateway/oauth_proxy.go b/pkg/deploy/gateway/oauth_proxy.go index 8d6ae8e29..27cf0dd18 100644 --- a/pkg/deploy/gateway/oauth_proxy.go +++ b/pkg/deploy/gateway/oauth_proxy.go @@ -21,18 +21,21 @@ import ( "k8s.io/apimachinery/pkg/api/resource" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func getGatewayOauthProxyConfigSpec(ctx *deploy.DeployContext, cookieSecret string) corev1.ConfigMap { +func getGatewayOauthProxyConfigSpec(ctx *chetypes.DeployContext, cookieSecret string) corev1.ConfigMap { instance := ctx.CheCluster var config string - if util.IsOpenShift { + if infrastructure.IsOpenShift() { config = openshiftOauthProxyConfig(ctx, cookieSecret) } else { config = kubernetesOauthProxyConfig(ctx, cookieSecret) @@ -45,7 +48,7 @@ func getGatewayOauthProxyConfigSpec(ctx *deploy.DeployContext, cookieSecret stri ObjectMeta: metav1.ObjectMeta{ Name: "che-gateway-config-oauth-proxy", Namespace: instance.Namespace, - Labels: deploy.GetLabels(instance, GatewayServiceName), + Labels: deploy.GetLabels(GatewayServiceName), }, Data: map[string]string{ "oauth-proxy.cfg": config, @@ -53,7 +56,7 @@ func getGatewayOauthProxyConfigSpec(ctx *deploy.DeployContext, cookieSecret stri } } -func openshiftOauthProxyConfig(ctx *deploy.DeployContext, cookieSecret string) string { +func openshiftOauthProxyConfig(ctx *chetypes.DeployContext, cookieSecret string) string { oauthSecret := "" oauthClientName := "" @@ -64,7 +67,6 @@ func openshiftOauthProxyConfig(ctx *deploy.DeployContext, cookieSecret string) s oauthSecret = oauthClient.Secret oauthClientName = oauthClient.Name } - return fmt.Sprintf(` http_address = ":%d" https_address = "" @@ -85,7 +87,7 @@ pass_access_token = true skip_provider_button = false %s `, GatewayServicePort, - ctx.CheCluster.GetCheHost(), + ctx.CheHost, oauthClientName, oauthSecret, GatewayServiceName, @@ -93,7 +95,7 @@ skip_provider_button = false skipAuthConfig(ctx.CheCluster)) } -func kubernetesOauthProxyConfig(ctx *deploy.DeployContext, cookieSecret string) string { +func kubernetesOauthProxyConfig(ctx *chetypes.DeployContext, cookieSecret string) string { return fmt.Sprintf(` proxy_prefix = "/oauth" http_address = ":%d" @@ -116,28 +118,28 @@ pass_authorization_header = true skip_provider_button = true %s `, GatewayServicePort, - ctx.CheCluster.GetCheHost(), - ctx.CheCluster.Spec.Auth.IdentityProviderURL, - ctx.CheCluster.Spec.Auth.OAuthClientName, - ctx.CheCluster.Spec.Auth.OAuthSecret, + ctx.CheHost, + ctx.CheCluster.Spec.Networking.Auth.IdentityProviderURL, + ctx.CheCluster.Spec.Networking.Auth.OAuthClientName, + ctx.CheCluster.Spec.Networking.Auth.OAuthSecret, cookieSecret, skipAuthConfig(ctx.CheCluster)) } -func skipAuthConfig(instance *orgv1.CheCluster) string { +func skipAuthConfig(instance *chev2.CheCluster) string { var skipAuthPaths []string - if !instance.Spec.Server.ExternalPluginRegistry { - skipAuthPaths = append(skipAuthPaths, "^/"+deploy.PluginRegistryName) + if !instance.Spec.Components.PluginRegistry.DisableInternalRegistry { + skipAuthPaths = append(skipAuthPaths, "^/"+constants.PluginRegistryName) } - if !instance.Spec.Server.ExternalDevfileRegistry { - skipAuthPaths = append(skipAuthPaths, "^/"+deploy.DevfileRegistryName) + if !instance.Spec.Components.DevfileRegistry.DisableInternalRegistry { + skipAuthPaths = append(skipAuthPaths, "^/"+constants.DevfileRegistryName) } skipAuthPaths = append(skipAuthPaths, "^/$") skipAuthPaths = append(skipAuthPaths, "/healthz$") skipAuthPaths = append(skipAuthPaths, "^/dashboard/static/preload") if len(skipAuthPaths) > 0 { propName := "skip_auth_routes" - if util.IsOpenShift { + if infrastructure.IsOpenShift() { propName = "skip_auth_regex" } return fmt.Sprintf("%s = \"%s\"", propName, strings.Join(skipAuthPaths, "|")) @@ -145,17 +147,16 @@ func skipAuthConfig(instance *orgv1.CheCluster) string { return "" } -func getOauthProxyContainerSpec(ctx *deploy.DeployContext) corev1.Container { +func getOauthProxyContainerSpec(ctx *chetypes.DeployContext) corev1.Container { // append env var with ConfigMap revision to restore pod automatically when config has been changed cm := &corev1.ConfigMap{} exists, _ := deploy.GetNamespacedObject(ctx, "che-gateway-config-oauth-proxy", cm) configMapRevision := map[bool]string{true: cm.GetResourceVersion(), false: ""}[exists] - authnImage := util.GetValue(ctx.CheCluster.Spec.Auth.GatewayAuthenticationSidecarImage, deploy.DefaultGatewayAuthenticationSidecarImage(ctx.CheCluster)) return corev1.Container{ Name: "oauth-proxy", - Image: authnImage, - ImagePullPolicy: corev1.PullAlways, + Image: defaults.GetGatewayAuthenticationSidecarImage(ctx.CheCluster), + ImagePullPolicy: corev1.PullIfNotPresent, Args: []string{ "--config=/etc/oauth-proxy/oauth-proxy.cfg", }, diff --git a/pkg/deploy/identity-provider/identity_provider_reconciler.go b/pkg/deploy/identity-provider/identity_provider_reconciler.go index ccc229abe..693c19e94 100644 --- a/pkg/deploy/identity-provider/identity_provider_reconciler.go +++ b/pkg/deploy/identity-provider/identity_provider_reconciler.go @@ -16,8 +16,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" "github.com/google/go-cmp/cmp/cmpopts" oauth "github.com/openshift/api/oauth/v1" "github.com/sirupsen/logrus" @@ -40,12 +41,12 @@ func NewIdentityProviderReconciler() *IdentityProviderReconciler { return &IdentityProviderReconciler{} } -func (ip *IdentityProviderReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { +func (ip *IdentityProviderReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { done, err := syncNativeIdentityProviderItems(ctx) return reconcile.Result{Requeue: !done}, done, err } -func (ip *IdentityProviderReconciler) Finalize(ctx *deploy.DeployContext) bool { +func (ip *IdentityProviderReconciler) Finalize(ctx *chetypes.DeployContext) bool { oauthClient, err := FindOAuthClient(ctx) if err != nil { logrus.Errorf("Error deleting finalizer: %v", err) @@ -65,9 +66,9 @@ func (ip *IdentityProviderReconciler) Finalize(ctx *deploy.DeployContext) bool { return true } -func syncNativeIdentityProviderItems(ctx *deploy.DeployContext) (bool, error) { - oauthSecret := util.GeneratePasswd(12) - oauthClientName := ctx.CheCluster.Name + "-openshift-identity-provider-" + strings.ToLower(util.GeneratePasswd(6)) +func syncNativeIdentityProviderItems(ctx *chetypes.DeployContext) (bool, error) { + oauthSecret := utils.GeneratePassword(12) + oauthClientName := ctx.CheCluster.Name + "-openshift-identity-provider-" + strings.ToLower(utils.GeneratePassword(6)) oauthClient, err := FindOAuthClient(ctx) if err != nil { diff --git a/pkg/deploy/identity-provider/identity_provider_util.go b/pkg/deploy/identity-provider/identity_provider_util.go index 9ff51408f..3950c4d3f 100644 --- a/pkg/deploy/identity-provider/identity_provider_util.go +++ b/pkg/deploy/identity-provider/identity_provider_util.go @@ -15,7 +15,9 @@ import ( "context" "fmt" - "github.com/eclipse-che/che-operator/pkg/deploy" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + oauth "github.com/openshift/api/oauth/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -30,7 +32,7 @@ func getOAuthClientSpec(name string, oauthSecret string, redirectURIs []string) }, ObjectMeta: metav1.ObjectMeta{ Name: name, - Labels: map[string]string{deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg}, + Labels: map[string]string{constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg}, }, Secret: oauthSecret, @@ -39,9 +41,9 @@ func getOAuthClientSpec(name string, oauthSecret string, redirectURIs []string) } } -func FindOAuthClient(ctx *deploy.DeployContext) (*oauth.OAuthClient, error) { +func FindOAuthClient(ctx *chetypes.DeployContext) (*oauth.OAuthClient, error) { oauthClients := &oauth.OAuthClientList{} - listOptions := &client.ListOptions{LabelSelector: labels.SelectorFromSet(map[string]string{deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg})} + listOptions := &client.ListOptions{LabelSelector: labels.SelectorFromSet(map[string]string{constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg})} if err := ctx.ClusterAPI.Client.List( context.TODO(), @@ -56,6 +58,6 @@ func FindOAuthClient(ctx *deploy.DeployContext) (*oauth.OAuthClient, error) { case 1: return &oauthClients.Items[0], nil default: - return nil, fmt.Errorf("more than one OAuthClient found with '%s:%s' labels", deploy.KubernetesPartOfLabelKey, deploy.CheEclipseOrg) + return nil, fmt.Errorf("more than one OAuthClient found with '%s:%s' labels", constants.KubernetesPartOfLabelKey, constants.CheEclipseOrg) } } diff --git a/pkg/deploy/identity-provider/init_test.go b/pkg/deploy/identity-provider/init_test.go index b836a16fa..a431f56f0 100644 --- a/pkg/deploy/identity-provider/init_test.go +++ b/pkg/deploy/identity-provider/init_test.go @@ -11,11 +11,15 @@ // package identityprovider -import "github.com/eclipse-che/che-operator/pkg/deploy" +import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" +) func init() { - err := deploy.InitTestDefaultsFromDeployment("../../../config/manager/manager.yaml") - if err != nil { - panic(err) - } + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../../../config/manager/manager.yaml") } diff --git a/pkg/deploy/image-puller/imagepuller.go b/pkg/deploy/image-puller/imagepuller.go index 65e22481e..2128bfe29 100644 --- a/pkg/deploy/image-puller/imagepuller.go +++ b/pkg/deploy/image-puller/imagepuller.go @@ -18,9 +18,12 @@ import ( "time" chev1alpha1 "github.com/che-incubator/kubernetes-image-puller-operator/api/v1alpha1" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" packagesv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1" @@ -54,17 +57,17 @@ func NewImagePuller() *ImagePuller { return &ImagePuller{} } -func (ip *ImagePuller) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { +func (ip *ImagePuller) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { return ReconcileImagePuller(ctx) } -func (ip *ImagePuller) Finalize(ctx *deploy.DeployContext) bool { +func (ip *ImagePuller) Finalize(ctx *chetypes.DeployContext) bool { return DeleteImagePullerOperatorAndFinalizer(ctx) } // Reconcile the imagePuller section of the CheCluster CR. If imagePuller.enable is set to true, install the Kubernetes Image Puller operator and create // a KubernetesImagePuller CR. Add a finalizer to the CheCluster CR. If false, remove the KubernetesImagePuller CR, uninstall the operator, and remove the finalizer. -func ReconcileImagePuller(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { +func ReconcileImagePuller(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { // Determine what server groups the API Server knows about foundPackagesAPI, foundOperatorsAPI, _, err := CheckNeededImagePullerApis(ctx) if err != nil { @@ -73,12 +76,12 @@ func ReconcileImagePuller(ctx *deploy.DeployContext) (reconcile.Result, bool, er } // If the image puller should be installed but the APIServer doesn't know about PackageManifests/Subscriptions, log a warning and requeue - if ctx.CheCluster.Spec.ImagePuller.Enable && (!foundPackagesAPI || !foundOperatorsAPI) { + if ctx.CheCluster.Spec.Components.ImagePuller.Enable && (!foundPackagesAPI || !foundOperatorsAPI) { logrus.Infof("Couldn't find Operator Lifecycle Manager types to install the Kubernetes Image Puller Operator. Please install Operator Lifecycle Manager to install the operator or disable the image puller by setting spec.imagePuller.enable to false.") return reconcile.Result{RequeueAfter: time.Second}, false, nil } - if ctx.CheCluster.Spec.ImagePuller.Enable { + if ctx.CheCluster.Spec.Components.ImagePuller.Enable { if foundOperatorsAPI && foundPackagesAPI { packageManifest, err := GetPackageManifest(ctx) if err != nil { @@ -168,19 +171,19 @@ func ReconcileImagePuller(ctx *deploy.DeployContext) (reconcile.Result, bool, er return reconcile.Result{}, false, err } - if ctx.CheCluster.Spec.ImagePuller.Spec.DeploymentName == "" { - ctx.CheCluster.Spec.ImagePuller.Spec.DeploymentName = imagePuller.Spec.DeploymentName + if ctx.CheCluster.Spec.Components.ImagePuller.Spec.DeploymentName == "" { + ctx.CheCluster.Spec.Components.ImagePuller.Spec.DeploymentName = imagePuller.Spec.DeploymentName } - if ctx.CheCluster.Spec.ImagePuller.Spec.ConfigMapName == "" { - ctx.CheCluster.Spec.ImagePuller.Spec.ConfigMapName = imagePuller.Spec.ConfigMapName + if ctx.CheCluster.Spec.Components.ImagePuller.Spec.ConfigMapName == "" { + ctx.CheCluster.Spec.Components.ImagePuller.Spec.ConfigMapName = imagePuller.Spec.ConfigMapName } - if ctx.CheCluster.Spec.ImagePuller.Spec.ImagePullerImage == "" { - ctx.CheCluster.Spec.ImagePuller.Spec.ImagePullerImage = imagePuller.Spec.ImagePullerImage + if ctx.CheCluster.Spec.Components.ImagePuller.Spec.ImagePullerImage == "" { + ctx.CheCluster.Spec.Components.ImagePuller.Spec.ImagePullerImage = imagePuller.Spec.ImagePullerImage } // If ImagePuller specs are different, update the KubernetesImagePuller CR - if imagePuller.Spec != ctx.CheCluster.Spec.ImagePuller.Spec { - imagePuller.Spec = ctx.CheCluster.Spec.ImagePuller.Spec + if imagePuller.Spec != ctx.CheCluster.Spec.Components.ImagePuller.Spec { + imagePuller.Spec = ctx.CheCluster.Spec.Components.ImagePuller.Spec logrus.Infof("Updating KubernetesImagePuller %v", imagePuller.Name) if err = ctx.ClusterAPI.Client.Update(context.TODO(), imagePuller, &client.UpdateOptions{}); err != nil { logrus.Errorf("Error updating KubernetesImagePuller: %v", err) @@ -202,7 +205,7 @@ func ReconcileImagePuller(ctx *deploy.DeployContext) (reconcile.Result, bool, er return reconcile.Result{}, true, nil } -func DeleteImagePullerOperatorAndFinalizer(ctx *deploy.DeployContext) bool { +func DeleteImagePullerOperatorAndFinalizer(ctx *chetypes.DeployContext) bool { done := true if _, err := GetImagePullerOperator(ctx); err == nil { @@ -222,7 +225,7 @@ func DeleteImagePullerOperatorAndFinalizer(ctx *deploy.DeployContext) bool { return done } -func HasImagePullerFinalizer(instance *orgv1.CheCluster) bool { +func HasImagePullerFinalizer(instance *chev2.CheCluster) bool { finalizers := instance.ObjectMeta.GetFinalizers() for _, finalizer := range finalizers { if finalizer == imagePullerFinalizerName { @@ -232,13 +235,13 @@ func HasImagePullerFinalizer(instance *orgv1.CheCluster) bool { return false } -func ReconcileImagePullerFinalizer(ctx *deploy.DeployContext) (err error) { +func ReconcileImagePullerFinalizer(ctx *chetypes.DeployContext) (err error) { instance := ctx.CheCluster if instance.ObjectMeta.DeletionTimestamp.IsZero() { return deploy.AppendFinalizer(ctx, imagePullerFinalizerName) } else { - if util.ContainsString(instance.ObjectMeta.Finalizers, imagePullerFinalizerName) { - clusterServiceVersionName := deploy.DefaultKubernetesImagePullerOperatorCSV() + if utils.Contains(instance.ObjectMeta.Finalizers, imagePullerFinalizerName) { + clusterServiceVersionName := constants.KubernetesImagePullerOperatorCSV logrus.Infof("Custom resource %s is being deleted. Deleting ClusterServiceVersion %s first", instance.Name, clusterServiceVersionName) clusterServiceVersion := &operatorsv1alpha1.ClusterServiceVersion{} err := ctx.ClusterAPI.NonCachingClient.Get(context.TODO(), types.NamespacedName{Namespace: instance.Namespace, Name: clusterServiceVersionName}, clusterServiceVersion) @@ -257,9 +260,9 @@ func ReconcileImagePullerFinalizer(ctx *deploy.DeployContext) (err error) { } } -func DeleteImagePullerFinalizer(ctx *deploy.DeployContext) (err error) { +func DeleteImagePullerFinalizer(ctx *chetypes.DeployContext) (err error) { instance := ctx.CheCluster - instance.ObjectMeta.Finalizers = util.DoRemoveString(instance.ObjectMeta.Finalizers, imagePullerFinalizerName) + instance.ObjectMeta.Finalizers = utils.Remove(instance.ObjectMeta.Finalizers, imagePullerFinalizerName) logrus.Infof("Removing image puller finalizer on %s CR", instance.Name) if err := ctx.ClusterAPI.Client.Update(context.Background(), instance); err != nil { logrus.Errorf("Failed to update %s CR: %s", instance.Name, err) @@ -285,7 +288,7 @@ func SubscriptionsAreEqual(expected *operatorsv1alpha1.Subscription, actual *ope // foundOperatorsAPI - true if the server discovers the operators.coreos.com API // foundKubernetesImagePullerAPI - true if the server discovers the che.eclipse.org API // error - any error returned by the call to discoveryClient.ServerGroups() -func CheckNeededImagePullerApis(ctx *deploy.DeployContext) (bool, bool, bool, error) { +func CheckNeededImagePullerApis(ctx *chetypes.DeployContext) (bool, bool, bool, error) { groupList, resourcesList, err := ctx.ClusterAPI.DiscoveryClient.ServerGroupsAndResources() if err != nil { return false, false, false, err @@ -313,7 +316,7 @@ func CheckNeededImagePullerApis(ctx *deploy.DeployContext) (bool, bool, bool, er } // Search for the kubernetes-imagepuller-operator PackageManifest -func GetPackageManifest(ctx *deploy.DeployContext) (*packagesv1.PackageManifest, error) { +func GetPackageManifest(ctx *chetypes.DeployContext) (*packagesv1.PackageManifest, error) { packageManifest := &packagesv1.PackageManifest{} err := ctx.ClusterAPI.NonCachingClient.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: "kubernetes-imagepuller-operator"}, packageManifest) if err != nil { @@ -324,7 +327,7 @@ func GetPackageManifest(ctx *deploy.DeployContext) (*packagesv1.PackageManifest, // Create an OperatorGroup in the CheCluster namespace if it does not exist. Returns true if the // OperatorGroup was created, and any error returned during the List and Create operation -func CreateOperatorGroupIfNotFound(ctx *deploy.DeployContext) (bool, error) { +func CreateOperatorGroupIfNotFound(ctx *chetypes.DeployContext) (bool, error) { operatorGroupList := &operatorsv1.OperatorGroupList{} err := ctx.ClusterAPI.NonCachingClient.List(context.TODO(), operatorGroupList, &client.ListOptions{Namespace: ctx.CheCluster.Namespace}) if err != nil { @@ -355,7 +358,7 @@ func CreateOperatorGroupIfNotFound(ctx *deploy.DeployContext) (bool, error) { return false, nil } -func CreateImagePullerSubscription(ctx *deploy.DeployContext, packageManifest *packagesv1.PackageManifest) (bool, error) { +func CreateImagePullerSubscription(ctx *chetypes.DeployContext, packageManifest *packagesv1.PackageManifest) (bool, error) { imagePullerOperatorSubscription := &operatorsv1alpha1.Subscription{} err := ctx.ClusterAPI.NonCachingClient.Get(context.TODO(), types.NamespacedName{ Name: "kubernetes-imagepuller-operator", @@ -375,7 +378,7 @@ func CreateImagePullerSubscription(ctx *deploy.DeployContext, packageManifest *p return false, nil } -func GetExpectedSubscription(ctx *deploy.DeployContext, packageManifest *packagesv1.PackageManifest) *operatorsv1alpha1.Subscription { +func GetExpectedSubscription(ctx *chetypes.DeployContext, packageManifest *packagesv1.PackageManifest) *operatorsv1alpha1.Subscription { return &operatorsv1alpha1.Subscription{ ObjectMeta: metav1.ObjectMeta{ Name: "kubernetes-imagepuller-operator", @@ -394,7 +397,7 @@ func GetExpectedSubscription(ctx *deploy.DeployContext, packageManifest *package } } -func GetActualSubscription(ctx *deploy.DeployContext) (*operatorsv1alpha1.Subscription, error) { +func GetActualSubscription(ctx *chetypes.DeployContext) (*operatorsv1alpha1.Subscription, error) { actual := &operatorsv1alpha1.Subscription{} err := ctx.ClusterAPI.NonCachingClient.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: "kubernetes-imagepuller-operator"}, actual) if err != nil { @@ -405,21 +408,21 @@ func GetActualSubscription(ctx *deploy.DeployContext) (*operatorsv1alpha1.Subscr // Update the CheCluster ImagePuller spec if the default values are not set // returns the updated spec and an error during update -func UpdateImagePullerSpecIfEmpty(ctx *deploy.DeployContext) (orgv1.CheClusterSpecImagePuller, error) { - if ctx.CheCluster.Spec.ImagePuller.Spec.DeploymentName == "" { - ctx.CheCluster.Spec.ImagePuller.Spec.DeploymentName = "kubernetes-image-puller" +func UpdateImagePullerSpecIfEmpty(ctx *chetypes.DeployContext) (chev2.ImagePuller, error) { + if ctx.CheCluster.Spec.Components.ImagePuller.Spec.DeploymentName == "" { + ctx.CheCluster.Spec.Components.ImagePuller.Spec.DeploymentName = "kubernetes-image-puller" } - if ctx.CheCluster.Spec.ImagePuller.Spec.ConfigMapName == "" { - ctx.CheCluster.Spec.ImagePuller.Spec.ConfigMapName = "k8s-image-puller" + if ctx.CheCluster.Spec.Components.ImagePuller.Spec.ConfigMapName == "" { + ctx.CheCluster.Spec.Components.ImagePuller.Spec.ConfigMapName = "k8s-image-puller" } err := ctx.ClusterAPI.Client.Update(context.TODO(), ctx.CheCluster, &client.UpdateOptions{}) if err != nil { - return ctx.CheCluster.Spec.ImagePuller, err + return ctx.CheCluster.Spec.Components.ImagePuller, err } - return ctx.CheCluster.Spec.ImagePuller, nil + return ctx.CheCluster.Spec.Components.ImagePuller, nil } -func SetDefaultImages(ctx *deploy.DeployContext) error { +func SetDefaultImages(ctx *chetypes.DeployContext) error { defaultImages := GetDefaultImages() if len(defaultImages) == 0 { return nil @@ -482,7 +485,7 @@ func GetDefaultImages() []ImageAndName { "^RELATED_IMAGE_.*_java.{1,2}(_maven)?_devfile_registry_image.*", } for _, pattern := range imagePatterns { - matches := util.GetEnvByRegExp(pattern) + matches := utils.GetEnvByRegExp(pattern) for _, match := range matches { match.Name = match.Name[len("RELATED_IMAGE_"):] images = append(images, ImageAndName{Name: match.Name, Image: match.Value}) @@ -518,7 +521,7 @@ func IsRFC1123Char(ch byte) bool { return len(errs) == 0 } -func CreateKubernetesImagePuller(ctx *deploy.DeployContext) (bool, error) { +func CreateKubernetesImagePuller(ctx *chetypes.DeployContext) (bool, error) { imagePuller := GetExpectedKubernetesImagePuller(ctx) err := ctx.ClusterAPI.Client.Create(context.TODO(), imagePuller, &client.CreateOptions{}) if err != nil { @@ -527,7 +530,7 @@ func CreateKubernetesImagePuller(ctx *deploy.DeployContext) (bool, error) { return true, nil } -func GetExpectedKubernetesImagePuller(ctx *deploy.DeployContext) *chev1alpha1.KubernetesImagePuller { +func GetExpectedKubernetesImagePuller(ctx *chetypes.DeployContext) *chev1alpha1.KubernetesImagePuller { return &chev1alpha1.KubernetesImagePuller{ ObjectMeta: metav1.ObjectMeta{ Name: ctx.CheCluster.Name + "-image-puller", @@ -536,18 +539,18 @@ func GetExpectedKubernetesImagePuller(ctx *deploy.DeployContext) *chev1alpha1.Ku *metav1.NewControllerRef(ctx.CheCluster, ctx.CheCluster.GroupVersionKind()), }, Labels: map[string]string{ - "app.kubernetes.io/part-of": deploy.CheEclipseOrg, - "app": deploy.DefaultCheFlavor(ctx.CheCluster), + "app.kubernetes.io/part-of": constants.CheEclipseOrg, + "app": defaults.GetCheFlavor(), "component": "kubernetes-image-puller", }, }, - Spec: ctx.CheCluster.Spec.ImagePuller.Spec, + Spec: ctx.CheCluster.Spec.Components.ImagePuller.Spec, } } // UpdateDefaultImagesIfNeeded updates the default images from `spec.images` if needed -func UpdateDefaultImagesIfNeeded(ctx *deploy.DeployContext) error { - specImages := StringToImageSlice(ctx.CheCluster.Spec.ImagePuller.Spec.Images) +func UpdateDefaultImagesIfNeeded(ctx *chetypes.DeployContext) error { + specImages := StringToImageSlice(ctx.CheCluster.Spec.Components.ImagePuller.Spec.Images) defaultImages := GetDefaultImages() if UpdateSpecImages(specImages, defaultImages) { return SetImages(ctx, specImages) @@ -563,9 +566,9 @@ func UpdateDefaultImagesIfNeeded(ctx *deploy.DeployContext) error { func UpdateSpecImages(specImages []ImageAndName, defaultImages []ImageAndName) bool { match := false for i, specImage := range specImages { - specImageName, specImageTag := util.GetImageNameAndTag(specImage.Image) + specImageName, specImageTag := utils.GetImageNameAndTag(specImage.Image) for _, defaultImage := range defaultImages { - defaultImageName, defaultImageTag := util.GetImageNameAndTag(defaultImage.Image) + defaultImageName, defaultImageTag := utils.GetImageNameAndTag(defaultImage.Image) // if the image tags are different for this image, then update if defaultImageName == specImageName && defaultImageTag != specImageTag { match = true @@ -578,15 +581,15 @@ func UpdateSpecImages(specImages []ImageAndName, defaultImages []ImageAndName) b } // SetImages sets the provided images to the imagePuller spec -func SetImages(ctx *deploy.DeployContext, images []ImageAndName) error { +func SetImages(ctx *chetypes.DeployContext, images []ImageAndName) error { imagesStr := ImageSliceToString(images) - ctx.CheCluster.Spec.ImagePuller.Spec.Images = imagesStr + ctx.CheCluster.Spec.Components.ImagePuller.Spec.Images = imagesStr return deploy.UpdateCheCRSpec(ctx, "Kubernetes Image Puller images", imagesStr) } // Uninstall the CSV, OperatorGroup, Subscription, KubernetesImagePuller, and update the CheCluster to remove // the image puller spec. Returns true if the CheCluster was updated -func UninstallImagePullerOperator(ctx *deploy.DeployContext) (bool, error) { +func UninstallImagePullerOperator(ctx *chetypes.DeployContext) (bool, error) { updated := false _, hasOperatorsAPIs, hasImagePullerAPIs, err := CheckNeededImagePullerApis(ctx) if err != nil { @@ -610,7 +613,7 @@ func UninstallImagePullerOperator(ctx *deploy.DeployContext) (bool, error) { if hasOperatorsAPIs { // Delete the ClusterServiceVersion csv := &operatorsv1alpha1.ClusterServiceVersion{} - err = ctx.ClusterAPI.NonCachingClient.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: deploy.DefaultKubernetesImagePullerOperatorCSV()}, csv) + err = ctx.ClusterAPI.NonCachingClient.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: constants.KubernetesImagePullerOperatorCSV}, csv) if err != nil && !errors.IsNotFound(err) { return updated, err } @@ -654,9 +657,9 @@ func UninstallImagePullerOperator(ctx *deploy.DeployContext) (bool, error) { } // Update CR to remove imagePullerSpec - if (ctx.CheCluster.Spec.ImagePuller.Enable || ctx.CheCluster.Spec.ImagePuller.Spec != (chev1alpha1.KubernetesImagePullerSpec{})) && + if (ctx.CheCluster.Spec.Components.ImagePuller.Enable || ctx.CheCluster.Spec.Components.ImagePuller.Spec != (chev1alpha1.KubernetesImagePullerSpec{})) && ctx.CheCluster.ObjectMeta.DeletionTimestamp.IsZero() { - ctx.CheCluster.Spec.ImagePuller.Spec = chev1alpha1.KubernetesImagePullerSpec{} + ctx.CheCluster.Spec.Components.ImagePuller.Spec = chev1alpha1.KubernetesImagePullerSpec{} logrus.Infof("Updating CheCluster %v to remove image puller spec", ctx.CheCluster.Name) err := ctx.ClusterAPI.Client.Update(context.TODO(), ctx.CheCluster, &client.UpdateOptions{}) if err != nil { @@ -668,7 +671,7 @@ func UninstallImagePullerOperator(ctx *deploy.DeployContext) (bool, error) { } // GetImagePullerOperator returns the current kubernetes-imagepuller-operator if exists -func GetImagePullerOperator(ctx *deploy.DeployContext) (*chev1alpha1.KubernetesImagePuller, error) { +func GetImagePullerOperator(ctx *chetypes.DeployContext) (*chev1alpha1.KubernetesImagePuller, error) { imagePuller := &chev1alpha1.KubernetesImagePuller{} err := ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: ctx.CheCluster.Name + "-image-puller"}, imagePuller) if err != nil { diff --git a/pkg/deploy/image-puller/imagepuller_test.go b/pkg/deploy/image-puller/imagepuller_test.go index aa4a17850..60fbb4bf4 100644 --- a/pkg/deploy/image-puller/imagepuller_test.go +++ b/pkg/deploy/image-puller/imagepuller_test.go @@ -13,7 +13,6 @@ package imagepuller import ( "context" - "io/ioutil" "os" "sort" "strings" @@ -25,18 +24,18 @@ import ( chev1alpha1 "github.com/che-incubator/kubernetes-image-puller-operator/api/v1alpha1" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "sigs.k8s.io/yaml" - "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" + "github.com/eclipse-che/che-operator/pkg/common/utils" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" routev1 "github.com/openshift/api/route/v1" operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" packagesv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1" - appsv1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -60,9 +59,9 @@ var ( func TestImagePullerConfiguration(t *testing.T) { type testCase struct { name string - initCR *orgv1.CheCluster + initCR *chev2.CheCluster initObjects []runtime.Object - expectedCR *orgv1.CheCluster + expectedCR *chev2.CheCluster expectedOperatorGroup *operatorsv1.OperatorGroup expectedSubscription *operatorsv1alpha1.Subscription expectedImagePuller *chev1alpha1.KubernetesImagePuller @@ -100,25 +99,23 @@ func TestImagePullerConfiguration(t *testing.T) { { name: "image puller enabled with finalizer but default values are empty, subscription exists, should update the CR", initCR: InitCheCRWithImagePullerFinalizer(), - expectedCR: &orgv1.CheCluster{ - TypeMeta: metav1.TypeMeta{ - Kind: "CheCluster", - APIVersion: "org.eclipse.che/v1", - }, + expectedCR: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ - Name: os.Getenv("CHE_FLAVOR"), + Name: "eclipse-che", Namespace: namespace, ResourceVersion: "1", Finalizers: []string{ "kubernetesimagepullers.finalizers.che.eclipse.org", }, }, - Spec: orgv1.CheClusterSpec{ - ImagePuller: orgv1.CheClusterSpecImagePuller{ - Enable: true, - Spec: chev1alpha1.KubernetesImagePullerSpec{ - DeploymentName: "kubernetes-image-puller", - ConfigMapName: "k8s-image-puller", + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + ImagePuller: chev2.ImagePuller{ + Enable: true, + Spec: chev1alpha1.KubernetesImagePullerSpec{ + DeploymentName: "kubernetes-image-puller", + ConfigMapName: "k8s-image-puller", + }, }, }, }, @@ -184,20 +181,20 @@ func TestImagePullerConfiguration(t *testing.T) { TypeMeta: metav1.TypeMeta{Kind: "KubernetesImagePuller", APIVersion: "che.eclipse.org/v1alpha1"}, ObjectMeta: metav1.ObjectMeta{ ResourceVersion: "2", - Name: os.Getenv("CHE_FLAVOR") + "-image-puller", + Name: "eclipse-che-image-puller", Namespace: namespace, Labels: map[string]string{ - "app": os.Getenv("CHE_FLAVOR"), + "app": defaults.GetCheFlavor(), "component": "kubernetes-image-puller", - "app.kubernetes.io/part-of": deploy.CheEclipseOrg, + "app.kubernetes.io/part-of": constants.CheEclipseOrg, }, OwnerReferences: []metav1.OwnerReference{ { - APIVersion: "org.eclipse.che/v1", + APIVersion: "org.eclipse.che/v2", Kind: "CheCluster", BlockOwnerDeletion: &valueTrue, Controller: &valueTrue, - Name: os.Getenv("CHE_FLAVOR"), + Name: "eclipse-che", }, }, }, @@ -239,9 +236,9 @@ func TestImagePullerConfiguration(t *testing.T) { t.Run(testCase.name, func(t *testing.T) { logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true))) - deployContext := deploy.GetTestDeployContext(testCase.initCR, []runtime.Object{}) + deployContext := test.GetDeployContext(testCase.initCR, []runtime.Object{}) - orgv1.SchemeBuilder.AddToScheme(deployContext.ClusterAPI.Scheme) + chev2.SchemeBuilder.AddToScheme(deployContext.ClusterAPI.Scheme) packagesv1.AddToScheme(deployContext.ClusterAPI.Scheme) operatorsv1alpha1.AddToScheme(deployContext.ClusterAPI.Scheme) operatorsv1.AddToScheme(deployContext.ClusterAPI.Scheme) @@ -315,8 +312,8 @@ func TestImagePullerConfiguration(t *testing.T) { } // if expectedCR is not set, don't check it if testCase.expectedCR != nil && !reflect.DeepEqual(testCase.initCR, testCase.expectedCR) { - gotCR := &orgv1.CheCluster{} - err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: os.Getenv("CHE_FLAVOR")}, gotCR) + gotCR := &chev2.CheCluster{} + err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: "eclipse-che"}, gotCR) if err != nil { t.Errorf("Error getting CheCluster: %v", err) } @@ -349,15 +346,15 @@ func TestImagePullerConfiguration(t *testing.T) { } if testCase.shouldDelete { if testCase.expectedCR == nil { - gotCR := &orgv1.CheCluster{} - err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: os.Getenv("CHE_FLAVOR")}, gotCR) + gotCR := &chev2.CheCluster{} + err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: "eclipse-che"}, gotCR) if !errors.IsNotFound(err) { t.Fatal("CR CheCluster should be removed") } } imagePuller := &chev1alpha1.KubernetesImagePuller{} - err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: os.Getenv("CHE_FLAVOR") + "-image-puller"}, imagePuller) + err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: "eclipse-che-image-puller"}, imagePuller) if err == nil || !errors.IsNotFound(err) { t.Fatalf("Should not have found KubernetesImagePuller: %v", err) } @@ -393,7 +390,7 @@ func TestEnvVars(t *testing.T) { // unset RELATED_IMAGE environment variables, set them back // after tests complete - matches := util.GetEnvByRegExp("^RELATED_IMAGE_.*") + matches := utils.GetEnvByRegExp("^RELATED_IMAGE_.*") for _, match := range matches { if originalValue, exists := os.LookupEnv(match.Name); exists { os.Unsetenv(match.Name) @@ -493,172 +490,177 @@ func sortImages(images []ImageAndName) []ImageAndName { return imagesCopy } -func InitCheCRWithImagePullerEnabled() *orgv1.CheCluster { - return &orgv1.CheCluster{ +func InitCheCRWithImagePullerEnabled() *chev2.CheCluster { + return &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ - Name: os.Getenv("CHE_FLAVOR"), + Name: "eclipse-che", Namespace: namespace, ResourceVersion: "0", }, - TypeMeta: metav1.TypeMeta{ - APIVersion: "org.eclipse.che/v1", - Kind: "CheCluster", - }, - Spec: orgv1.CheClusterSpec{ - ImagePuller: orgv1.CheClusterSpecImagePuller{ - Enable: true, + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + ImagePuller: chev2.ImagePuller{ + Enable: true, + }, }, }, } } -func InitCheCRWithImagePullerFinalizer() *orgv1.CheCluster { - return &orgv1.CheCluster{ +func InitCheCRWithImagePullerFinalizer() *chev2.CheCluster { + return &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ - Name: os.Getenv("CHE_FLAVOR"), + Name: "eclipse-che", Namespace: namespace, Finalizers: []string{ "kubernetesimagepullers.finalizers.che.eclipse.org", }, ResourceVersion: "0", }, - TypeMeta: metav1.TypeMeta{ - APIVersion: "org.eclipse.che/v1", - Kind: "CheCluster", - }, - Spec: orgv1.CheClusterSpec{ - ImagePuller: orgv1.CheClusterSpecImagePuller{ - Enable: true, + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + ImagePuller: chev2.ImagePuller{ + Enable: true, + }, }, }, } } -func InitCheCRWithImagePullerFinalizerAndDeletionTimestamp() *orgv1.CheCluster { - return &orgv1.CheCluster{ +func InitCheCRWithImagePullerFinalizerAndDeletionTimestamp() *chev2.CheCluster { + return &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ - Name: os.Getenv("CHE_FLAVOR"), + Name: "eclipse-che", Namespace: namespace, Finalizers: []string{ "kubernetesimagepullers.finalizers.che.eclipse.org", }, DeletionTimestamp: &metav1.Time{Time: time.Unix(1, 0)}, }, - Spec: orgv1.CheClusterSpec{ - ImagePuller: orgv1.CheClusterSpecImagePuller{ - Enable: true, + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + ImagePuller: chev2.ImagePuller{ + Enable: true, + }, }, }, } } -func ExpectedCheCRWithImagePullerFinalizer() *orgv1.CheCluster { - return &orgv1.CheCluster{ +func ExpectedCheCRWithImagePullerFinalizer() *chev2.CheCluster { + return &chev2.CheCluster{ TypeMeta: metav1.TypeMeta{ + APIVersion: "org.eclipse.che/v2", Kind: "CheCluster", - APIVersion: "org.eclipse.che/v1", }, ObjectMeta: metav1.ObjectMeta{ - Name: os.Getenv("CHE_FLAVOR"), + Name: "eclipse-che", Namespace: namespace, Finalizers: []string{ "kubernetesimagepullers.finalizers.che.eclipse.org", }, ResourceVersion: "1", }, - Spec: orgv1.CheClusterSpec{ - ImagePuller: orgv1.CheClusterSpecImagePuller{ - Enable: true, + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + ImagePuller: chev2.ImagePuller{ + Enable: true, + }, }, }, } } -func InitCheCRWithImagePullerDisabled() *orgv1.CheCluster { - return &orgv1.CheCluster{ - TypeMeta: metav1.TypeMeta{ - Kind: "CheCluster", - APIVersion: "org.eclipse.che/v1", - }, +func InitCheCRWithImagePullerDisabled() *chev2.CheCluster { + return &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ - Name: os.Getenv("CHE_FLAVOR"), + Name: "eclipse-che", Namespace: namespace, ResourceVersion: "0", }, - Spec: orgv1.CheClusterSpec{ - ImagePuller: orgv1.CheClusterSpecImagePuller{ - Enable: false, - }, - }, - } -} - -func InitCheCRWithImagePullerEnabledAndDefaultValuesSet() *orgv1.CheCluster { - return &orgv1.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: os.Getenv("CHE_FLAVOR"), - Namespace: namespace, - Finalizers: []string{ - "kubernetesimagepullers.finalizers.che.eclipse.org", - }, - }, - TypeMeta: metav1.TypeMeta{ - APIVersion: "org.eclipse.che/v1", - Kind: "CheCluster", - }, - Spec: orgv1.CheClusterSpec{ - ImagePuller: orgv1.CheClusterSpecImagePuller{ - Enable: true, - Spec: chev1alpha1.KubernetesImagePullerSpec{ - DeploymentName: "kubernetes-image-puller", - ConfigMapName: "k8s-image-puller", + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + ImagePuller: chev2.ImagePuller{ + Enable: false, }, }, }, } } -func InitCheCRWithImagePullerEnabledAndImagesSet(images string) *orgv1.CheCluster { - return &orgv1.CheCluster{ +func InitCheCRWithImagePullerEnabledAndDefaultValuesSet() *chev2.CheCluster { + return &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ - Name: os.Getenv("CHE_FLAVOR"), + Name: "eclipse-che", Namespace: namespace, Finalizers: []string{ "kubernetesimagepullers.finalizers.che.eclipse.org", }, }, TypeMeta: metav1.TypeMeta{ - APIVersion: "org.eclipse.che/v1", + APIVersion: "org.eclipse.che/v2", Kind: "CheCluster", }, - Spec: orgv1.CheClusterSpec{ - ImagePuller: orgv1.CheClusterSpecImagePuller{ - Enable: true, - Spec: chev1alpha1.KubernetesImagePullerSpec{ - DeploymentName: "kubernetes-image-puller", - ConfigMapName: "k8s-image-puller", - Images: images, + + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + ImagePuller: chev2.ImagePuller{ + Enable: true, + Spec: chev1alpha1.KubernetesImagePullerSpec{ + DeploymentName: "kubernetes-image-puller", + ConfigMapName: "k8s-image-puller", + }, }, }, }, } } -func InitCheCRWithImagePullerEnabledAndNewValuesSet() *orgv1.CheCluster { - return &orgv1.CheCluster{ +func InitCheCRWithImagePullerEnabledAndImagesSet(images string) *chev2.CheCluster { + return &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ - Name: os.Getenv("CHE_FLAVOR"), + Name: "eclipse-che", Namespace: namespace, Finalizers: []string{ "kubernetesimagepullers.finalizers.che.eclipse.org", }, }, - Spec: orgv1.CheClusterSpec{ - ImagePuller: orgv1.CheClusterSpecImagePuller{ - Enable: true, - Spec: chev1alpha1.KubernetesImagePullerSpec{ - DeploymentName: "kubernetes-image-puller-trigger-update", - ConfigMapName: "k8s-image-puller-trigger-update", + TypeMeta: metav1.TypeMeta{ + APIVersion: "org.eclipse.che/v2", + Kind: "CheCluster", + }, + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + ImagePuller: chev2.ImagePuller{ + Enable: true, + Spec: chev1alpha1.KubernetesImagePullerSpec{ + DeploymentName: "kubernetes-image-puller", + ConfigMapName: "k8s-image-puller", + Images: images, + }, + }, + }, + }, + } +} + +func InitCheCRWithImagePullerEnabledAndNewValuesSet() *chev2.CheCluster { + return &chev2.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eclipse-che", + Namespace: namespace, + Finalizers: []string{ + "kubernetesimagepullers.finalizers.che.eclipse.org", + }, + }, + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + ImagePuller: chev2.ImagePuller{ + Enable: true, + Spec: chev1alpha1.KubernetesImagePullerSpec{ + DeploymentName: "kubernetes-image-puller-trigger-update", + ConfigMapName: "k8s-image-puller-trigger-update", + }, }, }, }, @@ -677,21 +679,21 @@ func InitImagePuller(options ImagePullerOptions) *chev1alpha1.KubernetesImagePul Kind: "KubernetesImagePuller", }, ObjectMeta: metav1.ObjectMeta{ - Name: os.Getenv("CHE_FLAVOR") + "-image-puller", + Name: "eclipse-che-image-puller", Namespace: namespace, ResourceVersion: options.ObjectMetaResourceVersion, Labels: map[string]string{ - "app.kubernetes.io/part-of": deploy.CheEclipseOrg, - "app": os.Getenv("CHE_FLAVOR"), + "app": defaults.GetCheFlavor(), + "app.kubernetes.io/part-of": constants.CheEclipseOrg, "component": "kubernetes-image-puller", }, OwnerReferences: []metav1.OwnerReference{ { - APIVersion: "org.eclipse.che/v1", + APIVersion: "org.eclipse.che/v2", Kind: "CheCluster", Controller: &valueTrue, BlockOwnerDeletion: &valueTrue, - Name: os.Getenv("CHE_FLAVOR"), + Name: "eclipse-che", }, }, }, @@ -710,20 +712,20 @@ func getDefaultImagePuller() *chev1alpha1.KubernetesImagePuller { Kind: "KubernetesImagePuller", }, ObjectMeta: metav1.ObjectMeta{ - Name: os.Getenv("CHE_FLAVOR") + "-image-puller", + Name: "eclipse-che-image-puller", Namespace: namespace, Labels: map[string]string{ - "app.kubernetes.io/part-of": deploy.CheEclipseOrg, - "app": os.Getenv("CHE_FLAVOR"), + "app": defaults.GetCheFlavor(), + "app.kubernetes.io/part-of": constants.CheEclipseOrg, "component": "kubernetes-image-puller", }, OwnerReferences: []metav1.OwnerReference{ { - APIVersion: "org.eclipse.che/v1", + APIVersion: "org.eclipse.che/v2", Kind: "CheCluster", Controller: &valueTrue, BlockOwnerDeletion: &valueTrue, - Name: os.Getenv("CHE_FLAVOR"), + Name: "eclipse-che", }, }, }, @@ -796,15 +798,3 @@ func getClusterServiceVersion() *operatorsv1alpha1.ClusterServiceVersion { }, } } - -func init() { - operator := &appsv1.Deployment{} - data, err := ioutil.ReadFile("../../config/manager/manager.yaml") - yaml.Unmarshal(data, operator) - if err == nil { - for _, env := range operator.Spec.Template.Spec.Containers[0].Env { - os.Setenv(env.Name, env.Value) - } - } - defaultImagePullerImages = "" -} diff --git a/pkg/deploy/image-puller/init_test.go b/pkg/deploy/image-puller/init_test.go index ad0f6e546..ffb0455f6 100644 --- a/pkg/deploy/image-puller/init_test.go +++ b/pkg/deploy/image-puller/init_test.go @@ -11,11 +11,15 @@ // package imagepuller -import "github.com/eclipse-che/che-operator/pkg/deploy" +import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" +) func init() { - err := deploy.InitTestDefaultsFromDeployment("../../../config/manager/manager.yaml") - if err != nil { - panic(err) - } + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../../../config/manager/manager.yaml") } diff --git a/pkg/deploy/ingres_test.go b/pkg/deploy/ingres_test.go deleted file mode 100644 index b06173d62..000000000 --- a/pkg/deploy/ingres_test.go +++ /dev/null @@ -1,310 +0,0 @@ -// -// Copyright (c) 2019-2021 Red Hat, Inc. -// This program and the accompanying materials are made -// available under the terms of the Eclipse Public License 2.0 -// which is available at https://www.eclipse.org/legal/epl-2.0/ -// -// SPDX-License-Identifier: EPL-2.0 -// -// Contributors: -// Red Hat, Inc. - initial API and implementation -// -package deploy - -import ( - "context" - "reflect" - - "github.com/google/go-cmp/cmp" - - orgv1 "github.com/eclipse-che/che-operator/api/v1" - networking "k8s.io/api/networking/v1" - v1 "k8s.io/api/networking/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/utils/pointer" - - "testing" -) - -func TestIngressSpec(t *testing.T) { - type testCase struct { - name string - ingressName string - ingressHost string - ingressPath string - ingressComponent string - serviceName string - servicePort int - ingressCustomSettings orgv1.IngressCustomSettings - expectedIngress *networking.Ingress - cheCluster *orgv1.CheCluster - } - cheFlavor := getDefaultFromEnv("CHE_FLAVOR") - cheCluster := &orgv1.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - Name: cheFlavor, - }, - } - cheClusterWithGatewaySingleHostExposure := &orgv1.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - Name: cheFlavor, - }, - } - - testCases := []testCase{ - { - name: "Test custom host", - ingressName: "test", - ingressComponent: DefaultCheFlavor(cheCluster), - ingressHost: "test-host", - ingressPath: "", - serviceName: "che-host", - servicePort: 8080, - ingressCustomSettings: orgv1.IngressCustomSettings{ - Labels: "type=default", - Annotations: map[string]string{"annotation-key": "annotation-value"}, - }, - cheCluster: cheCluster, - expectedIngress: &networking.Ingress{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "eclipse-che", - Labels: map[string]string{ - "type": "default", - "app.kubernetes.io/component": DefaultCheFlavor(cheCluster), - "app.kubernetes.io/instance": DefaultCheFlavor(cheCluster), - "app.kubernetes.io/part-of": "che.eclipse.org", - "app.kubernetes.io/managed-by": DefaultCheFlavor(cheCluster) + "-operator", - "app.kubernetes.io/name": DefaultCheFlavor(cheCluster), - }, - Annotations: map[string]string{ - "che.eclipse.org/managed-annotations-digest": "0000", - "kubernetes.io/ingress.class": "nginx", - "nginx.ingress.kubernetes.io/proxy-connect-timeout": "3600", - "nginx.ingress.kubernetes.io/proxy-read-timeout": "3600", - "nginx.ingress.kubernetes.io/ssl-redirect": "true", - "nginx.ingress.kubernetes.io/proxy-buffer-size": "16k", - "nginx.org/websocket-services": "che-host", - "annotation-key": "annotation-value", - }, - }, - TypeMeta: metav1.TypeMeta{ - Kind: "Ingress", - APIVersion: networking.SchemeGroupVersion.String(), - }, - Spec: networking.IngressSpec{ - TLS: []v1.IngressTLS{{Hosts: []string{"test-host"}}}, - Rules: []networking.IngressRule{ - { - Host: "test-host", - IngressRuleValue: networking.IngressRuleValue{ - HTTP: &networking.HTTPIngressRuleValue{ - Paths: []networking.HTTPIngressPath{ - { - Backend: networking.IngressBackend{ - Service: &networking.IngressServiceBackend{ - Name: "che-host", - Port: networking.ServiceBackendPort{ - Number: 8080, - }, - }, - }, - Path: "/", - PathType: (*networking.PathType)(pointer.StringPtr(string(networking.PathTypeImplementationSpecific))), - }, - }, - }, - }, - }, - }, - }, - }, - }, - { - name: "Test custom host", - ingressName: "test", - ingressComponent: DefaultCheFlavor(cheClusterWithGatewaySingleHostExposure), - ingressHost: "test-host", - ingressPath: "", - serviceName: "che-host", - servicePort: 8080, - ingressCustomSettings: orgv1.IngressCustomSettings{ - Labels: "type=default", - Annotations: map[string]string{"annotation-key": "annotation-value"}, - }, - cheCluster: cheClusterWithGatewaySingleHostExposure, - expectedIngress: &networking.Ingress{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "eclipse-che", - Labels: map[string]string{ - "type": "default", - "app.kubernetes.io/component": DefaultCheFlavor(cheCluster), - "app.kubernetes.io/instance": DefaultCheFlavor(cheCluster), - "app.kubernetes.io/part-of": "che.eclipse.org", - "app.kubernetes.io/managed-by": DefaultCheFlavor(cheCluster) + "-operator", - "app.kubernetes.io/name": DefaultCheFlavor(cheCluster), - }, - Annotations: map[string]string{ - "che.eclipse.org/managed-annotations-digest": "0000", - "kubernetes.io/ingress.class": "nginx", - "nginx.ingress.kubernetes.io/proxy-connect-timeout": "3600", - "nginx.ingress.kubernetes.io/proxy-read-timeout": "3600", - "nginx.ingress.kubernetes.io/proxy-buffer-size": "16k", - "nginx.ingress.kubernetes.io/ssl-redirect": "true", - "nginx.org/websocket-services": "che-host", - "annotation-key": "annotation-value", - }, - }, - TypeMeta: metav1.TypeMeta{ - Kind: "Ingress", - APIVersion: networking.SchemeGroupVersion.String(), - }, - Spec: networking.IngressSpec{ - TLS: []v1.IngressTLS{{Hosts: []string{"test-host"}}}, - Rules: []networking.IngressRule{ - { - Host: "test-host", - IngressRuleValue: networking.IngressRuleValue{ - HTTP: &networking.HTTPIngressRuleValue{ - Paths: []networking.HTTPIngressPath{ - { - Backend: networking.IngressBackend{ - Service: &networking.IngressServiceBackend{ - Name: "che-host", - Port: networking.ServiceBackendPort{ - Number: 8080, - }, - }, - }, - Path: "/", - PathType: (*networking.PathType)(pointer.StringPtr(string(networking.PathTypeImplementationSpecific))), - }, - }, - }, - }, - }, - }, - }, - }, - }, - { - name: "Test custom host", - ingressName: "test", - ingressComponent: DefaultCheFlavor(cheClusterWithGatewaySingleHostExposure), - ingressHost: "test-host", - ingressPath: "", - serviceName: "che-host", - servicePort: 8080, - ingressCustomSettings: orgv1.IngressCustomSettings{ - Labels: "type=default", - Annotations: map[string]string{"annotation-key": "annotation-value"}, - }, - expectedIngress: &networking.Ingress{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "eclipse-che", - Labels: map[string]string{ - "type": "default", - "app.kubernetes.io/component": DefaultCheFlavor(cheCluster), - "app.kubernetes.io/instance": DefaultCheFlavor(cheCluster), - "app.kubernetes.io/part-of": "che.eclipse.org", - "app.kubernetes.io/managed-by": DefaultCheFlavor(cheCluster) + "-operator", - "app.kubernetes.io/name": DefaultCheFlavor(cheCluster), - }, - Annotations: map[string]string{ - "che.eclipse.org/managed-annotations-digest": "0000", - "kubernetes.io/ingress.class": "nginx", - "nginx.ingress.kubernetes.io/proxy-connect-timeout": "3600", - "nginx.ingress.kubernetes.io/proxy-read-timeout": "3600", - "nginx.ingress.kubernetes.io/proxy-buffer-size": "16k", - "nginx.ingress.kubernetes.io/ssl-redirect": "true", - "nginx.org/websocket-services": "che-host", - "annotation-key": "annotation-value", - }, - }, - TypeMeta: metav1.TypeMeta{ - Kind: "Ingress", - APIVersion: networking.SchemeGroupVersion.String(), - }, - Spec: networking.IngressSpec{ - TLS: []v1.IngressTLS{{Hosts: []string{"test-host"}}}, - Rules: []networking.IngressRule{ - { - Host: "test-host", - IngressRuleValue: networking.IngressRuleValue{ - HTTP: &networking.HTTPIngressRuleValue{ - Paths: []networking.HTTPIngressPath{ - { - Backend: networking.IngressBackend{ - Service: &networking.IngressServiceBackend{ - Name: "che-host", - Port: networking.ServiceBackendPort{ - Number: 8080, - }, - }, - }, - Path: "/", - PathType: (*networking.PathType)(pointer.StringPtr(string(networking.PathTypeImplementationSpecific))), - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - deployContext := GetTestDeployContext(testCase.cheCluster, []runtime.Object{}) - - _, actualIngress := GetIngressSpec(deployContext, - testCase.ingressName, - testCase.ingressHost, - testCase.ingressPath, - testCase.serviceName, - testCase.servicePort, - testCase.ingressCustomSettings, - testCase.ingressComponent, - ) - - if !reflect.DeepEqual(testCase.expectedIngress, actualIngress) { - t.Errorf("Expected ingress and ingress returned from API server differ (-want, +got): %v", cmp.Diff(testCase.expectedIngress, actualIngress)) - } - }) - } -} - -func TestSyncIngressToCluster(t *testing.T) { - deployContext := GetTestDeployContext(nil, []runtime.Object{}) - - _, done, err := SyncIngressToCluster(deployContext, "test", "host-1", "", "service-1", 8080, orgv1.IngressCustomSettings{}, "component") - if !done || err != nil { - t.Fatalf("Failed to sync ingress: %v", err) - } - - _, done, err = SyncIngressToCluster(deployContext, "test", "host-2", "", "service-2", 8080, orgv1.IngressCustomSettings{}, "component") - if !done || err != nil { - t.Fatalf("Failed to sync ingress: %v", err) - } - - actual := &networking.Ingress{} - err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "test", Namespace: "eclipse-che"}, actual) - if err != nil { - t.Fatalf("Failed to get ingress: %v", err) - } - - if actual.Spec.Rules[0].Host != "host-2" { - t.Fatalf("Failed to sync ingress") - } - if actual.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].Backend.Service.Name != "service-2" { - t.Fatalf("Failed to sync ingress") - } -} diff --git a/pkg/deploy/ingress.go b/pkg/deploy/ingress.go index 6d196eb90..545ca8d29 100644 --- a/pkg/deploy/ingress.go +++ b/pkg/deploy/ingress.go @@ -15,8 +15,10 @@ import ( "reflect" "sort" - orgv1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/pkg/util" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + "github.com/eclipse-che/che-operator/pkg/common/test" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" networking "k8s.io/api/networking/v1" @@ -28,23 +30,30 @@ var ingressDiffOpts = cmp.Options{ cmpopts.IgnoreFields(networking.HTTPIngressPath{}, "PathType"), cmp.Comparer(func(x, y metav1.ObjectMeta) bool { return reflect.DeepEqual(x.Labels, y.Labels) && - x.Annotations[CheEclipseOrgManagedAnnotationsDigest] == y.Annotations[CheEclipseOrgManagedAnnotationsDigest] + x.Annotations[constants.CheEclipseOrgManagedAnnotationsDigest] == y.Annotations[constants.CheEclipseOrgManagedAnnotationsDigest] }), } +var ( + DefaultIngressAnnotations = map[string]string{ + "kubernetes.io/ingress.class": "nginx", + "nginx.ingress.kubernetes.io/proxy-read-timeout": "3600", + "nginx.ingress.kubernetes.io/proxy-connect-timeout": "3600", + "nginx.ingress.kubernetes.io/ssl-redirect": "true", + } +) + // SyncIngressToCluster creates ingress to expose service with the set settings // host and path are evaluated if they are empty func SyncIngressToCluster( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, - host string, path string, serviceName string, servicePort int, - ingressCustomSettings orgv1.IngressCustomSettings, component string) (endpointUrl string, done bool, err error) { - ingressUrl, ingressSpec := GetIngressSpec(deployContext, name, host, path, serviceName, servicePort, ingressCustomSettings, component) + ingressUrl, ingressSpec := GetIngressSpec(deployContext, name, path, serviceName, servicePort, component) sync, err := Sync(deployContext, ingressSpec, ingressDiffOpts) return ingressUrl, sync, err } @@ -52,28 +61,22 @@ func SyncIngressToCluster( // GetIngressSpec returns expected ingress config for given parameters // host and path are evaluated if they are empty func GetIngressSpec( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, - host string, path string, serviceName string, servicePort int, - ingressCustomSettings orgv1.IngressCustomSettings, component string) (ingressUrl string, i *networking.Ingress) { - cheFlavor := DefaultCheFlavor(deployContext.CheCluster) - ingressDomain := deployContext.CheCluster.Spec.K8s.IngressDomain - tlsSecretName := deployContext.CheCluster.Spec.K8s.TlsSecretName - ingressClass := util.GetValue(deployContext.CheCluster.Spec.K8s.IngressClass, DefaultIngressClass) - labels := GetLabels(deployContext.CheCluster, component) - MergeLabels(labels, ingressCustomSettings.Labels) + ingressDomain := deployContext.CheCluster.Spec.Networking.Domain + tlsSecretName := deployContext.CheCluster.Spec.Networking.TlsSecretName + labels := GetLabels(component) + for k, v := range deployContext.CheCluster.Spec.Networking.Labels { + labels[k] = v + } pathType := networking.PathTypeImplementationSpecific - // for server and dashboard ingresses - if (component == cheFlavor || component == cheFlavor+"-dashboard") && deployContext.CheCluster.Spec.Server.CheHostTLSSecret != "" { - tlsSecretName = deployContext.CheCluster.Spec.Server.CheHostTLSSecret - } - + host := deployContext.CheCluster.Spec.Networking.Hostname if host == "" { host = ingressDomain } @@ -86,20 +89,19 @@ func GetIngressSpec( endpointPath = path } - annotations := map[string]string{ - "kubernetes.io/ingress.class": ingressClass, - "nginx.ingress.kubernetes.io/proxy-read-timeout": "3600", - "nginx.ingress.kubernetes.io/proxy-connect-timeout": "3600", - "nginx.ingress.kubernetes.io/ssl-redirect": "true", - } - if component == DevfileRegistryName || component == PluginRegistryName { - annotations["nginx.ingress.kubernetes.io/rewrite-target"] = "/$1" - } - // Set bigger proxy buffer size to prevent 502 auth error. - annotations["nginx.ingress.kubernetes.io/proxy-buffer-size"] = "16k" + annotations := map[string]string{} + if len(deployContext.CheCluster.Spec.Networking.Annotations) > 0 { + for k, v := range deployContext.CheCluster.Spec.Networking.Annotations { + annotations[k] = v + } + } else { + for k, v := range DefaultIngressAnnotations { + annotations[k] = v + } - for k, v := range ingressCustomSettings.Annotations { - annotations[k] = v + // Set bigger proxy buffer size to prevent 502 auth error. + annotations["nginx.ingress.kubernetes.io/proxy-buffer-size"] = "16k" + annotations["nginx.org/websocket-services"] = serviceName } // add 'che.eclipse.org/managed-annotations-digest' annotation @@ -115,10 +117,10 @@ func GetIngressSpec( for _, k := range annotationsKeys { data += k + ":" + annotations[k] + "," } - if util.IsTestMode() { - annotations[CheEclipseOrgManagedAnnotationsDigest] = "0000" + if test.IsTestMode() { + annotations[constants.CheEclipseOrgManagedAnnotationsDigest] = "0000" } else { - annotations[CheEclipseOrgManagedAnnotationsDigest] = util.ComputeHash256([]byte(data)) + annotations[constants.CheEclipseOrgManagedAnnotationsDigest] = utils.ComputeHash256([]byte(data)) } } @@ -160,11 +162,6 @@ func GetIngressSpec( }, } - if component == cheFlavor { - // adds annotation, see details https://github.com/eclipse/che/issues/19434#issuecomment-810325262 - ingress.ObjectMeta.Annotations["nginx.org/websocket-services"] = serviceName - } - ingress.Spec.TLS = []networking.IngressTLS{ { Hosts: []string{host}, @@ -179,9 +176,9 @@ func GetIngressSpec( // and endpoint path (one which client should use during endpoint accessing) func evaluatePath(component string) (endpointPath, ingressPath string) { switch component { - case DevfileRegistryName: + case constants.DevfileRegistryName: fallthrough - case PluginRegistryName: + case constants.PluginRegistryName: endpointPath = "/" + component ingressPath = endpointPath + "/(.*)" default: diff --git a/pkg/deploy/ingress_test.go b/pkg/deploy/ingress_test.go new file mode 100644 index 000000000..6689f49e5 --- /dev/null +++ b/pkg/deploy/ingress_test.go @@ -0,0 +1,162 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// +package deploy + +import ( + "context" + + "github.com/stretchr/testify/assert" + + chev2 "github.com/eclipse-che/che-operator/api/v2" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" + networking "k8s.io/api/networking/v1" + v1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/pointer" + + "testing" +) + +func TestIngressSpec(t *testing.T) { + type testCase struct { + name string + ingressName string + ingressPath string + ingressComponent string + serviceName string + servicePort int + expectedIngress *networking.Ingress + cheCluster *chev2.CheCluster + } + + testCases := []testCase{ + { + name: "Test ingress", + ingressName: "test", + cheCluster: &chev2.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "eclipse-che", + Name: "eclipse-che", + }, + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + Hostname: "test-host", + Labels: map[string]string{"type": "default"}, + Annotations: map[string]string{"annotation-key": "annotation-value"}, + }, + }, + }, + ingressPath: "", + ingressComponent: defaults.GetCheFlavor(), + serviceName: "che-host", + servicePort: 8080, + expectedIngress: &networking.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "eclipse-che", + Labels: map[string]string{ + "type": "default", + "app.kubernetes.io/component": defaults.GetCheFlavor(), + "app.kubernetes.io/instance": defaults.GetCheFlavor(), + "app.kubernetes.io/part-of": "che.eclipse.org", + "app.kubernetes.io/managed-by": defaults.GetCheFlavor() + "-operator", + "app.kubernetes.io/name": defaults.GetCheFlavor(), + }, + Annotations: map[string]string{ + "che.eclipse.org/managed-annotations-digest": "0000", + "annotation-key": "annotation-value", + }, + }, + TypeMeta: metav1.TypeMeta{ + Kind: "Ingress", + APIVersion: networking.SchemeGroupVersion.String(), + }, + Spec: networking.IngressSpec{ + TLS: []v1.IngressTLS{{Hosts: []string{"test-host"}}}, + Rules: []networking.IngressRule{ + { + Host: "test-host", + IngressRuleValue: networking.IngressRuleValue{ + HTTP: &networking.HTTPIngressRuleValue{ + Paths: []networking.HTTPIngressPath{ + { + Backend: networking.IngressBackend{ + Service: &networking.IngressServiceBackend{ + Name: "che-host", + Port: networking.ServiceBackendPort{ + Number: 8080, + }, + }, + }, + Path: "/", + PathType: (*networking.PathType)(pointer.StringPtr(string(networking.PathTypeImplementationSpecific))), + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + deployContext := test.GetDeployContext(testCase.cheCluster, []runtime.Object{}) + + _, actualIngress := GetIngressSpec(deployContext, + testCase.ingressName, + testCase.ingressPath, + testCase.serviceName, + testCase.servicePort, + testCase.ingressComponent, + ) + + assert.Equal(t, testCase.expectedIngress, actualIngress) + }) + } +} + +func TestSyncIngressToCluster(t *testing.T) { + cheCluster := &chev2.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "eclipse-che", + Name: "eclipse-che", + }, + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + Hostname: "host-1", + }, + }, + } + + deployContext := test.GetDeployContext(cheCluster, []runtime.Object{}) + _, done, err := SyncIngressToCluster(deployContext, "test", "", "service-1", 8080, "component") + assert.Nil(t, err) + assert.True(t, done) + + cheCluster.Spec.Networking.Hostname = "host-2" + _, done, err = SyncIngressToCluster(deployContext, "test", "", "service-2", 8080, "component") + assert.Nil(t, err) + assert.True(t, done) + + actual := &networking.Ingress{} + err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "test", Namespace: "eclipse-che"}, actual) + assert.Nil(t, err) + + assert.Equal(t, "host-2", actual.Spec.Rules[0].Host) + assert.Equal(t, "service-2", actual.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].Backend.Service.Name) +} diff --git a/pkg/deploy/init_test.go b/pkg/deploy/init_test.go index 624fb0bc3..3b1aa7c37 100644 --- a/pkg/deploy/init_test.go +++ b/pkg/deploy/init_test.go @@ -11,9 +11,15 @@ // package deploy +import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" +) + func init() { - err := InitTestDefaultsFromDeployment("../../config/manager/manager.yaml") - if err != nil { - panic(err) - } + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../../config/manager/manager.yaml") } diff --git a/pkg/deploy/job.go b/pkg/deploy/job.go index 7efca8880..d8379d90a 100644 --- a/pkg/deploy/job.go +++ b/pkg/deploy/job.go @@ -14,7 +14,8 @@ package deploy import ( "reflect" - "github.com/eclipse-che/che-operator/pkg/util" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" batchv1 "k8s.io/api/batch/v1" @@ -44,7 +45,7 @@ var ( ) func SyncJobToCluster( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, component string, image string, @@ -57,19 +58,19 @@ func SyncJobToCluster( // GetSpecJob creates new job configuration by given parameters. func getJobSpec( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, component string, image string, serviceAccountName string, env map[string]string) *batchv1.Job { - labels := GetLabels(deployContext.CheCluster, component) + labels := GetLabels(component) backoffLimit := int32(3) parallelism := int32(1) comletions := int32(1) terminationGracePeriodSeconds := int64(30) ttlSecondsAfterFinished := int32(30) - pullPolicy := corev1.PullPolicy(util.GetValue(string(deployContext.CheCluster.Spec.Server.CheImagePullPolicy), "IfNotPresent")) + pullPolicy := corev1.PullPolicy(utils.GetPullPolicyFromDockerImage(image)) var jobEnvVars []corev1.EnvVar for envVarName, envVarValue := range env { diff --git a/pkg/deploy/job_test.go b/pkg/deploy/job_test.go index e59fa8948..94aacd5fa 100644 --- a/pkg/deploy/job_test.go +++ b/pkg/deploy/job_test.go @@ -14,7 +14,8 @@ package deploy import ( "context" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" batchv1 "k8s.io/api/batch/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -25,16 +26,16 @@ import ( ) func TestSyncJobToCluster(t *testing.T) { - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme) - deployContext := &DeployContext{ - CheCluster: &orgv1.CheCluster{ + deployContext := &chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, }, - ClusterAPI: ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, diff --git a/pkg/deploy/labels.go b/pkg/deploy/labels.go index f2dc592cf..f5ad055b6 100644 --- a/pkg/deploy/labels.go +++ b/pkg/deploy/labels.go @@ -12,25 +12,23 @@ package deploy import ( - "strings" - - orgv1 "github.com/eclipse-che/che-operator/api/v1" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" ) -func GetLabels(cheCluster *orgv1.CheCluster, component string) map[string]string { - cheFlavor := DefaultCheFlavor(cheCluster) +func GetLabels(component string) map[string]string { return map[string]string{ - KubernetesNameLabelKey: cheFlavor, - KubernetesInstanceLabelKey: cheFlavor, - KubernetesPartOfLabelKey: CheEclipseOrg, - KubernetesComponentLabelKey: component, - KubernetesManagedByLabelKey: cheFlavor + "-operator", + constants.KubernetesNameLabelKey: defaults.GetCheFlavor(), + constants.KubernetesInstanceLabelKey: defaults.GetCheFlavor(), + constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg, + constants.KubernetesComponentLabelKey: component, + constants.KubernetesManagedByLabelKey: defaults.GetCheFlavor() + "-operator", } } -func GetLabelsAndSelector(cheCluster *orgv1.CheCluster, component string) (map[string]string, map[string]string) { - labels := GetLabels(cheCluster, component) - legacyLabels := GetLegacyLabels(cheCluster, component) +func GetLabelsAndSelector(component string) (map[string]string, map[string]string) { + labels := GetLabels(component) + legacyLabels := GetLegacyLabels(component) // For the backward compatability // We have to keep these labels for a deployment since this field is immutable @@ -41,18 +39,9 @@ func GetLabelsAndSelector(cheCluster *orgv1.CheCluster, component string) (map[s return labels, legacyLabels } -func MergeLabels(labels map[string]string, additionalLabels string) { - for _, l := range strings.Split(additionalLabels, ",") { - pair := strings.SplitN(l, "=", 2) - if len(pair) == 2 { - labels[pair[0]] = pair[1] - } - } -} - -func GetLegacyLabels(cheCluster *orgv1.CheCluster, component string) map[string]string { +func GetLegacyLabels(component string) map[string]string { return map[string]string{ - "app": DefaultCheFlavor(cheCluster), + "app": defaults.GetCheFlavor(), "component": component, } } diff --git a/pkg/deploy/labels_test.go b/pkg/deploy/labels_test.go deleted file mode 100644 index f3e4ef3e5..000000000 --- a/pkg/deploy/labels_test.go +++ /dev/null @@ -1,36 +0,0 @@ -// -// Copyright (c) 2019-2021 Red Hat, Inc. -// This program and the accompanying materials are made -// available under the terms of the Eclipse Public License 2.0 -// which is available at https://www.eclipse.org/legal/epl-2.0/ -// -// SPDX-License-Identifier: EPL-2.0 -// -// Contributors: -// Red Hat, Inc. - initial API and implementation -// -package deploy - -import ( - "testing" - - orgv1 "github.com/eclipse-che/che-operator/api/v1" -) - -func TestLabels(t *testing.T) { - cheCluster := &orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - CheFlavor: "che", - }, - }, - } - - labels, _ := GetLabelsAndSelector(cheCluster, "test") - if labels[KubernetesNameLabelKey] == "" || - labels[KubernetesComponentLabelKey] == "" || - labels[KubernetesInstanceLabelKey] == "" || - labels[KubernetesManagedByLabelKey] == "" { - t.Errorf("Default kubernetes labels aren't set.") - } -} diff --git a/pkg/deploy/migration/on-reconcile-one-time-migration.go b/pkg/deploy/migration/on-reconcile-one-time-migration.go index 24938ba20..5928f4a03 100644 --- a/pkg/deploy/migration/on-reconcile-one-time-migration.go +++ b/pkg/deploy/migration/on-reconcile-one-time-migration.go @@ -17,10 +17,14 @@ import ( "fmt" "time" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/utils" oauthv1 "github.com/openshift/api/oauth/v1" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" routev1 "github.com/openshift/api/route/v1" "github.com/sirupsen/logrus" appsv1 "k8s.io/api/apps/v1" @@ -49,7 +53,7 @@ func NewMigrator() *Migrator { } } -func (m *Migrator) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { +func (m *Migrator) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { if m.migrationDone { return reconcile.Result{}, true, nil } @@ -63,17 +67,17 @@ func (m *Migrator) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, return reconcile.Result{}, done, err } -func (m *Migrator) Finalize(ctx *deploy.DeployContext) bool { +func (m *Migrator) Finalize(ctx *chetypes.DeployContext) bool { return true } -func (m *Migrator) migrate(ctx *deploy.DeployContext) (bool, error) { +func (m *Migrator) migrate(ctx *chetypes.DeployContext) (bool, error) { if err := addPartOfCheLabeltoUserDefinedObjects(ctx); err != nil { return false, err } - cheFlavor := deploy.DefaultCheFlavor(ctx.CheCluster) - if err := addPartOfCheLabelForObjectsWithLabel(ctx, deploy.KubernetesInstanceLabelKey, cheFlavor); err != nil { + cheFlavor := defaults.GetCheFlavor() + if err := addPartOfCheLabelForObjectsWithLabel(ctx, constants.KubernetesInstanceLabelKey, cheFlavor); err != nil { return false, err } @@ -85,81 +89,66 @@ func (m *Migrator) migrate(ctx *deploy.DeployContext) (bool, error) { } // addPartOfCheLabeltoUserDefinedObjects processes the following objects to add 'app.kubernetes.io/part-of=che.eclipse.org' label: -// - spec.server.cheHostTLSSecret // - spec.server.cheClusterRoles // - spec.server.cheWorkspaceClusterRole // - spec.server.serverTrustStoreConfigMapName -// - spec.server.gitSelfSignedCert ('che-git-self-signed-cert' config map) -// - spec.server.proxySecret -// - spec.database.chePostgresSecret -// - spec.k8s.tlsSecretName +// - spec.server.proxy.credentialssecretname +// - spec.database.credentialssecretname +// - spec.networking.tlsSecretName // Note, most of the objects above are autogenerated and do not require any migration, // but to handle the case when some were created manually or operator updated, the check is done here. -func addPartOfCheLabeltoUserDefinedObjects(ctx *deploy.DeployContext) error { - if !util.IsOpenShift { +func addPartOfCheLabeltoUserDefinedObjects(ctx *chetypes.DeployContext) error { + if !infrastructure.IsOpenShift() { // Kubernetes only - - tlsSecretName := deploy.DefaultCheTLSSecretName - if ctx.CheCluster.Spec.K8s.TlsSecretName != "" { - tlsSecretName = ctx.CheCluster.Spec.K8s.TlsSecretName - } + tlsSecretName := utils.GetValue(ctx.CheCluster.Spec.Networking.TlsSecretName, constants.DefaultCheTLSSecretName) if err := addPartOfCheLabelToSecret(ctx, tlsSecretName); err != nil { return err } } // TLS - if err := addPartOfCheLabelToSecret(ctx, deploy.CheTLSSelfSignedCertificateSecretName); err != nil { + if err := addPartOfCheLabelToSecret(ctx, constants.DefaultSelfSignedCertificateSecretName); err != nil { return err } - if ctx.CheCluster.Spec.Server.CheHostTLSSecret != "" { - if err := addPartOfCheLabelToSecret(ctx, ctx.CheCluster.Spec.Server.CheHostTLSSecret); err != nil { - return err - } - } - // Proxy credentials - if ctx.CheCluster.Spec.Server.ProxySecret != "" { - if err := addPartOfCheLabelToSecret(ctx, ctx.CheCluster.Spec.Server.ProxySecret); err != nil { - return err - } + proxyCredentialsSecret := utils.GetValue(ctx.CheCluster.Spec.Components.CheServer.Proxy.CredentialsSecretName, constants.DefaultProxyCredentialsSecret) + if err := addPartOfCheLabelToSecret(ctx, proxyCredentialsSecret); err != nil { + return err } // Database credentials - chePostgresCredentialsSecret := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresSecret, deploy.DefaultChePostgresCredentialsSecret) + chePostgresCredentialsSecret := utils.GetValue(ctx.CheCluster.Spec.Components.Database.CredentialsSecretName, constants.DefaultPostgresCredentialsSecret) if err := addPartOfCheLabelToSecret(ctx, chePostgresCredentialsSecret); err != nil { return err } // Legacy config map with additional CA certificates - trustStoreConfigMap := util.GetValue(ctx.CheCluster.Spec.Server.ServerTrustStoreConfigMapName, deploy.DefaultServerTrustStoreConfigMapName) - if err := addPartOfCheLabelToConfigMap(ctx, trustStoreConfigMap); err != nil { + if err := addPartOfCheLabelToConfigMap(ctx, constants.DefaultServerTrustStoreConfigMapName); err != nil { return err } // Config map with CA certificates for git - if ctx.CheCluster.Spec.Server.GitSelfSignedCert { - if err := addPartOfCheLabelToConfigMap(ctx, deploy.GitSelfSignedCertsConfigMapName); err != nil { - return err - } + gitTrustedCertsConfigMapName := utils.GetValue(ctx.CheCluster.Spec.DevEnvironments.TrustedCerts.GitTrustedCertsConfigMapName, constants.DefaultGitSelfSignedCertsConfigMapName) + if err := addPartOfCheLabelToConfigMap(ctx, gitTrustedCertsConfigMapName); err != nil { + return err } return nil } -func addPartOfCheLabelToSecret(ctx *deploy.DeployContext, secretName string) error { +func addPartOfCheLabelToSecret(ctx *chetypes.DeployContext, secretName string) error { return addPartOfCheLabelToObject(ctx, secretName, &corev1.Secret{}) } -func addPartOfCheLabelToConfigMap(ctx *deploy.DeployContext, configMapName string) error { +func addPartOfCheLabelToConfigMap(ctx *chetypes.DeployContext, configMapName string) error { return addPartOfCheLabelToObject(ctx, configMapName, &corev1.ConfigMap{}) } // addPartOfCheLabelToObject adds 'app.kubernetes.io/part-of=che.eclipse.org' label to the object with given name to be cached by operator's k8s client. // As the function doesn't know the kind of the object with given name an empty object should be passed, // for example: addPartOfCheLabelToObject(ctx, "my-secret", &corev1.Secret{}) -func addPartOfCheLabelToObject(ctx *deploy.DeployContext, objectName string, obj client.Object) error { +func addPartOfCheLabelToObject(ctx *chetypes.DeployContext, objectName string, obj client.Object) error { // Check if the object is already migrated if exists, _ := deploy.GetNamespacedObject(ctx, objectName, obj); exists { // Default client sees the object in cache, no need in adding anything @@ -188,14 +177,14 @@ func setPartOfLabel(obj client.Object) client.Object { if labels == nil { labels = make(map[string]string) } - labels[deploy.KubernetesPartOfLabelKey] = deploy.CheEclipseOrg + labels[constants.KubernetesPartOfLabelKey] = constants.CheEclipseOrg obj.SetLabels(labels) return obj } // addPartOfCheLabelForObjectsWithLabel searches for objects in Che installation namespace, // that have given label and adds 'app.kubernetes.io/part-of=che.eclipse.org' -func addPartOfCheLabelForObjectsWithLabel(ctx *deploy.DeployContext, labelKey string, labelValue string) error { +func addPartOfCheLabelForObjectsWithLabel(ctx *chetypes.DeployContext, labelKey string, labelValue string) error { // Prepare selector for all instance=che objects in the installation namespace instanceCheSelectorRequirement, err := labels.NewRequirement(labelKey, selection.Equals, []string{labelValue}) if err != nil { @@ -203,7 +192,7 @@ func addPartOfCheLabelForObjectsWithLabel(ctx *deploy.DeployContext, labelKey st return err } // Do not migrate already migrated objects - notPartOfCheSelectorRequirement, err := labels.NewRequirement(deploy.KubernetesPartOfLabelKey, selection.NotEquals, []string{deploy.CheEclipseOrg}) + notPartOfCheSelectorRequirement, err := labels.NewRequirement(constants.KubernetesPartOfLabelKey, selection.NotEquals, []string{constants.CheEclipseOrg}) if err != nil { logrus.Error(getFailedToCreateSelectorErrorMessage()) return err @@ -231,7 +220,7 @@ func addPartOfCheLabelForObjectsWithLabel(ctx *deploy.DeployContext, labelKey st &rbacv1.ClusterRoleBindingList{}, &corev1.PersistentVolumeClaimList{}, } - if util.IsOpenShift { + if infrastructure.IsOpenShift() { kindsToMigrate = append(kindsToMigrate, &routev1.RouteList{}) kindsToMigrate = append(kindsToMigrate, &oauthv1.OAuthClientList{}) } else { @@ -249,9 +238,9 @@ func addPartOfCheLabelForObjectsWithLabel(ctx *deploy.DeployContext, labelKey st // addPartOfCheLabelToObjectsBySelector adds 'app.kubernetes.io/part-of=che.eclipse.org' label to all objects // of given objectsList kind that match the provided in listOptions selector and namespace. -func addPartOfCheLabelToObjectsBySelector(ctx *deploy.DeployContext, listOptions *client.ListOptions, objectsList client.ObjectList) error { +func addPartOfCheLabelToObjectsBySelector(ctx *chetypes.DeployContext, listOptions *client.ListOptions, objectsList client.ObjectList) error { if err := ctx.ClusterAPI.NonCachingClient.List(context.TODO(), objectsList, listOptions); err != nil { - logrus.Warnf("Failed to get %s to add %s label", objectsList.GetObjectKind().GroupVersionKind().Kind, deploy.KubernetesPartOfLabelKey) + logrus.Warnf("Failed to get %s to add %s label", objectsList.GetObjectKind().GroupVersionKind().Kind, constants.KubernetesPartOfLabelKey) return err } @@ -277,5 +266,5 @@ func getFailedToCreateSelectorErrorMessage() string { func getObjectMigratedMessage(obj client.Object) string { return fmt.Sprintf("Added '%s=%s' label to %s object of %s kind", - deploy.KubernetesPartOfLabelKey, deploy.CheEclipseOrg, obj.GetName(), deploy.GetObjectType(obj)) + constants.KubernetesPartOfLabelKey, constants.CheEclipseOrg, obj.GetName(), deploy.GetObjectType(obj)) } diff --git a/pkg/deploy/pluginregistry/init_test.go b/pkg/deploy/pluginregistry/init_test.go index f442a677f..7db0dec8b 100644 --- a/pkg/deploy/pluginregistry/init_test.go +++ b/pkg/deploy/pluginregistry/init_test.go @@ -11,11 +11,15 @@ // package pluginregistry -import "github.com/eclipse-che/che-operator/pkg/deploy" +import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" +) func init() { - err := deploy.InitTestDefaultsFromDeployment("../../../config/manager/manager.yaml") - if err != nil { - panic(err) - } + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../../../config/manager/manager.yaml") } diff --git a/pkg/deploy/pluginregistry/pluginregistry.go b/pkg/deploy/pluginregistry/pluginregistry.go index 19ecf5f8d..6b491b6a1 100644 --- a/pkg/deploy/pluginregistry/pluginregistry.go +++ b/pkg/deploy/pluginregistry/pluginregistry.go @@ -15,6 +15,8 @@ import ( "fmt" "strings" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" "github.com/eclipse-che/che-operator/pkg/deploy/gateway" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -30,16 +32,11 @@ func NewPluginRegistryReconciler() *PluginRegistryReconciler { return &PluginRegistryReconciler{} } -func (p *PluginRegistryReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { - if ctx.CheCluster.Spec.Server.ExternalPluginRegistry { - if ctx.CheCluster.Spec.Server.PluginRegistryUrl != ctx.CheCluster.Status.PluginRegistryURL { - ctx.CheCluster.Status.PluginRegistryURL = ctx.CheCluster.Spec.Server.PluginRegistryUrl - if err := deploy.UpdateCheCRStatus(ctx, "PluginRegistryUrl", ctx.CheCluster.Spec.Server.PluginRegistryUrl); err != nil { - return reconcile.Result{}, false, err - } - } - - return reconcile.Result{}, true, nil +func (p *PluginRegistryReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { + if ctx.CheCluster.Spec.Components.PluginRegistry.DisableInternalRegistry { + ctx.CheCluster.Status.PluginRegistryURL = "" + err := deploy.UpdateCheCRStatus(ctx, "PluginRegistryURL", "") + return reconcile.Result{}, err == nil, err } done, err := p.syncService(ctx) @@ -70,35 +67,35 @@ func (p *PluginRegistryReconciler) Reconcile(ctx *deploy.DeployContext) (reconci return reconcile.Result{}, true, nil } -func (p *PluginRegistryReconciler) Finalize(ctx *deploy.DeployContext) bool { +func (p *PluginRegistryReconciler) Finalize(ctx *chetypes.DeployContext) bool { return true } -func (p *PluginRegistryReconciler) syncService(ctx *deploy.DeployContext) (bool, error) { +func (p *PluginRegistryReconciler) syncService(ctx *chetypes.DeployContext) (bool, error) { return deploy.SyncServiceToCluster( ctx, - deploy.PluginRegistryName, + constants.PluginRegistryName, []string{"http"}, []int32{8080}, - deploy.PluginRegistryName) + constants.PluginRegistryName) } -func (p *PluginRegistryReconciler) syncConfigMap(ctx *deploy.DeployContext) (bool, error) { +func (p *PluginRegistryReconciler) syncConfigMap(ctx *chetypes.DeployContext) (bool, error) { data, err := p.getConfigMapData(ctx) if err != nil { return false, err } - return deploy.SyncConfigMapDataToCluster(ctx, deploy.PluginRegistryName, data, deploy.PluginRegistryName) + return deploy.SyncConfigMapDataToCluster(ctx, constants.PluginRegistryName, data, constants.PluginRegistryName) } -func (p *PluginRegistryReconciler) ExposeEndpoint(ctx *deploy.DeployContext) (string, bool, error) { +func (p *PluginRegistryReconciler) ExposeEndpoint(ctx *chetypes.DeployContext) (string, bool, error) { return expose.Expose( ctx, - deploy.PluginRegistryName, + constants.PluginRegistryName, p.createGatewayConfig(ctx)) } -func (p *PluginRegistryReconciler) updateStatus(endpoint string, ctx *deploy.DeployContext) (bool, error) { +func (p *PluginRegistryReconciler) updateStatus(endpoint string, ctx *chetypes.DeployContext) (bool, error) { pluginRegistryURL := "https://" + endpoint // append the API version to plugin registry @@ -118,18 +115,18 @@ func (p *PluginRegistryReconciler) updateStatus(endpoint string, ctx *deploy.Dep return true, nil } -func (p *PluginRegistryReconciler) syncDeployment(ctx *deploy.DeployContext) (bool, error) { +func (p *PluginRegistryReconciler) syncDeployment(ctx *chetypes.DeployContext) (bool, error) { spec := p.getPluginRegistryDeploymentSpec(ctx) return deploy.SyncDeploymentSpecToCluster(ctx, spec, deploy.DefaultDeploymentDiffOpts) } -func (p *PluginRegistryReconciler) createGatewayConfig(ctx *deploy.DeployContext) *gateway.TraefikConfig { - pathPrefix := "/" + deploy.PluginRegistryName +func (p *PluginRegistryReconciler) createGatewayConfig(ctx *chetypes.DeployContext) *gateway.TraefikConfig { + pathPrefix := "/" + constants.PluginRegistryName cfg := gateway.CreateCommonTraefikConfig( - deploy.PluginRegistryName, + constants.PluginRegistryName, fmt.Sprintf("PathPrefix(`%s`)", pathPrefix), 10, - "http://"+deploy.PluginRegistryName+":8080", + "http://"+constants.PluginRegistryName+":8080", []string{pathPrefix}) return cfg diff --git a/pkg/deploy/pluginregistry/pluginregistry_configmap.go b/pkg/deploy/pluginregistry/pluginregistry_configmap.go index a035d5fba..d0f43bd9c 100644 --- a/pkg/deploy/pluginregistry/pluginregistry_configmap.go +++ b/pkg/deploy/pluginregistry/pluginregistry_configmap.go @@ -15,7 +15,9 @@ import ( "encoding/json" "fmt" - "github.com/eclipse-che/che-operator/pkg/deploy" + "github.com/eclipse-che/che-operator/pkg/common/constants" + + "github.com/eclipse-che/che-operator/pkg/common/chetypes" ) type PluginRegistryConfigMap struct { @@ -25,13 +27,13 @@ type PluginRegistryConfigMap struct { ChePluginRegistryInternalURL string `json:"CHE_PLUGIN_REGISTRY_INTERNAL_URL"` } -func (p *PluginRegistryReconciler) getConfigMapData(ctx *deploy.DeployContext) (map[string]string, error) { +func (p *PluginRegistryReconciler) getConfigMapData(ctx *chetypes.DeployContext) (map[string]string, error) { pluginRegistryEnv := make(map[string]string) data := &PluginRegistryConfigMap{ - CheSidecarContainersRegistryURL: ctx.CheCluster.Spec.Server.AirGapContainerRegistryHostname, - CheSidecarContainersRegistryOrganization: ctx.CheCluster.Spec.Server.AirGapContainerRegistryOrganization, + CheSidecarContainersRegistryURL: ctx.CheCluster.Spec.ContainerRegistry.Hostname, + CheSidecarContainersRegistryOrganization: ctx.CheCluster.Spec.ContainerRegistry.Organization, ChePluginRegistryURL: ctx.CheCluster.Status.PluginRegistryURL, - ChePluginRegistryInternalURL: fmt.Sprintf("http://%s.%s.svc:8080", deploy.PluginRegistryName, ctx.CheCluster.Namespace), + ChePluginRegistryInternalURL: fmt.Sprintf("http://%s.%s.svc:8080", constants.PluginRegistryName, ctx.CheCluster.Namespace), } out, err := json.Marshal(data) diff --git a/pkg/deploy/pluginregistry/pluginregistry_deployment.go b/pkg/deploy/pluginregistry/pluginregistry_deployment.go index dc91b680b..7bd3d27a5 100644 --- a/pkg/deploy/pluginregistry/pluginregistry_deployment.go +++ b/pkg/deploy/pluginregistry/pluginregistry_deployment.go @@ -12,40 +12,36 @@ package pluginregistry import ( + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy/registry" - "github.com/eclipse-che/che-operator/pkg/util" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" ) -func (p *PluginRegistryReconciler) getPluginRegistryDeploymentSpec(ctx *deploy.DeployContext) *appsv1.Deployment { +func (p *PluginRegistryReconciler) getPluginRegistryDeploymentSpec(ctx *chetypes.DeployContext) *appsv1.Deployment { registryType := "plugin" - registryImage := util.GetValue(ctx.CheCluster.Spec.Server.PluginRegistryImage, deploy.DefaultPluginRegistryImage(ctx.CheCluster)) - registryImagePullPolicy := corev1.PullPolicy(util.GetValue(string(ctx.CheCluster.Spec.Server.PluginRegistryPullPolicy), deploy.DefaultPullPolicyFromDockerImage(registryImage))) + registryImage := defaults.GetPluginRegistryImage(ctx.CheCluster) + registryImagePullPolicy := corev1.PullPolicy(utils.GetPullPolicyFromDockerImage(registryImage)) probePath := "/v3/plugins/" - pluginImagesEnv := util.GetEnvByRegExp("^.*plugin_registry_image.*$") + pluginImagesEnv := utils.GetEnvByRegExp("^.*plugin_registry_image.*$") resources := corev1.ResourceRequirements{ Requests: corev1.ResourceList{ - corev1.ResourceMemory: util.GetResourceQuantity( - ctx.CheCluster.Spec.Server.PluginRegistryMemoryRequest, - deploy.DefaultPluginRegistryMemoryRequest), - corev1.ResourceCPU: util.GetResourceQuantity( - ctx.CheCluster.Spec.Server.PluginRegistryCpuRequest, - deploy.DefaultPluginRegistryCpuRequest), + corev1.ResourceMemory: resource.MustParse(constants.DefaultPluginRegistryMemoryRequest), + corev1.ResourceCPU: resource.MustParse(constants.DefaultPluginRegistryCpuRequest), }, Limits: corev1.ResourceList{ - corev1.ResourceMemory: util.GetResourceQuantity( - ctx.CheCluster.Spec.Server.PluginRegistryMemoryLimit, - deploy.DefaultPluginRegistryMemoryLimit), - corev1.ResourceCPU: util.GetResourceQuantity( - ctx.CheCluster.Spec.Server.PluginRegistryCpuLimit, - deploy.DefaultPluginRegistryCpuLimit), + corev1.ResourceMemory: resource.MustParse(constants.DefaultPluginRegistryMemoryLimit), + corev1.ResourceCPU: resource.MustParse(constants.DefaultPluginRegistryCpuLimit), }, } - return registry.GetSpecRegistryDeployment( + deployment := registry.GetSpecRegistryDeployment( ctx, registryType, registryImage, @@ -53,4 +49,7 @@ func (p *PluginRegistryReconciler) getPluginRegistryDeploymentSpec(ctx *deploy.D registryImagePullPolicy, resources, probePath) + + deploy.CustomizeDeployment(deployment, &ctx.CheCluster.Spec.Components.PluginRegistry.Deployment, false) + return deployment } diff --git a/pkg/deploy/pluginregistry/pluginregistry_deployment_test.go b/pkg/deploy/pluginregistry/pluginregistry_deployment_test.go index fb48eb8ae..9f5d1a23e 100644 --- a/pkg/deploy/pluginregistry/pluginregistry_deployment_test.go +++ b/pkg/deploy/pluginregistry/pluginregistry_deployment_test.go @@ -12,11 +12,11 @@ package pluginregistry import ( - "github.com/eclipse-che/che-operator/pkg/util" + "github.com/eclipse-che/che-operator/pkg/common/constants" + "github.com/eclipse-che/che-operator/pkg/common/test" + "k8s.io/apimachinery/pkg/api/resource" - "github.com/eclipse-che/che-operator/pkg/deploy" - - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -31,18 +31,18 @@ func TestGetPluginRegistryDeploymentSpec(t *testing.T) { memoryRequest string cpuRequest string cpuLimit string - cheCluster *orgv1.CheCluster + cheCluster *chev2.CheCluster } testCases := []testCase{ { name: "Test default limits", initObjects: []runtime.Object{}, - memoryLimit: deploy.DefaultPluginRegistryMemoryLimit, - memoryRequest: deploy.DefaultPluginRegistryMemoryRequest, - cpuLimit: deploy.DefaultPluginRegistryCpuLimit, - cpuRequest: deploy.DefaultPluginRegistryCpuRequest, - cheCluster: &orgv1.CheCluster{ + memoryLimit: constants.DefaultPluginRegistryMemoryLimit, + memoryRequest: constants.DefaultPluginRegistryMemoryRequest, + cpuLimit: constants.DefaultPluginRegistryCpuLimit, + cpuRequest: constants.DefaultPluginRegistryCpuRequest, + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", @@ -56,17 +56,32 @@ func TestGetPluginRegistryDeploymentSpec(t *testing.T) { cpuRequest: "150m", memoryLimit: "250Mi", memoryRequest: "150Mi", - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - PluginRegistryCpuLimit: "250m", - PluginRegistryCpuRequest: "150m", - PluginRegistryMemoryLimit: "250Mi", - PluginRegistryMemoryRequest: "150Mi", + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + PluginRegistry: chev2.PluginRegistry{ + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: constants.PluginRegistryName, + Resources: chev2.ResourceRequirements{ + Requests: chev2.ResourceList{ + Memory: resource.MustParse("150Mi"), + Cpu: resource.MustParse("150m"), + }, + Limits: chev2.ResourceList{ + Memory: resource.MustParse("250Mi"), + Cpu: resource.MustParse("250m"), + }, + }, + }, + }, + }, + }, }, }, }, @@ -75,13 +90,13 @@ func TestGetPluginRegistryDeploymentSpec(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{}) + ctx := test.GetDeployContext(testCase.cheCluster, []runtime.Object{}) pluginregistry := NewPluginRegistryReconciler() deployment := pluginregistry.getPluginRegistryDeploymentSpec(ctx) - util.CompareResources(deployment, - util.TestExpectedResources{ + test.CompareResources(deployment, + test.TestExpectedResources{ MemoryLimit: testCase.memoryLimit, MemoryRequest: testCase.memoryRequest, CpuRequest: testCase.cpuRequest, @@ -89,7 +104,7 @@ func TestGetPluginRegistryDeploymentSpec(t *testing.T) { }, t) - util.ValidateSecurityContext(deployment, t) + test.ValidateSecurityContext(deployment, t) }) } } diff --git a/pkg/deploy/pluginregistry/pluginregistry_test.go b/pkg/deploy/pluginregistry/pluginregistry_test.go index 3f0a5cf8b..7a0411266 100644 --- a/pkg/deploy/pluginregistry/pluginregistry_test.go +++ b/pkg/deploy/pluginregistry/pluginregistry_test.go @@ -12,8 +12,8 @@ package pluginregistry import ( - "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/test" "github.com/stretchr/testify/assert" appsv1 "k8s.io/api/apps/v1" @@ -25,16 +25,17 @@ import ( ) func TestPluginRegistryReconcile(t *testing.T) { - util.IsOpenShift = true - ctx := deploy.GetTestDeployContext(nil, []runtime.Object{}) + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + + ctx := test.GetDeployContext(nil, []runtime.Object{}) pluginregistry := NewPluginRegistryReconciler() _, done, err := pluginregistry.Reconcile(ctx) assert.True(t, done) assert.Nil(t, err) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, &corev1.Service{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, &corev1.ConfigMap{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, &appsv1.Deployment{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, &corev1.Service{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, &corev1.ConfigMap{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, &appsv1.Deployment{})) assert.NotEmpty(t, ctx.CheCluster.Status.PluginRegistryURL) } diff --git a/pkg/deploy/postgres/init_test.go b/pkg/deploy/postgres/init_test.go index 53e8c6311..510e12db0 100644 --- a/pkg/deploy/postgres/init_test.go +++ b/pkg/deploy/postgres/init_test.go @@ -11,11 +11,15 @@ // package postgres -import "github.com/eclipse-che/che-operator/pkg/deploy" +import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" +) func init() { - err := deploy.InitTestDefaultsFromDeployment("../../../config/manager/manager.yaml") - if err != nil { - panic(err) - } + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../../../config/manager/manager.yaml") } diff --git a/pkg/deploy/postgres/postgres.go b/pkg/deploy/postgres/postgres.go index 9a97cf1db..4953ddb3b 100644 --- a/pkg/deploy/postgres/postgres.go +++ b/pkg/deploy/postgres/postgres.go @@ -14,9 +14,12 @@ package postgres import ( "strings" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + k8shelper "github.com/eclipse-che/che-operator/pkg/common/k8s-helper" + "github.com/eclipse-che/che-operator/pkg/common/test" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" "github.com/sirupsen/logrus" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -31,8 +34,8 @@ func NewPostgresReconciler() *PostgresReconciler { return &PostgresReconciler{} } -func (p *PostgresReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { - if ctx.CheCluster.Spec.Database.ExternalDb { +func (p *PostgresReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { + if ctx.CheCluster.Spec.Components.Database.ExternalDb { return reconcile.Result{}, true, nil } @@ -56,8 +59,8 @@ func (p *PostgresReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Res return reconcile.Result{}, false, err } - if ctx.CheCluster.Spec.Database.PostgresVersion == "" { - if !util.IsTestMode() { // ignore in tests + if ctx.CheCluster.Status.PostgresVersion == "" { + if !test.IsTestMode() { // ignore in tests done, err := p.setDbVersion(ctx) if !done { return reconcile.Result{}, false, err @@ -68,28 +71,30 @@ func (p *PostgresReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Res return reconcile.Result{}, true, nil } -func (p *PostgresReconciler) Finalize(ctx *deploy.DeployContext) bool { +func (p *PostgresReconciler) Finalize(ctx *chetypes.DeployContext) bool { return true } -func (p *PostgresReconciler) syncService(ctx *deploy.DeployContext) (bool, error) { - return deploy.SyncServiceToCluster(ctx, deploy.PostgresName, []string{deploy.PostgresName}, []int32{5432}, deploy.PostgresName) +func (p *PostgresReconciler) syncService(ctx *chetypes.DeployContext) (bool, error) { + return deploy.SyncServiceToCluster(ctx, constants.PostgresName, []string{constants.PostgresName}, []int32{5432}, constants.PostgresName) } -func (p *PostgresReconciler) syncPVC(ctx *deploy.DeployContext) (bool, error) { - pvcClaimSize := util.GetValue(ctx.CheCluster.Spec.Database.PvcClaimSize, deploy.DefaultPostgresPvcClaimSize) - done, err := deploy.SyncPVCToCluster(ctx, deploy.DefaultPostgresVolumeClaimName, pvcClaimSize, deploy.PostgresName) +func (p *PostgresReconciler) syncPVC(ctx *chetypes.DeployContext) (bool, error) { + pvc := ctx.CheCluster.Spec.Components.Database.Pvc.DeepCopy() + pvc.ClaimSize = utils.GetValue(ctx.CheCluster.Spec.Components.Database.Pvc.ClaimSize, constants.DefaultPostgresPvcClaimSize) + + done, err := deploy.SyncPVCToCluster(ctx, constants.DefaultPostgresVolumeClaimName, pvc, constants.PostgresName) if !done { if err == nil { - logrus.Infof("Waiting on pvc '%s' to be bound. Sometimes PVC can be bound only when the first consumer is created.", deploy.DefaultPostgresVolumeClaimName) + logrus.Infof("Waiting on pvc '%s' to be bound. Sometimes PVC can be bound only when the first consumer is created.", constants.DefaultPostgresVolumeClaimName) } } return done, err } -func (p *PostgresReconciler) syncDeployment(ctx *deploy.DeployContext) (bool, error) { +func (p *PostgresReconciler) syncDeployment(ctx *chetypes.DeployContext) (bool, error) { clusterDeployment := &appsv1.Deployment{} - exists, err := deploy.GetNamespacedObject(ctx, deploy.PostgresName, clusterDeployment) + exists, err := deploy.GetNamespacedObject(ctx, constants.PostgresName, clusterDeployment) if err != nil { return false, err } @@ -106,22 +111,20 @@ func (p *PostgresReconciler) syncDeployment(ctx *deploy.DeployContext) (bool, er return deploy.SyncDeploymentSpecToCluster(ctx, specDeployment, deploy.DefaultDeploymentDiffOpts) } -func (p *PostgresReconciler) setDbVersion(ctx *deploy.DeployContext) (bool, error) { - postgresVersion, err := util.K8sclient.ExecIntoPod( - ctx.CheCluster, - deploy.PostgresName, - func(cr *orgv1.CheCluster) (string, error) { - // don't take into account bugfix version - return "postgres -V | awk '{print $NF}' | cut -d '.' -f1-2", nil - }, - "get PostgreSQL version") +func (p *PostgresReconciler) setDbVersion(ctx *chetypes.DeployContext) (bool, error) { + k8sHelper := k8shelper.New() + postgresVersion, err := k8sHelper.ExecIntoPod( + constants.PostgresName, + "postgres -V | awk '{print $NF}' | cut -d '.' -f1-2", + "get PostgreSQL version", + ctx.CheCluster.Namespace) if err != nil { return false, err } postgresVersion = strings.TrimSpace(postgresVersion) - ctx.CheCluster.Spec.Database.PostgresVersion = postgresVersion - err = deploy.UpdateCheCRSpec(ctx, "database.postgresVersion", postgresVersion) + ctx.CheCluster.Status.PostgresVersion = postgresVersion + err = deploy.UpdateCheCRStatus(ctx, "postgresVersion", postgresVersion) if err != nil { return false, err } @@ -130,17 +133,17 @@ func (p *PostgresReconciler) setDbVersion(ctx *deploy.DeployContext) (bool, erro } // Create secret with PostgreSQL credentials. -func (p *PostgresReconciler) syncCredentials(ctx *deploy.DeployContext) (bool, error) { - postgresCredentialsSecretRef := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresSecret, deploy.DefaultChePostgresCredentialsSecret) - exists, err := deploy.GetNamespacedObject(ctx, postgresCredentialsSecretRef, &corev1.Secret{}) +func (p *PostgresReconciler) syncCredentials(ctx *chetypes.DeployContext) (bool, error) { + postgresCredentialsSecretName := utils.GetValue(ctx.CheCluster.Spec.Components.Database.CredentialsSecretName, constants.DefaultPostgresCredentialsSecret) + exists, err := deploy.GetNamespacedObject(ctx, postgresCredentialsSecretName, &corev1.Secret{}) if err != nil { return false, err } if !exists { - postgresUser := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresUser, deploy.DefaultChePostgresUser) - postgresPassword := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresPassword, util.GeneratePasswd(12)) - return deploy.SyncSecretToCluster(ctx, postgresCredentialsSecretRef, ctx.CheCluster.Namespace, map[string][]byte{"user": []byte(postgresUser), "password": []byte(postgresPassword)}) + postgresUser := constants.DefaultPostgresUser + postgresPassword := utils.GeneratePassword(12) + return deploy.SyncSecretToCluster(ctx, postgresCredentialsSecretName, ctx.CheCluster.Namespace, map[string][]byte{"user": []byte(postgresUser), "password": []byte(postgresPassword)}) } return true, nil diff --git a/pkg/deploy/postgres/postgres_deployment.go b/pkg/deploy/postgres/postgres_deployment.go index a4bd877a8..ba91902b3 100644 --- a/pkg/deploy/postgres/postgres_deployment.go +++ b/pkg/deploy/postgres/postgres_deployment.go @@ -15,11 +15,16 @@ import ( "fmt" "strings" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" ) @@ -30,24 +35,24 @@ const ( ) var ( - postgresAdminPassword = util.GeneratePasswd(12) + postgresAdminPassword = utils.GeneratePassword(12) ) -func (p *PostgresReconciler) getDeploymentSpec(clusterDeployment *appsv1.Deployment, ctx *deploy.DeployContext) (*appsv1.Deployment, error) { +func (p *PostgresReconciler) getDeploymentSpec(clusterDeployment *appsv1.Deployment, ctx *chetypes.DeployContext) (*appsv1.Deployment, error) { terminationGracePeriodSeconds := int64(30) - labels, labelSelector := deploy.GetLabelsAndSelector(ctx.CheCluster, deploy.PostgresName) - chePostgresDb := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresDb, deploy.DefaultChePostgresDb) + labels, labelSelector := deploy.GetLabelsAndSelector(constants.PostgresName) + chePostgresDb := utils.GetValue(ctx.CheCluster.Spec.Components.Database.PostgresDb, constants.DefaultPostgresDb) postgresImage, err := getPostgresImage(clusterDeployment, ctx.CheCluster) if err != nil { return nil, err } - pullPolicy := corev1.PullPolicy(util.GetValue(string(ctx.CheCluster.Spec.Database.PostgresImagePullPolicy), deploy.DefaultPullPolicyFromDockerImage(postgresImage))) + pullPolicy := corev1.PullPolicy(utils.GetPullPolicyFromDockerImage(postgresImage)) if clusterDeployment != nil { clusterContainer := &clusterDeployment.Spec.Template.Spec.Containers[0] - env := util.FindEnv(clusterContainer.Env, "POSTGRESQL_ADMIN_PASSWORD") - if env != nil { - postgresAdminPassword = env.Value + value := utils.GetEnv(clusterContainer.Env, "POSTGRESQL_ADMIN_PASSWORD") + if value != "" { + postgresAdminPassword = value } } @@ -57,7 +62,7 @@ func (p *PostgresReconciler) getDeploymentSpec(clusterDeployment *appsv1.Deploym APIVersion: "apps/v1", }, ObjectMeta: metav1.ObjectMeta{ - Name: deploy.PostgresName, + Name: constants.PostgresName, Namespace: ctx.CheCluster.Namespace, Labels: labels, }, @@ -73,47 +78,39 @@ func (p *PostgresReconciler) getDeploymentSpec(clusterDeployment *appsv1.Deploym Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: deploy.DefaultPostgresVolumeClaimName, + Name: constants.DefaultPostgresVolumeClaimName, VolumeSource: corev1.VolumeSource{ PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: deploy.DefaultPostgresVolumeClaimName, + ClaimName: constants.DefaultPostgresVolumeClaimName, }, }, }, }, Containers: []corev1.Container{ { - Name: deploy.PostgresName, + Name: constants.PostgresName, Image: postgresImage, ImagePullPolicy: pullPolicy, Ports: []corev1.ContainerPort{ { - Name: deploy.PostgresName, + Name: constants.PostgresName, ContainerPort: 5432, Protocol: "TCP", }, }, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ - corev1.ResourceMemory: util.GetResourceQuantity( - ctx.CheCluster.Spec.Database.ChePostgresContainerResources.Requests.Memory, - deploy.DefaultPostgresMemoryRequest), - corev1.ResourceCPU: util.GetResourceQuantity( - ctx.CheCluster.Spec.Database.ChePostgresContainerResources.Requests.Cpu, - deploy.DefaultPostgresCpuRequest), + corev1.ResourceMemory: resource.MustParse(constants.DefaultPostgresMemoryRequest), + corev1.ResourceCPU: resource.MustParse(constants.DefaultPostgresCpuRequest), }, Limits: corev1.ResourceList{ - corev1.ResourceMemory: util.GetResourceQuantity( - ctx.CheCluster.Spec.Database.ChePostgresContainerResources.Limits.Memory, - deploy.DefaultPostgresMemoryLimit), - corev1.ResourceCPU: util.GetResourceQuantity( - ctx.CheCluster.Spec.Database.ChePostgresContainerResources.Limits.Cpu, - deploy.DefaultPostgresCpuLimit), + corev1.ResourceMemory: resource.MustParse(constants.DefaultPostgresMemoryLimit), + corev1.ResourceCPU: resource.MustParse(constants.DefaultPostgresCpuLimit), }, }, VolumeMounts: []corev1.VolumeMount{ { - Name: deploy.DefaultPostgresVolumeClaimName, + Name: constants.DefaultPostgresVolumeClaimName, MountPath: "/var/lib/pgsql/data", }, }, @@ -171,7 +168,7 @@ func (p *PostgresReconciler) getDeploymentSpec(clusterDeployment *appsv1.Deploym container := &deployment.Spec.Template.Spec.Containers[0] - chePostgresCredentialsSecret := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresSecret, deploy.DefaultChePostgresCredentialsSecret) + chePostgresCredentialsSecret := utils.GetValue(ctx.CheCluster.Spec.Components.Database.CredentialsSecretName, constants.DefaultPostgresCredentialsSecret) container.Env = append(container.Env, corev1.EnvVar{ Name: "POSTGRESQL_USER", @@ -195,7 +192,7 @@ func (p *PostgresReconciler) getDeploymentSpec(clusterDeployment *appsv1.Deploym }, }) - if !util.IsOpenShift { + if !infrastructure.IsOpenShift() { var runAsUser int64 = 26 deployment.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{ RunAsUser: &runAsUser, @@ -203,26 +200,28 @@ func (p *PostgresReconciler) getDeploymentSpec(clusterDeployment *appsv1.Deploym } } + deploy.CustomizeDeployment(deployment, &ctx.CheCluster.Spec.Components.Database.Deployment, false) return deployment, nil } -func getPostgresImage(clusterDeployment *appsv1.Deployment, cheCluster *orgv1.CheCluster) (string, error) { - if cheCluster.Spec.Database.PostgresImage != "" { +func getPostgresImage(clusterDeployment *appsv1.Deployment, cheCluster *chev2.CheCluster) (string, error) { + containers := cheCluster.Spec.Components.Database.Deployment.Containers + if len(containers) > 0 && containers[0].Image != "" { // use image explicitly set in a CR - return cheCluster.Spec.Database.PostgresImage, nil - } else if cheCluster.Spec.Database.PostgresVersion == PostgresVersion9_6 { - return deploy.DefaultPostgresImage(cheCluster), nil - } else if strings.HasPrefix(cheCluster.Spec.Database.PostgresVersion, "13.") { - return deploy.DefaultPostgres13Image(cheCluster), nil - } else if cheCluster.Spec.Database.PostgresVersion == "" { + return containers[0].Image, nil + } else if cheCluster.Status.PostgresVersion == PostgresVersion9_6 { + return defaults.GetPostgresImage(cheCluster), nil + } else if strings.HasPrefix(cheCluster.Status.PostgresVersion, "13.") { + return defaults.GetPostgres13Image(cheCluster), nil + } else if cheCluster.Status.PostgresVersion == "" { if clusterDeployment == nil { // Use PostgreSQL 13.3 for a new deployment if there is so. // It allows to work in downstream until a new image is ready for production. - postgres13Image := deploy.DefaultPostgres13Image(cheCluster) + postgres13Image := defaults.GetPostgres13Image(cheCluster) if postgres13Image != "" { return postgres13Image, nil } else { - return deploy.DefaultPostgresImage(cheCluster), nil + return defaults.GetPostgresImage(cheCluster), nil } } else { // Keep using current image @@ -230,5 +229,5 @@ func getPostgresImage(clusterDeployment *appsv1.Deployment, cheCluster *orgv1.Ch } } - return "", fmt.Errorf("PostgreSQL image for '%s' version not found", cheCluster.Spec.Database.PostgresVersion) + return "", fmt.Errorf("PostgreSQL image for '%s' version not found", cheCluster.Status.PostgresVersion) } diff --git a/pkg/deploy/postgres/postgres_readiness.go b/pkg/deploy/postgres/postgres_readiness.go index ee5820160..e59b18098 100644 --- a/pkg/deploy/postgres/postgres_readiness.go +++ b/pkg/deploy/postgres/postgres_readiness.go @@ -14,15 +14,17 @@ package postgres import ( "fmt" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" ) -func GetWaitForPostgresInitContainer(deployContext *deploy.DeployContext) (*corev1.Container, error) { +func GetWaitForPostgresInitContainer(deployContext *chetypes.DeployContext) (*corev1.Container, error) { postgresDeployment := &appsv1.Deployment{} - exists, err := deploy.GetNamespacedObject(deployContext, deploy.PostgresName, postgresDeployment) + exists, err := deploy.GetNamespacedObject(deployContext, constants.PostgresName, postgresDeployment) if err != nil { return nil, err } @@ -33,7 +35,7 @@ func GetWaitForPostgresInitContainer(deployContext *deploy.DeployContext) (*core if err != nil { return nil, err } - imagePullPolicy := corev1.PullPolicy(deploy.DefaultPullPolicyFromDockerImage(postgresReadinessCheckerImage)) + imagePullPolicy := corev1.PullPolicy(utils.GetPullPolicyFromDockerImage(postgresReadinessCheckerImage)) return &corev1.Container{ Name: "wait-for-postgres", @@ -47,9 +49,9 @@ func GetWaitForPostgresInitContainer(deployContext *deploy.DeployContext) (*core }, nil } -func getCheckPostgresReadinessScript(deployContext *deploy.DeployContext) string { - chePostgresHostName := util.GetValue(deployContext.CheCluster.Spec.Database.ChePostgresHostName, deploy.DefaultChePostgresHostName) - chePostgresPort := util.GetValue(deployContext.CheCluster.Spec.Database.ChePostgresPort, deploy.DefaultChePostgresPort) +func getCheckPostgresReadinessScript(deployContext *chetypes.DeployContext) string { + chePostgresHostName := utils.GetValue(deployContext.CheCluster.Spec.Components.Database.PostgresHostName, constants.DefaultPostgresHostName) + chePostgresPort := utils.GetValue(deployContext.CheCluster.Spec.Components.Database.PostgresPort, constants.DefaultPostgresPort) return fmt.Sprintf( "until pg_isready -h %s -p %s; do echo 'waiting for Postgres'; sleep 2; done;", diff --git a/pkg/deploy/postgres/postgres_test.go b/pkg/deploy/postgres/postgres_test.go index d34e79098..2496d207c 100644 --- a/pkg/deploy/postgres/postgres_test.go +++ b/pkg/deploy/postgres/postgres_test.go @@ -12,18 +12,21 @@ package postgres import ( - "context" "fmt" "os" - "github.com/eclipse-che/che-operator/pkg/util" + "k8s.io/apimachinery/pkg/api/resource" + + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" "github.com/stretchr/testify/assert" - "github.com/eclipse-che/che-operator/pkg/deploy" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -41,18 +44,18 @@ func TestDeploymentSpec(t *testing.T) { memoryRequest string cpuLimit string cpuRequest string - cheCluster *orgv1.CheCluster + cheCluster *chev2.CheCluster } testCases := []testCase{ { name: "Test default limits", initObjects: []runtime.Object{}, - memoryLimit: deploy.DefaultPostgresMemoryLimit, - memoryRequest: deploy.DefaultPostgresMemoryRequest, - cpuLimit: deploy.DefaultPostgresCpuLimit, - cpuRequest: deploy.DefaultPostgresCpuRequest, - cheCluster: &orgv1.CheCluster{ + memoryLimit: constants.DefaultPostgresMemoryLimit, + memoryRequest: constants.DefaultPostgresMemoryRequest, + cpuLimit: constants.DefaultPostgresCpuLimit, + cpuRequest: constants.DefaultPostgresCpuRequest, + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", @@ -66,21 +69,30 @@ func TestDeploymentSpec(t *testing.T) { cpuRequest: "150m", memoryLimit: "250Mi", memoryRequest: "150Mi", - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Database: orgv1.CheClusterSpecDB{ - ChePostgresContainerResources: orgv1.ResourcesCustomSettings{ - Limits: orgv1.Resources{ - Cpu: "250m", - Memory: "250Mi", - }, - Requests: orgv1.Resources{ - Memory: "150Mi", - Cpu: "150m", + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + Database: chev2.Database{ + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: constants.PostgresName, + Resources: chev2.ResourceRequirements{ + Requests: chev2.ResourceList{ + Memory: resource.MustParse("150Mi"), + Cpu: resource.MustParse("150m"), + }, + Limits: chev2.ResourceList{ + Memory: resource.MustParse("250Mi"), + Cpu: resource.MustParse("250m"), + }, + }, + }, + }, }, }, }, @@ -93,13 +105,13 @@ func TestDeploymentSpec(t *testing.T) { t.Run(testCase.name, func(t *testing.T) { logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true))) - ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{}) + ctx := test.GetDeployContext(testCase.cheCluster, []runtime.Object{}) postgres := NewPostgresReconciler() deployment, err := postgres.getDeploymentSpec(nil, ctx) assert.Nil(t, err) - util.CompareResources(deployment, - util.TestExpectedResources{ + test.CompareResources(deployment, + test.TestExpectedResources{ MemoryLimit: testCase.memoryLimit, MemoryRequest: testCase.memoryRequest, CpuRequest: testCase.cpuRequest, @@ -107,58 +119,30 @@ func TestDeploymentSpec(t *testing.T) { }, t) - util.ValidateSecurityContext(deployment, t) + test.ValidateSecurityContext(deployment, t) }) } } func TestPostgresReconcile(t *testing.T) { - util.IsOpenShift = true - ctx := deploy.GetTestDeployContext(nil, []runtime.Object{}) + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + ctx := test.GetDeployContext(nil, []runtime.Object{}) postgres := NewPostgresReconciler() _, done, err := postgres.Reconcile(ctx) assert.True(t, done) assert.Nil(t, err) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "postgres-credentials", Namespace: "eclipse-che"}, &corev1.Secret{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "postgres", Namespace: "eclipse-che"}, &corev1.Service{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "postgres-data", Namespace: "eclipse-che"}, &corev1.PersistentVolumeClaim{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "postgres", Namespace: "eclipse-che"}, &appsv1.Deployment{})) -} - -func TestSyncPostgresCredentials(t *testing.T) { - cheCluster := &orgv1.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: "eclipse-che", - Namespace: "eclipse-che", - }, - Spec: orgv1.CheClusterSpec{ - Database: orgv1.CheClusterSpecDB{ - ChePostgresUser: "postgresUser", - ChePostgresPassword: "postgresPassword", - }, - }, - } - - ctx := deploy.GetTestDeployContext(cheCluster, []runtime.Object{}) - - postgres := NewPostgresReconciler() - done, err := postgres.syncCredentials(ctx) - assert.True(t, done) - assert.Nil(t, err) - - postgresCredentialsSecret := &corev1.Secret{} - err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "postgres-credentials", Namespace: "eclipse-che"}, postgresCredentialsSecret) - assert.Nil(t, err) - assert.Equal(t, string(postgresCredentialsSecret.Data["user"]), "postgresUser") - assert.Equal(t, string(postgresCredentialsSecret.Data["password"]), "postgresPassword") + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "postgres", Namespace: "eclipse-che"}, &corev1.Service{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "postgres-data", Namespace: "eclipse-che"}, &corev1.PersistentVolumeClaim{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "postgres", Namespace: "eclipse-che"}, &appsv1.Deployment{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "postgres-credentials", Namespace: "eclipse-che"}, &corev1.Secret{})) } func TestGetPostgresImage(t *testing.T) { type testCase struct { name string - cheCluster *orgv1.CheCluster + cheCluster *chev2.CheCluster postgresDeployment *appsv1.Deployment expectedPostgresImage string @@ -167,87 +151,91 @@ func TestGetPostgresImage(t *testing.T) { testCases := []testCase{ { - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, }, - expectedPostgresImage: deploy.DefaultPostgres13Image(&orgv1.CheCluster{}), + expectedPostgresImage: defaults.GetPostgres13Image(&chev2.CheCluster{}), }, { - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Database: orgv1.CheClusterSpecDB{ - PostgresVersion: "13.3", - }, + Status: chev2.CheClusterStatus{ + PostgresVersion: "13.3", }, }, - expectedPostgresImage: deploy.DefaultPostgres13Image(&orgv1.CheCluster{}), + expectedPostgresImage: defaults.GetPostgres13Image(&chev2.CheCluster{}), }, { - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Database: orgv1.CheClusterSpecDB{ - PostgresVersion: "13.5", - }, + Status: chev2.CheClusterStatus{ + PostgresVersion: "13.5", }, }, - expectedPostgresImage: deploy.DefaultPostgres13Image(&orgv1.CheCluster{}), + expectedPostgresImage: defaults.GetPostgres13Image(&chev2.CheCluster{}), }, { - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Database: orgv1.CheClusterSpecDB{ - PostgresVersion: "9.6", - }, + Status: chev2.CheClusterStatus{ + PostgresVersion: "9.6", }, }, - expectedPostgresImage: deploy.DefaultPostgresImage(&orgv1.CheCluster{}), + expectedPostgresImage: defaults.GetPostgresImage(&chev2.CheCluster{}), }, { - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Database: orgv1.CheClusterSpecDB{ - PostgresImage: "custom_postgre_image", - PostgresVersion: "", + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + Database: chev2.Database{ + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + chev2.Container{ + Image: "custom_postgre_image", + }, + }, + }, + }, }, }, + Status: chev2.CheClusterStatus{ + PostgresVersion: "", + }, }, expectedPostgresImage: "custom_postgre_image", }, { - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Database: orgv1.CheClusterSpecDB{ - PostgresVersion: "unrecognized_version", - }, + Status: chev2.CheClusterStatus{ + PostgresVersion: "", }, }, expectedError: true, }, { - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Database: orgv1.CheClusterSpecDB{}, + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + Database: chev2.Database{}, + }, }, }, postgresDeployment: &appsv1.Deployment{ @@ -266,14 +254,12 @@ func TestGetPostgresImage(t *testing.T) { expectedPostgresImage: "current_postgres_image", }, { - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Database: orgv1.CheClusterSpecDB{ - PostgresVersion: "13.3", - }, + Status: chev2.CheClusterStatus{ + PostgresVersion: "13.3", }, }, postgresDeployment: &appsv1.Deployment{ @@ -289,7 +275,7 @@ func TestGetPostgresImage(t *testing.T) { }, }, }, - expectedPostgresImage: deploy.DefaultPostgres13Image(&orgv1.CheCluster{}), + expectedPostgresImage: defaults.GetPostgres13Image(&chev2.CheCluster{}), }, } diff --git a/pkg/deploy/proxy.go b/pkg/deploy/proxy.go index 82dd42844..132663aec 100644 --- a/pkg/deploy/proxy.go +++ b/pkg/deploy/proxy.go @@ -15,21 +15,23 @@ package deploy import ( "strings" - "github.com/eclipse-che/che-operator/pkg/util" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + "github.com/eclipse-che/che-operator/pkg/common/utils" + corev1 "k8s.io/api/core/v1" "golang.org/x/net/http/httpproxy" "net/http" "net/url" - orgv1 "github.com/eclipse-che/che-operator/api/v1" configv1 "github.com/openshift/api/config/v1" "github.com/sirupsen/logrus" ) -func ReadClusterWideProxyConfiguration(clusterProxy *configv1.Proxy) (*Proxy, error) { - proxy := &Proxy{} +func ReadClusterWideProxyConfiguration(clusterProxy *configv1.Proxy) (*chetypes.Proxy, error) { + proxy := &chetypes.Proxy{} // Cluster components consume the status values to configure the proxy for their component. proxy.HttpProxy = clusterProxy.Status.HTTPProxy @@ -60,8 +62,8 @@ func ReadClusterWideProxyConfiguration(clusterProxy *configv1.Proxy) (*Proxy, er return proxy, nil } -func ReadCheClusterProxyConfiguration(checluster *orgv1.CheCluster) (*Proxy, error) { - proxyParts := strings.Split(checluster.Spec.Server.ProxyURL, "://") +func ReadCheClusterProxyConfiguration(ctx *chetypes.DeployContext) (*chetypes.Proxy, error) { + proxyParts := strings.Split(ctx.CheCluster.Spec.Components.CheServer.Proxy.Url, "://") proxyProtocol := "" proxyHost := "" if len(proxyParts) == 1 { @@ -73,21 +75,21 @@ func ReadCheClusterProxyConfiguration(checluster *orgv1.CheCluster) (*Proxy, err } proxyURL := proxyHost - if checluster.Spec.Server.ProxyPort != "" { - proxyURL = proxyURL + ":" + checluster.Spec.Server.ProxyPort + if ctx.CheCluster.Spec.Components.CheServer.Proxy.Port != "" { + proxyURL = proxyURL + ":" + ctx.CheCluster.Spec.Components.CheServer.Proxy.Port } - proxyUser := checluster.Spec.Server.ProxyUser - proxyPassword := checluster.Spec.Server.ProxyPassword - proxySecret := checluster.Spec.Server.ProxySecret - if len(proxySecret) > 0 { - user, password, err := util.K8sclient.ReadSecret(proxySecret, checluster.Namespace) - if err == nil { - proxyUser = user - proxyPassword = password - } else { - return nil, err - } + proxyUser := "" + proxyPassword := "" + + proxyCredentialsSecretName := utils.GetValue(ctx.CheCluster.Spec.Components.CheServer.Proxy.CredentialsSecretName, constants.DefaultProxyCredentialsSecret) + proxyCredentialsSecret := &corev1.Secret{} + exists, err := GetNamespacedObject(ctx, proxyCredentialsSecretName, proxyCredentialsSecret) + if err != nil { + return nil, err + } else if exists { + proxyUser = string(proxyCredentialsSecret.Data["user"]) + proxyPassword = string(proxyCredentialsSecret.Data["password"]) } if len(proxyUser) > 1 && len(proxyPassword) > 1 { @@ -98,20 +100,20 @@ func ReadCheClusterProxyConfiguration(checluster *orgv1.CheCluster) (*Proxy, err proxyURL = proxyProtocol + "://" + proxyURL } - return &Proxy{ + return &chetypes.Proxy{ HttpProxy: proxyURL, HttpUser: proxyUser, HttpHost: proxyHost, - HttpPort: checluster.Spec.Server.ProxyPort, + HttpPort: ctx.CheCluster.Spec.Components.CheServer.Proxy.Port, HttpPassword: proxyPassword, HttpsProxy: proxyURL, HttpsUser: proxyUser, HttpsHost: proxyHost, - HttpsPort: checluster.Spec.Server.ProxyPort, + HttpsPort: ctx.CheCluster.Spec.Components.CheServer.Proxy.Port, HttpsPassword: proxyPassword, - NoProxy: strings.Replace(checluster.Spec.Server.NonProxyHosts, "|", ",", -1), + NoProxy: strings.Join(ctx.CheCluster.Spec.Components.CheServer.Proxy.NonProxyHosts, ","), }, nil } @@ -126,7 +128,7 @@ func MergeNonProxy(noProxy1 string, noProxy2 string) string { } // GenerateProxyJavaOpts converts given proxy configuration into Java format. -func GenerateProxyJavaOpts(proxy *Proxy, noProxy string) (javaOpts string, err error) { +func GenerateProxyJavaOpts(proxy *chetypes.Proxy, noProxy string) (javaOpts string, err error) { if noProxy == "" { noProxy = proxy.NoProxy } @@ -164,7 +166,7 @@ func removeProtocolPrefix(url string) string { } // ConfigureProxy adds existing proxy configuration into provided transport object. -func ConfigureProxy(deployContext *DeployContext, transport *http.Transport) { +func ConfigureProxy(deployContext *chetypes.DeployContext, transport *http.Transport) { config := httpproxy.Config{ HTTPProxy: deployContext.Proxy.HttpProxy, HTTPSProxy: deployContext.Proxy.HttpsProxy, diff --git a/pkg/deploy/proxy_test.go b/pkg/deploy/proxy_test.go index 7a4637649..5b761c7f3 100644 --- a/pkg/deploy/proxy_test.go +++ b/pkg/deploy/proxy_test.go @@ -16,9 +16,15 @@ import ( "reflect" "testing" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/test" configv1 "github.com/openshift/api/config/v1" "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" ) const ( @@ -28,7 +34,7 @@ const ( ) func TestGenerateProxyJavaOptsWithUsernameAndPassword(t *testing.T) { - proxy := &Proxy{ + proxy := &chetypes.Proxy{ HttpProxy: "https://user:password@myproxy.com:1234", HttpUser: "user", HttpPassword: "password", @@ -60,7 +66,7 @@ func TestGenerateProxyJavaOptsWithUsernameAndPassword(t *testing.T) { } func TestGenerateProxyJavaOptsWithoutAuthentication(t *testing.T) { - proxy := &Proxy{ + proxy := &chetypes.Proxy{ HttpProxy: "http://myproxy.com:1234", HttpHost: "myproxy.com", HttpPort: "1234", @@ -81,7 +87,7 @@ func TestGenerateProxyJavaOptsWithoutAuthentication(t *testing.T) { } func TestGenerateProxyJavaOptsWildcardInNonProxyHosts(t *testing.T) { - proxy := &Proxy{ + proxy := &chetypes.Proxy{ HttpProxy: "http://myproxy.com:1234", HttpHost: "myproxy.com", HttpPort: "1234", @@ -102,18 +108,36 @@ func TestGenerateProxyJavaOptsWildcardInNonProxyHosts(t *testing.T) { } func TestReadCheClusterProxyConfiguration(t *testing.T) { - checluster := &orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ProxyPassword: "password", - ProxyUser: "user", - ProxyPort: "1234", - ProxyURL: "https://myproxy.com", - NonProxyHosts: "host1|host2", + checluster := &chev2.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eclipse-che", + Namespace: "eclipse-che", + }, + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + CheServer: chev2.CheServer{ + Proxy: chev2.Proxy{ + Port: "1234", + Url: "https://myproxy.com", + NonProxyHosts: []string{"host1", "host2"}, + CredentialsSecretName: "proxy", + }, + }, }, }, } - expectedProxy := &Proxy{ + proxySecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "proxy", + Namespace: "eclipse-che", + }, + Data: map[string][]byte{ + "user": []byte("user"), + "password": []byte("password"), + }, + } + + expectedProxy := &chetypes.Proxy{ HttpProxy: "https://user:password@myproxy.com:1234", HttpUser: "user", HttpPassword: "password", @@ -129,7 +153,8 @@ func TestReadCheClusterProxyConfiguration(t *testing.T) { NoProxy: "host1,host2", } - actualProxy, _ := ReadCheClusterProxyConfiguration(checluster) + ctx := test.GetDeployContext(checluster, []runtime.Object{proxySecret}) + actualProxy, _ := ReadCheClusterProxyConfiguration(ctx) if !reflect.DeepEqual(actualProxy, expectedProxy) { t.Errorf("Test failed. Expected '%v', but got '%v'", expectedProxy, actualProxy) @@ -137,16 +162,21 @@ func TestReadCheClusterProxyConfiguration(t *testing.T) { } func TestReadCheClusterProxyConfigurationNoUser(t *testing.T) { - checluster := &orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ProxyPort: "1234", - ProxyURL: "https://myproxy.com", - NonProxyHosts: "host1|host2", + checluster := &chev2.CheCluster{ + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + CheServer: chev2.CheServer{ + Proxy: chev2.Proxy{ + Port: "1234", + Url: "https://myproxy.com", + NonProxyHosts: []string{"host1", "host2"}, + }, + }, }, }, } - expectedProxy := &Proxy{ + + expectedProxy := &chetypes.Proxy{ HttpProxy: "https://myproxy.com:1234", HttpHost: "myproxy.com", HttpPort: "1234", @@ -158,7 +188,8 @@ func TestReadCheClusterProxyConfigurationNoUser(t *testing.T) { NoProxy: "host1,host2", } - actualProxy, _ := ReadCheClusterProxyConfiguration(checluster) + ctx := test.GetDeployContext(checluster, []runtime.Object{}) + actualProxy, _ := ReadCheClusterProxyConfiguration(ctx) if !reflect.DeepEqual(actualProxy, expectedProxy) { t.Errorf("Test failed. Expected '%v', but got '%v'", expectedProxy, actualProxy) @@ -166,17 +197,34 @@ func TestReadCheClusterProxyConfigurationNoUser(t *testing.T) { } func TestReadCheClusterProxyConfigurationNoPort(t *testing.T) { - checluster := &orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ProxyPassword: "password", - ProxyUser: "user", - ProxyURL: "https://myproxy.com", - NonProxyHosts: "host1|host2", + checluster := &chev2.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eclipse-che", + Namespace: "eclipse-che", + }, + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + CheServer: chev2.CheServer{ + Proxy: chev2.Proxy{ + Url: "https://myproxy.com", + NonProxyHosts: []string{"host1", "host2"}, + }, + }, }, }, } - expectedProxy := &Proxy{ + proxySecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "proxy-credentials", + Namespace: "eclipse-che", + }, + Data: map[string][]byte{ + "user": []byte("user"), + "password": []byte("password"), + }, + } + + expectedProxy := &chetypes.Proxy{ HttpProxy: "https://user:password@myproxy.com", HttpUser: "user", HttpPassword: "password", @@ -190,11 +238,9 @@ func TestReadCheClusterProxyConfigurationNoPort(t *testing.T) { NoProxy: "host1,host2", } - actualProxy, _ := ReadCheClusterProxyConfiguration(checluster) - - if !reflect.DeepEqual(actualProxy, expectedProxy) { - t.Errorf("Test failed. Expected '%v', but got '%v'", expectedProxy, actualProxy) - } + ctx := test.GetDeployContext(checluster, []runtime.Object{proxySecret}) + actualProxy, _ := ReadCheClusterProxyConfiguration(ctx) + assert.Equal(t, actualProxy, expectedProxy) } func TestReadClusterWideProxyConfiguration(t *testing.T) { @@ -206,7 +252,7 @@ func TestReadClusterWideProxyConfiguration(t *testing.T) { }, } - expectedProxy := &Proxy{ + expectedProxy := &chetypes.Proxy{ HttpProxy: "http://user1:password1@myproxy1.com:1234", HttpUser: "user1", HttpPassword: "password1", @@ -237,7 +283,7 @@ func TestReadClusterWideProxyConfigurationNoUser(t *testing.T) { }, } - expectedProxy := &Proxy{ + expectedProxy := &chetypes.Proxy{ HttpProxy: "http://myproxy.com:1234", HttpHost: "myproxy.com", HttpPort: "1234", @@ -263,7 +309,7 @@ func TestReadClusterWideProxyConfigurationNoPort(t *testing.T) { }, } - expectedProxy := &Proxy{ + expectedProxy := &chetypes.Proxy{ HttpProxy: "http://user:password@myproxy.com", HttpUser: "user", HttpPassword: "password", diff --git a/pkg/deploy/pvc.go b/pkg/deploy/pvc.go index 5ab8e22d9..03010e1f5 100644 --- a/pkg/deploy/pvc.go +++ b/pkg/deploy/pvc.go @@ -13,11 +13,14 @@ package deploy import ( + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/pointer" ) var pvcDiffOpts = cmp.Options{ @@ -30,19 +33,19 @@ var pvcDiffOpts = cmp.Options{ } func SyncPVCToCluster( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, - claimSize string, + pvc *chev2.PVC, component string) (bool, error) { - pvcSpec := getPVCSpec(deployContext, name, claimSize, component) + pvcSpec := getPVCSpec(deployContext, name, pvc, component) actual := &corev1.PersistentVolumeClaim{} exists, err := GetNamespacedObject(deployContext, name, actual) if err != nil { return false, err - } else if err == nil && exists { - actual.Spec.Resources.Requests[corev1.ResourceName(corev1.ResourceStorage)] = resource.MustParse(claimSize) + } else if exists { + actual.Spec.Resources.Requests[corev1.ResourceName(corev1.ResourceStorage)] = resource.MustParse(pvc.ClaimSize) return Sync(deployContext, actual, pvcDiffOpts) } @@ -50,28 +53,28 @@ func SyncPVCToCluster( } func getPVCSpec( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, - claimSize string, + pvc *chev2.PVC, component string) *corev1.PersistentVolumeClaim { - labels := GetLabels(deployContext.CheCluster, component) + labels := GetLabels(component) accessModes := []corev1.PersistentVolumeAccessMode{ corev1.ReadWriteOnce, } resources := corev1.ResourceRequirements{ Requests: corev1.ResourceList{ - corev1.ResourceName(corev1.ResourceStorage): resource.MustParse(claimSize), + corev1.ResourceName(corev1.ResourceStorage): resource.MustParse(pvc.ClaimSize), }} pvcSpec := corev1.PersistentVolumeClaimSpec{ AccessModes: accessModes, Resources: resources, } - if len(deployContext.CheCluster.Spec.Storage.PostgresPVCStorageClassName) > 1 { - pvcSpec.StorageClassName = &deployContext.CheCluster.Spec.Storage.PostgresPVCStorageClassName + if pvc.StorageClass != "" { + pvcSpec.StorageClassName = pointer.StringPtr(pvc.StorageClass) } - pvc := &corev1.PersistentVolumeClaim{ + return &corev1.PersistentVolumeClaim{ TypeMeta: metav1.TypeMeta{ Kind: "PersistentVolumeClaim", APIVersion: "v1", @@ -83,6 +86,4 @@ func getPVCSpec( }, Spec: pvcSpec, } - - return pvc } diff --git a/pkg/deploy/pvc_test.go b/pkg/deploy/pvc_test.go index c55dff681..896ce918b 100644 --- a/pkg/deploy/pvc_test.go +++ b/pkg/deploy/pvc_test.go @@ -16,57 +16,32 @@ import ( "testing" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/test" + "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client/fake" ) func TestSyncPVCToCluster(t *testing.T) { - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) - corev1.SchemeBuilder.AddToScheme(scheme.Scheme) - cli := fake.NewFakeClientWithScheme(scheme.Scheme) - deployContext := &DeployContext{ - CheCluster: &orgv1.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - Name: "eclipse-che", - }, - }, - ClusterAPI: ClusterAPI{ - Client: cli, - NonCachingClient: cli, - Scheme: scheme.Scheme, - }, - } + ctx := test.GetDeployContext(nil, []runtime.Object{}) - done, err := SyncPVCToCluster(deployContext, "test", "1Gi", "che") - if !done || err != nil { - t.Fatalf("Failed to sync pvc: %v", err) - } + done, err := SyncPVCToCluster(ctx, "test", &chev2.PVC{ClaimSize: "1Gi"}, "che") + assert.True(t, done) + assert.Nil(t, err) // sync a new pvc - _, err = SyncPVCToCluster(deployContext, "test", "2Gi", "che") - if err != nil { - t.Fatalf("Failed to sync pvc: %v", err) - } + _, err = SyncPVCToCluster(ctx, "test", &chev2.PVC{ClaimSize: "2Gi"}, "che") + assert.Nil(t, err) // sync pvc twice to be sure update done correctly - done, err = SyncPVCToCluster(deployContext, "test", "2Gi", "che") - if !done || err != nil { - t.Fatalf("Failed to sync pvc: %v", err) - } + _, err = SyncPVCToCluster(ctx, "test", &chev2.PVC{ClaimSize: "2Gi"}, "che") + assert.Nil(t, err) actual := &corev1.PersistentVolumeClaim{} - err = cli.Get(context.TODO(), types.NamespacedName{Name: "test", Namespace: "eclipse-che"}, actual) - if err != nil { - t.Fatalf("Failed to get pvc: %v", err) - } - - if !actual.Spec.Resources.Requests[corev1.ResourceStorage].Equal(resource.MustParse("2Gi")) { - t.Fatalf("Failed to sync pvc: %v", err) - } + err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "test", Namespace: "eclipse-che"}, actual) + assert.Nil(t, err) + assert.Equal(t, actual.Spec.Resources.Requests[corev1.ResourceStorage], resource.MustParse("2Gi")) } diff --git a/pkg/deploy/rbac/gateway_permissions.go b/pkg/deploy/rbac/gateway_permissions.go index f6dc537f5..7a1818c48 100644 --- a/pkg/deploy/rbac/gateway_permissions.go +++ b/pkg/deploy/rbac/gateway_permissions.go @@ -13,7 +13,8 @@ package rbac import ( - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" "github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy/gateway" "github.com/sirupsen/logrus" @@ -34,7 +35,7 @@ func NewGatewayPermissionsReconciler() *GatewayPermissionsReconciler { return &GatewayPermissionsReconciler{} } -func (gp *GatewayPermissionsReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { +func (gp *GatewayPermissionsReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { name := gp.gatewayPermissionsName(ctx.CheCluster) if done, err := deploy.SyncClusterRoleToCluster(ctx, name, gp.getGatewayClusterRoleRules()); !done { return reconcile.Result{Requeue: true}, false, err @@ -51,7 +52,7 @@ func (gp *GatewayPermissionsReconciler) Reconcile(ctx *deploy.DeployContext) (re return reconcile.Result{}, true, nil } -func (gp *GatewayPermissionsReconciler) Finalize(ctx *deploy.DeployContext) bool { +func (gp *GatewayPermissionsReconciler) Finalize(ctx *chetypes.DeployContext) bool { if _, err := gp.deleteGatewayPermissions(ctx); err != nil { logrus.Errorf("Error deleting finalizer: %v", err) return false @@ -59,7 +60,7 @@ func (gp *GatewayPermissionsReconciler) Finalize(ctx *deploy.DeployContext) bool return true } -func (gp *GatewayPermissionsReconciler) deleteGatewayPermissions(deployContext *deploy.DeployContext) (bool, error) { +func (gp *GatewayPermissionsReconciler) deleteGatewayPermissions(deployContext *chetypes.DeployContext) (bool, error) { name := gp.gatewayPermissionsName(deployContext.CheCluster) if done, err := deploy.Delete(deployContext, types.NamespacedName{Name: name}, &rbacv1.ClusterRoleBinding{}); !done { return false, err @@ -76,7 +77,7 @@ func (gp *GatewayPermissionsReconciler) deleteGatewayPermissions(deployContext * return true, nil } -func (gp *GatewayPermissionsReconciler) gatewayPermissionsName(instance *orgv1.CheCluster) string { +func (gp *GatewayPermissionsReconciler) gatewayPermissionsName(instance *chev2.CheCluster) string { return instance.Namespace + "-" + gateway.GatewayServiceName } diff --git a/pkg/deploy/rbac/init_test.go b/pkg/deploy/rbac/init_test.go index c57f210ba..fe45d7732 100644 --- a/pkg/deploy/rbac/init_test.go +++ b/pkg/deploy/rbac/init_test.go @@ -11,11 +11,15 @@ // package rbac -import "github.com/eclipse-che/che-operator/pkg/deploy" +import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" +) func init() { - err := deploy.InitTestDefaultsFromDeployment("../../../config/manager/manager.yaml") - if err != nil { - panic(err) - } + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../../../config/manager/manager.yaml") } diff --git a/pkg/deploy/rbac/rbac.go b/pkg/deploy/rbac/rbac.go index b3ecf9f6b..cf19c724e 100644 --- a/pkg/deploy/rbac/rbac.go +++ b/pkg/deploy/rbac/rbac.go @@ -15,6 +15,8 @@ package rbac import ( "strings" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" "github.com/eclipse-che/che-operator/pkg/deploy" "github.com/sirupsen/logrus" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -28,21 +30,20 @@ func NewCheServerPermissionsReconciler() *CheServerPermissionsReconciler { return &CheServerPermissionsReconciler{} } -func (c *CheServerPermissionsReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { +func (c *CheServerPermissionsReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { // Create service account "che" for che-server component. // "che" is the one which token is used to create workspace objects. // Notice: Also we have on more "che-workspace" SA used by plugins like exec, terminal, metrics with limited privileges. - done, err := deploy.SyncServiceAccountToCluster(ctx, deploy.CheServiceAccountName) + done, err := deploy.SyncServiceAccountToCluster(ctx, constants.DefaultCheServiceAccountName) if !done { return reconcile.Result{Requeue: true}, false, err } - if len(ctx.CheCluster.Spec.Server.CheClusterRoles) > 0 { - cheClusterRoles := strings.Split(ctx.CheCluster.Spec.Server.CheClusterRoles, ",") - for _, cheClusterRole := range cheClusterRoles { - cheClusterRole := strings.TrimSpace(cheClusterRole) + for _, cheClusterRole := range ctx.CheCluster.Spec.Components.CheServer.ClusterRoles { + cheClusterRole := strings.TrimSpace(cheClusterRole) + if cheClusterRole != "" { cheClusterRoleBindingName := cheClusterRole - done, err := deploy.SyncClusterRoleBindingAndAddFinalizerToCluster(ctx, cheClusterRoleBindingName, deploy.CheServiceAccountName, cheClusterRole) + done, err := deploy.SyncClusterRoleBindingAndAddFinalizerToCluster(ctx, cheClusterRoleBindingName, constants.DefaultCheServiceAccountName, cheClusterRole) if !done { return reconcile.Result{Requeue: true}, false, err } @@ -52,13 +53,12 @@ func (c *CheServerPermissionsReconciler) Reconcile(ctx *deploy.DeployContext) (r return reconcile.Result{}, true, err } -func (c *CheServerPermissionsReconciler) Finalize(ctx *deploy.DeployContext) bool { +func (c *CheServerPermissionsReconciler) Finalize(ctx *chetypes.DeployContext) bool { done := true - if len(ctx.CheCluster.Spec.Server.CheClusterRoles) > 0 { - cheClusterRoles := strings.Split(ctx.CheCluster.Spec.Server.CheClusterRoles, ",") - for _, cheClusterRole := range cheClusterRoles { - cheClusterRole := strings.TrimSpace(cheClusterRole) + for _, cheClusterRole := range ctx.CheCluster.Spec.Components.CheServer.ClusterRoles { + cheClusterRole := strings.TrimSpace(cheClusterRole) + if cheClusterRole != "" { cheClusterRoleBindingName := cheClusterRole if err := deploy.ReconcileClusterRoleBindingFinalizer(ctx, cheClusterRoleBindingName); err != nil { done = false @@ -66,7 +66,7 @@ func (c *CheServerPermissionsReconciler) Finalize(ctx *deploy.DeployContext) boo } // Removes any legacy CRB https://github.com/eclipse/che/issues/19506 - cheClusterRoleBindingName = deploy.GetLegacyUniqueClusterRoleBindingName(ctx, deploy.CheServiceAccountName, cheClusterRole) + cheClusterRoleBindingName = deploy.GetLegacyUniqueClusterRoleBindingName(ctx, constants.DefaultCheServiceAccountName, cheClusterRole) if err := deploy.ReconcileLegacyClusterRoleBindingFinalizer(ctx, cheClusterRoleBindingName); err != nil { done = false logrus.Errorf("Error deleting finalizer: %v", err) diff --git a/pkg/deploy/rbac/workspace_permissions.go b/pkg/deploy/rbac/workspace_permissions.go index 6c837ba55..2a8b2e76b 100644 --- a/pkg/deploy/rbac/workspace_permissions.go +++ b/pkg/deploy/rbac/workspace_permissions.go @@ -15,8 +15,10 @@ package rbac import ( "fmt" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" "github.com/sirupsen/logrus" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/types" @@ -24,17 +26,6 @@ import ( ) const ( - // EditClusterRoleName - default "edit" cluster role. This role is pre-created on the cluster. - // See more: https://kubernetes.io/blog/2017/10/using-rbac-generally-available-18/#granting-access-to-users - EditClusterRoleName = "edit" - // EditRoleBindingName - "edit" rolebinding for che-server. - EditRoleBindingName = "che" - // CheWorkspacesServiceAccount - service account created for Che workspaces. - CheWorkspacesServiceAccount = "che-workspace" - // ViewRoleBindingName - "view" role for "che-workspace" service account. - ViewRoleBindingName = "che-workspace-view" - // ExecRoleBindingName - "exec" role for "che-workspace" service account. - ExecRoleBindingName = "che-workspace-exec" // CheNamespaceEditorClusterRoleNameTemplate - manage namespaces "cluster role" and "clusterrolebinding" template name CheNamespaceEditorClusterRoleNameTemplate = "%s-cheworkspaces-namespaces-clusterrole" // CheWorkspacesClusterRoleNameTemplate - manage workspaces "cluster role" and "clusterrolebinding" template name @@ -55,7 +46,7 @@ func NewWorkspacePermissionsReconciler() *WorkspacePermissionsReconciler { return &WorkspacePermissionsReconciler{} } -func (wp *WorkspacePermissionsReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { +func (wp *WorkspacePermissionsReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { done, err := wp.delegateWorkspacePermissionsInTheDifferNamespaceThanChe(ctx) if !done { return reconcile.Result{Requeue: true}, false, err @@ -71,20 +62,10 @@ func (wp *WorkspacePermissionsReconciler) Reconcile(ctx *deploy.DeployContext) ( return reconcile.Result{Requeue: true}, false, err } - // If the user specified an additional cluster role to use for the Che workspace, create a role binding for it - // Use a role binding instead of a cluster role binding to keep the additional access scoped to the workspace's namespace - workspaceClusterRole := ctx.CheCluster.Spec.Server.CheWorkspaceClusterRole - if workspaceClusterRole != "" { - done, err := deploy.SyncRoleBindingToCluster(ctx, "che-workspace-custom", "view", workspaceClusterRole, "ClusterRole") - if !done { - return reconcile.Result{Requeue: true}, false, err - } - } - return reconcile.Result{}, true, nil } -func (wp *WorkspacePermissionsReconciler) Finalize(ctx *deploy.DeployContext) bool { +func (wp *WorkspacePermissionsReconciler) Finalize(ctx *chetypes.DeployContext) bool { done := true if completed := wp.removeNamespaceEditorPermissions(ctx); !completed { @@ -111,7 +92,7 @@ func (wp *WorkspacePermissionsReconciler) Finalize(ctx *deploy.DeployContext) bo // workspace components. // Notice: After permission delegation che-server will create service account "che-workspace" ITSELF with // "exec" and "view" roles for each new workspace. -func (wp *WorkspacePermissionsReconciler) delegateWorkspacePermissionsInTheDifferNamespaceThanChe(deployContext *deploy.DeployContext) (bool, error) { +func (wp *WorkspacePermissionsReconciler) delegateWorkspacePermissionsInTheDifferNamespaceThanChe(deployContext *chetypes.DeployContext) (bool, error) { сheWorkspacesClusterRoleName := fmt.Sprintf(CheWorkspacesClusterRoleNameTemplate, deployContext.CheCluster.Namespace) сheWorkspacesClusterRoleBindingName := сheWorkspacesClusterRoleName @@ -121,7 +102,7 @@ func (wp *WorkspacePermissionsReconciler) delegateWorkspacePermissionsInTheDiffe return false, err } - done, err = deploy.SyncClusterRoleBindingToCluster(deployContext, сheWorkspacesClusterRoleBindingName, deploy.CheServiceAccountName, сheWorkspacesClusterRoleName) + done, err = deploy.SyncClusterRoleBindingToCluster(deployContext, сheWorkspacesClusterRoleBindingName, constants.DefaultCheServiceAccountName, сheWorkspacesClusterRoleName) if !done { return false, err } @@ -130,7 +111,7 @@ func (wp *WorkspacePermissionsReconciler) delegateWorkspacePermissionsInTheDiffe return err == nil, err } -func (wp *WorkspacePermissionsReconciler) removeWorkspacePermissionsInTheDifferNamespaceThanChe(deployContext *deploy.DeployContext) bool { +func (wp *WorkspacePermissionsReconciler) removeWorkspacePermissionsInTheDifferNamespaceThanChe(deployContext *chetypes.DeployContext) bool { done := true cheWorkspacesClusterRoleName := fmt.Sprintf(CheWorkspacesClusterRoleNameTemplate, deployContext.CheCluster.Namespace) @@ -154,7 +135,7 @@ func (wp *WorkspacePermissionsReconciler) removeWorkspacePermissionsInTheDifferN return done } -func (wp *WorkspacePermissionsReconciler) delegateNamespaceEditorPermissions(deployContext *deploy.DeployContext) (bool, error) { +func (wp *WorkspacePermissionsReconciler) delegateNamespaceEditorPermissions(deployContext *chetypes.DeployContext) (bool, error) { сheNamespaceEditorClusterRoleName := fmt.Sprintf(CheNamespaceEditorClusterRoleNameTemplate, deployContext.CheCluster.Namespace) сheNamespaceEditorClusterRoleBindingName := сheNamespaceEditorClusterRoleName @@ -164,7 +145,7 @@ func (wp *WorkspacePermissionsReconciler) delegateNamespaceEditorPermissions(dep return false, err } - done, err = deploy.SyncClusterRoleBindingToCluster(deployContext, сheNamespaceEditorClusterRoleBindingName, deploy.CheServiceAccountName, сheNamespaceEditorClusterRoleName) + done, err = deploy.SyncClusterRoleBindingToCluster(deployContext, сheNamespaceEditorClusterRoleBindingName, constants.DefaultCheServiceAccountName, сheNamespaceEditorClusterRoleName) if !done { return false, err } @@ -173,7 +154,7 @@ func (wp *WorkspacePermissionsReconciler) delegateNamespaceEditorPermissions(dep return err == nil, err } -func (wp *WorkspacePermissionsReconciler) removeNamespaceEditorPermissions(deployContext *deploy.DeployContext) bool { +func (wp *WorkspacePermissionsReconciler) removeNamespaceEditorPermissions(deployContext *chetypes.DeployContext) bool { done := true cheNamespaceEditorClusterRoleName := fmt.Sprintf(CheNamespaceEditorClusterRoleNameTemplate, deployContext.CheCluster.Namespace) @@ -196,7 +177,7 @@ func (wp *WorkspacePermissionsReconciler) removeNamespaceEditorPermissions(deplo return done } -func (wp *WorkspacePermissionsReconciler) delegateDevWorkspacePermissions(deployContext *deploy.DeployContext) (bool, error) { +func (wp *WorkspacePermissionsReconciler) delegateDevWorkspacePermissions(deployContext *chetypes.DeployContext) (bool, error) { devWorkspaceClusterRoleName := fmt.Sprintf(DevWorkspaceClusterRoleNameTemplate, deployContext.CheCluster.Namespace) devWorkspaceClusterRoleBindingName := devWorkspaceClusterRoleName @@ -205,7 +186,7 @@ func (wp *WorkspacePermissionsReconciler) delegateDevWorkspacePermissions(deploy return false, err } - done, err = deploy.SyncClusterRoleBindingToCluster(deployContext, devWorkspaceClusterRoleBindingName, deploy.CheServiceAccountName, devWorkspaceClusterRoleName) + done, err = deploy.SyncClusterRoleBindingToCluster(deployContext, devWorkspaceClusterRoleBindingName, constants.DefaultCheServiceAccountName, devWorkspaceClusterRoleName) if !done { return false, err } @@ -214,7 +195,7 @@ func (wp *WorkspacePermissionsReconciler) delegateDevWorkspacePermissions(deploy return err == nil, err } -func (wp *WorkspacePermissionsReconciler) removeDevWorkspacePermissions(deployContext *deploy.DeployContext) bool { +func (wp *WorkspacePermissionsReconciler) removeDevWorkspacePermissions(deployContext *chetypes.DeployContext) bool { done := true devWorkspaceClusterRoleName := fmt.Sprintf(DevWorkspaceClusterRoleNameTemplate, deployContext.CheCluster.Namespace) @@ -272,7 +253,7 @@ func (wp *WorkspacePermissionsReconciler) getNamespaceEditorPolicies() []rbacv1. }, } - if util.IsOpenShift { + if infrastructure.IsOpenShift() { return append(k8sPolicies, openshiftPolicies...) } return k8sPolicies @@ -389,7 +370,7 @@ func (c *WorkspacePermissionsReconciler) getWorkspacesPolicies() []rbacv1.Policy }, } - if util.IsOpenShift { + if infrastructure.IsOpenShift() { return append(k8sPolicies, openshiftPolicies...) } return k8sPolicies diff --git a/pkg/deploy/rbac/workspace_permissions_test.go b/pkg/deploy/rbac/workspace_permissions_test.go index 5e270f94f..df3b0f919 100644 --- a/pkg/deploy/rbac/workspace_permissions_test.go +++ b/pkg/deploy/rbac/workspace_permissions_test.go @@ -14,9 +14,10 @@ package rbac import ( "testing" - orgv1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/test" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/stretchr/testify/assert" rbac "k8s.io/api/rbac/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -25,26 +26,28 @@ import ( ) func TestReconcileWorkspacePermissions(t *testing.T) { - util.IsOpenShift = true + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) type testCase struct { name string initObjects []runtime.Object - checluster *orgv1.CheCluster + checluster *chev2.CheCluster } testCases := []testCase{ { name: "che-operator should delegate permission for workspaces in differ namespace than Che. WorkspaceNamespaceDefault = 'some-test-namespace'", initObjects: []runtime.Object{}, - checluster: &orgv1.CheCluster{ + checluster: &chev2.CheCluster{ ObjectMeta: v1.ObjectMeta{ Name: "eclipse-che", Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - WorkspaceNamespaceDefault: "some-test-namespace", + Spec: chev2.CheClusterSpec{ + DevEnvironments: chev2.CheClusterDevEnvironments{ + DefaultNamespace: chev2.DefaultNamespace{ + Template: "some-test-namespace", + }, }, }, }, @@ -52,15 +55,17 @@ func TestReconcileWorkspacePermissions(t *testing.T) { { name: "che-operator should delegate permission for workspaces in differ namespace than Che. Property CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT = 'some-test-namespace'", initObjects: []runtime.Object{}, - checluster: &orgv1.CheCluster{ + checluster: &chev2.CheCluster{ ObjectMeta: v1.ObjectMeta{ Name: "eclipse-che", Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - CustomCheProperties: map[string]string{ - "CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT": "some-test-namespace", + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + CheServer: chev2.CheServer{ + ExtraProperties: map[string]string{ + "CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT": "some-test-namespace", + }, }, }, }, @@ -70,28 +75,28 @@ func TestReconcileWorkspacePermissions(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - ctx := deploy.GetTestDeployContext(testCase.checluster, testCase.initObjects) + ctx := test.GetDeployContext(testCase.checluster, testCase.initObjects) wp := NewWorkspacePermissionsReconciler() _, done, err := wp.Reconcile(ctx) assert.Nil(t, err) assert.True(t, done) - assert.True(t, util.ContainsString(ctx.CheCluster.Finalizers, CheWorkspacesClusterPermissionsFinalizerName)) - assert.True(t, util.ContainsString(ctx.CheCluster.Finalizers, NamespacesEditorPermissionsFinalizerName)) - assert.True(t, util.ContainsString(ctx.CheCluster.Finalizers, DevWorkspacePermissionsFinalizerName)) + assert.True(t, utils.Contains(ctx.CheCluster.Finalizers, CheWorkspacesClusterPermissionsFinalizerName)) + assert.True(t, utils.Contains(ctx.CheCluster.Finalizers, NamespacesEditorPermissionsFinalizerName)) + assert.True(t, utils.Contains(ctx.CheCluster.Finalizers, DevWorkspacePermissionsFinalizerName)) name := "eclipse-che-cheworkspaces-clusterrole" - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRole{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRoleBinding{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRole{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRoleBinding{})) name = "eclipse-che-cheworkspaces-namespaces-clusterrole" - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRole{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRoleBinding{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRole{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRoleBinding{})) name = "eclipse-che-cheworkspaces-devworkspace-clusterrole" - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRole{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRoleBinding{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRole{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: name}, &rbac.ClusterRoleBinding{})) }) } } diff --git a/pkg/deploy/reconcile_manager.go b/pkg/deploy/reconcile_manager.go index 38d529d07..3561bf8df 100644 --- a/pkg/deploy/reconcile_manager.go +++ b/pkg/deploy/reconcile_manager.go @@ -12,15 +12,19 @@ package deploy import ( + "fmt" + + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" "github.com/sirupsen/logrus" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) type Reconcilable interface { // Reconcile object. - Reconcile(ctx *DeployContext) (result reconcile.Result, done bool, err error) + Reconcile(ctx *chetypes.DeployContext) (result reconcile.Result, done bool, err error) // Does finalization (removes cluster scope objects, etc) - Finalize(ctx *DeployContext) (done bool) + Finalize(ctx *chetypes.DeployContext) (done bool) } type ReconcileManager struct { @@ -41,17 +45,19 @@ func (manager *ReconcileManager) RegisterReconciler(reconciler Reconcilable) { // Reconcile all objects in a order they have been added // If reconciliation failed then CheCluster status will be updated accordingly. -func (manager *ReconcileManager) ReconcileAll(ctx *DeployContext) (reconcile.Result, bool, error) { +func (manager *ReconcileManager) ReconcileAll(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { for _, reconciler := range manager.reconcilers { result, done, err := reconciler.Reconcile(ctx) if err != nil { manager.failedReconciler = reconciler - if err := SetStatusDetails(ctx, InstallOrUpdateFailed, err.Error(), ""); err != nil { + reconcilerName := GetObjectType(reconciler) + errMsg := fmt.Sprintf("Reconciler failed %s, cause: %v", reconcilerName, err) + if err := SetStatusDetails(ctx, constants.InstallOrUpdateFailed, errMsg); err != nil { logrus.Errorf("Failed to update checluster status, cause: %v", err) } } else if manager.failedReconciler == reconciler { manager.failedReconciler = nil - if err := SetStatusDetails(ctx, "", "", ""); err != nil { + if err := SetStatusDetails(ctx, "", ""); err != nil { logrus.Errorf("Failed to update checluster status, cause: %v", err) } } @@ -64,12 +70,16 @@ func (manager *ReconcileManager) ReconcileAll(ctx *DeployContext) (reconcile.Res return reconcile.Result{}, true, nil } -func (manager *ReconcileManager) FinalizeAll(ctx *DeployContext) (done bool) { +func (manager *ReconcileManager) FinalizeAll(ctx *chetypes.DeployContext) (done bool) { done = true for _, reconciler := range manager.reconcilers { if completed := reconciler.Finalize(ctx); !completed { reconcilerName := GetObjectType(reconciler) - logrus.Errorf("Finalization failed for reconciler: `%s`", reconcilerName) + errMsg := fmt.Sprintf("Finalization failed for reconciler: %s", reconcilerName) + + ctx.CheCluster.Status.Message = errMsg + _ = UpdateCheCRStatus(ctx, "Message", errMsg) + done = false } } diff --git a/pkg/deploy/reconcile_manager_test.go b/pkg/deploy/reconcile_manager_test.go index 15079c706..b63fd69da 100644 --- a/pkg/deploy/reconcile_manager_test.go +++ b/pkg/deploy/reconcile_manager_test.go @@ -15,6 +15,8 @@ import ( "fmt" "testing" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/test" "github.com/stretchr/testify/assert" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -29,7 +31,7 @@ func NewTestReconcilable(shouldFailReconcileOnce bool) *TestReconcilable { return &TestReconcilable{shouldFailReconcileOnce, false} } -func (tr *TestReconcilable) Reconcile(ctx *DeployContext) (reconcile.Result, bool, error) { +func (tr *TestReconcilable) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { // Fails on first invocation passes on others if !tr.alreadyFailed && tr.shouldFailReconcileOnce { tr.alreadyFailed = true @@ -39,12 +41,12 @@ func (tr *TestReconcilable) Reconcile(ctx *DeployContext) (reconcile.Result, boo } } -func (tr *TestReconcilable) Finalize(ctx *DeployContext) bool { +func (tr *TestReconcilable) Finalize(ctx *chetypes.DeployContext) bool { return true } func TestShouldUpdateAndCleanStatus(t *testing.T) { - deployContext := GetTestDeployContext(nil, []runtime.Object{}) + deployContext := test.GetDeployContext(nil, []runtime.Object{}) tr := NewTestReconcilable(true) @@ -56,7 +58,7 @@ func TestShouldUpdateAndCleanStatus(t *testing.T) { assert.False(t, done) assert.NotNil(t, err) assert.NotEmpty(t, deployContext.CheCluster.Status.Reason) - assert.Equal(t, "Reconcile error", deployContext.CheCluster.Status.Message) + assert.Equal(t, "Reconciler failed deploy.TestReconcilable, cause: Reconcile error", deployContext.CheCluster.Status.Message) assert.Equal(t, tr, rm.failedReconciler) _, done, err = rm.ReconcileAll(deployContext) diff --git a/pkg/deploy/registry/registry_deployment.go b/pkg/deploy/registry/registry_deployment.go index 543b6d922..c947a8fce 100644 --- a/pkg/deploy/registry/registry_deployment.go +++ b/pkg/deploy/registry/registry_deployment.go @@ -12,15 +12,17 @@ package registry import ( + "github.com/eclipse-che/che-operator/pkg/common/chetypes" "github.com/eclipse-che/che-operator/pkg/deploy" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/pointer" ) func GetSpecRegistryDeployment( - deployContext *deploy.DeployContext, + deployContext *chetypes.DeployContext, registryType string, registryImage string, env []corev1.EnvVar, @@ -36,10 +38,8 @@ func GetSpecRegistryDeployment( terminationGracePeriodSeconds := int64(30) name := registryType + "-registry" - labels, labelSelector := deploy.GetLabelsAndSelector(deployContext.CheCluster, name) + labels, labelSelector := deploy.GetLabelsAndSelector(name) _25Percent := intstr.FromString("25%") - _1 := int32(1) - _2 := int32(2) isOptional := true deployment := &appsv1.Deployment{ TypeMeta: metav1.TypeMeta{ @@ -52,8 +52,8 @@ func GetSpecRegistryDeployment( Labels: labels, }, Spec: appsv1.DeploymentSpec{ - Replicas: &_1, - RevisionHistoryLimit: &_2, + Replicas: pointer.Int32Ptr(1), + RevisionHistoryLimit: pointer.Int32Ptr(2), Selector: &metav1.LabelSelector{MatchLabels: labelSelector}, Strategy: appsv1.DeploymentStrategy{ Type: appsv1.RollingUpdateDeploymentStrategyType, @@ -69,7 +69,7 @@ func GetSpecRegistryDeployment( Spec: corev1.PodSpec{ Containers: []corev1.Container{ { - Name: "che-" + name, + Name: name, Image: registryImage, ImagePullPolicy: registryImagePullPolicy, Ports: []corev1.ContainerPort{ diff --git a/pkg/deploy/role.go b/pkg/deploy/role.go index 855a2a505..cab1e15eb 100644 --- a/pkg/deploy/role.go +++ b/pkg/deploy/role.go @@ -12,6 +12,8 @@ package deploy import ( + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" rbac "k8s.io/api/rbac/v1" @@ -24,7 +26,7 @@ var roleDiffOpts = cmp.Options{ } func SyncRoleToCluster( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, policyRule []rbac.PolicyRule) (bool, error) { @@ -32,8 +34,8 @@ func SyncRoleToCluster( return Sync(deployContext, roleSpec, roleDiffOpts) } -func getRoleSpec(deployContext *DeployContext, name string, policyRule []rbac.PolicyRule) *rbac.Role { - labels := GetLabels(deployContext.CheCluster, DefaultCheFlavor(deployContext.CheCluster)) +func getRoleSpec(deployContext *chetypes.DeployContext, name string, policyRule []rbac.PolicyRule) *rbac.Role { + labels := GetLabels(defaults.GetCheFlavor()) role := &rbac.Role{ TypeMeta: metav1.TypeMeta{ Kind: "Role", diff --git a/pkg/deploy/role_test.go b/pkg/deploy/role_test.go index f1bd07dea..039514278 100644 --- a/pkg/deploy/role_test.go +++ b/pkg/deploy/role_test.go @@ -14,7 +14,8 @@ package deploy import ( "context" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -25,17 +26,17 @@ import ( ) func TestSyncRoleToCluster(t *testing.T) { - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) rbacv1.SchemeBuilder.AddToScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme) - deployContext := &DeployContext{ - CheCluster: &orgv1.CheCluster{ + deployContext := &chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, }, - ClusterAPI: ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, diff --git a/pkg/deploy/rolebinding.go b/pkg/deploy/rolebinding.go index c3783f991..b4a10641a 100644 --- a/pkg/deploy/rolebinding.go +++ b/pkg/deploy/rolebinding.go @@ -12,6 +12,8 @@ package deploy import ( + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" rbac "k8s.io/api/rbac/v1" @@ -23,7 +25,7 @@ var rolebindingDiffOpts = cmp.Options{ } func SyncRoleBindingToCluster( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, serviceAccountName string, roleName string, @@ -34,13 +36,13 @@ func SyncRoleBindingToCluster( } func getRoleBindingSpec( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, serviceAccountName string, roleName string, roleKind string) *rbac.RoleBinding { - labels := GetLabels(deployContext.CheCluster, DefaultCheFlavor(deployContext.CheCluster)) + labels := GetLabels(defaults.GetCheFlavor()) roleBinding := &rbac.RoleBinding{ TypeMeta: metav1.TypeMeta{ Kind: "RoleBinding", diff --git a/pkg/deploy/rolebinding_test.go b/pkg/deploy/rolebinding_test.go index b4cc3931c..b114f6fa3 100644 --- a/pkg/deploy/rolebinding_test.go +++ b/pkg/deploy/rolebinding_test.go @@ -14,7 +14,8 @@ package deploy import ( "context" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -25,16 +26,16 @@ import ( ) func TestSyncRoleBindingToCluster(t *testing.T) { - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) rbacv1.SchemeBuilder.AddToScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme) - deployContext := &DeployContext{ - CheCluster: &orgv1.CheCluster{ + deployContext := &chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, }, - ClusterAPI: ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, diff --git a/pkg/deploy/route.go b/pkg/deploy/route.go index e4429171c..c08fa8615 100644 --- a/pkg/deploy/route.go +++ b/pkg/deploy/route.go @@ -18,8 +18,12 @@ import ( "sort" "strings" - orgv1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/pkg/util" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + "github.com/eclipse-che/che-operator/pkg/common/test" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" routev1 "github.com/openshift/api/route/v1" @@ -34,21 +38,19 @@ var routeDiffOpts = cmp.Options{ cmpopts.IgnoreFields(routev1.RouteSpec{}, "WildcardPolicy"), cmp.Comparer(func(x, y metav1.ObjectMeta) bool { return reflect.DeepEqual(x.Labels, y.Labels) && - x.Annotations[CheEclipseOrgManagedAnnotationsDigest] == y.Annotations[CheEclipseOrgManagedAnnotationsDigest] + x.Annotations[constants.CheEclipseOrgManagedAnnotationsDigest] == y.Annotations[constants.CheEclipseOrgManagedAnnotationsDigest] }), } func SyncRouteToCluster( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, - host string, path string, serviceName string, servicePort int32, - routeCustomSettings orgv1.RouteCustomSettings, component string) (bool, error) { - routeSpec, err := GetRouteSpec(deployContext, name, host, path, serviceName, servicePort, routeCustomSettings, component) + routeSpec, err := GetRouteSpec(deployContext, name, path, serviceName, servicePort, component) if err != nil { return false, err } @@ -58,24 +60,23 @@ func SyncRouteToCluster( // GetRouteSpec returns default configuration of a route in Che namespace. func GetRouteSpec( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, - host string, path string, serviceName string, servicePort int32, - routeCustomSettings orgv1.RouteCustomSettings, component string) (*routev1.Route, error) { - cheFlavor := DefaultCheFlavor(deployContext.CheCluster) - labels := GetLabels(deployContext.CheCluster, component) - MergeLabels(labels, routeCustomSettings.Labels) + labels := GetLabels(component) + for k, v := range deployContext.CheCluster.Spec.Networking.Labels { + labels[k] = v + } // add custom annotations var annotations map[string]string - if len(routeCustomSettings.Annotations) > 0 { + if len(deployContext.CheCluster.Spec.Networking.Annotations) > 0 { annotations = make(map[string]string) - for k, v := range routeCustomSettings.Annotations { + for k, v := range deployContext.CheCluster.Spec.Networking.Annotations { annotations[k] = v } } @@ -93,10 +94,10 @@ func GetRouteSpec( for _, k := range annotationsKeys { data += k + ":" + annotations[k] + "," } - if util.IsTestMode() { - annotations[CheEclipseOrgManagedAnnotationsDigest] = "0000" + if test.IsTestMode() { + annotations[constants.CheEclipseOrgManagedAnnotationsDigest] = "0000" } else { - annotations[CheEclipseOrgManagedAnnotationsDigest] = util.ComputeHash256([]byte(data)) + annotations[constants.CheEclipseOrgManagedAnnotationsDigest] = utils.ComputeHash256([]byte(data)) } } @@ -131,10 +132,9 @@ func GetRouteSpec( }, } - if host != "" { - route.Spec.Host = host - } else { - hostSuffix := routeCustomSettings.Domain + route.Spec.Host = deployContext.CheCluster.Spec.Networking.Hostname + if route.Spec.Host == "" { + hostSuffix := deployContext.CheCluster.Spec.Networking.Domain if hostSuffix == "" { existedRoute := &routev1.Route{} @@ -152,7 +152,7 @@ func GetRouteSpec( // If we know domain then we can create a route with a shorter host: . if hostSuffix != "" { hostPrefix := "eclipse-che" - if DefaultCheFlavor(deployContext.CheCluster) == "devspaces" { + if defaults.GetCheFlavor() == "devspaces" { hostPrefix = "devspaces" } @@ -166,11 +166,11 @@ func GetRouteSpec( } // for server and dashboard ingresses - if (component == cheFlavor || component == cheFlavor+"-dashboard") && deployContext.CheCluster.Spec.Server.CheHostTLSSecret != "" { + if deployContext.CheCluster.Spec.Networking.TlsSecretName != "" { secret := &corev1.Secret{} namespacedName := types.NamespacedName{ Namespace: deployContext.CheCluster.Namespace, - Name: deployContext.CheCluster.Spec.Server.CheHostTLSSecret, + Name: deployContext.CheCluster.Spec.Networking.TlsSecretName, } if err := deployContext.ClusterAPI.Client.Get(context.TODO(), namespacedName, secret); err != nil { return nil, err diff --git a/pkg/deploy/route_test.go b/pkg/deploy/route_test.go index 16cd964a3..537a9ac11 100644 --- a/pkg/deploy/route_test.go +++ b/pkg/deploy/route_test.go @@ -14,12 +14,14 @@ package deploy import ( "context" "os" - "reflect" - "github.com/google/go-cmp/cmp" routev1 "github.com/openshift/api/route/v1" + "github.com/stretchr/testify/assert" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -32,22 +34,14 @@ func TestRouteSpec(t *testing.T) { weight := int32(100) type testCase struct { - name string - routeName string - routeHost string - routePath string - routeComponent string - serviceName string - servicePort int32 - routeCustomSettings orgv1.RouteCustomSettings - expectedRoute *routev1.Route - } - - cheCluster := &orgv1.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - Name: "eclipse-che", - }, + name string + routeName string + routePath string + routeComponent string + serviceName string + servicePort int32 + cheCluster *chev2.CheCluster + expectedRoute *routev1.Route } testCases := []testCase{ @@ -57,9 +51,17 @@ func TestRouteSpec(t *testing.T) { routeComponent: "test-component", serviceName: "che", servicePort: 8080, - routeCustomSettings: orgv1.RouteCustomSettings{ - Labels: "type=default", - Domain: "route-domain", + cheCluster: &chev2.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "eclipse-che", + Name: "eclipse-che", + }, + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + Labels: map[string]string{"type": "default"}, + Domain: "route-domain", + }, + }, }, expectedRoute: &routev1.Route{ ObjectMeta: metav1.ObjectMeta{ @@ -68,10 +70,10 @@ func TestRouteSpec(t *testing.T) { Labels: map[string]string{ "type": "default", "app.kubernetes.io/component": "test-component", - "app.kubernetes.io/instance": DefaultCheFlavor(cheCluster), + "app.kubernetes.io/instance": defaults.GetCheFlavor(), "app.kubernetes.io/part-of": "che.eclipse.org", - "app.kubernetes.io/managed-by": DefaultCheFlavor(cheCluster) + "-operator", - "app.kubernetes.io/name": DefaultCheFlavor(cheCluster), + "app.kubernetes.io/managed-by": defaults.GetCheFlavor() + "-operator", + "app.kubernetes.io/name": defaults.GetCheFlavor(), }, }, TypeMeta: metav1.TypeMeta{ @@ -99,11 +101,19 @@ func TestRouteSpec(t *testing.T) { name: "Test custom host", routeName: "test", routeComponent: "test-component", - routeHost: "test-host", serviceName: "che", servicePort: 8080, - routeCustomSettings: orgv1.RouteCustomSettings{ - Labels: "type=default", + cheCluster: &chev2.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "eclipse-che", + Name: "eclipse-che", + }, + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + Labels: map[string]string{"type": "default"}, + Hostname: "test-host", + }, + }, }, expectedRoute: &routev1.Route{ ObjectMeta: metav1.ObjectMeta{ @@ -112,10 +122,10 @@ func TestRouteSpec(t *testing.T) { Labels: map[string]string{ "type": "default", "app.kubernetes.io/component": "test-component", - "app.kubernetes.io/instance": DefaultCheFlavor(cheCluster), + "app.kubernetes.io/instance": defaults.GetCheFlavor(), "app.kubernetes.io/part-of": "che.eclipse.org", - "app.kubernetes.io/managed-by": DefaultCheFlavor(cheCluster) + "-operator", - "app.kubernetes.io/name": DefaultCheFlavor(cheCluster), + "app.kubernetes.io/managed-by": defaults.GetCheFlavor() + "-operator", + "app.kubernetes.io/name": defaults.GetCheFlavor(), }, }, TypeMeta: metav1.TypeMeta{ @@ -143,81 +153,62 @@ func TestRouteSpec(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - deployContext := GetTestDeployContext(cheCluster, []runtime.Object{}) + deployContext := test.GetDeployContext(testCase.cheCluster, []runtime.Object{}) actualRoute, err := GetRouteSpec(deployContext, testCase.routeName, - testCase.routeHost, testCase.routePath, testCase.serviceName, testCase.servicePort, - testCase.routeCustomSettings, testCase.routeComponent, ) - if err != nil { - t.Fatalf("Error creating route: %v", err) - } - if !reflect.DeepEqual(testCase.expectedRoute, actualRoute) { - t.Errorf("Expected route and route returned from API server differ (-want, +got): %v", cmp.Diff(testCase.expectedRoute, actualRoute)) - } + assert.Nil(t, err) + assert.Equal(t, testCase.expectedRoute, actualRoute) }) } } func TestSyncRouteToCluster(t *testing.T) { - // init context - deployContext := GetTestDeployContext(nil, []runtime.Object{}) + deployContext := test.GetDeployContext(nil, []runtime.Object{}) - done, err := SyncRouteToCluster(deployContext, "test", "", "", "service", 80, orgv1.RouteCustomSettings{}, "test") - if !done || err != nil { - t.Fatalf("Failed to sync route: %v", err) - } + done, err := SyncRouteToCluster(deployContext, "test", "", "service", 80, "test") + assert.Nil(t, err) + assert.True(t, done) // sync another route - done, err = SyncRouteToCluster(deployContext, "test", "", "", "service", 90, orgv1.RouteCustomSettings{}, "test") - if !done || err != nil { - t.Fatalf("Failed to sync route: %v", err) - } + done, err = SyncRouteToCluster(deployContext, "test", "", "service", 90, "test") + assert.Nil(t, err) + assert.True(t, done) actual := &routev1.Route{} err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "test", Namespace: "eclipse-che"}, actual) - if err != nil { - t.Fatalf("Failed to get route: %v", err) - } - if actual.Spec.Port.TargetPort.IntVal != 90 { - t.Fatalf("Failed to sync route: %v", err) - } + assert.Nil(t, err) + assert.Equal(t, int32(90), actual.Spec.Port.TargetPort.IntVal) // sync route with labels & domain - done, err = SyncRouteToCluster(deployContext, "test", "", "", "service", 90, orgv1.RouteCustomSettings{Labels: "a=b", Domain: "domain"}, "test") - if !done || err != nil { - t.Fatalf("Failed to sync route: %v", err) - } + deployContext.CheCluster.Spec.Networking.Domain = "domain" + deployContext.CheCluster.Spec.Networking.Labels = map[string]string{"a": "b"} + done, err = SyncRouteToCluster(deployContext, "test", "", "service", 90, "test") + assert.Nil(t, err) + assert.True(t, done) actual = &routev1.Route{} err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "test", Namespace: "eclipse-che"}, actual) - if err != nil { - t.Fatalf("Failed to get route: %v", err) - } - if actual.ObjectMeta.Labels["a"] != "b" { - t.Fatalf("Failed to sync route") - } + assert.Nil(t, err) + assert.Equal(t, "b", actual.ObjectMeta.Labels["a"]) - expectedHost := map[bool]string{false: "eclipse-che.domain", true: "devspaces.domain"}[DefaultCheFlavor(deployContext.CheCluster) == "devspaces"] - if actual.Spec.Host != expectedHost { - t.Fatalf("Failed to sync route") - } + expectedHost := map[bool]string{false: "eclipse-che.domain", true: "devspaces.domain"}[defaults.GetCheFlavor() == "devspaces"] + assert.Equal(t, expectedHost, actual.Spec.Host) // sync route with annotations - done, err = SyncRouteToCluster(deployContext, "test", "", "", "service", 90, orgv1.RouteCustomSettings{Annotations: map[string]string{"a": "b"}}, "test") + deployContext.CheCluster.Spec.Networking.Annotations = map[string]string{"a": "b"} + done, err = SyncRouteToCluster(deployContext, "test", "", "service", 90, "test") actual = &routev1.Route{} err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "test", Namespace: "eclipse-che"}, actual) - if !done || err != nil { - t.Fatalf("Failed to sync route: %v", err) - } - if actual.ObjectMeta.Annotations["a"] != "b" || actual.ObjectMeta.Annotations[CheEclipseOrgManagedAnnotationsDigest] == "" { - t.Fatalf("Failed to sync route") - } + assert.Nil(t, err) + assert.True(t, done) + assert.Equal(t, "b", actual.ObjectMeta.Annotations["a"]) + assert.NotEmpty(t, actual.ObjectMeta.Annotations[constants.CheEclipseOrgManagedAnnotationsDigest]) } diff --git a/pkg/deploy/secret.go b/pkg/deploy/secret.go index 9fc1e52c5..9098ac569 100644 --- a/pkg/deploy/secret.go +++ b/pkg/deploy/secret.go @@ -16,6 +16,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" corev1 "k8s.io/api/core/v1" @@ -30,7 +32,7 @@ var SecretDiffOpts = cmp.Options{ // SyncSecret applies secret into cluster or external namespace func SyncSecretToCluster( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, namespace string, data map[string][]byte) (bool, error) { @@ -40,7 +42,7 @@ func SyncSecretToCluster( } // Get all secrets by labels and annotations -func GetSecrets(deployContext *DeployContext, labels map[string]string, annotations map[string]string) ([]corev1.Secret, error) { +func GetSecrets(deployContext *chetypes.DeployContext, labels map[string]string, annotations map[string]string) ([]corev1.Secret, error) { secrets := []corev1.Secret{} labelSelector := k8slabels.NewSelector() @@ -80,8 +82,8 @@ func GetSecrets(deployContext *DeployContext, labels map[string]string, annotati } // GetSecretSpec return default secret config for given data -func GetSecretSpec(deployContext *DeployContext, name string, namespace string, data map[string][]byte) *corev1.Secret { - labels := GetLabels(deployContext.CheCluster, DefaultCheFlavor(deployContext.CheCluster)) +func GetSecretSpec(deployContext *chetypes.DeployContext, name string, namespace string, data map[string][]byte) *corev1.Secret { + labels := GetLabels(defaults.GetCheFlavor()) secret := &corev1.Secret{ TypeMeta: metav1.TypeMeta{ Kind: "Secret", diff --git a/pkg/deploy/sercet_test.go b/pkg/deploy/secret_test.go similarity index 91% rename from pkg/deploy/sercet_test.go rename to pkg/deploy/secret_test.go index 7cd6ebcb3..e5ece0778 100644 --- a/pkg/deploy/sercet_test.go +++ b/pkg/deploy/secret_test.go @@ -15,7 +15,8 @@ import ( "context" "os" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -138,17 +139,17 @@ func TestGetSecrets(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true))) - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) testCase.initObjects = append(testCase.initObjects, runtimeSecrets...) cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...) - deployContext := &DeployContext{ - CheCluster: &orgv1.CheCluster{ + deployContext := &chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, }, - ClusterAPI: ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, @@ -168,16 +169,16 @@ func TestGetSecrets(t *testing.T) { } func TestSyncSecretToCluster(t *testing.T) { - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme) - deployContext := &DeployContext{ - CheCluster: &orgv1.CheCluster{ + deployContext := &chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, }, - ClusterAPI: ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, diff --git a/pkg/deploy/server/chehost_reconciler.go b/pkg/deploy/server/chehost_reconciler.go index f5225a4e0..69307e141 100644 --- a/pkg/deploy/server/chehost_reconciler.go +++ b/pkg/deploy/server/chehost_reconciler.go @@ -12,9 +12,11 @@ package server import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" "github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy/gateway" - "github.com/eclipse-che/che-operator/pkg/util" routev1 "github.com/openshift/api/route/v1" networking "k8s.io/api/networking/v1" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -28,18 +30,13 @@ func NewCheHostReconciler() *CheHostReconciler { return &CheHostReconciler{} } -func (s *CheHostReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { +func (s *CheHostReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { done, err := s.syncCheService(ctx) if !done { return reconcile.Result{}, false, err } - cheHost, done, err := s.exposeCheEndpoint(ctx) - if !done { - return reconcile.Result{}, false, err - } - - done, err = s.updateCheURL(cheHost, ctx) + ctx.CheHost, done, err = s.exposeCheEndpoint(ctx) if !done { return reconcile.Result{}, false, err } @@ -47,39 +44,36 @@ func (s *CheHostReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Resu return reconcile.Result{}, true, nil } -func (s *CheHostReconciler) Finalize(ctx *deploy.DeployContext) bool { +func (s *CheHostReconciler) Finalize(ctx *chetypes.DeployContext) bool { return true } -func (s *CheHostReconciler) syncCheService(ctx *deploy.DeployContext) (bool, error) { +func (s *CheHostReconciler) syncCheService(ctx *chetypes.DeployContext) (bool, error) { portName := []string{"http"} portNumber := []int32{8080} - if ctx.CheCluster.Spec.Metrics.Enable { + if ctx.CheCluster.Spec.Components.Metrics.Enable { portName = append(portName, "metrics") - portNumber = append(portNumber, deploy.DefaultCheMetricsPort) + portNumber = append(portNumber, constants.DefaultServerMetricsPort) } - cheDebug := util.GetValue(ctx.CheCluster.Spec.Server.CheDebug, deploy.DefaultCheDebug) - if cheDebug == "true" { + if ctx.CheCluster.Spec.Components.CheServer.Debug != nil && *ctx.CheCluster.Spec.Components.CheServer.Debug { portName = append(portName, "debug") - portNumber = append(portNumber, deploy.DefaultCheDebugPort) + portNumber = append(portNumber, constants.DefaultServerDebugPort) } spec := deploy.GetServiceSpec(ctx, deploy.CheServiceName, portName, portNumber, getComponentName(ctx)) return deploy.Sync(ctx, spec, deploy.ServiceDefaultDiffOpts) } -func (s CheHostReconciler) exposeCheEndpoint(ctx *deploy.DeployContext) (string, bool, error) { - if !util.IsOpenShift { +func (s CheHostReconciler) exposeCheEndpoint(ctx *chetypes.DeployContext) (string, bool, error) { + if !infrastructure.IsOpenShift() { _, done, err := deploy.SyncIngressToCluster( ctx, getComponentName(ctx), - ctx.CheCluster.Spec.Server.CheHost, "", gateway.GatewayServiceName, 8080, - ctx.CheCluster.Spec.Server.CheServerIngress, getComponentName(ctx)) if !done { return "", false, err @@ -97,11 +91,9 @@ func (s CheHostReconciler) exposeCheEndpoint(ctx *deploy.DeployContext) (string, done, err := deploy.SyncRouteToCluster( ctx, getComponentName(ctx), - ctx.CheCluster.Spec.Server.CheHost, "/", gateway.GatewayServiceName, 8080, - ctx.CheCluster.Spec.Server.CheServerRoute, getComponentName(ctx)) if !done { return "", false, err @@ -115,14 +107,3 @@ func (s CheHostReconciler) exposeCheEndpoint(ctx *deploy.DeployContext) (string, return route.Spec.Host, true, nil } - -func (s CheHostReconciler) updateCheURL(cheHost string, ctx *deploy.DeployContext) (bool, error) { - var cheUrl = "https://" + cheHost - if ctx.CheCluster.Status.CheURL != cheUrl { - ctx.CheCluster.Status.CheURL = cheUrl - err := deploy.UpdateCheCRStatus(ctx, getComponentName(ctx)+" server URL", cheUrl) - return err == nil, err - } - - return true, nil -} diff --git a/pkg/deploy/server/chehost_reconciler_test.go b/pkg/deploy/server/chehost_reconciler_test.go index d4cb04825..56ea3a4c5 100644 --- a/pkg/deploy/server/chehost_reconciler_test.go +++ b/pkg/deploy/server/chehost_reconciler_test.go @@ -13,15 +13,20 @@ package server import ( "context" - "os" + "k8s.io/utils/pointer" + + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" routev1 "github.com/openshift/api/route/v1" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -32,25 +37,27 @@ import ( ) func TestSyncService(t *testing.T) { - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) corev1.SchemeBuilder.AddToScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme) - ctx := &deploy.DeployContext{ - CheCluster: &orgv1.CheCluster{ + ctx := &chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", - Name: os.Getenv("CHE_FLAVOR"), + Name: defaults.GetCheFlavor(), }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - CheDebug: "true", - }, - Metrics: orgv1.CheClusterSpecMetrics{ - Enable: true, + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + CheServer: chev2.CheServer{ + Debug: pointer.BoolPtr(true), + }, + Metrics: chev2.ServerMetrics{ + Enable: true, + }, }, }, }, - ClusterAPI: deploy.ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, @@ -70,30 +77,28 @@ func TestSyncService(t *testing.T) { assert.Equal(t, service.Spec.Ports[0].Name, "http") assert.Equal(t, service.Spec.Ports[0].Port, int32(8080)) assert.Equal(t, service.Spec.Ports[1].Name, "metrics") - assert.Equal(t, service.Spec.Ports[1].Port, deploy.DefaultCheMetricsPort) + assert.Equal(t, service.Spec.Ports[1].Port, constants.DefaultServerMetricsPort) assert.Equal(t, service.Spec.Ports[2].Name, "debug") - assert.Equal(t, service.Spec.Ports[2].Port, deploy.DefaultCheDebugPort) + assert.Equal(t, service.Spec.Ports[2].Port, constants.DefaultServerDebugPort) } func TestConfiguringLabelsForRoutes(t *testing.T) { - util.IsOpenShift = true + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) - cheCluster := &orgv1.CheCluster{ + cheCluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", - Name: os.Getenv("CHE_FLAVOR"), + Name: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - CheServerRoute: orgv1.RouteCustomSettings{ - Labels: "route=one", - }, + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + Labels: map[string]string{"route": "one"}, }, }, - Status: orgv1.CheClusterStatus{}, + Status: chev2.CheClusterStatus{}, } - ctx := deploy.GetTestDeployContext(cheCluster, []runtime.Object{}) + ctx := test.GetDeployContext(cheCluster, []runtime.Object{}) server := NewCheHostReconciler() _, done, err := server.exposeCheEndpoint(ctx) @@ -107,21 +112,20 @@ func TestConfiguringLabelsForRoutes(t *testing.T) { } func TestCheHostReconciler(t *testing.T) { - util.IsOpenShift = true + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) - cheCluster := &orgv1.CheCluster{ + cheCluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, } - ctx := deploy.GetTestDeployContext(cheCluster, []runtime.Object{}) + ctx := test.GetDeployContext(cheCluster, []runtime.Object{}) cheHostReconciler := NewCheHostReconciler() _, done, err := cheHostReconciler.Reconcile(ctx) assert.True(t, done) assert.Nil(t, err) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: getComponentName(ctx), Namespace: "eclipse-che"}, &routev1.Route{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: deploy.CheServiceName, Namespace: "eclipse-che"}, &corev1.Service{})) - assert.NotEmpty(t, cheCluster.Status.CheURL) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: getComponentName(ctx), Namespace: "eclipse-che"}, &routev1.Route{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: deploy.CheServiceName, Namespace: "eclipse-che"}, &corev1.Service{})) } diff --git a/pkg/deploy/server/init_test.go b/pkg/deploy/server/init_test.go index 2c3c9be6d..9ffc41fc4 100644 --- a/pkg/deploy/server/init_test.go +++ b/pkg/deploy/server/init_test.go @@ -11,11 +11,15 @@ // package server -import "github.com/eclipse-che/che-operator/pkg/deploy" +import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" +) func init() { - err := deploy.InitTestDefaultsFromDeployment("../../../config/manager/manager.yaml") - if err != nil { - panic(err) - } + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../../../config/manager/manager.yaml") } diff --git a/pkg/deploy/server/server_configmap.go b/pkg/deploy/server/server_configmap.go index 7f74d03ef..d83114267 100644 --- a/pkg/deploy/server/server_configmap.go +++ b/pkg/deploy/server/server_configmap.go @@ -18,10 +18,14 @@ import ( "strconv" "strings" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/eclipse-che/che-operator/pkg/deploy" deploytls "github.com/eclipse-che/che-operator/pkg/deploy/tls" - "github.com/eclipse-che/che-operator/pkg/util" "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" @@ -53,9 +57,7 @@ type CheConfigMap struct { DefaultTargetNamespace string `json:"CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT"` PvcStrategy string `json:"CHE_INFRA_KUBERNETES_PVC_STRATEGY"` PvcClaimSize string `json:"CHE_INFRA_KUBERNETES_PVC_QUANTITY"` - PvcJobsImage string `json:"CHE_INFRA_KUBERNETES_PVC_JOBS_IMAGE"` WorkspacePvcStorageClassName string `json:"CHE_INFRA_KUBERNETES_PVC_STORAGE__CLASS__NAME"` - PreCreateSubPaths string `json:"CHE_INFRA_KUBERNETES_PVC_PRECREATE__SUBPATHS"` TlsSupport string `json:"CHE_INFRA_OPENSHIFT_TLS__ENABLED"` K8STrustCerts string `json:"CHE_INFRA_KUBERNETES_TRUST__CERTS"` DatabaseURL string `json:"CHE_JDBC_URL,omitempty"` @@ -90,20 +92,14 @@ type CheConfigMap struct { // GetCheConfigMapData gets env values from CR spec and returns a map with key:value // which is used in CheCluster ConfigMap to configure CheCluster master behavior -func (s *CheServerReconciler) getCheConfigMapData(ctx *deploy.DeployContext) (cheEnv map[string]string, err error) { - identityProviderURL := ctx.CheCluster.Spec.Auth.IdentityProviderURL +func (s *CheServerReconciler) getCheConfigMapData(ctx *chetypes.DeployContext) (cheEnv map[string]string, err error) { + identityProviderURL := ctx.CheCluster.Spec.Networking.Auth.IdentityProviderURL infra := "kubernetes" - if util.IsOpenShift { - infra = "openshift" - } - tls := "false" openShiftIdentityProviderId := "NULL" - if util.IsOpenShift { - openShiftIdentityProviderId = "openshift-v3" - if util.IsOpenShift4 { - openShiftIdentityProviderId = "openshift-v4" - } + if infrastructure.IsOpenShift() { + infra = "openshift" + openShiftIdentityProviderId = "openshift-v4" } proxyJavaOpts := "" @@ -120,33 +116,32 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *deploy.DeployContext) (ch } } - ingressDomain := ctx.CheCluster.Spec.K8s.IngressDomain - tlsSecretName := ctx.CheCluster.Spec.K8s.TlsSecretName - securityContextFsGroup := util.GetValue(ctx.CheCluster.Spec.K8s.SecurityContextFsGroup, deploy.DefaultSecurityContextFsGroup) - securityContextRunAsUser := util.GetValue(ctx.CheCluster.Spec.K8s.SecurityContextRunAsUser, deploy.DefaultSecurityContextRunAsUser) - pvcStrategy := util.GetValue(ctx.CheCluster.Spec.Storage.PvcStrategy, deploy.DefaultPvcStrategy) - pvcClaimSize := util.GetValue(ctx.CheCluster.Spec.Storage.PvcClaimSize, deploy.DefaultPvcClaimSize) - workspacePvcStorageClassName := ctx.CheCluster.Spec.Storage.WorkspacePVCStorageClassName + ingressDomain := ctx.CheCluster.Spec.Networking.Domain + tlsSecretName := ctx.CheCluster.Spec.Networking.TlsSecretName - defaultPVCJobsImage := deploy.DefaultPvcJobsImage(ctx.CheCluster) - pvcJobsImage := util.GetValue(ctx.CheCluster.Spec.Storage.PvcJobsImage, defaultPVCJobsImage) - preCreateSubPaths := "true" - if !ctx.CheCluster.Spec.Storage.PreCreateSubPaths { - preCreateSubPaths = "false" + securityContextFsGroup := strconv.FormatInt(constants.DefaultSecurityContextFsGroup, 10) + if ctx.CheCluster.Spec.Components.CheServer.Deployment.SecurityContext.FsGroup != nil { + securityContextFsGroup = strconv.FormatInt(*ctx.CheCluster.Spec.Components.CheServer.Deployment.SecurityContext.FsGroup, 10) } - chePostgresHostName := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresHostName, deploy.DefaultChePostgresHostName) - chePostgresPort := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresPort, deploy.DefaultChePostgresPort) - chePostgresDb := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresDb, deploy.DefaultChePostgresDb) - ingressClass := util.GetValue(ctx.CheCluster.Spec.K8s.IngressClass, deploy.DefaultIngressClass) + + securityContextRunAsUser := strconv.FormatInt(constants.DefaultSecurityContextRunAsUser, 10) + if ctx.CheCluster.Spec.Components.CheServer.Deployment.SecurityContext.RunAsUser != nil { + securityContextRunAsUser = strconv.FormatInt(*ctx.CheCluster.Spec.Components.CheServer.Deployment.SecurityContext.RunAsUser, 10) + } + + pvcStrategy := utils.GetValue(ctx.CheCluster.Spec.DevEnvironments.Storage.PvcStrategy, constants.DefaultPvcStrategy) + pvcClaimSize := utils.GetValue(ctx.CheCluster.Spec.DevEnvironments.Storage.Pvc.ClaimSize, constants.DefaultPvcClaimSize) + workspacePvcStorageClassName := ctx.CheCluster.Spec.DevEnvironments.Storage.Pvc.StorageClass + + chePostgresHostName := utils.GetValue(ctx.CheCluster.Spec.Components.Database.PostgresHostName, constants.DefaultPostgresHostName) + chePostgresPort := utils.GetValue(ctx.CheCluster.Spec.Components.Database.PostgresPort, constants.DefaultPostgresPort) + chePostgresDb := utils.GetValue(ctx.CheCluster.Spec.Components.Database.PostgresDb, constants.DefaultPostgresDb) + ingressClass := utils.GetValue(ctx.CheCluster.Spec.Networking.Annotations["kubernetes.io/ingress.class"], constants.DefaultIngressClass) // grab first the devfile registry url which is deployed by operator devfileRegistryURL := ctx.CheCluster.Status.DevfileRegistryURL - // `Spec.Server.DevfileRegistryUrl` is deprecated in favor of `Server.ExternalDevfileRegistries` - if ctx.CheCluster.Spec.Server.DevfileRegistryUrl != "" { - devfileRegistryURL += " " + ctx.CheCluster.Spec.Server.DevfileRegistryUrl - } - for _, r := range ctx.CheCluster.Spec.Server.ExternalDevfileRegistries { + for _, r := range ctx.CheCluster.Spec.Components.DevfileRegistry.ExternalDevfileRegistries { if strings.Index(devfileRegistryURL, r.Url) == -1 { devfileRegistryURL += " " + r.Url } @@ -154,34 +149,44 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *deploy.DeployContext) (ch devfileRegistryURL = strings.TrimSpace(devfileRegistryURL) pluginRegistryURL := ctx.CheCluster.Status.PluginRegistryURL - cheLogLevel := util.GetValue(ctx.CheCluster.Spec.Server.CheLogLevel, deploy.DefaultCheLogLevel) - cheDebug := util.GetValue(ctx.CheCluster.Spec.Server.CheDebug, deploy.DefaultCheDebug) - cheMetrics := strconv.FormatBool(ctx.CheCluster.Spec.Metrics.Enable) - cheLabels := util.MapToKeyValuePairs(deploy.GetLabels(ctx.CheCluster, deploy.DefaultCheFlavor(ctx.CheCluster))) - singleHostGatewayConfigMapLabels := labels.FormatLabels(util.GetMapValue(ctx.CheCluster.Spec.Server.SingleHostGatewayConfigMapLabels, deploy.DefaultSingleHostGatewayConfigMapLabels)) - workspaceNamespaceDefault := deploy.GetWorkspaceNamespaceDefault(ctx.CheCluster) + cheLogLevel := utils.GetValue(ctx.CheCluster.Spec.Components.CheServer.LogLevel, constants.DefaultServerLogLevel) + cheDebug := "false" + if ctx.CheCluster.Spec.Components.CheServer.Debug != nil { + cheDebug = strconv.FormatBool(*ctx.CheCluster.Spec.Components.CheServer.Debug) + } + cheMetrics := strconv.FormatBool(ctx.CheCluster.Spec.Components.Metrics.Enable) + cheLabels := labels.FormatLabels(deploy.GetLabels(defaults.GetCheFlavor())) - cheAPI := ctx.CheCluster.Status.CheURL + "/api" + singleHostGatewayConfigMapLabels := "" + if len(ctx.CheCluster.Spec.Networking.Auth.Gateway.ConfigLabels) != 0 { + singleHostGatewayConfigMapLabels = labels.FormatLabels(ctx.CheCluster.Spec.Networking.Auth.Gateway.ConfigLabels) + } else { + singleHostGatewayConfigMapLabels = labels.FormatLabels(constants.DefaultSingleHostGatewayConfigMapLabels) + + } + workspaceNamespaceDefault := ctx.CheCluster.GetDefaultNamespace() + + cheAPI := "https://" + ctx.CheHost + "/api" var pluginRegistryInternalURL, devfileRegistryInternalURL string // If there is a devfile registry deployed by operator - if !ctx.CheCluster.Spec.Server.ExternalDevfileRegistry { - devfileRegistryInternalURL = fmt.Sprintf("http://%s.%s.svc:8080", deploy.DevfileRegistryName, ctx.CheCluster.Namespace) + if !ctx.CheCluster.Spec.Components.DevfileRegistry.DisableInternalRegistry { + devfileRegistryInternalURL = fmt.Sprintf("http://%s.%s.svc:8080", constants.DevfileRegistryName, ctx.CheCluster.Namespace) } - if !ctx.CheCluster.Spec.Server.ExternalPluginRegistry { - pluginRegistryInternalURL = fmt.Sprintf("http://%s.%s.svc:8080/v3", deploy.PluginRegistryName, ctx.CheCluster.Namespace) + if !ctx.CheCluster.Spec.Components.PluginRegistry.DisableInternalRegistry { + pluginRegistryInternalURL = fmt.Sprintf("http://%s.%s.svc:8080/v3", constants.PluginRegistryName, ctx.CheCluster.Namespace) } cheInternalAPI := fmt.Sprintf("http://%s.%s.svc:8080/api", deploy.CheServiceName, ctx.CheCluster.Namespace) webSocketInternalEndpoint := fmt.Sprintf("ws://%s.%s.svc:8080/api/websocket", deploy.CheServiceName, ctx.CheCluster.Namespace) - webSocketEndpoint := "wss://" + ctx.CheCluster.GetCheHost() + "/api/websocket" + webSocketEndpoint := "wss://" + ctx.CheHost + "/api/websocket" cheWorkspaceServiceAccount := "NULL" cheUserClusterRoleNames := fmt.Sprintf("%s-cheworkspaces-clusterrole, %s-cheworkspaces-devworkspace-clusterrole", ctx.CheCluster.Namespace, ctx.CheCluster.Namespace) data := &CheConfigMap{ CheMultiUser: "true", - CheHost: ctx.CheCluster.GetCheHost(), + CheHost: ctx.CheHost, ChePort: "8080", CheApi: cheAPI, CheApiInternal: cheInternalAPI, @@ -195,15 +200,13 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *deploy.DeployContext) (ch PvcStrategy: pvcStrategy, PvcClaimSize: pvcClaimSize, WorkspacePvcStorageClassName: workspacePvcStorageClassName, - PvcJobsImage: pvcJobsImage, - PreCreateSubPaths: preCreateSubPaths, TlsSupport: "true", K8STrustCerts: "true", CheLogLevel: cheLogLevel, OpenShiftIdentityProvider: openShiftIdentityProviderId, - JavaOpts: deploy.DefaultJavaOpts + " " + proxyJavaOpts, - WorkspaceJavaOpts: deploy.DefaultWorkspaceJavaOpts + " " + proxyJavaOpts, - WorkspaceMavenOpts: deploy.DefaultWorkspaceJavaOpts + " " + proxyJavaOpts, + JavaOpts: constants.DefaultJavaOpts + " " + proxyJavaOpts, + WorkspaceJavaOpts: constants.DefaultWorkspaceJavaOpts + " " + proxyJavaOpts, + WorkspaceMavenOpts: constants.DefaultWorkspaceJavaOpts + " " + proxyJavaOpts, WorkspaceProxyJavaOpts: proxyJavaOpts, WorkspaceHttpProxy: ctx.Proxy.HttpProxy, WorkspaceHttpsProxy: ctx.Proxy.HttpsProxy, @@ -212,14 +215,14 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *deploy.DeployContext) (ch PluginRegistryInternalUrl: pluginRegistryInternalURL, DevfileRegistryUrl: devfileRegistryURL, DevfileRegistryInternalUrl: devfileRegistryInternalURL, - CheWorkspacePluginBrokerMetadataImage: deploy.DefaultCheWorkspacePluginBrokerMetadataImage(ctx.CheCluster), - CheWorkspacePluginBrokerArtifactsImage: deploy.DefaultCheWorkspacePluginBrokerArtifactsImage(ctx.CheCluster), - CheServerSecureExposerJwtProxyImage: deploy.DefaultCheServerSecureExposerJwtProxyImage(ctx.CheCluster), + CheWorkspacePluginBrokerMetadataImage: defaults.GetCheWorkspacePluginBrokerMetadataImage(ctx.CheCluster), + CheWorkspacePluginBrokerArtifactsImage: defaults.GetCheWorkspacePluginBrokerArtifactsImage(ctx.CheCluster), + CheServerSecureExposerJwtProxyImage: defaults.GetCheServerSecureExposerJwtProxyImage(ctx.CheCluster), CheJGroupsKubernetesLabels: cheLabels, CheMetricsEnabled: cheMetrics, CheTrustedCABundlesConfigMap: deploytls.CheAllCACertsConfigMapName, - ServerStrategy: deploy.ServerExposureStrategy, - WorkspaceExposure: deploy.GatewaySingleHostExposureType, + ServerStrategy: "single-host", + WorkspaceExposure: "gateway", SingleHostGatewayConfigMapLabels: singleHostGatewayConfigMapLabels, CheDevWorkspacesEnabled: strconv.FormatBool(true), // Disable HTTP2 protocol. @@ -240,43 +243,42 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *deploy.DeployContext) (ch err = json.Unmarshal(out, &cheEnv) // k8s specific envs - if !util.IsOpenShift { + if !infrastructure.IsOpenShift() { k8sCheEnv := map[string]string{ "CHE_INFRA_KUBERNETES_POD_SECURITY__CONTEXT_FS__GROUP": securityContextFsGroup, "CHE_INFRA_KUBERNETES_POD_SECURITY__CONTEXT_RUN__AS__USER": securityContextRunAsUser, "CHE_INFRA_KUBERNETES_INGRESS_DOMAIN": ingressDomain, "CHE_INFRA_KUBERNETES_TLS__SECRET": tlsSecretName, - "CHE_INFRA_KUBERNETES_INGRESS_ANNOTATIONS__JSON": "{\"kubernetes.io/ingress.class\": " + ingressClass + ", \"nginx.ingress.kubernetes.io/rewrite-target\": \"/$1\",\"nginx.ingress.kubernetes.io/ssl-redirect\": " + tls + ",\"nginx.ingress.kubernetes.io/proxy-connect-timeout\": \"3600\",\"nginx.ingress.kubernetes.io/proxy-read-timeout\": \"3600\"}", + "CHE_INFRA_KUBERNETES_INGRESS_ANNOTATIONS__JSON": "{\"kubernetes.io/ingress.class\": " + ingressClass + ", \"nginx.ingress.kubernetes.io/rewrite-target\": \"/$1\",\"nginx.ingress.kubernetes.io/ssl-redirect\": \"true\",\"nginx.ingress.kubernetes.io/proxy-connect-timeout\": \"3600\",\"nginx.ingress.kubernetes.io/proxy-read-timeout\": \"3600\"}", "CHE_INFRA_KUBERNETES_INGRESS_PATH__TRANSFORM": "%s(.*)", } k8sCheEnv["CHE_INFRA_KUBERNETES_ENABLE__UNSUPPORTED__K8S"] = "true" - - // Add TLS key and server certificate to properties since user workspaces is created in another - // than Che server namespace, from where the Che TLS secret is not accessable - if ctx.CheCluster.Spec.K8s.TlsSecretName != "" { - cheTLSSecret := &corev1.Secret{} - exists, err := deploy.GetNamespacedObject(ctx, ctx.CheCluster.Spec.K8s.TlsSecretName, cheTLSSecret) - if err != nil { - return nil, err - } - if !exists { - return nil, fmt.Errorf("%s secret not found", ctx.CheCluster.Spec.K8s.TlsSecretName) - } else { - if _, exists := cheTLSSecret.Data["tls.key"]; !exists { - return nil, fmt.Errorf("%s secret has no 'tls.key' key in data", ctx.CheCluster.Spec.K8s.TlsSecretName) - } - if _, exists := cheTLSSecret.Data["tls.crt"]; !exists { - return nil, fmt.Errorf("%s secret has no 'tls.crt' key in data", ctx.CheCluster.Spec.K8s.TlsSecretName) - } - k8sCheEnv["CHE_INFRA_KUBERNETES_TLS__KEY"] = string(cheTLSSecret.Data["tls.key"]) - k8sCheEnv["CHE_INFRA_KUBERNETES_TLS__CERT"] = string(cheTLSSecret.Data["tls.crt"]) - } - } - addMap(cheEnv, k8sCheEnv) } - addMap(cheEnv, ctx.CheCluster.Spec.Server.CustomCheProperties) + // Add TLS key and server certificate to properties since user workspaces is created in another + // than Che server namespace, from where the Che TLS secret is not accessable + if tlsSecretName != "" { + cheTLSSecret := &corev1.Secret{} + exists, err := deploy.GetNamespacedObject(ctx, tlsSecretName, cheTLSSecret) + if err != nil { + return nil, err + } + if !exists { + return nil, fmt.Errorf("%s secret not found", tlsSecretName) + } else { + if _, exists := cheTLSSecret.Data["tls.key"]; !exists { + return nil, fmt.Errorf("%s secret has no 'tls.key' key in data", tlsSecretName) + } + if _, exists := cheTLSSecret.Data["tls.crt"]; !exists { + return nil, fmt.Errorf("%s secret has no 'tls.crt' key in data", tlsSecretName) + } + cheEnv["CHE_INFRA_KUBERNETES_TLS__KEY"] = string(cheTLSSecret.Data["tls.key"]) + cheEnv["CHE_INFRA_KUBERNETES_TLS__CERT"] = string(cheTLSSecret.Data["tls.crt"]) + } + } + + addMap(cheEnv, ctx.CheCluster.Spec.Components.CheServer.ExtraProperties) for _, oauthProvider := range []string{"bitbucket", "gitlab", "github"} { err := updateIntegrationServerEndpoints(ctx, cheEnv, oauthProvider) @@ -288,7 +290,7 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *deploy.DeployContext) (ch return cheEnv, nil } -func updateIntegrationServerEndpoints(ctx *deploy.DeployContext, cheEnv map[string]string, oauthProvider string) error { +func updateIntegrationServerEndpoints(ctx *chetypes.DeployContext, cheEnv map[string]string, oauthProvider string) error { secret, err := getOAuthConfig(ctx, oauthProvider) if secret == nil { return err @@ -300,14 +302,14 @@ func updateIntegrationServerEndpoints(ctx *deploy.DeployContext, cheEnv map[stri } if cheEnv[envName] != "" { - cheEnv[envName] = secret.Annotations[deploy.CheEclipseOrgScmServerEndpoint] + "," + cheEnv[envName] + cheEnv[envName] = secret.Annotations[constants.CheEclipseOrgScmServerEndpoint] + "," + cheEnv[envName] } else { - cheEnv[envName] = secret.Annotations[deploy.CheEclipseOrgScmServerEndpoint] + cheEnv[envName] = secret.Annotations[constants.CheEclipseOrgScmServerEndpoint] } return nil } -func GetCheConfigMapVersion(deployContext *deploy.DeployContext) string { +func GetCheConfigMapVersion(deployContext *chetypes.DeployContext) string { cheConfigMap := &corev1.ConfigMap{} exists, _ := deploy.GetNamespacedObject(deployContext, CheConfigMapName, cheConfigMap) if exists { diff --git a/pkg/deploy/server/server_configmap_test.go b/pkg/deploy/server/server_configmap_test.go index 0d11d39eb..e82e5675a 100644 --- a/pkg/deploy/server/server_configmap_test.go +++ b/pkg/deploy/server/server_configmap_test.go @@ -14,44 +14,42 @@ package server import ( "testing" - "github.com/eclipse-che/che-operator/pkg/deploy" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/test" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - orgv1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/pkg/util" + chev2 "github.com/eclipse-che/che-operator/api/v2" ) func TestNewCheConfigMap(t *testing.T) { type testCase struct { name string - isOpenShift bool - isOpenShift4 bool initObjects []runtime.Object - cheCluster *orgv1.CheCluster + cheCluster *chev2.CheCluster expectedData map[string]string } testCases := []testCase{ { - name: "Test", - initObjects: []runtime.Object{}, - isOpenShift: true, - isOpenShift4: true, - cheCluster: &orgv1.CheCluster{ + name: "Test", + initObjects: []runtime.Object{}, + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - CustomCheProperties: map[string]string{ - "CHE_WORKSPACE_NO_PROXY": "myproxy.myhostname.com", + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + CheServer: chev2.CheServer{ + ExtraProperties: map[string]string{ + "CHE_WORKSPACE_NO_PROXY": "myproxy.myhostname.com", + }, }, }, }, - Status: orgv1.CheClusterStatus{ + Status: chev2.CheClusterStatus{ CheURL: "https://che-host", }, }, @@ -65,14 +63,14 @@ func TestNewCheConfigMap(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - util.IsOpenShift = testCase.isOpenShift - util.IsOpenShift4 = testCase.isOpenShift4 - ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{}) + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + + ctx := test.GetDeployContext(testCase.cheCluster, []runtime.Object{}) server := NewCheServerReconciler() actualData, err := server.getCheConfigMapData(ctx) assert.Nil(t, err) - util.ValidateContainData(actualData, testCase.expectedData, t) + test.ValidateContainData(actualData, testCase.expectedData, t) }) } } @@ -80,26 +78,24 @@ func TestNewCheConfigMap(t *testing.T) { func TestConfigMap(t *testing.T) { type testCase struct { name string - isOpenShift bool - isOpenShift4 bool initObjects []runtime.Object - cheCluster *orgv1.CheCluster + cheCluster *chev2.CheCluster expectedData map[string]string } testCases := []testCase{ { - name: "Test k8s data, no tls secret", - isOpenShift: false, - isOpenShift4: false, - initObjects: []runtime.Object{}, - cheCluster: &orgv1.CheCluster{ + name: "Test k8s data, no tls secret", + initObjects: []runtime.Object{}, + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - WorkspaceNamespaceDefault: "-che", + Spec: chev2.CheClusterSpec{ + DevEnvironments: chev2.CheClusterDevEnvironments{ + DefaultNamespace: chev2.DefaultNamespace{ + Template: "-che", + }, }, }, }, @@ -109,9 +105,7 @@ func TestConfigMap(t *testing.T) { }, }, { - name: "Test k8s data, with tls secret", - isOpenShift: false, - isOpenShift4: false, + name: "Test k8s data, with tls secret", initObjects: []runtime.Object{ &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ @@ -124,15 +118,17 @@ func TestConfigMap(t *testing.T) { }, }, }, - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - WorkspaceNamespaceDefault: "-che", + Spec: chev2.CheClusterSpec{ + DevEnvironments: chev2.CheClusterDevEnvironments{ + DefaultNamespace: chev2.DefaultNamespace{ + Template: "-che", + }, }, - K8s: orgv1.CheClusterSpecK8SOnly{ + Networking: chev2.CheClusterSpecNetworking{ TlsSecretName: "che-tls", }, }, @@ -144,12 +140,12 @@ func TestConfigMap(t *testing.T) { }, { name: "Test k8s data, check public url when internal network enabled.", - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", Namespace: "eclipse-che", }, - Status: orgv1.CheClusterStatus{ + Status: chev2.CheClusterStatus{ CheURL: "https://che-host", }, }, @@ -159,7 +155,7 @@ func TestConfigMap(t *testing.T) { }, { name: "Test k8s data, with internal cluster svc names", - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", Namespace: "eclipse-che", @@ -171,12 +167,12 @@ func TestConfigMap(t *testing.T) { }, { name: "Test k8s data, without internal cluster svc names", - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", Namespace: "eclipse-che", }, - Status: orgv1.CheClusterStatus{ + Status: chev2.CheClusterStatus{ CheURL: "https://che-host", }, }, @@ -188,14 +184,12 @@ func TestConfigMap(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - util.IsOpenShift = testCase.isOpenShift - util.IsOpenShift4 = testCase.isOpenShift4 - ctx := deploy.GetTestDeployContext(testCase.cheCluster, testCase.initObjects) + ctx := test.GetDeployContext(testCase.cheCluster, testCase.initObjects) server := NewCheServerReconciler() actualData, err := server.getCheConfigMapData(ctx) assert.Nil(t, err) - util.ValidateContainData(actualData, testCase.expectedData, t) + test.ValidateContainData(actualData, testCase.expectedData, t) }) } } @@ -204,7 +198,7 @@ func TestUpdateIntegrationServerEndpoints(t *testing.T) { type testCase struct { name string initObjects []runtime.Object - cheCluster *orgv1.CheCluster + cheCluster *chev2.CheCluster expectedData map[string]string } @@ -231,7 +225,7 @@ func TestUpdateIntegrationServerEndpoints(t *testing.T) { }, }, }, - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", @@ -263,15 +257,17 @@ func TestUpdateIntegrationServerEndpoints(t *testing.T) { }, }, }, - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - CustomCheProperties: map[string]string{ - "CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint_1", + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + CheServer: chev2.CheServer{ + ExtraProperties: map[string]string{ + "CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint_1", + }, }, }, }, @@ -283,14 +279,16 @@ func TestUpdateIntegrationServerEndpoints(t *testing.T) { { name: "Test don't update BitBucket endpoints", initObjects: []runtime.Object{}, - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - CustomCheProperties: map[string]string{ - "CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint_1", + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + CheServer: chev2.CheServer{ + ExtraProperties: map[string]string{ + "CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint_1", + }, }, }, }, @@ -303,12 +301,12 @@ func TestUpdateIntegrationServerEndpoints(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - ctx := deploy.GetTestDeployContext(testCase.cheCluster, testCase.initObjects) + ctx := test.GetDeployContext(testCase.cheCluster, testCase.initObjects) server := NewCheServerReconciler() actualData, err := server.getCheConfigMapData(ctx) assert.Nil(t, err) - util.ValidateContainData(actualData, testCase.expectedData, t) + test.ValidateContainData(actualData, testCase.expectedData, t) }) } } @@ -316,29 +314,25 @@ func TestUpdateIntegrationServerEndpoints(t *testing.T) { func TestShouldSetUpCorrectlyDevfileRegistryURL(t *testing.T) { type testCase struct { name string - isOpenShift bool - isOpenShift4 bool initObjects []runtime.Object - cheCluster *orgv1.CheCluster + cheCluster *chev2.CheCluster expectedData map[string]string } testCases := []testCase{ { name: "Test devfile registry urls #1", - cheCluster: &orgv1.CheCluster{ - TypeMeta: metav1.TypeMeta{ - Kind: "CheCluster", - APIVersion: "org.eclipse.che/v1", - }, + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ExternalDevfileRegistry: true, - ExternalDevfileRegistries: []orgv1.ExternalDevfileRegistries{ - {Url: "http://devfile-registry.external.1"}, + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + DevfileRegistry: chev2.DevfileRegistry{ + DisableInternalRegistry: true, + ExternalDevfileRegistries: []chev2.ExternalDevfileRegistry{ + {Url: "http://devfile-registry.external.1"}, + }, }, }, }, @@ -350,70 +344,35 @@ func TestShouldSetUpCorrectlyDevfileRegistryURL(t *testing.T) { }, { name: "Test devfile registry urls #2", - cheCluster: &orgv1.CheCluster{ - TypeMeta: metav1.TypeMeta{ - Kind: "CheCluster", - APIVersion: "org.eclipse.che/v1", - }, + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ExternalDevfileRegistry: true, - DevfileRegistryUrl: "http://devfile-registry.external.1", - ExternalDevfileRegistries: []orgv1.ExternalDevfileRegistries{ - {Url: "http://devfile-registry.external.2"}, + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + DevfileRegistry: chev2.DevfileRegistry{ + ExternalDevfileRegistries: []chev2.ExternalDevfileRegistry{ + {Url: "http://devfile-registry.external.2"}, + }, }, }, }, - }, - expectedData: map[string]string{ - "CHE_WORKSPACE_DEVFILE__REGISTRY__URL": "http://devfile-registry.external.1 http://devfile-registry.external.2", - "CHE_WORKSPACE_DEVFILE__REGISTRY__INTERNAL__URL": "", - }, - }, - { - name: "Test devfile registry urls #3", - cheCluster: &orgv1.CheCluster{ - TypeMeta: metav1.TypeMeta{ - Kind: "CheCluster", - APIVersion: "org.eclipse.che/v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ExternalDevfileRegistry: true, - DevfileRegistryUrl: "http://devfile-registry.external.1", - ExternalDevfileRegistries: []orgv1.ExternalDevfileRegistries{ - {Url: "http://devfile-registry.external.2"}, - }, - }, + Status: chev2.CheClusterStatus{ + DevfileRegistryURL: "http://devfile-registry.internal.1", }, }, expectedData: map[string]string{ - "CHE_WORKSPACE_DEVFILE__REGISTRY__URL": "http://devfile-registry.external.1 http://devfile-registry.external.2", - "CHE_WORKSPACE_DEVFILE__REGISTRY__INTERNAL__URL": "", + "CHE_WORKSPACE_DEVFILE__REGISTRY__URL": "http://devfile-registry.internal.1 http://devfile-registry.external.2", + "CHE_WORKSPACE_DEVFILE__REGISTRY__INTERNAL__URL": "http://devfile-registry.eclipse-che.svc:8080", }, }, { name: "Test devfile registry urls #5", - cheCluster: &orgv1.CheCluster{ - TypeMeta: metav1.TypeMeta{ - Kind: "CheCluster", - APIVersion: "org.eclipse.che/v1", - }, + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ExternalDevfileRegistry: false, - }, - }, - Status: orgv1.CheClusterStatus{ + Status: chev2.CheClusterStatus{ DevfileRegistryURL: "http://devfile-registry.internal", }, }, @@ -422,46 +381,16 @@ func TestShouldSetUpCorrectlyDevfileRegistryURL(t *testing.T) { "CHE_WORKSPACE_DEVFILE__REGISTRY__URL": "http://devfile-registry.internal", }, }, - { - name: "Test devfile registry urls #7", - cheCluster: &orgv1.CheCluster{ - TypeMeta: metav1.TypeMeta{ - Kind: "CheCluster", - APIVersion: "org.eclipse.che/v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ExternalDevfileRegistry: false, - DevfileRegistryUrl: "http://devfile-registry.external.1", - ExternalDevfileRegistries: []orgv1.ExternalDevfileRegistries{ - {Url: "http://devfile-registry.external.2"}, - }, - }, - }, - Status: orgv1.CheClusterStatus{ - DevfileRegistryURL: "http://devfile-registry.internal", - }, - }, - expectedData: map[string]string{ - "CHE_WORKSPACE_DEVFILE__REGISTRY__INTERNAL__URL": "http://devfile-registry.eclipse-che.svc:8080", - "CHE_WORKSPACE_DEVFILE__REGISTRY__URL": "http://devfile-registry.internal http://devfile-registry.external.1 http://devfile-registry.external.2", - }, - }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - util.IsOpenShift = testCase.isOpenShift - util.IsOpenShift4 = testCase.isOpenShift4 - ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{}) + ctx := test.GetDeployContext(testCase.cheCluster, []runtime.Object{}) server := NewCheServerReconciler() actualData, err := server.getCheConfigMapData(ctx) assert.Nil(t, err) - util.ValidateContainData(actualData, testCase.expectedData, t) + test.ValidateContainData(actualData, testCase.expectedData, t) }) } } @@ -469,30 +398,26 @@ func TestShouldSetUpCorrectlyDevfileRegistryURL(t *testing.T) { func TestShouldSetUpCorrectlyInternalPluginRegistryServiceURL(t *testing.T) { type testCase struct { name string - isOpenShift bool - isOpenShift4 bool initObjects []runtime.Object - cheCluster *orgv1.CheCluster + cheCluster *chev2.CheCluster expectedData map[string]string } testCases := []testCase{ { name: "Test CHE_WORKSPACE_PLUGIN__REGISTRY__INTERNAL__URL #1", - cheCluster: &orgv1.CheCluster{ - TypeMeta: metav1.TypeMeta{ - Kind: "CheCluster", - APIVersion: "org.eclipse.che/v1", - }, + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ExternalPluginRegistry: true, + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + PluginRegistry: chev2.PluginRegistry{ + DisableInternalRegistry: true, + }, }, }, - Status: orgv1.CheClusterStatus{ + Status: chev2.CheClusterStatus{ PluginRegistryURL: "http://external-plugin-registry", }, }, @@ -502,20 +427,11 @@ func TestShouldSetUpCorrectlyInternalPluginRegistryServiceURL(t *testing.T) { }, { name: "Test CHE_WORKSPACE_PLUGIN__REGISTRY__INTERNAL__URL #2", - cheCluster: &orgv1.CheCluster{ - TypeMeta: metav1.TypeMeta{ - Kind: "CheCluster", - APIVersion: "org.eclipse.che/v1", - }, + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ExternalPluginRegistry: false, - }, - }, - Status: orgv1.CheClusterStatus{ + Status: chev2.CheClusterStatus{ PluginRegistryURL: "http://external-plugin-registry", }, }, @@ -527,14 +443,12 @@ func TestShouldSetUpCorrectlyInternalPluginRegistryServiceURL(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - util.IsOpenShift = testCase.isOpenShift - util.IsOpenShift4 = testCase.isOpenShift4 - ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{}) + ctx := test.GetDeployContext(testCase.cheCluster, []runtime.Object{}) server := NewCheServerReconciler() actualData, err := server.getCheConfigMapData(ctx) assert.Nil(t, err) - util.ValidateContainData(actualData, testCase.expectedData, t) + test.ValidateContainData(actualData, testCase.expectedData, t) }) } } @@ -542,27 +456,21 @@ func TestShouldSetUpCorrectlyInternalPluginRegistryServiceURL(t *testing.T) { func TestShouldSetUpCorrectlyInternalCheServerURL(t *testing.T) { type testCase struct { name string - isOpenShift bool - isOpenShift4 bool initObjects []runtime.Object - cheCluster *orgv1.CheCluster + cheCluster *chev2.CheCluster expectedData map[string]string } testCases := []testCase{ { name: "Should use internal che-server url, when internal network is enabled", - cheCluster: &orgv1.CheCluster{ - TypeMeta: metav1.TypeMeta{ - Kind: "CheCluster", - APIVersion: "org.eclipse.che/v1", - }, + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - CheHost: "http://che-host", + Spec: chev2.CheClusterSpec{ + Networking: chev2.CheClusterSpecNetworking{ + Hostname: "che-host", }, }, }, @@ -574,14 +482,12 @@ func TestShouldSetUpCorrectlyInternalCheServerURL(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - util.IsOpenShift = testCase.isOpenShift - util.IsOpenShift4 = testCase.isOpenShift4 - ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{}) + ctx := test.GetDeployContext(testCase.cheCluster, []runtime.Object{}) server := NewCheServerReconciler() actualData, err := server.getCheConfigMapData(ctx) assert.Nil(t, err) - util.ValidateContainData(actualData, testCase.expectedData, t) + test.ValidateContainData(actualData, testCase.expectedData, t) }) } } diff --git a/pkg/deploy/server/server_deployment.go b/pkg/deploy/server/server_deployment.go index 1b3b79a5c..92d9994d5 100644 --- a/pkg/deploy/server/server_deployment.go +++ b/pkg/deploy/server/server_deployment.go @@ -12,22 +12,24 @@ package server import ( - "strconv" - "strings" - + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy/postgres" "github.com/eclipse-che/che-operator/pkg/deploy/tls" - orgv1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/pkg/util" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/pointer" ) -func (s CheServerReconciler) getDeploymentSpec(ctx *deploy.DeployContext) (*appsv1.Deployment, error) { +func (s CheServerReconciler) getDeploymentSpec(ctx *chetypes.DeployContext) (*appsv1.Deployment, error) { selfSignedCASecretExists, err := tls.IsSelfSignedCASecretExists(ctx) if err != nil { return nil, err @@ -37,8 +39,7 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *deploy.DeployContext) (*apps cmResourceVersions += "," + tls.GetAdditionalCACertsConfigMapVersion(ctx) terminationGracePeriodSeconds := int64(30) - cheFlavor := deploy.DefaultCheFlavor(ctx.CheCluster) - labels, labelSelector := deploy.GetLabelsAndSelector(ctx.CheCluster, cheFlavor) + labels, labelSelector := deploy.GetLabelsAndSelector(defaults.GetCheFlavor()) optionalEnv := true selfSignedCertEnv := corev1.EnvVar{ Name: "CHE_SELF__SIGNED__CERT", @@ -73,21 +74,21 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *deploy.DeployContext) (*apps SecretKeyRef: &corev1.SecretKeySelector{ Key: "ca.crt", LocalObjectReference: corev1.LocalObjectReference{ - Name: deploy.CheTLSSelfSignedCertificateSecretName, + Name: constants.DefaultSelfSignedCertificateSecretName, }, Optional: &optionalEnv, }, }, } } - if ctx.CheCluster.Spec.Server.GitSelfSignedCert { + if ctx.CheCluster.Spec.DevEnvironments.TrustedCerts.GitTrustedCertsConfigMapName != "" { gitSelfSignedCertEnv = corev1.EnvVar{ Name: "CHE_GIT_SELF__SIGNED__CERT", ValueFrom: &corev1.EnvVarSource{ ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ Key: "ca.crt", LocalObjectReference: corev1.LocalObjectReference{ - Name: deploy.GitSelfSignedCertsConfigMapName, + Name: ctx.CheCluster.Spec.DevEnvironments.TrustedCerts.GitTrustedCertsConfigMapName, }, Optional: &optionalEnv, }, @@ -99,7 +100,7 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *deploy.DeployContext) (*apps ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ Key: "githost", LocalObjectReference: corev1.LocalObjectReference{ - Name: deploy.GitSelfSignedCertsConfigMapName, + Name: ctx.CheCluster.Spec.DevEnvironments.TrustedCerts.GitTrustedCertsConfigMapName, }, Optional: &optionalEnv, }, @@ -128,8 +129,8 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *deploy.DeployContext) (*apps Value: "true", }) - cheImageAndTag := GetFullCheServerImageLink(ctx.CheCluster) - pullPolicy := corev1.PullPolicy(util.GetValue(string(ctx.CheCluster.Spec.Server.CheImagePullPolicy), deploy.DefaultPullPolicyFromDockerImage(cheImageAndTag))) + image := defaults.GetCheServerImage(ctx.CheCluster) + pullPolicy := corev1.PullPolicy(utils.GetPullPolicyFromDockerImage(image)) deployment := &appsv1.Deployment{ TypeMeta: metav1.TypeMeta{ @@ -137,7 +138,7 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *deploy.DeployContext) (*apps APIVersion: "apps/v1", }, ObjectMeta: metav1.ObjectMeta{ - Name: cheFlavor, + Name: defaults.GetCheFlavor(), Namespace: ctx.CheCluster.Namespace, Labels: labels, }, @@ -158,9 +159,9 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *deploy.DeployContext) (*apps }, Containers: []corev1.Container{ { - Name: cheFlavor, + Name: defaults.GetCheFlavor(), + Image: image, ImagePullPolicy: pullPolicy, - Image: cheImageAndTag, Ports: []corev1.ContainerPort{ { Name: "http", @@ -180,20 +181,12 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *deploy.DeployContext) (*apps }, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ - corev1.ResourceMemory: util.GetResourceQuantity( - ctx.CheCluster.Spec.Server.ServerMemoryRequest, - deploy.DefaultServerMemoryRequest), - corev1.ResourceCPU: util.GetResourceQuantity( - ctx.CheCluster.Spec.Server.ServerCpuRequest, - deploy.DefaultServerCpuRequest), + corev1.ResourceMemory: resource.MustParse(constants.DefaultServerMemoryRequest), + corev1.ResourceCPU: resource.MustParse(constants.DefaultServerCpuRequest), }, Limits: corev1.ResourceList{ - corev1.ResourceMemory: util.GetResourceQuantity( - ctx.CheCluster.Spec.Server.ServerMemoryLimit, - deploy.DefaultServerMemoryLimit), - corev1.ResourceCPU: util.GetResourceQuantity( - ctx.CheCluster.Spec.Server.ServerCpuLimit, - deploy.DefaultServerCpuLimit), + corev1.ResourceMemory: resource.MustParse(constants.DefaultServerMemoryLimit), + corev1.ResourceCPU: resource.MustParse(constants.DefaultServerCpuLimit), }, }, SecurityContext: &corev1.SecurityContext{ @@ -238,7 +231,7 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *deploy.DeployContext) (*apps container := &deployment.Spec.Template.Spec.Containers[0] - chePostgresCredentialsSecret := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresSecret, deploy.DefaultChePostgresCredentialsSecret) + chePostgresCredentialsSecret := utils.GetValue(ctx.CheCluster.Spec.Components.Database.CredentialsSecretName, constants.DefaultPostgresCredentialsSecret) container.Env = append(container.Env, corev1.EnvVar{ Name: "CHE_JDBC_USERNAME", @@ -263,8 +256,7 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *deploy.DeployContext) (*apps }) // configure probes if debug isn't set - cheDebug := util.GetValue(ctx.CheCluster.Spec.Server.CheDebug, deploy.DefaultCheDebug) - if cheDebug != "true" { + if ctx.CheCluster.Spec.Components.CheServer.Debug == nil || !*ctx.CheCluster.Spec.Components.CheServer.Debug { container.ReadinessProbe = &corev1.Probe{ Handler: corev1.Handler{ HTTPGet: &corev1.HTTPGetAction{ @@ -304,23 +296,15 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *deploy.DeployContext) (*apps } } - if !util.IsOpenShift { - runAsUser, err := strconv.ParseInt(util.GetValue(ctx.CheCluster.Spec.K8s.SecurityContextRunAsUser, deploy.DefaultSecurityContextRunAsUser), 10, 64) - if err != nil { - return nil, err - } - fsGroup, err := strconv.ParseInt(util.GetValue(ctx.CheCluster.Spec.K8s.SecurityContextFsGroup, deploy.DefaultSecurityContextFsGroup), 10, 64) - if err != nil { - return nil, err - } + if !infrastructure.IsOpenShift() { deployment.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{ - RunAsUser: &runAsUser, - FSGroup: &fsGroup, + RunAsUser: pointer.Int64Ptr(constants.DefaultSecurityContextRunAsUser), + FSGroup: pointer.Int64Ptr(constants.DefaultSecurityContextFsGroup), } } - if deploy.IsComponentReadinessInitContainersConfigured(ctx.CheCluster) { - if !ctx.CheCluster.Spec.Database.ExternalDb { + if defaults.IsComponentReadinessInitContainersConfigured() { + if !ctx.CheCluster.Spec.Components.Database.ExternalDb { waitForPostgresInitContainer, err := postgres.GetWaitForPostgresInitContainer(ctx) if err != nil { return nil, err @@ -329,76 +313,56 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *deploy.DeployContext) (*apps } } + deploy.CustomizeDeployment(deployment, &ctx.CheCluster.Spec.Components.CheServer.Deployment, true) + return deployment, nil } -// GetFullCheServerImageLink evaluate full cheImage link(with repo and tag) -// based on Checluster information and image defaults from env variables -func GetFullCheServerImageLink(checluster *orgv1.CheCluster) string { - if len(checluster.Spec.Server.CheImage) > 0 { - cheServerImageTag := util.GetValue(checluster.Spec.Server.CheImageTag, deploy.DefaultCheVersion()) - return checluster.Spec.Server.CheImage + ":" + cheServerImageTag - } - - defaultCheServerImage := deploy.DefaultCheServerImage(checluster) - if len(checluster.Spec.Server.CheImageTag) == 0 { - return defaultCheServerImage - } - - // For back compatibility with version < 7.9.0: - // if cr.Spec.Server.CheImage is empty, but cr.Spec.Server.CheImageTag is not empty, - // parse from default Che image(value comes from env variable) "Che image repository" - // and return "Che image", like concatenation: "cheImageRepo:cheImageTag" - separator := map[bool]string{true: "@", false: ":"}[strings.Contains(defaultCheServerImage, "@")] - imageParts := strings.Split(defaultCheServerImage, separator) - return imageParts[0] + ":" + checluster.Spec.Server.CheImageTag -} - -func MountBitBucketOAuthConfig(ctx *deploy.DeployContext, deployment *appsv1.Deployment) error { +func MountBitBucketOAuthConfig(ctx *chetypes.DeployContext, deployment *appsv1.Deployment) error { secret, err := getOAuthConfig(ctx, "bitbucket") if secret == nil { return err } - mountVolumes(deployment, secret, deploy.BitBucketOAuthConfigMountPath) - mountEnv(deployment, "CHE_OAUTH1_BITBUCKET_CONSUMERKEYPATH", deploy.BitBucketOAuthConfigMountPath+"/"+deploy.BitBucketOAuthConfigConsumerKeyFileName) - mountEnv(deployment, "CHE_OAUTH1_BITBUCKET_PRIVATEKEYPATH", deploy.BitBucketOAuthConfigMountPath+"/"+deploy.BitBucketOAuthConfigPrivateKeyFileName) + mountVolumes(deployment, secret, constants.BitBucketOAuthConfigMountPath) + mountEnv(deployment, "CHE_OAUTH1_BITBUCKET_CONSUMERKEYPATH", constants.BitBucketOAuthConfigMountPath+"/"+constants.BitBucketOAuthConfigConsumerKeyFileName) + mountEnv(deployment, "CHE_OAUTH1_BITBUCKET_PRIVATEKEYPATH", constants.BitBucketOAuthConfigMountPath+"/"+constants.BitBucketOAuthConfigPrivateKeyFileName) - oauthEndpoint := secret.Annotations[deploy.CheEclipseOrgScmServerEndpoint] + oauthEndpoint := secret.Annotations[constants.CheEclipseOrgScmServerEndpoint] if oauthEndpoint != "" { mountEnv(deployment, "CHE_OAUTH1_BITBUCKET_ENDPOINT", oauthEndpoint) } return nil } -func MountGitHubOAuthConfig(ctx *deploy.DeployContext, deployment *appsv1.Deployment) error { +func MountGitHubOAuthConfig(ctx *chetypes.DeployContext, deployment *appsv1.Deployment) error { secret, err := getOAuthConfig(ctx, "github") if secret == nil { return err } - mountVolumes(deployment, secret, deploy.GitHubOAuthConfigMountPath) - mountEnv(deployment, "CHE_OAUTH2_GITHUB_CLIENTID__FILEPATH", deploy.GitHubOAuthConfigMountPath+"/"+deploy.GitHubOAuthConfigClientIdFileName) - mountEnv(deployment, "CHE_OAUTH2_GITHUB_CLIENTSECRET__FILEPATH", deploy.GitHubOAuthConfigMountPath+"/"+deploy.GitHubOAuthConfigClientSecretFileName) + mountVolumes(deployment, secret, constants.GitHubOAuthConfigMountPath) + mountEnv(deployment, "CHE_OAUTH2_GITHUB_CLIENTID__FILEPATH", constants.GitHubOAuthConfigMountPath+"/"+constants.GitHubOAuthConfigClientIdFileName) + mountEnv(deployment, "CHE_OAUTH2_GITHUB_CLIENTSECRET__FILEPATH", constants.GitHubOAuthConfigMountPath+"/"+constants.GitHubOAuthConfigClientSecretFileName) - oauthEndpoint := secret.Annotations[deploy.CheEclipseOrgScmServerEndpoint] + oauthEndpoint := secret.Annotations[constants.CheEclipseOrgScmServerEndpoint] if oauthEndpoint != "" { mountEnv(deployment, "CHE_INTEGRATION_GITHUB_OAUTH__ENDPOINT", oauthEndpoint) } return nil } -func MountGitLabOAuthConfig(ctx *deploy.DeployContext, deployment *appsv1.Deployment) error { +func MountGitLabOAuthConfig(ctx *chetypes.DeployContext, deployment *appsv1.Deployment) error { secret, err := getOAuthConfig(ctx, "gitlab") if secret == nil { return err } - mountVolumes(deployment, secret, deploy.GitLabOAuthConfigMountPath) - mountEnv(deployment, "CHE_OAUTH2_GITLAB_CLIENTID__FILEPATH", deploy.GitLabOAuthConfigMountPath+"/"+deploy.GitLabOAuthConfigClientIdFileName) - mountEnv(deployment, "CHE_OAUTH2_GITLAB_CLIENTSECRET__FILEPATH", deploy.GitLabOAuthConfigMountPath+"/"+deploy.GitLabOAuthConfigClientSecretFileName) + mountVolumes(deployment, secret, constants.GitLabOAuthConfigMountPath) + mountEnv(deployment, "CHE_OAUTH2_GITLAB_CLIENTID__FILEPATH", constants.GitLabOAuthConfigMountPath+"/"+constants.GitLabOAuthConfigClientIdFileName) + mountEnv(deployment, "CHE_OAUTH2_GITLAB_CLIENTSECRET__FILEPATH", constants.GitLabOAuthConfigMountPath+"/"+constants.GitLabOAuthConfigClientSecretFileName) - oauthEndpoint := secret.Annotations[deploy.CheEclipseOrgScmServerEndpoint] + oauthEndpoint := secret.Annotations[constants.CheEclipseOrgScmServerEndpoint] if oauthEndpoint != "" { mountEnv(deployment, "CHE_INTEGRATION_GITLAB_OAUTH__ENDPOINT", oauthEndpoint) } diff --git a/pkg/deploy/server/server_deployment_test.go b/pkg/deploy/server/server_deployment_test.go index 242ecb954..b40662e04 100644 --- a/pkg/deploy/server/server_deployment_test.go +++ b/pkg/deploy/server/server_deployment_test.go @@ -14,12 +14,16 @@ package server import ( "os" - "github.com/eclipse-che/che-operator/pkg/util" + "k8s.io/apimachinery/pkg/api/resource" + + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/stretchr/testify/assert" - "github.com/eclipse-che/che-operator/pkg/deploy" - - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -39,18 +43,18 @@ func TestDeployment(t *testing.T) { memoryRequest string cpuLimit string cpuRequest string - cheCluster *orgv1.CheCluster + cheCluster *chev2.CheCluster } testCases := []testCase{ { name: "Test default limits", initObjects: []runtime.Object{}, - memoryLimit: deploy.DefaultServerMemoryLimit, - memoryRequest: deploy.DefaultServerMemoryRequest, - cpuLimit: deploy.DefaultServerCpuLimit, - cpuRequest: deploy.DefaultServerCpuRequest, - cheCluster: &orgv1.CheCluster{ + memoryLimit: constants.DefaultServerMemoryLimit, + memoryRequest: constants.DefaultServerMemoryRequest, + cpuLimit: constants.DefaultServerCpuLimit, + cpuRequest: constants.DefaultServerCpuRequest, + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, @@ -63,16 +67,31 @@ func TestDeployment(t *testing.T) { cpuRequest: "150m", memoryLimit: "250Mi", memoryRequest: "150Mi", - cheCluster: &orgv1.CheCluster{ + cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ServerCpuLimit: "250m", - ServerCpuRequest: "150m", - ServerMemoryLimit: "250Mi", - ServerMemoryRequest: "150Mi", + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + CheServer: chev2.CheServer{ + Deployment: chev2.Deployment{ + Containers: []chev2.Container{ + { + Name: defaults.GetCheFlavor(), + Resources: chev2.ResourceRequirements{ + Requests: chev2.ResourceList{ + Memory: resource.MustParse("150Mi"), + Cpu: resource.MustParse("150m"), + }, + Limits: chev2.ResourceList{ + Memory: resource.MustParse("250Mi"), + Cpu: resource.MustParse("250m"), + }, + }, + }, + }, + }, + }, }, }, }, @@ -82,13 +101,13 @@ func TestDeployment(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true))) - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) testCase.initObjects = append(testCase.initObjects) cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...) - ctx := &deploy.DeployContext{ + ctx := &chetypes.DeployContext{ CheCluster: testCase.cheCluster, - ClusterAPI: deploy.ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, Scheme: scheme.Scheme, }, @@ -98,8 +117,8 @@ func TestDeployment(t *testing.T) { deployment, err := server.getDeploymentSpec(ctx) assert.Nil(t, err) - util.CompareResources(deployment, - util.TestExpectedResources{ + test.CompareResources(deployment, + test.TestExpectedResources{ MemoryLimit: testCase.memoryLimit, MemoryRequest: testCase.memoryRequest, CpuRequest: testCase.cpuRequest, @@ -107,20 +126,20 @@ func TestDeployment(t *testing.T) { }, t) - util.ValidateSecurityContext(deployment, t) + test.ValidateSecurityContext(deployment, t) }) } } func TestMountBitBucketOAuthEnvVar(t *testing.T) { type testCase struct { - name string - initObjects []runtime.Object - expectedConsumerKeyPathEnv corev1.EnvVar - expectedPrivateKeyPathEnv corev1.EnvVar - expectedOAuthEndpointEnv corev1.EnvVar - expectedVolume corev1.Volume - expectedVolumeMount corev1.VolumeMount + name string + initObjects []runtime.Object + expectedConsumerKeyPath string + expectedPrivateKeyPath string + expectedOAuthEndpoint string + expectedVolume corev1.Volume + expectedVolumeMount corev1.VolumeMount } testCases := []testCase{ @@ -150,18 +169,9 @@ func TestMountBitBucketOAuthEnvVar(t *testing.T) { }, }, }, - expectedConsumerKeyPathEnv: corev1.EnvVar{ - Name: "CHE_OAUTH1_BITBUCKET_CONSUMERKEYPATH", - Value: "/che-conf/oauth/bitbucket/consumer.key", - }, - expectedPrivateKeyPathEnv: corev1.EnvVar{ - Name: "CHE_OAUTH1_BITBUCKET_PRIVATEKEYPATH", - Value: "/che-conf/oauth/bitbucket/private.key", - }, - expectedOAuthEndpointEnv: corev1.EnvVar{ - Name: "CHE_OAUTH1_BITBUCKET_ENDPOINT", - Value: "endpoint_1", - }, + expectedConsumerKeyPath: "/che-conf/oauth/bitbucket/consumer.key", + expectedPrivateKeyPath: "/che-conf/oauth/bitbucket/private.key", + expectedOAuthEndpoint: "endpoint_1", expectedVolume: corev1.Volume{ Name: "github-oauth-config", VolumeSource: corev1.VolumeSource{ @@ -179,7 +189,7 @@ func TestMountBitBucketOAuthEnvVar(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - ctx := deploy.GetTestDeployContext(nil, testCase.initObjects) + ctx := test.GetDeployContext(nil, testCase.initObjects) server := NewCheServerReconciler() deployment, err := server.getDeploymentSpec(ctx) @@ -187,23 +197,20 @@ func TestMountBitBucketOAuthEnvVar(t *testing.T) { container := &deployment.Spec.Template.Spec.Containers[0] - env := util.FindEnv(container.Env, "CHE_OAUTH1_BITBUCKET_CONSUMERKEYPATH") - assert.NotNil(t, env) - assert.Equal(t, testCase.expectedConsumerKeyPathEnv, *env) + value := utils.GetEnv(container.Env, "CHE_OAUTH1_BITBUCKET_CONSUMERKEYPATH") + assert.Equal(t, testCase.expectedConsumerKeyPath, value) - env = util.FindEnv(container.Env, "CHE_OAUTH1_BITBUCKET_PRIVATEKEYPATH") - assert.NotNil(t, env) - assert.Equal(t, testCase.expectedPrivateKeyPathEnv, *env) + value = utils.GetEnv(container.Env, "CHE_OAUTH1_BITBUCKET_PRIVATEKEYPATH") + assert.Equal(t, testCase.expectedPrivateKeyPath, value) - env = util.FindEnv(container.Env, "CHE_OAUTH1_BITBUCKET_ENDPOINT") - assert.NotNil(t, env) - assert.Equal(t, testCase.expectedOAuthEndpointEnv, *env) + value = utils.GetEnv(container.Env, "CHE_OAUTH1_BITBUCKET_ENDPOINT") + assert.Equal(t, testCase.expectedOAuthEndpoint, value) - volume := util.FindVolume(deployment.Spec.Template.Spec.Volumes, "github-oauth-config") + volume := test.FindVolume(deployment.Spec.Template.Spec.Volumes, "github-oauth-config") assert.NotNil(t, volume) assert.Equal(t, testCase.expectedVolume, volume) - volumeMount := util.FindVolumeMount(container.VolumeMounts, "github-oauth-config") + volumeMount := test.FindVolumeMount(container.VolumeMounts, "github-oauth-config") assert.NotNil(t, volumeMount) assert.Equal(t, testCase.expectedVolumeMount, volumeMount) }) @@ -212,13 +219,13 @@ func TestMountBitBucketOAuthEnvVar(t *testing.T) { func TestMountGitHubOAuthEnvVar(t *testing.T) { type testCase struct { - name string - initObjects []runtime.Object - expectedIdKeyPathEnv corev1.EnvVar - expectedSecretKeyPathEnv corev1.EnvVar - expectedOAuthEndpointEnv corev1.EnvVar - expectedVolume corev1.Volume - expectedVolumeMount corev1.VolumeMount + name string + initObjects []runtime.Object + expectedIdKeyPath string + expectedSecretKeyPath string + expectedOAuthEndpoint string + expectedVolume corev1.Volume + expectedVolumeMount corev1.VolumeMount } testCases := []testCase{ @@ -248,18 +255,9 @@ func TestMountGitHubOAuthEnvVar(t *testing.T) { }, }, }, - expectedIdKeyPathEnv: corev1.EnvVar{ - Name: "CHE_OAUTH2_GITHUB_CLIENTID__FILEPATH", - Value: "/che-conf/oauth/github/id", - }, - expectedSecretKeyPathEnv: corev1.EnvVar{ - Name: "CHE_OAUTH2_GITHUB_CLIENTSECRET__FILEPATH", - Value: "/che-conf/oauth/github/secret", - }, - expectedOAuthEndpointEnv: corev1.EnvVar{ - Name: "CHE_INTEGRATION_GITHUB_OAUTH__ENDPOINT", - Value: "endpoint_1", - }, + expectedIdKeyPath: "/che-conf/oauth/github/id", + expectedSecretKeyPath: "/che-conf/oauth/github/secret", + expectedOAuthEndpoint: "endpoint_1", expectedVolume: corev1.Volume{ Name: "github-oauth-config", VolumeSource: corev1.VolumeSource{ @@ -277,7 +275,7 @@ func TestMountGitHubOAuthEnvVar(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - ctx := deploy.GetTestDeployContext(nil, testCase.initObjects) + ctx := test.GetDeployContext(nil, testCase.initObjects) server := NewCheServerReconciler() deployment, err := server.getDeploymentSpec(ctx) @@ -285,23 +283,20 @@ func TestMountGitHubOAuthEnvVar(t *testing.T) { container := &deployment.Spec.Template.Spec.Containers[0] - env := util.FindEnv(container.Env, "CHE_OAUTH2_GITHUB_CLIENTID__FILEPATH") - assert.NotNil(t, env) - assert.Equal(t, testCase.expectedIdKeyPathEnv, *env) + value := utils.GetEnv(container.Env, "CHE_OAUTH2_GITHUB_CLIENTID__FILEPATH") + assert.Equal(t, testCase.expectedIdKeyPath, value) - env = util.FindEnv(container.Env, "CHE_OAUTH2_GITHUB_CLIENTSECRET__FILEPATH") - assert.NotNil(t, env) - assert.Equal(t, testCase.expectedSecretKeyPathEnv, *env) + value = utils.GetEnv(container.Env, "CHE_OAUTH2_GITHUB_CLIENTSECRET__FILEPATH") + assert.Equal(t, testCase.expectedSecretKeyPath, value) - env = util.FindEnv(container.Env, "CHE_INTEGRATION_GITHUB_OAUTH__ENDPOINT") - assert.NotNil(t, env) - assert.Equal(t, testCase.expectedOAuthEndpointEnv, *env) + value = utils.GetEnv(container.Env, "CHE_INTEGRATION_GITHUB_OAUTH__ENDPOINT") + assert.Equal(t, testCase.expectedOAuthEndpoint, value) - volume := util.FindVolume(deployment.Spec.Template.Spec.Volumes, "github-oauth-config") + volume := test.FindVolume(deployment.Spec.Template.Spec.Volumes, "github-oauth-config") assert.NotNil(t, volume) assert.Equal(t, testCase.expectedVolume, volume) - volumeMount := util.FindVolumeMount(container.VolumeMounts, "github-oauth-config") + volumeMount := test.FindVolumeMount(container.VolumeMounts, "github-oauth-config") assert.NotNil(t, volumeMount) assert.Equal(t, testCase.expectedVolumeMount, volumeMount) }) @@ -310,13 +305,13 @@ func TestMountGitHubOAuthEnvVar(t *testing.T) { func TestMountGitLabOAuthEnvVar(t *testing.T) { type testCase struct { - name string - initObjects []runtime.Object - expectedIdKeyPathEnv corev1.EnvVar - expectedSecretKeyPathEnv corev1.EnvVar - expectedOAuthEndpointEnv corev1.EnvVar - expectedVolume corev1.Volume - expectedVolumeMount corev1.VolumeMount + name string + initObjects []runtime.Object + expectedIdKeyPath string + expectedSecretKeyPath string + expectedOAuthEndpoint string + expectedVolume corev1.Volume + expectedVolumeMount corev1.VolumeMount } testCases := []testCase{ @@ -346,18 +341,9 @@ func TestMountGitLabOAuthEnvVar(t *testing.T) { }, }, }, - expectedIdKeyPathEnv: corev1.EnvVar{ - Name: "CHE_OAUTH2_GITLAB_CLIENTID__FILEPATH", - Value: "/che-conf/oauth/gitlab/id", - }, - expectedSecretKeyPathEnv: corev1.EnvVar{ - Name: "CHE_OAUTH2_GITLAB_CLIENTSECRET__FILEPATH", - Value: "/che-conf/oauth/gitlab/secret", - }, - expectedOAuthEndpointEnv: corev1.EnvVar{ - Name: "CHE_INTEGRATION_GITLAB_OAUTH__ENDPOINT", - Value: "endpoint_1", - }, + expectedIdKeyPath: "/che-conf/oauth/gitlab/id", + expectedSecretKeyPath: "/che-conf/oauth/gitlab/secret", + expectedOAuthEndpoint: "endpoint_1", expectedVolume: corev1.Volume{ Name: "gitlab-oauth-config", VolumeSource: corev1.VolumeSource{ @@ -375,7 +361,7 @@ func TestMountGitLabOAuthEnvVar(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - ctx := deploy.GetTestDeployContext(nil, testCase.initObjects) + ctx := test.GetDeployContext(nil, testCase.initObjects) server := NewCheServerReconciler() deployment, err := server.getDeploymentSpec(ctx) @@ -383,23 +369,20 @@ func TestMountGitLabOAuthEnvVar(t *testing.T) { container := &deployment.Spec.Template.Spec.Containers[0] - env := util.FindEnv(container.Env, "CHE_OAUTH2_GITLAB_CLIENTID__FILEPATH") - assert.NotNil(t, env) - assert.Equal(t, testCase.expectedIdKeyPathEnv, *env) + value := utils.GetEnv(container.Env, "CHE_OAUTH2_GITLAB_CLIENTID__FILEPATH") + assert.Equal(t, testCase.expectedIdKeyPath, value) - env = util.FindEnv(container.Env, "CHE_OAUTH2_GITLAB_CLIENTSECRET__FILEPATH") - assert.NotNil(t, env) - assert.Equal(t, testCase.expectedSecretKeyPathEnv, *env) + value = utils.GetEnv(container.Env, "CHE_OAUTH2_GITLAB_CLIENTSECRET__FILEPATH") + assert.Equal(t, testCase.expectedSecretKeyPath, value) - env = util.FindEnv(container.Env, "CHE_INTEGRATION_GITLAB_OAUTH__ENDPOINT") - assert.NotNil(t, env) - assert.Equal(t, testCase.expectedOAuthEndpointEnv, *env) + value = utils.GetEnv(container.Env, "CHE_INTEGRATION_GITLAB_OAUTH__ENDPOINT") + assert.Equal(t, testCase.expectedOAuthEndpoint, value) - volume := util.FindVolume(deployment.Spec.Template.Spec.Volumes, "gitlab-oauth-config") + volume := test.FindVolume(deployment.Spec.Template.Spec.Volumes, "gitlab-oauth-config") assert.NotNil(t, volume) assert.Equal(t, testCase.expectedVolume, volume) - volumeMount := util.FindVolumeMount(container.VolumeMounts, "gitlab-oauth-config") + volumeMount := test.FindVolumeMount(container.VolumeMounts, "gitlab-oauth-config") assert.NotNil(t, volumeMount) assert.Equal(t, testCase.expectedVolumeMount, volumeMount) }) diff --git a/pkg/deploy/server/server_reconciler.go b/pkg/deploy/server/server_reconciler.go index 438223e1c..d621025f3 100644 --- a/pkg/deploy/server/server_reconciler.go +++ b/pkg/deploy/server/server_reconciler.go @@ -12,8 +12,9 @@ package server import ( - "context" - + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" "github.com/eclipse-che/che-operator/pkg/deploy" "github.com/sirupsen/logrus" appsv1 "k8s.io/api/apps/v1" @@ -21,12 +22,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) -const ( - AvailableStatus = "Available" - UnavailableStatus = "Unavailable" - RollingUpdateInProgressStatus = "Available: Rolling update in progress" -) - type CheServerReconciler struct { deploy.Reconcilable } @@ -35,13 +30,8 @@ func NewCheServerReconciler() *CheServerReconciler { return &CheServerReconciler{} } -func (s *CheServerReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { - done, err := s.syncLegacyConfigMap(ctx) - if !done { - return reconcile.Result{}, false, err - } - - done, err = s.syncCheConfigMap(ctx) +func (s *CheServerReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { + done, err := s.syncCheConfigMap(ctx) if !done { return reconcile.Result{}, false, err } @@ -58,12 +48,17 @@ func (s *CheServerReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Re return reconcile.Result{}, false, err } - done, err = s.updateAvailabilityStatus(ctx) + done, err = s.syncActiveChePhase(ctx) if !done { return reconcile.Result{}, false, err } - done, err = s.updateCheVersion(ctx) + done, err = s.syncCheVersion(ctx) + if !done { + return reconcile.Result{}, false, err + } + + done, err = s.syncCheURL(ctx) if !done { return reconcile.Result{}, false, err } @@ -71,11 +66,11 @@ func (s *CheServerReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Re return reconcile.Result{}, true, nil } -func (s *CheServerReconciler) Finalize(ctx *deploy.DeployContext) bool { +func (s *CheServerReconciler) Finalize(ctx *chetypes.DeployContext) bool { return true } -func (s *CheServerReconciler) syncCheConfigMap(ctx *deploy.DeployContext) (bool, error) { +func (s *CheServerReconciler) syncCheConfigMap(ctx *chetypes.DeployContext) (bool, error) { data, err := s.getCheConfigMapData(ctx) if err != nil { return false, err @@ -84,35 +79,7 @@ func (s *CheServerReconciler) syncCheConfigMap(ctx *deploy.DeployContext) (bool, return deploy.SyncConfigMapDataToCluster(ctx, CheConfigMapName, data, getComponentName(ctx)) } -func (s CheServerReconciler) syncLegacyConfigMap(ctx *deploy.DeployContext) (bool, error) { - // Get custom ConfigMap - // if it exists, add the data into CustomCheProperties - customConfigMap := &corev1.ConfigMap{} - exists, err := deploy.GetNamespacedObject(ctx, "custom", customConfigMap) - if err != nil { - return false, err - } else if exists { - logrus.Info("Found legacy custom ConfigMap. Adding those values to CheCluster.Spec.Server.CustomCheProperties") - - if ctx.CheCluster.Spec.Server.CustomCheProperties == nil { - ctx.CheCluster.Spec.Server.CustomCheProperties = make(map[string]string) - } - for k, v := range customConfigMap.Data { - ctx.CheCluster.Spec.Server.CustomCheProperties[k] = v - } - - err := ctx.ClusterAPI.Client.Update(context.TODO(), ctx.CheCluster) - if err != nil { - return false, err - } - - return deploy.DeleteNamespacedObject(ctx, "custom", &corev1.ConfigMap{}) - } - - return true, nil -} - -func (s *CheServerReconciler) updateAvailabilityStatus(ctx *deploy.DeployContext) (bool, error) { +func (s *CheServerReconciler) syncActiveChePhase(ctx *chetypes.DeployContext) (bool, error) { cheDeployment := &appsv1.Deployment{} exists, err := deploy.GetNamespacedObject(ctx, getComponentName(ctx), cheDeployment) if err != nil { @@ -121,41 +88,34 @@ func (s *CheServerReconciler) updateAvailabilityStatus(ctx *deploy.DeployContext if exists { if cheDeployment.Status.AvailableReplicas < 1 { - if ctx.CheCluster.Status.CheClusterRunning != UnavailableStatus { - ctx.CheCluster.Status.CheClusterRunning = UnavailableStatus - err := deploy.UpdateCheCRStatus(ctx, "status: Che API", UnavailableStatus) - return err == nil, err + if ctx.CheCluster.Status.ChePhase != chev2.ClusterPhaseInactive { + ctx.CheCluster.Status.ChePhase = chev2.ClusterPhaseInactive + err := deploy.UpdateCheCRStatus(ctx, "Phase", chev2.ClusterPhaseInactive) + return false, err } - } else if cheDeployment.Status.Replicas != 1 { - if ctx.CheCluster.Status.CheClusterRunning != RollingUpdateInProgressStatus { - ctx.CheCluster.Status.CheClusterRunning = RollingUpdateInProgressStatus - err := deploy.UpdateCheCRStatus(ctx, "status: Che API", RollingUpdateInProgressStatus) - return err == nil, err + } else if cheDeployment.Status.Replicas > 1 { + if ctx.CheCluster.Status.ChePhase != chev2.RollingUpdate { + ctx.CheCluster.Status.ChePhase = chev2.RollingUpdate + err := deploy.UpdateCheCRStatus(ctx, "Phase", chev2.RollingUpdate) + return false, err } } else { - if ctx.CheCluster.Status.CheClusterRunning != AvailableStatus { - cheFlavor := deploy.DefaultCheFlavor(ctx.CheCluster) - name := "Eclipse Che" - if cheFlavor == "devspaces" { - name = "Red Hat OpenShift Dev Spaces" - } - - logrus.Infof(name+" is now available at: %s", ctx.CheCluster.Status.CheURL) - ctx.CheCluster.Status.CheClusterRunning = AvailableStatus - err := deploy.UpdateCheCRStatus(ctx, "status: Che API", AvailableStatus) + if ctx.CheCluster.Status.ChePhase != chev2.ClusterPhaseActive { + ctx.CheCluster.Status.ChePhase = chev2.ClusterPhaseActive + err := deploy.UpdateCheCRStatus(ctx, "Phase", chev2.ClusterPhaseActive) return err == nil, err } } } else { - ctx.CheCluster.Status.CheClusterRunning = UnavailableStatus - err := deploy.UpdateCheCRStatus(ctx, "status: Che API", UnavailableStatus) - return err == nil, err + ctx.CheCluster.Status.ChePhase = chev2.ClusterPhaseInactive + err := deploy.UpdateCheCRStatus(ctx, "Phase", chev2.ClusterPhaseInactive) + return false, err } return true, nil } -func (s *CheServerReconciler) syncDeployment(ctx *deploy.DeployContext) (bool, error) { +func (s *CheServerReconciler) syncDeployment(ctx *chetypes.DeployContext) (bool, error) { spec, err := s.getDeploymentSpec(ctx) if err != nil { return false, err @@ -164,8 +124,8 @@ func (s *CheServerReconciler) syncDeployment(ctx *deploy.DeployContext) (bool, e return deploy.SyncDeploymentSpecToCluster(ctx, spec, deploy.DefaultDeploymentDiffOpts) } -func (s CheServerReconciler) updateCheVersion(ctx *deploy.DeployContext) (bool, error) { - cheVersion := deploy.DefaultCheVersion() +func (s CheServerReconciler) syncCheVersion(ctx *chetypes.DeployContext) (bool, error) { + cheVersion := defaults.GetCheVersion() if ctx.CheCluster.Status.CheVersion != cheVersion { ctx.CheCluster.Status.CheVersion = cheVersion err := deploy.UpdateCheCRStatus(ctx, "version", cheVersion) @@ -173,3 +133,16 @@ func (s CheServerReconciler) updateCheVersion(ctx *deploy.DeployContext) (bool, } return true, nil } +func (s CheServerReconciler) syncCheURL(ctx *chetypes.DeployContext) (bool, error) { + var cheUrl = "https://" + ctx.CheHost + if ctx.CheCluster.Status.CheURL != cheUrl { + product := map[bool]string{true: "Red Hat OpenShift Dev Spaces", false: "Eclipse Che"}[defaults.GetCheFlavor() == "devspaces"] + logrus.Infof("%s is now available at: %s", product, cheUrl) + + ctx.CheCluster.Status.CheURL = cheUrl + err := deploy.UpdateCheCRStatus(ctx, getComponentName(ctx)+" server URL", cheUrl) + return err == nil, err + } + + return true, nil +} diff --git a/pkg/deploy/server/server_reconciler_test.go b/pkg/deploy/server/server_reconciler_test.go index 6d42e3022..f1ef985f7 100644 --- a/pkg/deploy/server/server_reconciler_test.go +++ b/pkg/deploy/server/server_reconciler_test.go @@ -13,16 +13,15 @@ package server import ( "context" - "os" - "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" - routev1 "github.com/openshift/api/route/v1" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" "github.com/stretchr/testify/assert" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -31,59 +30,47 @@ import ( ) func TestReconcile(t *testing.T) { - cheCluster := &orgv1.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - Name: os.Getenv("CHE_FLAVOR"), - }, - } + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) - util.IsOpenShift = true - ctx := deploy.GetTestDeployContext(cheCluster, []runtime.Object{}) - - chehost := NewCheHostReconciler() - _, done, err := chehost.exposeCheEndpoint(ctx) - assert.True(t, done) - assert.Nil(t, err) - - server := NewCheServerReconciler() - _, done, err = server.Reconcile(ctx) - assert.True(t, done) - assert.Nil(t, err) - - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: getComponentName(ctx), Namespace: "eclipse-che"}, &routev1.Route{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: CheConfigMapName, Namespace: "eclipse-che"}, &corev1.ConfigMap{})) - assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{})) - assert.NotEmpty(t, cheCluster.Status.CheClusterRunning) - assert.NotEmpty(t, cheCluster.Status.CheVersion) -} - -func TestSyncLegacyConfigMap(t *testing.T) { - cheCluster := &orgv1.CheCluster{ + cheCluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, } - ctx := deploy.GetTestDeployContext(cheCluster, []runtime.Object{}) - legacyConfigMap := deploy.GetConfigMapSpec(ctx, "custom", map[string]string{"a": "b"}, "test") - err := ctx.ClusterAPI.Client.Create(context.TODO(), legacyConfigMap) - assert.Nil(t, err) + ctx := test.GetDeployContext(cheCluster, []runtime.Object{}) server := NewCheServerReconciler() - done, err := server.syncLegacyConfigMap(ctx) + _, done, err := server.Reconcile(ctx) + assert.False(t, done) + assert.Nil(t, err) + + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: CheConfigMapName, Namespace: "eclipse-che"}, &corev1.ConfigMap{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{})) + assert.Equal(t, ctx.CheCluster.Status.ChePhase, chev2.CheClusterPhase(chev2.ClusterPhaseInactive)) + + cheDeployment := &appsv1.Deployment{} + err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: defaults.GetCheFlavor(), Namespace: "eclipse-che"}, cheDeployment) + assert.Nil(t, err) + + cheDeployment.Status.Replicas = 1 + cheDeployment.Status.AvailableReplicas = 1 + err = ctx.ClusterAPI.Client.Update(context.TODO(), cheDeployment) + + _, done, err = server.Reconcile(ctx) assert.True(t, done) assert.Nil(t, err) - assert.False(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "custom", Namespace: "eclipse-che"}, &corev1.ConfigMap{})) - assert.Equal(t, cheCluster.Spec.Server.CustomCheProperties["a"], "b") + assert.Equal(t, ctx.CheCluster.Status.ChePhase, chev2.CheClusterPhase(chev2.ClusterPhaseActive)) + assert.NotEmpty(t, cheCluster.Status.CheVersion) + assert.NotEmpty(t, cheCluster.Status.CheURL) } func TestUpdateAvailabilityStatus(t *testing.T) { cheDeployment := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ - Name: os.Getenv("CHE_FLAVOR"), + Name: defaults.GetCheFlavor(), Namespace: "eclipse-che", }, Status: appsv1.DeploymentStatus{ @@ -91,37 +78,28 @@ func TestUpdateAvailabilityStatus(t *testing.T) { Replicas: 1, }, } - cheCluster := &orgv1.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - Name: os.Getenv("CHE_FLAVOR"), - }, - Spec: orgv1.CheClusterSpec{}, - Status: orgv1.CheClusterStatus{}, - } - - ctx := deploy.GetTestDeployContext(cheCluster, []runtime.Object{}) + ctx := test.GetDeployContext(nil, []runtime.Object{}) server := NewCheServerReconciler() - done, err := server.updateAvailabilityStatus(ctx) - assert.True(t, done) + done, err := server.syncActiveChePhase(ctx) + assert.False(t, done) assert.Nil(t, err) - assert.Equal(t, cheCluster.Status.CheClusterRunning, UnavailableStatus) + assert.Equal(t, ctx.CheCluster.Status.ChePhase, chev2.CheClusterPhase(chev2.ClusterPhaseInactive)) err = ctx.ClusterAPI.Client.Create(context.TODO(), cheDeployment) assert.Nil(t, err) - done, err = server.updateAvailabilityStatus(ctx) + done, err = server.syncActiveChePhase(ctx) assert.True(t, done) assert.Nil(t, err) - assert.Equal(t, cheCluster.Status.CheClusterRunning, AvailableStatus) + assert.Equal(t, ctx.CheCluster.Status.ChePhase, chev2.CheClusterPhase(chev2.ClusterPhaseActive)) cheDeployment.Status.Replicas = 2 err = ctx.ClusterAPI.Client.Update(context.TODO(), cheDeployment) assert.Nil(t, err) - done, err = server.updateAvailabilityStatus(ctx) - assert.True(t, done) + done, err = server.syncActiveChePhase(ctx) + assert.False(t, done) assert.Nil(t, err) - assert.Equal(t, cheCluster.Status.CheClusterRunning, RollingUpdateInProgressStatus) + assert.Equal(t, ctx.CheCluster.Status.ChePhase, chev2.CheClusterPhase(chev2.RollingUpdate)) } diff --git a/pkg/deploy/server/server_util.go b/pkg/deploy/server/server_util.go index 2fd9d7153..89f770d1c 100644 --- a/pkg/deploy/server/server_util.go +++ b/pkg/deploy/server/server_util.go @@ -14,20 +14,23 @@ package server import ( "fmt" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" "github.com/eclipse-che/che-operator/pkg/deploy" corev1 "k8s.io/api/core/v1" ) -func getComponentName(ctx *deploy.DeployContext) string { - return deploy.DefaultCheFlavor(ctx.CheCluster) +func getComponentName(ctx *chetypes.DeployContext) string { + return defaults.GetCheFlavor() } -func getOAuthConfig(ctx *deploy.DeployContext, oauthProvider string) (*corev1.Secret, error) { +func getOAuthConfig(ctx *chetypes.DeployContext, oauthProvider string) (*corev1.Secret, error) { secrets, err := deploy.GetSecrets(ctx, map[string]string{ - deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg, - deploy.KubernetesComponentLabelKey: deploy.OAuthScmConfiguration, + constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg, + constants.KubernetesComponentLabelKey: constants.OAuthScmConfiguration, }, map[string]string{ - deploy.CheEclipseOrgOAuthScmServer: oauthProvider, + constants.CheEclipseOrgOAuthScmServer: oauthProvider, }) if err != nil { diff --git a/pkg/deploy/service.go b/pkg/deploy/service.go index 6f00ff8a3..05475895f 100644 --- a/pkg/deploy/service.go +++ b/pkg/deploy/service.go @@ -15,6 +15,7 @@ package deploy import ( "reflect" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" corev1 "k8s.io/api/core/v1" @@ -34,7 +35,7 @@ var ServiceDefaultDiffOpts = cmp.Options{ } func SyncServiceToCluster( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, portName []string, portNumber []int32, @@ -44,18 +45,18 @@ func SyncServiceToCluster( return SyncServiceSpecToCluster(deployContext, serviceSpec) } -func SyncServiceSpecToCluster(deployContext *DeployContext, serviceSpec *corev1.Service) (bool, error) { +func SyncServiceSpecToCluster(deployContext *chetypes.DeployContext, serviceSpec *corev1.Service) (bool, error) { return Sync(deployContext, serviceSpec, ServiceDefaultDiffOpts) } func GetServiceSpec( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, name string, portName []string, portNumber []int32, component string) *corev1.Service { - labels := GetLabels(deployContext.CheCluster, component) + labels := GetLabels(component) ports := []corev1.ServicePort{} for i := range portName { port := corev1.ServicePort{ diff --git a/pkg/deploy/service_account.go b/pkg/deploy/service_account.go index 0420c6b36..f48a13c5a 100644 --- a/pkg/deploy/service_account.go +++ b/pkg/deploy/service_account.go @@ -12,18 +12,20 @@ package deploy import ( + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func SyncServiceAccountToCluster(deployContext *DeployContext, name string) (bool, error) { +func SyncServiceAccountToCluster(deployContext *chetypes.DeployContext, name string) (bool, error) { saSpec := getServiceAccountSpec(deployContext, name) _, err := CreateIfNotExists(deployContext, saSpec) return err == nil, err } -func getServiceAccountSpec(deployContext *DeployContext, name string) *corev1.ServiceAccount { - labels := GetLabels(deployContext.CheCluster, DefaultCheFlavor(deployContext.CheCluster)) +func getServiceAccountSpec(deployContext *chetypes.DeployContext, name string) *corev1.ServiceAccount { + labels := GetLabels(defaults.GetCheFlavor()) serviceAccount := &corev1.ServiceAccount{ TypeMeta: metav1.TypeMeta{ Kind: "ServiceAccount", diff --git a/pkg/deploy/service_test.go b/pkg/deploy/service_test.go index 6b1b9dcca..b7c05ea66 100644 --- a/pkg/deploy/service_test.go +++ b/pkg/deploy/service_test.go @@ -14,7 +14,8 @@ package deploy import ( "context" - orgv1 "github.com/eclipse-che/che-operator/api/v1" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -25,16 +26,16 @@ import ( ) func TestServiceToCluster(t *testing.T) { - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme) - deployContext := &DeployContext{ - CheCluster: &orgv1.CheCluster{ + deployContext := &chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, }, - ClusterAPI: ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, diff --git a/pkg/deploy/sync.go b/pkg/deploy/sync.go index e4b4d711b..3cbfac62c 100644 --- a/pkg/deploy/sync.go +++ b/pkg/deploy/sync.go @@ -16,6 +16,7 @@ import ( "fmt" "reflect" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" "github.com/google/go-cmp/cmp" "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/api/errors" @@ -32,7 +33,7 @@ import ( // WARNING: For legacy reasons, this method bails out quickly without doing anything if the CheCluster resource // is being deleted (it does this by examining the deployContext, not the cluster). If you don't want // this behavior, use the DoSync method. -func Sync(deployContext *DeployContext, blueprint client.Object, diffOpts ...cmp.Option) (bool, error) { +func Sync(deployContext *chetypes.DeployContext, blueprint client.Object, diffOpts ...cmp.Option) (bool, error) { // eclipse-che custom resource is being deleted, we shouldn't sync // TODO move this check before `Sync` invocation if !deployContext.CheCluster.ObjectMeta.DeletionTimestamp.IsZero() { @@ -44,7 +45,7 @@ func Sync(deployContext *DeployContext, blueprint client.Object, diffOpts ...cmp // Sync syncs the blueprint to the cluster in a generic (as much as Go allows) manner. // Returns true if object is up to date otherwiser returns false -func DoSync(deployContext *DeployContext, blueprint client.Object, diffOpts ...cmp.Option) (bool, error) { +func DoSync(deployContext *chetypes.DeployContext, blueprint client.Object, diffOpts ...cmp.Option) (bool, error) { runtimeObject, ok := blueprint.(runtime.Object) if !ok { return false, fmt.Errorf("object %T is not a runtime.Object. Cannot sync it", runtimeObject) @@ -75,7 +76,7 @@ func DoSync(deployContext *DeployContext, blueprint client.Object, diffOpts ...c } func SyncAndAddFinalizer( - deployContext *DeployContext, + deployContext *chetypes.DeployContext, blueprint metav1.Object, diffOpts cmp.Option, finalizer string) (bool, error) { @@ -95,14 +96,14 @@ func SyncAndAddFinalizer( // Gets object by key. // Returns true if object exists otherwise returns false. -func Get(deployContext *DeployContext, key client.ObjectKey, actual client.Object) (bool, error) { +func Get(deployContext *chetypes.DeployContext, key client.ObjectKey, actual client.Object) (bool, error) { cli := getClientForObject(key.Namespace, deployContext) return doGet(cli, key, actual) } // Gets namespaced scope object by name // Returns true if object exists otherwise returns false. -func GetNamespacedObject(deployContext *DeployContext, name string, actual client.Object) (bool, error) { +func GetNamespacedObject(deployContext *chetypes.DeployContext, name string, actual client.Object) (bool, error) { client := deployContext.ClusterAPI.Client key := types.NamespacedName{Name: name, Namespace: deployContext.CheCluster.Namespace} return doGet(client, key, actual) @@ -110,7 +111,7 @@ func GetNamespacedObject(deployContext *DeployContext, name string, actual clien // Gets cluster scope object by name // Returns true if object exists otherwise returns false -func GetClusterObject(deployContext *DeployContext, name string, actual client.Object) (bool, error) { +func GetClusterObject(deployContext *chetypes.DeployContext, name string, actual client.Object) (bool, error) { client := deployContext.ClusterAPI.NonCachingClient key := types.NamespacedName{Name: name} return doGet(client, key, actual) @@ -118,7 +119,7 @@ func GetClusterObject(deployContext *DeployContext, name string, actual client.O // Creates object. // Return true if a new object is created, false if it has been already created or error occurred. -func CreateIfNotExists(deployContext *DeployContext, blueprint client.Object) (isCreated bool, err error) { +func CreateIfNotExists(deployContext *chetypes.DeployContext, blueprint client.Object) (isCreated bool, err error) { // eclipse-che custom resource is being deleted, we shouldn't sync // TODO move this check before `Sync` invocation if !deployContext.CheCluster.ObjectMeta.DeletionTimestamp.IsZero() { @@ -148,7 +149,7 @@ func CreateIfNotExists(deployContext *DeployContext, blueprint client.Object) (i // Creates object. // Return true if a new object is created otherwise returns false. -func Create(deployContext *DeployContext, blueprint client.Object) (bool, error) { +func Create(deployContext *chetypes.DeployContext, blueprint client.Object) (bool, error) { // eclipse-che custom resource is being deleted, we shouldn't sync // TODO move this check before `Sync` invocation if !deployContext.CheCluster.ObjectMeta.DeletionTimestamp.IsZero() { @@ -169,18 +170,18 @@ func Create(deployContext *DeployContext, blueprint client.Object) (bool, error) // Deletes object. // Returns true if object deleted or not found otherwise returns false. -func Delete(deployContext *DeployContext, key client.ObjectKey, objectMeta client.Object) (bool, error) { +func Delete(deployContext *chetypes.DeployContext, key client.ObjectKey, objectMeta client.Object) (bool, error) { client := getClientForObject(key.Namespace, deployContext) return doDeleteByKey(client, deployContext.ClusterAPI.Scheme, key, objectMeta) } -func DeleteNamespacedObject(deployContext *DeployContext, name string, objectMeta client.Object) (bool, error) { +func DeleteNamespacedObject(deployContext *chetypes.DeployContext, name string, objectMeta client.Object) (bool, error) { client := deployContext.ClusterAPI.Client key := types.NamespacedName{Name: name, Namespace: deployContext.CheCluster.Namespace} return doDeleteByKey(client, deployContext.ClusterAPI.Scheme, key, objectMeta) } -func DeleteClusterObject(deployContext *DeployContext, name string, objectMeta client.Object) (bool, error) { +func DeleteClusterObject(deployContext *chetypes.DeployContext, name string, objectMeta client.Object) (bool, error) { client := deployContext.ClusterAPI.NonCachingClient key := types.NamespacedName{Name: name} return doDeleteByKey(client, deployContext.ClusterAPI.Scheme, key, objectMeta) @@ -188,7 +189,7 @@ func DeleteClusterObject(deployContext *DeployContext, name string, objectMeta c // Updates object. // Returns true if object is up to date otherwiser return false -func Update(deployContext *DeployContext, actual client.Object, blueprint client.Object, diffOpts ...cmp.Option) (bool, error) { +func Update(deployContext *chetypes.DeployContext, actual client.Object, blueprint client.Object, diffOpts ...cmp.Option) (bool, error) { // eclipse-che custom resource is being deleted, we shouldn't sync // TODO move this check before `Sync` invocation if !deployContext.CheCluster.ObjectMeta.DeletionTimestamp.IsZero() { @@ -316,7 +317,7 @@ func isUpdateUsingDeleteCreate(kind string) bool { return "Service" == kind || "Ingress" == kind || "Route" == kind || "Job" == kind || "Secret" == kind } -func setOwnerReferenceIfNeeded(deployContext *DeployContext, blueprint metav1.Object) error { +func setOwnerReferenceIfNeeded(deployContext *chetypes.DeployContext, blueprint metav1.Object) error { if shouldSetOwnerReferenceForObject(deployContext, blueprint) { return controllerutil.SetControllerReference(deployContext.CheCluster, blueprint, deployContext.ClusterAPI.Scheme) } @@ -324,12 +325,12 @@ func setOwnerReferenceIfNeeded(deployContext *DeployContext, blueprint metav1.Ob return nil } -func shouldSetOwnerReferenceForObject(deployContext *DeployContext, blueprint metav1.Object) bool { +func shouldSetOwnerReferenceForObject(deployContext *chetypes.DeployContext, blueprint metav1.Object) bool { // empty workspace (cluster scope object) or object in another namespace return blueprint.GetNamespace() == deployContext.CheCluster.Namespace } -func getClientForObject(objectNamespace string, deployContext *DeployContext) client.Client { +func getClientForObject(objectNamespace string, deployContext *chetypes.DeployContext) client.Client { // empty namespace (cluster scope object) or object in another namespace if deployContext.CheCluster.Namespace == objectNamespace { return deployContext.ClusterAPI.Client diff --git a/pkg/deploy/sync_test.go b/pkg/deploy/sync_test.go index ec102c473..83e2b6bab 100644 --- a/pkg/deploy/sync_test.go +++ b/pkg/deploy/sync_test.go @@ -14,8 +14,9 @@ package deploy import ( "context" - orgv1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/pkg/util" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" corev1 "k8s.io/api/core/v1" @@ -187,7 +188,7 @@ func TestSyncAndAddFinalizer(t *testing.T) { t.Fatalf("Failed to get object: %v", err) } - if !util.ContainsString(deployContext.CheCluster.Finalizers, "test-finalizer") { + if !utils.Contains(deployContext.CheCluster.Finalizers, "test-finalizer") { t.Fatalf("Failed to add finalizer") } } @@ -233,17 +234,17 @@ func TestShouldNotDeleteObject(t *testing.T) { } } -func initDeployContext() (client.Client, *DeployContext) { - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) +func initDeployContext() (client.Client, *chetypes.DeployContext) { + chev2.SchemeBuilder.AddToScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme) - deployContext := &DeployContext{ - CheCluster: &orgv1.CheCluster{ + deployContext := &chetypes.DeployContext{ + CheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, }, - ClusterAPI: ClusterAPI{ + ClusterAPI: chetypes.ClusterAPI{ Client: cli, NonCachingClient: cli, Scheme: scheme.Scheme, diff --git a/pkg/deploy/test_util.go b/pkg/deploy/test_util.go deleted file mode 100644 index ef7b552f2..000000000 --- a/pkg/deploy/test_util.go +++ /dev/null @@ -1,80 +0,0 @@ -// -// Copyright (c) 2019-2021 Red Hat, Inc. -// This program and the accompanying materials are made -// available under the terms of the Eclipse Public License 2.0 -// which is available at https://www.eclipse.org/legal/epl-2.0/ -// -// SPDX-License-Identifier: EPL-2.0 -// -// Contributors: -// Red Hat, Inc. - initial API and implementation -// -package deploy - -import ( - orgv1 "github.com/eclipse-che/che-operator/api/v1" - console "github.com/openshift/api/console/v1" - oauthv1 "github.com/openshift/api/oauth/v1" - routev1 "github.com/openshift/api/route/v1" - userv1 "github.com/openshift/api/user/v1" - operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" - corev1 "k8s.io/api/core/v1" - crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - - configv1 "github.com/openshift/api/config/v1" - fakeDiscovery "k8s.io/client-go/discovery/fake" - fakeclientset "k8s.io/client-go/kubernetes/fake" - "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client/fake" -) - -// Initialize DeployContext for tests -func GetTestDeployContext(cheCluster *orgv1.CheCluster, initObjs []runtime.Object) *DeployContext { - if cheCluster == nil { - // use a default checluster - cheCluster = &orgv1.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: "eclipse-che", - Namespace: "eclipse-che", - }, - TypeMeta: metav1.TypeMeta{ - APIVersion: "org.eclipse.che/v1", - Kind: "CheCluster", - }, - Status: orgv1.CheClusterStatus{ - CheURL: "che-host", - }, - } - } - - scheme := scheme.Scheme - orgv1.SchemeBuilder.AddToScheme(scheme) - scheme.AddKnownTypes(operatorsv1alpha1.SchemeGroupVersion, &operatorsv1alpha1.Subscription{}) - scheme.AddKnownTypes(crdv1.SchemeGroupVersion, &crdv1.CustomResourceDefinition{}) - scheme.AddKnownTypes(operatorsv1alpha1.SchemeGroupVersion, &operatorsv1alpha1.Subscription{}) - scheme.AddKnownTypes(oauthv1.SchemeGroupVersion, &oauthv1.OAuthClient{}) - scheme.AddKnownTypes(userv1.SchemeGroupVersion, &userv1.UserList{}, &userv1.User{}, &userv1.Identity{}) - scheme.AddKnownTypes(configv1.SchemeGroupVersion, &configv1.OAuth{}, &configv1.Proxy{}, &configv1.Console{}) - scheme.AddKnownTypes(routev1.GroupVersion, &routev1.Route{}) - scheme.AddKnownTypes(corev1.SchemeGroupVersion, &corev1.Secret{}) - scheme.AddKnownTypes(corev1.SchemeGroupVersion, &corev1.Secret{}) - scheme.AddKnownTypes(console.SchemeGroupVersion, &console.ConsoleLink{}) - - initObjs = append(initObjs, cheCluster) - cli := fake.NewFakeClientWithScheme(scheme, initObjs...) - clientSet := fakeclientset.NewSimpleClientset() - fakeDiscovery, _ := clientSet.Discovery().(*fakeDiscovery.FakeDiscovery) - - return &DeployContext{ - CheCluster: cheCluster, - ClusterAPI: ClusterAPI{ - Client: cli, - NonCachingClient: cli, - Scheme: scheme, - DiscoveryClient: fakeDiscovery, - }, - Proxy: &Proxy{}, - } -} diff --git a/pkg/deploy/tls/certificates.go b/pkg/deploy/tls/certificates.go index 31bae4ae7..65fe30692 100644 --- a/pkg/deploy/tls/certificates.go +++ b/pkg/deploy/tls/certificates.go @@ -17,8 +17,10 @@ import ( "reflect" "strings" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -50,7 +52,7 @@ func NewCertificatesReconciler() *CertificatesReconciler { return &CertificatesReconciler{} } -func (c *CertificatesReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { +func (c *CertificatesReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { if ctx.Proxy.TrustedCAMapName != "" { done, err := c.syncTrustStoreConfigMapToCluster(ctx) if !done { @@ -62,21 +64,20 @@ func (c *CertificatesReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile return reconcile.Result{}, done, err } -func (c *CertificatesReconciler) Finalize(ctx *deploy.DeployContext) bool { +func (c *CertificatesReconciler) Finalize(ctx *chetypes.DeployContext) bool { return true } -func (c *CertificatesReconciler) syncTrustStoreConfigMapToCluster(ctx *deploy.DeployContext) (bool, error) { - trustStoreConfigMapName := util.GetValue(ctx.CheCluster.Spec.Server.ServerTrustStoreConfigMapName, deploy.DefaultServerTrustStoreConfigMapName) - configMapSpec := deploy.GetConfigMapSpec(ctx, trustStoreConfigMapName, map[string]string{}, deploy.DefaultCheFlavor(ctx.CheCluster)) +func (c *CertificatesReconciler) syncTrustStoreConfigMapToCluster(ctx *chetypes.DeployContext) (bool, error) { + configMapSpec := deploy.GetConfigMapSpec(ctx, constants.DefaultServerTrustStoreConfigMapName, map[string]string{}, defaults.GetCheFlavor()) // OpenShift will automatically injects all certs into the configmap configMapSpec.ObjectMeta.Labels[injector] = "true" - configMapSpec.ObjectMeta.Labels[deploy.KubernetesPartOfLabelKey] = deploy.CheEclipseOrg - configMapSpec.ObjectMeta.Labels[deploy.KubernetesComponentLabelKey] = CheCACertsConfigMapLabelValue + configMapSpec.ObjectMeta.Labels[constants.KubernetesPartOfLabelKey] = constants.CheEclipseOrg + configMapSpec.ObjectMeta.Labels[constants.KubernetesComponentLabelKey] = CheCACertsConfigMapLabelValue actual := &corev1.ConfigMap{} - exists, err := deploy.GetNamespacedObject(ctx, trustStoreConfigMapName, actual) + exists, err := deploy.GetNamespacedObject(ctx, constants.DefaultServerTrustStoreConfigMapName, actual) if err != nil { return false, err } @@ -88,12 +89,12 @@ func (c *CertificatesReconciler) syncTrustStoreConfigMapToCluster(ctx *deploy.De } if actual.ObjectMeta.Labels[injector] != "true" || - actual.ObjectMeta.Labels[deploy.KubernetesPartOfLabelKey] != deploy.CheEclipseOrg || - actual.ObjectMeta.Labels[deploy.KubernetesComponentLabelKey] != CheCACertsConfigMapLabelValue { + actual.ObjectMeta.Labels[constants.KubernetesPartOfLabelKey] != constants.CheEclipseOrg || + actual.ObjectMeta.Labels[constants.KubernetesComponentLabelKey] != CheCACertsConfigMapLabelValue { actual.ObjectMeta.Labels[injector] = "true" - actual.ObjectMeta.Labels[deploy.KubernetesPartOfLabelKey] = deploy.CheEclipseOrg - actual.ObjectMeta.Labels[deploy.KubernetesComponentLabelKey] = CheCACertsConfigMapLabelValue + actual.ObjectMeta.Labels[constants.KubernetesPartOfLabelKey] = constants.CheEclipseOrg + actual.ObjectMeta.Labels[constants.KubernetesComponentLabelKey] = CheCACertsConfigMapLabelValue logrus.Infof("Updating existed object: %s, name: %s", configMapSpec.Kind, configMapSpec.Name) if err := ctx.ClusterAPI.Client.Update(context.TODO(), actual); err != nil { @@ -104,7 +105,7 @@ func (c *CertificatesReconciler) syncTrustStoreConfigMapToCluster(ctx *deploy.De return true, nil } -func (c *CertificatesReconciler) syncAdditionalCACertsConfigMapToCluster(ctx *deploy.DeployContext) (bool, error) { +func (c *CertificatesReconciler) syncAdditionalCACertsConfigMapToCluster(ctx *chetypes.DeployContext) (bool, error) { // Get all source config maps, if any caConfigMaps, err := GetCACertsConfigMaps(ctx.ClusterAPI.Client, ctx.CheCluster.GetNamespace()) if err != nil { @@ -162,8 +163,8 @@ func (c *CertificatesReconciler) syncAdditionalCACertsConfigMapToCluster(ctx *de revisions += cm.ObjectMeta.Name + labelEqualSign + cm.ObjectMeta.ResourceVersion } - mergedCAConfigMapSpec := deploy.GetConfigMapSpec(ctx, CheAllCACertsConfigMapName, data, deploy.DefaultCheFlavor(ctx.CheCluster)) - mergedCAConfigMapSpec.ObjectMeta.Labels[deploy.KubernetesPartOfLabelKey] = deploy.CheEclipseOrg + mergedCAConfigMapSpec := deploy.GetConfigMapSpec(ctx, CheAllCACertsConfigMapName, data, defaults.GetCheFlavor()) + mergedCAConfigMapSpec.ObjectMeta.Labels[constants.KubernetesPartOfLabelKey] = constants.CheEclipseOrg mergedCAConfigMapSpec.ObjectMeta.Annotations[CheMergedCAConfigMapRevisionsAnnotationKey] = revisions done, err := deploy.SyncConfigMapSpecToCluster(ctx, mergedCAConfigMapSpec) return done, err diff --git a/pkg/deploy/tls/certificates_test.go b/pkg/deploy/tls/certificates_test.go index 5950fd281..fee51bd16 100644 --- a/pkg/deploy/tls/certificates_test.go +++ b/pkg/deploy/tls/certificates_test.go @@ -16,8 +16,8 @@ import ( "testing" - orgv1 "github.com/eclipse-che/che-operator/api/v1" - "github.com/eclipse-che/che-operator/pkg/deploy" + chev2 "github.com/eclipse-che/che-operator/api/v2" + "github.com/eclipse-che/che-operator/pkg/common/test" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -25,39 +25,14 @@ import ( "k8s.io/apimachinery/pkg/types" ) -func TestSyncGivenTrustStoreConfigMapToCluster(t *testing.T) { - checluster := &orgv1.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - Name: "eclipse-che", - }, - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - ServerTrustStoreConfigMapName: "trust", - }, - }, - } - ctx := deploy.GetTestDeployContext(checluster, []runtime.Object{}) - - certificates := NewCertificatesReconciler() - done, err := certificates.syncTrustStoreConfigMapToCluster(ctx) - assert.Nil(t, err) - assert.True(t, done) - - trustStoreConfigMap := &corev1.ConfigMap{} - err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "trust", Namespace: "eclipse-che"}, trustStoreConfigMap) - assert.Nil(t, err) - assert.Equal(t, trustStoreConfigMap.ObjectMeta.Labels[injector], "true") -} - func TestSyncDefaultTrustStoreConfigMapToCluster(t *testing.T) { - checluster := &orgv1.CheCluster{ + checluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, } - ctx := deploy.GetTestDeployContext(checluster, []runtime.Object{}) + ctx := test.GetDeployContext(checluster, []runtime.Object{}) certificates := NewCertificatesReconciler() done, err := certificates.syncTrustStoreConfigMapToCluster(ctx) @@ -79,13 +54,13 @@ func TestSyncExistedTrustStoreConfigMapToCluster(t *testing.T) { }, Data: map[string]string{"d": "c"}, } - checluster := &orgv1.CheCluster{ + checluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", Name: "eclipse-che", }, } - ctx := deploy.GetTestDeployContext(checluster, []runtime.Object{trustStoreConfigMap}) + ctx := test.GetDeployContext(checluster, []runtime.Object{trustStoreConfigMap}) certificates := NewCertificatesReconciler() done, err := certificates.syncTrustStoreConfigMapToCluster(ctx) @@ -124,7 +99,7 @@ func TestSyncAdditionalCACertsConfigMapToCluster(t *testing.T) { Data: map[string]string{"a2": "b2"}, } - ctx := deploy.GetTestDeployContext(nil, []runtime.Object{cert1}) + ctx := test.GetDeployContext(nil, []runtime.Object{cert1}) certificates := NewCertificatesReconciler() done, err := certificates.syncAdditionalCACertsConfigMapToCluster(ctx) diff --git a/pkg/deploy/tls/init_test.go b/pkg/deploy/tls/init_test.go index 2f2b3c30a..102cd7309 100644 --- a/pkg/deploy/tls/init_test.go +++ b/pkg/deploy/tls/init_test.go @@ -11,11 +11,15 @@ // package tls -import "github.com/eclipse-che/che-operator/pkg/deploy" +import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" +) func init() { - err := deploy.InitTestDefaultsFromDeployment("../../../config/manager/manager.yaml") - if err != nil { - panic(err) - } + test.EnableTestMode() + + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defaults.Initialize("../../../config/manager/manager.yaml") } diff --git a/pkg/deploy/tls/tls_secret.go b/pkg/deploy/tls/tls_secret.go index 67797b9a7..ed3db26a3 100644 --- a/pkg/deploy/tls/tls_secret.go +++ b/pkg/deploy/tls/tls_secret.go @@ -13,8 +13,10 @@ package tls import ( + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/util" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) @@ -26,17 +28,17 @@ func NewTlsSecretReconciler() *TlsSecretReconciler { return &TlsSecretReconciler{} } -func (t *TlsSecretReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { - if util.IsOpenShift { +func (t *TlsSecretReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { + if infrastructure.IsOpenShift() { // create a secret with router tls cert when on OpenShift infra and router is configured with a self signed certificate if ctx.IsSelfSignedCertificate { - if err := CreateTLSSecretFromEndpoint(ctx, "", deploy.CheTLSSelfSignedCertificateSecretName); err != nil { + if err := CreateTLSSecretFromEndpoint(ctx, "", constants.DefaultSelfSignedCertificateSecretName); err != nil { return reconcile.Result{}, false, err } } } else { // Handle Che TLS certificates on Kubernetes infrastructure - if ctx.CheCluster.Spec.K8s.TlsSecretName != "" { + if ctx.CheCluster.Spec.Networking.TlsSecretName != "" { // Self-signed certificate should be created to secure Che ingresses result, err := K8sHandleCheTLSSecrets(ctx) if result.Requeue || result.RequeueAfter > 0 { @@ -44,7 +46,7 @@ func (t *TlsSecretReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Re } } else if ctx.IsSelfSignedCertificate { // Use default self-signed ingress certificate - if err := CreateTLSSecretFromEndpoint(ctx, "", deploy.CheTLSSelfSignedCertificateSecretName); err != nil { + if err := CreateTLSSecretFromEndpoint(ctx, "", constants.DefaultSelfSignedCertificateSecretName); err != nil { return reconcile.Result{}, false, err } } @@ -53,6 +55,6 @@ func (t *TlsSecretReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Re return reconcile.Result{}, true, nil } -func (t *TlsSecretReconciler) Finalize(ctx *deploy.DeployContext) bool { +func (t *TlsSecretReconciler) Finalize(ctx *chetypes.DeployContext) bool { return true } diff --git a/pkg/deploy/tls/tls_utils.go b/pkg/deploy/tls/tls_utils.go index 591501c41..52a5af92e 100644 --- a/pkg/deploy/tls/tls_utils.go +++ b/pkg/deploy/tls/tls_utils.go @@ -23,12 +23,17 @@ import ( "strings" "time" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/eclipse-che/che-operator/pkg/common/chetypes" + "github.com/eclipse-che/che-operator/pkg/common/constants" + k8shelper "github.com/eclipse-che/che-operator/pkg/common/k8s-helper" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/common/test" "github.com/eclipse-che/che-operator/pkg/deploy" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/selection" k8sclient "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/eclipse-che/che-operator/pkg/util" routev1 "github.com/openshift/api/route/v1" "github.com/sirupsen/logrus" batchv1 "k8s.io/api/batch/v1" @@ -51,9 +56,9 @@ const ( ) // IsSelfSignedCASecretExists checks if CheTLSSelfSignedCertificateSecretName exists so depending components can mount it -func IsSelfSignedCASecretExists(ctx *deploy.DeployContext) (bool, error) { +func IsSelfSignedCASecretExists(ctx *chetypes.DeployContext) (bool, error) { cheTLSSelfSignedCertificateSecret := &corev1.Secret{} - err := ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: deploy.CheTLSSelfSignedCertificateSecretName}, cheTLSSelfSignedCertificateSecret) + err := ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: constants.DefaultSelfSignedCertificateSecretName}, cheTLSSelfSignedCertificateSecret) if err != nil { if errors.IsNotFound(err) { return false, nil @@ -64,8 +69,8 @@ func IsSelfSignedCASecretExists(ctx *deploy.DeployContext) (bool, error) { } // IsSelfSignedCertificateUsed detects whether endpoints are/should be secured by self-signed certificate. -func IsSelfSignedCertificateUsed(ctx *deploy.DeployContext) (bool, error) { - if util.IsTestMode() { +func IsSelfSignedCertificateUsed(ctx *chetypes.DeployContext) (bool, error) { + if test.IsTestMode() { return true, nil } @@ -78,30 +83,28 @@ func IsSelfSignedCertificateUsed(ctx *deploy.DeployContext) (bool, error) { return true, nil } - if !util.IsOpenShift { - // Handle custom tls secret for Che ingresses - cheTLSSecretName := ctx.CheCluster.Spec.K8s.TlsSecretName - if cheTLSSecretName != "" { - // The secret is specified in CR - cheTLSSecret := &corev1.Secret{} - err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: cheTLSSecretName}, cheTLSSecret) - if err != nil { - if !errors.IsNotFound(err) { - // Failed to get secret, return error to restart reconcile loop. - return false, err - } - - // Both secrets (che-tls and self-signed-certificate) are missing which means that we should generate them (i.e. use self-signed certificate). - return true, nil + // Handle custom tls secret + cheTLSSecretName := ctx.CheCluster.Spec.Networking.TlsSecretName + if cheTLSSecretName != "" { + // The secret is specified in CR + cheTLSSecret := &corev1.Secret{} + err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: cheTLSSecretName}, cheTLSSecret) + if err != nil { + if !errors.IsNotFound(err) { + // Failed to get secret, return error to restart reconcile loop. + return false, err } - // TLS secret found, consider it as commonly trusted. - return false, nil - } - // Empty secret name means using of default ingress certificate. - // Retrieve the info about certificate chain from test ingress below. + // Both secrets (che-tls and self-signed-certificate) are missing which means that we should generate them (i.e. use self-signed certificate). + return true, nil + } + // TLS secret found, consider it as commonly trusted. + return false, nil } + // Empty secret name means using of default ingress certificate. + // Retrieve the info about certificate chain from test ingress below. + // Get route/ingress TLS certificates chain peerCertificates, err := GetEndpointTLSCrtChain(ctx, "") if err != nil { @@ -121,27 +124,24 @@ func IsSelfSignedCertificateUsed(ctx *deploy.DeployContext) (bool, error) { // GetEndpointTLSCrtChain retrieves TLS certificates chain from given endpoint. // If endpoint is not specified, then a test route/ingress will be created and used to get router certificates. -func GetEndpointTLSCrtChain(ctx *deploy.DeployContext, endpointURL string) ([]*x509.Certificate, error) { - if util.IsTestMode() { +func GetEndpointTLSCrtChain(ctx *chetypes.DeployContext, endpointURL string) ([]*x509.Certificate, error) { + if test.IsTestMode() { return nil, stderrors.New("Not allowed for tests") } var useTestEndpoint bool = len(endpointURL) < 1 var requestURL string - cheFlavor := deploy.DefaultCheFlavor(ctx.CheCluster) if useTestEndpoint { - if util.IsOpenShift { + if infrastructure.IsOpenShift() { // Create test route to get certificates chain. // Note, it is not possible to use SyncRouteToCluster here as it may cause infinite reconcile loop. routeSpec, err := deploy.GetRouteSpec( ctx, "test", "", - "", "test", 8080, - ctx.CheCluster.Spec.Server.CheServerRoute, - cheFlavor) + defaults.GetCheFlavor()) if err != nil { return nil, err } @@ -184,11 +184,9 @@ func GetEndpointTLSCrtChain(ctx *deploy.DeployContext, endpointURL string) ([]*x ctx, "test", "", - "", "test", 8080, - ctx.CheCluster.Spec.Server.CheServerIngress, - cheFlavor) + defaults.GetCheFlavor()) // Create ingress manually if err := ctx.ClusterAPI.Client.Create(context.TODO(), ingressSpec); err != nil { if !errors.IsAlreadyExists(err) { @@ -237,7 +235,7 @@ func GetEndpointTLSCrtChain(ctx *deploy.DeployContext, endpointURL string) ([]*x return certificates, nil } -func doRequestForTLSCrtChain(ctx *deploy.DeployContext, requestURL string, skipProxy bool) ([]*x509.Certificate, error) { +func doRequestForTLSCrtChain(ctx *chetypes.DeployContext, requestURL string, skipProxy bool) ([]*x509.Certificate, error) { transport := &http.Transport{} // Adding the proxy settings to the Transport object. // However, in case of test route we need to reach cluter directly in order to get the right certificate. @@ -265,10 +263,10 @@ func doRequestForTLSCrtChain(ctx *deploy.DeployContext, requestURL string, skipP // Creates a test TLS route/ingress if endpoint url is empty. // There's an easier way which is to read tls secret in default (3.11) or openshift-ingress (4.0) namespace // which however requires extra privileges for operator service account -func GetEndpointTLSCrtBytes(ctx *deploy.DeployContext, endpointURL string) (certificates []byte, err error) { +func GetEndpointTLSCrtBytes(ctx *chetypes.DeployContext, endpointURL string) (certificates []byte, err error) { peerCertificates, err := GetEndpointTLSCrtChain(ctx, endpointURL) if err != nil { - if util.IsTestMode() { + if test.IsTestMode() { fakeCrt := make([]byte, 5) return fakeCrt, nil } @@ -288,11 +286,11 @@ func GetEndpointTLSCrtBytes(ctx *deploy.DeployContext, endpointURL string) (cert } // K8sHandleCheTLSSecrets handles TLS secrets required for Che deployment on Kubernetes infrastructure. -func K8sHandleCheTLSSecrets(ctx *deploy.DeployContext) (reconcile.Result, error) { - cheTLSSecretName := ctx.CheCluster.Spec.K8s.TlsSecretName +func K8sHandleCheTLSSecrets(ctx *chetypes.DeployContext) (reconcile.Result, error) { + cheTLSSecretName := ctx.CheCluster.Spec.Networking.TlsSecretName cheTLSSecretNamespacedName := types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: cheTLSSecretName} - CheTLSSelfSignedCertificateSecretNamespacedName := types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: deploy.CheTLSSelfSignedCertificateSecretName} + CheTLSSelfSignedCertificateSecretNamespacedName := types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: constants.DefaultSelfSignedCertificateSecretName} job := &batchv1.Job{} err := ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: CheTLSJobName, Namespace: ctx.CheCluster.Namespace}, job) @@ -336,14 +334,14 @@ func K8sHandleCheTLSSecrets(ctx *deploy.DeployContext) (reconcile.Result, error) if err != nil { if !errors.IsNotFound(err) { // Error reading secret info - logrus.Errorf("Error getting Che self-signed certificate secert \"%s\": %v", deploy.CheTLSSelfSignedCertificateSecretName, err) + logrus.Errorf("Error getting Che self-signed certificate secert \"%s\": %v", constants.DefaultSelfSignedCertificateSecretName, err) return reconcile.Result{RequeueAfter: time.Second}, err } // Che CA certificate doesn't exists (that's expected at this point), do nothing } else { // Remove Che CA secret because Che TLS secret is missing (they should be generated together). if err = ctx.ClusterAPI.Client.Delete(context.TODO(), cheCASelfSignedCertificateSecret); err != nil { - logrus.Errorf("Error deleting Che self-signed certificate secret \"%s\": %v", deploy.CheTLSSelfSignedCertificateSecretName, err) + logrus.Errorf("Error deleting Che self-signed certificate secret \"%s\": %v", constants.DefaultSelfSignedCertificateSecretName, err) return reconcile.Result{RequeueAfter: time.Second}, err } } @@ -364,22 +362,22 @@ func K8sHandleCheTLSSecrets(ctx *deploy.DeployContext) (reconcile.Result, error) return reconcile.Result{}, err } - domains := ctx.CheCluster.Spec.K8s.IngressDomain + ",*." + ctx.CheCluster.Spec.K8s.IngressDomain - if ctx.CheCluster.GetCheHost() != "" && !strings.Contains(ctx.CheCluster.GetCheHost(), ctx.CheCluster.Spec.K8s.IngressDomain) && ctx.CheCluster.Spec.Server.CheHostTLSSecret == "" { - domains += "," + ctx.CheCluster.GetCheHost() + domains := ctx.CheCluster.Spec.Networking.Domain + ",*." + ctx.CheCluster.Spec.Networking.Domain + if ctx.CheHost != "" && !strings.Contains(ctx.CheHost, ctx.CheCluster.Spec.Networking.Domain) { + domains += "," + ctx.CheHost } labels := "" - for labelName, labelValue := range deploy.GetLabels(ctx.CheCluster, cheTLSSecretName) { + for labelName, labelValue := range deploy.GetLabels(cheTLSSecretName) { labels += fmt.Sprintf("%s=%s ", labelName, labelValue) } - cheTLSSecretsCreationJobImage := deploy.DefaultCheTLSSecretsCreationJobImage() + cheTLSSecretsCreationJobImage := defaults.GetCheTLSSecretsCreationJobImage() jobEnvVars := map[string]string{ "DOMAIN": domains, "CHE_NAMESPACE": ctx.CheCluster.Namespace, "CHE_SERVER_TLS_SECRET_NAME": cheTLSSecretName, - "CHE_CA_CERTIFICATE_SECRET_NAME": deploy.CheTLSSelfSignedCertificateSecretName, + "CHE_CA_CERTIFICATE_SECRET_NAME": constants.DefaultSelfSignedCertificateSecretName, "LABELS": labels, } @@ -394,7 +392,7 @@ func K8sHandleCheTLSSecrets(ctx *deploy.DeployContext) (reconcile.Result, error) if jobExists { // The job object is present if job.Status.Succeeded > 0 { - logrus.Infof("Import public part of Eclipse Che self-signed CA certificate from \"%s\" secret into your browser.", deploy.CheTLSSelfSignedCertificateSecretName) + logrus.Infof("Import public part of Eclipse Che self-signed CA certificate from \"%s\" secret into your browser.", constants.DefaultSelfSignedCertificateSecretName) deleteJob(ctx, job) } else if job.Status.Failed > 0 { // The job failed, but the certificate is present, shouldn't happen @@ -438,7 +436,7 @@ func K8sHandleCheTLSSecrets(ctx *deploy.DeployContext) (reconcile.Result, error) if err != nil { if !errors.IsNotFound(err) { // Error reading Che self-signed secret info - logrus.Errorf("Error getting Che self-signed certificate secert \"%s\": %v", deploy.CheTLSSelfSignedCertificateSecretName, err) + logrus.Errorf("Error getting Che self-signed certificate secert \"%s\": %v", constants.DefaultSelfSignedCertificateSecretName, err) return reconcile.Result{RequeueAfter: time.Second}, err } // Che CA self-signed cetificate secret doesn't exist. @@ -446,10 +444,10 @@ func K8sHandleCheTLSSecrets(ctx *deploy.DeployContext) (reconcile.Result, error) } else { // Che CA self-signed certificate secret exists, check for required data fields if !isCheCASecretValid(cheTLSSelfSignedCertificateSecret) { - logrus.Infof("Che self-signed certificate secret \"%s\" is invalid. Recrating...", deploy.CheTLSSelfSignedCertificateSecretName) + logrus.Infof("Che self-signed certificate secret \"%s\" is invalid. Recrating...", constants.DefaultSelfSignedCertificateSecretName) // Che CA self-signed certificate secret is invalid, delete it if err = ctx.ClusterAPI.Client.Delete(context.TODO(), cheTLSSelfSignedCertificateSecret); err != nil { - logrus.Errorf("Error deleting Che self-signed certificate secret \"%s\": %v", deploy.CheTLSSelfSignedCertificateSecretName, err) + logrus.Errorf("Error deleting Che self-signed certificate secret \"%s\": %v", constants.DefaultSelfSignedCertificateSecretName, err) return reconcile.Result{RequeueAfter: time.Second}, err } // Also delete Che TLS as the certificates should be created together @@ -466,11 +464,11 @@ func K8sHandleCheTLSSecrets(ctx *deploy.DeployContext) (reconcile.Result, error) if cheTLSSelfSignedCertificateSecret.ObjectMeta.OwnerReferences == nil { // Set owner Che cluster as Che TLS secret owner if err := controllerutil.SetControllerReference(ctx.CheCluster, cheTLSSelfSignedCertificateSecret, ctx.ClusterAPI.Scheme); err != nil { - logrus.Errorf("Failed to set owner for Che self-signed certificate secret \"%s\". Error: %s", deploy.CheTLSSelfSignedCertificateSecretName, err) + logrus.Errorf("Failed to set owner for Che self-signed certificate secret \"%s\". Error: %s", constants.DefaultSelfSignedCertificateSecretName, err) return reconcile.Result{RequeueAfter: time.Second}, err } if err := ctx.ClusterAPI.Client.Update(context.TODO(), cheTLSSelfSignedCertificateSecret); err != nil { - logrus.Errorf("Failed to update owner for Che self-signed certificate secret \"%s\". Error: %s", deploy.CheTLSSelfSignedCertificateSecretName, err) + logrus.Errorf("Failed to update owner for Che self-signed certificate secret \"%s\". Error: %s", constants.DefaultSelfSignedCertificateSecretName, err) return reconcile.Result{RequeueAfter: time.Second}, err } } @@ -497,8 +495,9 @@ func isCheCASecretValid(cheCASelfSignedCertificateSecret *corev1.Secret) bool { return true } -func deleteJob(ctx *deploy.DeployContext, job *batchv1.Job) { - names := util.K8sclient.GetPodsByComponent(CheTLSJobComponentName, ctx.CheCluster.Namespace) +func deleteJob(ctx *chetypes.DeployContext, job *batchv1.Job) { + k8sHelper := k8shelper.New() + names := k8sHelper.GetPodsByComponent(CheTLSJobComponentName, ctx.CheCluster.Namespace) for _, podName := range names { pod := &corev1.Pod{} err := ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: podName, Namespace: ctx.CheCluster.Namespace}, pod) @@ -520,8 +519,8 @@ func deleteJob(ctx *deploy.DeployContext, job *batchv1.Job) { func GetCACertsConfigMaps(client k8sclient.Client, namespace string) ([]corev1.ConfigMap, error) { CACertsConfigMapList := &corev1.ConfigMapList{} - caBundleLabelSelectorRequirement, _ := labels.NewRequirement(deploy.KubernetesComponentLabelKey, selection.Equals, []string{CheCACertsConfigMapLabelValue}) - cheComponetLabelSelectorRequirement, _ := labels.NewRequirement(deploy.KubernetesPartOfLabelKey, selection.Equals, []string{deploy.CheEclipseOrg}) + caBundleLabelSelectorRequirement, _ := labels.NewRequirement(constants.KubernetesComponentLabelKey, selection.Equals, []string{CheCACertsConfigMapLabelValue}) + cheComponetLabelSelectorRequirement, _ := labels.NewRequirement(constants.KubernetesPartOfLabelKey, selection.Equals, []string{constants.CheEclipseOrg}) listOptions := &k8sclient.ListOptions{ LabelSelector: labels.NewSelector().Add(*cheComponetLabelSelectorRequirement).Add(*caBundleLabelSelectorRequirement), Namespace: namespace, @@ -534,7 +533,7 @@ func GetCACertsConfigMaps(client k8sclient.Client, namespace string) ([]corev1.C } // GetAdditionalCACertsConfigMapVersion returns revision of merged additional CA certs config map -func GetAdditionalCACertsConfigMapVersion(ctx *deploy.DeployContext) string { +func GetAdditionalCACertsConfigMapVersion(ctx *chetypes.DeployContext) string { trustStoreConfigMap := &corev1.ConfigMap{} exists, _ := deploy.GetNamespacedObject(ctx, CheAllCACertsConfigMapName, trustStoreConfigMap) if exists { @@ -547,7 +546,7 @@ func GetAdditionalCACertsConfigMapVersion(ctx *deploy.DeployContext) string { // CreateTLSSecretFromEndpoint creates TLS secret with given name which contains certificates obtained from the given url. // If the url is empty string, then cluster default certificate will be obtained. // Does nothing if secret with given name already exists. -func CreateTLSSecretFromEndpoint(ctx *deploy.DeployContext, url string, name string) (err error) { +func CreateTLSSecretFromEndpoint(ctx *chetypes.DeployContext, url string, name string) (err error) { secret := &corev1.Secret{} if err := ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: ctx.CheCluster.Namespace}, secret); err != nil && errors.IsNotFound(err) { crtBytes, err := GetEndpointTLSCrtBytes(ctx, url) @@ -565,7 +564,7 @@ func CreateTLSSecretFromEndpoint(ctx *deploy.DeployContext, url string, name str return nil } -func SyncTLSRoleToCluster(ctx *deploy.DeployContext) (bool, error) { +func SyncTLSRoleToCluster(ctx *chetypes.DeployContext) (bool, error) { tlsPolicyRule := []rbac.PolicyRule{ { APIGroups: []string{ diff --git a/pkg/util/k8s_helpers_test.go b/pkg/util/k8s_helpers_test.go deleted file mode 100644 index 0ace8180c..000000000 --- a/pkg/util/k8s_helpers_test.go +++ /dev/null @@ -1,62 +0,0 @@ -// -// Copyright (c) 2019-2021 Red Hat, Inc. -// This program and the accompanying materials are made -// available under the terms of the Eclipse Public License 2.0 -// which is available at https://www.eclipse.org/legal/epl-2.0/ -// -// SPDX-License-Identifier: EPL-2.0 -// -// Contributors: -// Red Hat, Inc. - initial API and implementation -// -package util - -import ( - "context" - "testing" - - "github.com/sirupsen/logrus" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes/fake" -) - -var ( - fakeK8s = fakeClientSet() - namespace = "eclipse-che" -) - -func fakeClientSet() *k8s { - client := k8s{} - client.clientset = fake.NewSimpleClientset() - return &client -} - -func TestGetDeploymentPod(t *testing.T) { - - // create a fake pod - _, err := fakeK8s.clientset.CoreV1().Pods(namespace).Create(context.TODO(), &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "fake-pod", - Namespace: namespace, - Labels: map[string]string{ - "component": "postgres", - }, - }, - }, metav1.CreateOptions{}) - if err != nil { - panic(err) - } - pod, err := fakeK8s.GetDeploymentPod("postgres", namespace) - if err != nil { - t.Errorf("Failed to det deployment pod: %s", err) - } - if len(pod) == 0 { - t.Errorf("Test failed. No pods found by label") - } - logrus.Infof("Test passed. Pod %s found", pod) -} diff --git a/pkg/util/namespace_provider.go b/pkg/util/namespace_provider.go deleted file mode 100644 index 993295d31..000000000 --- a/pkg/util/namespace_provider.go +++ /dev/null @@ -1,45 +0,0 @@ -// -// Copyright (c) 2012-2021 Red Hat, Inc. -// This program and the accompanying materials are made -// available under the terms of the Eclipse Public License 2.0 -// which is available at https://www.eclipse.org/legal/epl-2.0/ -// -// SPDX-License-Identifier: EPL-2.0 -// -// Contributors: -// Red Hat, Inc. - initial API and implementation -// -package util - -import ( - "errors" - "io/ioutil" - "os" - - "github.com/sirupsen/logrus" -) - -const ( - namespaceFile = "/var/run/secrets/kubernetes.io/serviceaccount/namespace" -) - -var operator_namespace string - -func readNamespace() string { - nsBytes, err := ioutil.ReadFile(namespaceFile) - if err != nil { - if errors.Is(err, os.ErrNotExist) { - return "" - } - logrus.Fatal("Failed to get operator namespace", err) - } - return string(nsBytes) -} - -// GetCheOperatorNamespace returns namespace for current Eclipse Che operator. -func GetCheOperatorNamespace() string { - if operator_namespace == "" && !IsTestMode() { - operator_namespace = readNamespace() - } - return operator_namespace -} diff --git a/pkg/util/test_util.go b/pkg/util/test_util.go deleted file mode 100644 index ea58d71ff..000000000 --- a/pkg/util/test_util.go +++ /dev/null @@ -1,116 +0,0 @@ -// -// Copyright (c) 2019-2021 Red Hat, Inc. -// This program and the accompanying materials are made -// available under the terms of the Eclipse Public License 2.0 -// which is available at https://www.eclipse.org/legal/epl-2.0/ -// -// SPDX-License-Identifier: EPL-2.0 -// -// Contributors: -// Red Hat, Inc. - initial API and implementation -// -package util - -import ( - "context" - "testing" - - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -type TestExpectedResources struct { - MemoryLimit string - MemoryRequest string - CpuRequest string - CpuLimit string -} - -func CompareResources(actualDeployment *appsv1.Deployment, expected TestExpectedResources, t *testing.T) { - container := &actualDeployment.Spec.Template.Spec.Containers[0] - compareQuantity( - "Memory limits", - container.Resources.Limits.Memory(), - expected.MemoryLimit, - t, - ) - - compareQuantity( - "Memory requests", - container.Resources.Requests.Memory(), - expected.MemoryRequest, - t, - ) - - compareQuantity( - "CPU limits", - container.Resources.Limits.Cpu(), - expected.CpuLimit, - t, - ) - - compareQuantity( - "CPU requests", - container.Resources.Requests.Cpu(), - expected.CpuRequest, - t, - ) -} - -func ValidateSecurityContext(actualDeployment *appsv1.Deployment, t *testing.T) { - if actualDeployment.Spec.Template.Spec.Containers[0].SecurityContext.Capabilities.Drop[0] != "ALL" { - t.Error("Deployment doesn't contain 'Capabilities Drop ALL' in a SecurityContext") - } -} - -func compareQuantity(resource string, actualQuantity *resource.Quantity, expected string, t *testing.T) { - expectedQuantity := GetResourceQuantity(expected, expected) - if !actualQuantity.Equal(expectedQuantity) { - t.Errorf("%s: expected %s, actual %s", resource, expectedQuantity.String(), actualQuantity.String()) - } -} - -func ValidateContainData(actualData map[string]string, expectedData map[string]string, t *testing.T) { - for k, v := range expectedData { - actualValue, exists := actualData[k] - if exists { - if actualValue != v { - t.Errorf("Key '%s', actual: '%s', expected: '%s'", k, actualValue, v) - } - } else if v != "" { - t.Errorf("Key '%s' does not exists, expected value: '%s'", k, v) - } - } -} - -func FindVolume(volumes []corev1.Volume, name string) corev1.Volume { - for _, volume := range volumes { - if volume.Name == name { - return volume - } - } - - return corev1.Volume{} -} - -func FindVolumeMount(volumes []corev1.VolumeMount, name string) corev1.VolumeMount { - for _, volumeMount := range volumes { - if volumeMount.Name == name { - return volumeMount - } - } - - return corev1.VolumeMount{} -} - -func IsObjectExists(client client.Client, key types.NamespacedName, blueprint client.Object) bool { - err := client.Get(context.TODO(), key, blueprint) - if err != nil { - return false - } - - return true -} diff --git a/pkg/util/util.go b/pkg/util/util.go deleted file mode 100644 index fd64f7a63..000000000 --- a/pkg/util/util.go +++ /dev/null @@ -1,412 +0,0 @@ -// -// Copyright (c) 2019-2021 Red Hat, Inc. -// This program and the accompanying materials are made -// available under the terms of the Eclipse Public License 2.0 -// which is available at https://www.eclipse.org/legal/epl-2.0/ -// -// SPDX-License-Identifier: EPL-2.0 -// -// Contributors: -// Red Hat, Inc. - initial API and implementation -// -package util - -import ( - "bytes" - "context" - "crypto/sha256" - "encoding/base64" - "fmt" - "io/ioutil" - "math/rand" - "os" - "regexp" - "runtime" - "sort" - "strconv" - "strings" - "time" - - orgv1 "github.com/eclipse-che/che-operator/api/v1" - routev1 "github.com/openshift/api/route/v1" - "github.com/sirupsen/logrus" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - k8sErrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/discovery" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/config" - "sigs.k8s.io/yaml" -) - -var ( - k8sclient = GetK8Client() - IsOpenShift, IsOpenShift4, _ = DetectOpenShift() -) - -func ContainsString(slice []string, s string) bool { - for _, item := range slice { - if item == s { - return true - } - } - return false -} - -func DoRemoveString(slice []string, s string) (result []string) { - for _, item := range slice { - if item == s { - continue - } - result = append(result, item) - } - return -} - -func GeneratePasswd(stringLength int) (passwd string) { - rand.Seed(time.Now().UnixNano()) - chars := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ" + - "abcdefghijklmnopqrstuvwxyz" + - "0123456789") - length := stringLength - buf := make([]rune, length) - for i := range buf { - buf[i] = chars[rand.Intn(len(chars))] - } - passwd = string(buf) - return passwd -} - -func MapToKeyValuePairs(m map[string]string) string { - buff := new(bytes.Buffer) - keys := make([]string, 0, len(m)) - - for key := range m { - keys = append(keys, key) - } - - sort.Strings(keys) //sort keys alphabetically - - for _, key := range keys { - fmt.Fprintf(buff, "%s=%s,", key, m[key]) - } - return strings.TrimSuffix(buff.String(), ",") -} - -func DetectOpenShift() (isOpenshift bool, isOpenshift4 bool, anError error) { - tests := IsTestMode() - if tests { - openshiftVersionEnv := os.Getenv("OPENSHIFT_VERSION") - openshiftVersion, err := strconv.ParseInt(openshiftVersionEnv, 0, 64) - if err == nil && openshiftVersion == 4 { - return true, true, nil - } - return true, false, nil - } - - apiGroups, err := getApiList() - if err != nil { - return false, false, err - } - for _, apiGroup := range apiGroups { - if apiGroup.Name == "route.openshift.io" { - isOpenshift = true - } - if apiGroup.Name == "config.openshift.io" { - isOpenshift4 = true - } - } - - return isOpenshift, isOpenshift4, nil -} - -func getDiscoveryClient() (*discovery.DiscoveryClient, error) { - kubeconfig, err := config.GetConfig() - if err != nil { - return nil, err - } - return discovery.NewDiscoveryClientForConfig(kubeconfig) -} - -func getApiList() ([]metav1.APIGroup, error) { - discoveryClient, err := getDiscoveryClient() - if err != nil { - return nil, err - } - apiList, err := discoveryClient.ServerGroups() - if err != nil { - return nil, err - } - return apiList.Groups, nil -} - -func HasK8SResourceObject(discoveryClient discovery.DiscoveryInterface, resourceName string) bool { - _, resourceList, err := discoveryClient.ServerGroupsAndResources() - if err != nil { - return false - } - - return HasAPIResourceNameInList(resourceName, resourceList) -} - -func HasAPIResourceNameInList(name string, resources []*metav1.APIResourceList) bool { - for _, l := range resources { - for _, r := range l.APIResources { - if r.Name == name { - return true - } - } - } - - return false -} - -func GetValue(key string, defaultValue string) (value string) { - value = key - if len(key) < 1 { - value = defaultValue - } - return value -} - -func GetMapValue(value map[string]string, defaultValue map[string]string) map[string]string { - ret := value - if len(value) < 1 { - ret = defaultValue - } - - return ret -} - -func MergeMaps(first map[string]string, second map[string]string) map[string]string { - ret := make(map[string]string) - for k, v := range first { - ret[k] = v - } - - for k, v := range second { - ret[k] = v - } - - return ret -} - -func IsTestMode() (isTesting bool) { - testMode := os.Getenv("MOCK_API") - if len(testMode) == 0 { - return false - } - return true -} - -func GetRouterCanonicalHostname(client client.Client, namespace string) (string, error) { - testRouteYaml, err := GetTestRouteYaml(client, namespace) - if err != nil { - return "", err - } - return testRouteYaml.Status.Ingress[0].RouterCanonicalHostname, nil -} - -// GetTestRouteYaml creates test route and returns its spec. -func GetTestRouteYaml(client client.Client, namespace string) (*routev1.Route, error) { - // Create test route to get the info - routeSpec := &routev1.Route{ - TypeMeta: metav1.TypeMeta{ - Kind: "Route", - APIVersion: routev1.SchemeGroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "canonical-hostname-route-name", - Namespace: namespace, - }, - Spec: routev1.RouteSpec{ - To: routev1.RouteTargetReference{ - Kind: "Service", - Name: "canonical-hostname-route-nonexisting-service", - }, - Port: &routev1.RoutePort{ - TargetPort: intstr.IntOrString{ - Type: intstr.Int, - IntVal: 8080, - }, - }, - }, - } - - if err := client.Create(context.TODO(), routeSpec); err != nil { - if !k8sErrors.IsAlreadyExists(err) { - return nil, err - } - } - - // Schedule test route cleanup after the job done. - defer func() { - if err := client.Delete(context.TODO(), routeSpec); err != nil { - logrus.Errorf("Failed to delete test route %s: %s", routeSpec.Name, err) - } - }() - - // Wait till the route is ready - route := &routev1.Route{} - errCount := 0 - for { - time.Sleep(time.Duration(1) * time.Second) - - routeNsName := types.NamespacedName{Name: routeSpec.Name, Namespace: namespace} - err := client.Get(context.TODO(), routeNsName, route) - if err == nil { - return route, nil - } else if !k8sErrors.IsNotFound(err) { - errCount++ - if errCount > 10 { - return nil, err - } - } else { - // Reset counter as got not found error - errCount = 0 - } - } -} - -func GetDeploymentEnv(deployment *appsv1.Deployment, key string) (value string) { - env := deployment.Spec.Template.Spec.Containers[0].Env - for i := range env { - name := env[i].Name - if name == key { - value = env[i].Value - break - } - } - return value -} - -func GetDeploymentEnvVarSource(deployment *appsv1.Deployment, key string) (valueFrom *corev1.EnvVarSource) { - env := deployment.Spec.Template.Spec.Containers[0].Env - for i := range env { - name := env[i].Name - if name == key { - valueFrom = env[i].ValueFrom - break - } - } - return valueFrom -} - -func GetEnvByRegExp(regExp string) []corev1.EnvVar { - var env []corev1.EnvVar - for _, e := range os.Environ() { - pair := strings.SplitN(e, "=", 2) - envName := pair[0] - rxp := regexp.MustCompile(regExp) - if rxp.MatchString(envName) { - envName = GetArchitectureDependentEnv(envName) - env = append(env, corev1.EnvVar{Name: envName, Value: pair[1]}) - } - } - return env -} - -// GetArchitectureDependentEnv returns environment variable dependending on architecture -// by adding "_" suffix. If variable is not set then the default will be return. -func GetArchitectureDependentEnv(env string) string { - archEnv := env + "_" + runtime.GOARCH - if _, ok := os.LookupEnv(archEnv); ok { - return archEnv - } - - return env -} - -// GetImageNameAndTag returns the image repository and tag name from the provided image -// -// Referenced from https://github.com/che-incubator/chectl/blob/main/src/util.ts -func GetImageNameAndTag(image string) (string, string) { - var imageName, imageTag string - if strings.Contains(image, "@") { - // Image is referenced via a digest - index := strings.Index(image, "@") - imageName = image[:index] - imageTag = image[index+1:] - } else { - // Image is referenced via a tag - lastColonIndex := strings.LastIndex(image, ":") - if lastColonIndex == -1 { - imageName = image - imageTag = "latest" - } else { - beforeLastColon := image[:lastColonIndex] - afterLastColon := image[lastColonIndex+1:] - if strings.Contains(afterLastColon, "/") { - // The colon is for registry port and not for a tag - imageName = image - imageTag = "latest" - } else { - // The colon separates image name from the tag - imageName = beforeLastColon - imageTag = afterLastColon - } - } - } - return imageName, imageTag -} - -// NewBoolPointer returns `bool` pointer to value in the memory. -// Unfortunately golang hasn't got syntax to create `bool` pointer. -func NewBoolPointer(value bool) *bool { - variable := value - return &variable -} - -func GetResourceQuantity(value string, defaultValue string) resource.Quantity { - if value != "" { - return resource.MustParse(value) - } - return resource.MustParse(defaultValue) -} - -// Finds Env by a given name -func FindEnv(envs []corev1.EnvVar, name string) *corev1.EnvVar { - for _, env := range envs { - if env.Name == name { - return &env - } - } - - return nil -} - -func ReadObject(yamlFile string, obj interface{}) error { - data, err := ioutil.ReadFile(yamlFile) - if err != nil { - return err - } - - err = yaml.Unmarshal(data, obj) - if err != nil { - return err - } - - return nil -} - -func ComputeHash256(data []byte) string { - hasher := sha256.New() - hasher.Write(data) - return base64.URLEncoding.EncodeToString(hasher.Sum(nil)) -} - -func IsCheMultiUser(cheCluster *orgv1.CheCluster) bool { - return cheCluster.Spec.Server.CustomCheProperties == nil || cheCluster.Spec.Server.CustomCheProperties["CHE_MULTIUSER"] != "false" -} - -// ClearMetadata removes extra fields from given metadata. -// It is required to remove ResourceVersion in order to be able to apply the yaml again. -func ClearMetadata(objectMeta *metav1.ObjectMeta) { - objectMeta.ResourceVersion = "" - objectMeta.Finalizers = []string{} - objectMeta.ManagedFields = []metav1.ManagedFieldsEntry{} -}