feat: CheCluster API v2 (#1324)

* feat: CheCluster API v2

Signed-off-by: Anatolii Bazko <abazko@redhat.com>
pull/1399/head
Anatolii Bazko 2022-06-09 11:35:01 +03:00 committed by GitHub
parent af9c29d7cc
commit 0bc1049017
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
295 changed files with 27564 additions and 12727 deletions

View File

@ -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

View File

@ -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

View File

@ -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"}

View File

@ -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"
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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."

156
.github/bin/common.sh vendored
View File

@ -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}"
}

34
.github/bin/minikube/test-helm.sh vendored Executable file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"
}

View File

@ -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

View File

@ -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 }}

View File

@ -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 }}

View File

@ -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:

142
.vscode/launch.json vendored
View File

@ -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}",
}
]
}

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"cSpell.words": []
}

4
.vscode/tasks.json vendored
View File

@ -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": [

View File

@ -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

1190
Makefile

File diff suppressed because it is too large Load Diff

View File

@ -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"

View File

@ -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 <NAMESPACE>
$ kubectl apply -f config/samples/org_v2_checluster.yaml -n <NAMESPACE>
```
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=<SOME_PATH>/che-operator
$ make gen-chectl-tmpl TARGET=<SOME_PATH>
```
Execute chectl:
@ -105,7 +103,7 @@ $ chectl server:deploy --installer operator -p <PLATFORM> --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

View File

@ -1 +1,2 @@
operator-sdk: v1.9.2
opm: v1.15.1

View File

@ -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")
}

View File

@ -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")
}

View File

@ -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()
}

View File

@ -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
}
}

View File

@ -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
}

25
api/init_test.go Normal file
View File

@ -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")
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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"`

View File

@ -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

View File

@ -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() {}

633
api/v2/checluster_types.go Normal file
View File

@ -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-<che-namespace>.<domain>. The <che-namespace> 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:
// - `<che-namespace>-cheworkspaces-namespaces-clusterrole`
// - `<che-namespace>-cheworkspaces-clusterrole`
// - `<che-namespace>-cheworkspaces-devworkspace-clusterrole`
// where the <che-namespace> 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 `<username>`, `<userid>` and `<workspaceid>` placeholders, such as che-workspace-<username>.
// +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 `.<DOMAIN>`, 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 "<username>-" + os.Getenv("CHE_FLAVOR")
}

View File

@ -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!

View File

@ -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}

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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": "<username>-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": "<username>-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

View File

@ -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

View File

@ -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: []

View File

@ -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: []

View File

@ -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: []

View File

@ -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

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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==

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

File diff suppressed because one or more lines are too long

View File

@ -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

View File

@ -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

View File

@ -13,6 +13,4 @@
apiVersion: v1
kind: Namespace
metadata:
labels:
app: che-operator
name: eclipse-che

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 `.<DOMAIN>` 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 `<username>`, `<userid>` and `<workspaceid>` placeholders, such as che-workspace-<username>.
# In that case, a new namespace will be created for each user or workspace.
workspaceNamespaceDefault: "<username>-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

View File

@ -17,37 +17,17 @@ metadata:
namespace: eclipse-che
spec:
server:
cheClusterRoles: ''
cheWorkspaceClusterRole: ''
gitSelfSignedCert: false
proxyURL: ''
proxyPort: ''
proxySecret: ''
nonProxyHosts: ''
workspaceNamespaceDefault: "<username>-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

View File

@ -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: '<username>-che'
storage:
pvcStrategy: 'common'
components:
database:
externalDb: false
metrics:
enable: true
networking:
domain: ''
tlsSecretName: ''
auth:
identityProviderURL: ''
oAuthClientName: ''
oAuthSecret: ''

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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 <username> or <userid> 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 <username> or <userid> 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, "<username>") == -1 && strings.Index(workspaceNamespaceDefault, "<userid>") == -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
}

View File

@ -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{}
}

View File

@ -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)

View File

@ -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")
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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,

View File

@ -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{}

View File

@ -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
}

View File

@ -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
}

View File

@ -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")
}

View File

@ -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
}

Some files were not shown because too many files have changed in this diff Show More