Merge branch 'main' into dependabot/maven/org.apache.maven.plugins-maven-antrun-plugin-3.1.0

pull/484/head
Igor Vinokur 2023-10-12 16:02:49 +03:00 committed by GitHub
commit 2acd86d688
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
298 changed files with 9300 additions and 2161 deletions

View File

@ -0,0 +1,40 @@
#!/bin/bash
#
# Copyright (c) 2023 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
# Dockerfile to bootstrap build and test in openshift-ci
FROM registry.access.redhat.com/ubi9/nodejs-18:1
# hadolint ignore=DL3002
USER 0
# Install yq, kubectl, chectl cli used by olm/olm.sh script.
# hadolint ignore=DL3041
RUN dnf install -y -q --allowerasing --nobest nodejs-devel nodejs-libs psmisc python3-pip jq golang httpd-tools \
# already installed or installed as deps:
openssl openssl-devel ca-certificates make cmake cpp gcc gcc-c++ zlib zlib-devel brotli brotli-devel python3 nodejs-packaging && \
dnf update -y && dnf clean all && \
npm install -g yarn@1.22 npm@9 && \
echo -n "node version: "; node -v; \
echo -n "npm version: "; npm -v; \
echo -n "yarn version: "; yarn -v; \
go version; \
pip3 install --upgrade pip setuptools yq && \
# Install kubectl, chectl cli used by olm/olm.sh script.
curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && \
chmod +x ./kubectl && \
mv ./kubectl /usr/local/bin && \
bash <(curl -sL https://www.eclipse.org/che/chectl/) --channel=next && \
curl https://mirror.openshift.com/pub/openshift-v4/clients/ocp/latest-4.12/openshift-client-linux.tar.gz | tar xvzf - -C /usr/local/bin/ oc && \
chmod ug+x /usr/local/bin/oc
SHELL ["/bin/bash", "-c"]

319
.ci/openshift-ci/common.sh Normal file
View File

@ -0,0 +1,319 @@
#!/bin/bash
#
# Copyright (c) 2023 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
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
PR_IMAGE_TAG="pr-${PULL_NUMBER}"
export CHE_NAMESPACE=${CHE_NAMESPACE:-"eclipse-che"}
export CHE_SERVER_IMAGE=${CHE_SERVER_IMAGE:-"quay.io/eclipse/che-server:${PR_IMAGE_TAG}"}
export ARTIFACTS_DIR=${ARTIFACT_DIR:-"/tmp/artifacts"}
export CHE_FORWARDED_PORT="8081"
export OCP_ADMIN_USER_NAME=${OCP_ADMIN_USER_NAME:-"admin"}
export OCP_NON_ADMIN_USER_NAME=${OCP_NON_ADMIN_USER_NAME:-"user"}
export OCP_LOGIN_PASSWORD=${OCP_LOGIN_PASSWORD:-"passw"}
export ADMIN_CHE_NAMESPACE=${OCP_ADMIN_USER_NAME}"-che"
export USER_CHE_NAMESPACE=${OCP_NON_ADMIN_USER_NAME}"-che"
export GIT_PROVIDER_USERNAME=${GIT_PROVIDER_USERNAME:-"chepullreq1"}
export PUBLIC_REPO_WORKSPACE_NAME=${PUBLIC_REPO_WORKSPACE_NAME:-"public-repo-wksp-testname"}
export PRIVATE_REPO_WORKSPACE_NAME=${PRIVATE_REPO_WORKSPACE_NAME:-"private-repo-wksp-testname"}
export PUBLIC_PROJECT_NAME=${PUBLIC_PROJECT_NAME:-"public-repo"}
export PRIVATE_PROJECT_NAME=${PRIVATE_PROJECT_NAME:-"private-repo"}
export YAML_FILE_NAME=${YAML_FILE_NAME:-"devfile.yaml"}
provisionOpenShiftOAuthUser() {
echo -e "[INFO] Provisioning Openshift OAuth user"
htpasswd -c -B -b users.htpasswd ${OCP_ADMIN_USER_NAME} ${OCP_LOGIN_PASSWORD}
htpasswd -b users.htpasswd ${OCP_NON_ADMIN_USER_NAME} ${OCP_LOGIN_PASSWORD}
oc create secret generic htpass-secret --from-file=htpasswd="users.htpasswd" -n openshift-config
oc apply -f ".ci/openshift-ci/htpasswdProvider.yaml"
oc adm policy add-cluster-role-to-user cluster-admin ${OCP_ADMIN_USER_NAME}
echo -e "[INFO] Waiting for htpasswd auth to be working up to 5 minutes"
CURRENT_TIME=$(date +%s)
ENDTIME=$((CURRENT_TIME + 300))
while [ "$(date +%s)" -lt $ENDTIME ]; do
if oc login -u=${OCP_ADMIN_USER_NAME} -p=${OCP_LOGIN_PASSWORD} --insecure-skip-tls-verify=false; then
break
fi
sleep 10
done
}
createCustomResourcesFile() {
cat > custom-resources.yaml <<-END
apiVersion: org.eclipse.che/v2
spec:
devEnvironments:
maxNumberOfRunningWorkspacesPerUser: 10000
END
echo "Generated custom resources file"
cat custom-resources.yaml
}
deployChe() {
chectl server:deploy --cheimage=$CHE_SERVER_IMAGE \
--che-operator-cr-patch-yaml=custom-resources.yaml \
--platform=openshift \
--telemetry=off \
--batch
}
# this command starts port forwarding between the local machine and the che-host service in the OpenShift cluster.
forwardPortToService() {
oc port-forward service/che-host ${CHE_FORWARDED_PORT}:8080 -n ${CHE_NAMESPACE} &
sleep 3s
}
killProcessByPort() {
fuser -k ${CHE_FORWARDED_PORT}/tcp
}
requestFactoryResolverGitRepoUrl() {
GIT_REPO_URL=$1
CLUSTER_ACCESS_TOKEN=$(oc whoami -t)
curl -i -X 'POST' \
http://localhost:${CHE_FORWARDED_PORT}/api/factory/resolver \
-H 'accept: */*' \
-H "Authorization: Bearer ${CLUSTER_ACCESS_TOKEN}" \
-H 'Content-Type: application/json' \
-d '{
"url": "'${GIT_REPO_URL}'"
}'
}
# check that factory resolver returns correct value without any PAT/OAuth setup
testFactoryResolverNoPatOAuth() {
echo "[INFO] Check factory resolver for public repository with NO PAT/OAuth setup"
testFactoryResolverResponse $1 200
echo "[INFO] Check factory resolver for private repository with NO PAT/OAuth setup"
testFactoryResolverResponse $2 400
}
# check that factory resolver returns correct value with PAT/OAuth setup
testFactoryResolverWithPatOAuth() {
echo "[INFO] Check factory resolver for public repository with PAT/OAuth setup"
testFactoryResolverResponse $1 200
echo "[INFO] Check factory resolver for private repository with PAT/OAuth setup"
testFactoryResolverResponse $2 200
}
testFactoryResolverResponse() {
URL=$1
RESPONSE_CODE=$2
echo "[INFO] Check factory resolver"
if [ "$(requestFactoryResolverGitRepoUrl ${URL} | grep "HTTP/1.1 ${RESPONSE_CODE}")" ]; then
echo "[INFO] Factory resolver returned 'HTTP/1.1 ${RESPONSE_CODE}' status code. Expected client side response."
else
echo "[ERROR] Factory resolver returned wrong status code. Expected: HTTP/1.1 ${RESPONSE_CODE}."
exit 1
fi
}
requestProvisionNamespace() {
CLUSTER_ACCESS_TOKEN=$(oc whoami -t)
curl -i -X 'POST' \
http://localhost:${CHE_FORWARDED_PORT}/api/kubernetes/namespace/provision \
-H 'accept: application/json' \
-H "Authorization: Bearer ${CLUSTER_ACCESS_TOKEN}" \
-d ''
}
initUserNamespace() {
OCP_USER_NAME=$1
echo "[INFO] Initialize user namespace"
oc login -u=${OCP_USER_NAME} -p=${OCP_LOGIN_PASSWORD} --insecure-skip-tls-verify=false
if [ "$(requestProvisionNamespace | grep "HTTP/1.1 200")" ]; then
echo "[INFO] Request provision user namespace returned 'HTTP/1.1 200' status code."
else
echo "[ERROR] Request provision user namespace returned wrong status code. Expected: HTTP/1.1 200"
exit 1
fi
}
setupPersonalAccessToken() {
GIT_PROVIDER_TYPE=$1
GIT_PROVIDER_URL=$2
GIT_PROVIDER_PAT=$3
echo "[INFO] Setup Personal Access Token Secret"
oc project ${USER_CHE_NAMESPACE}
CHE_USER_ID=$(oc get secret user-profile -o jsonpath='{.data.id}' | base64 -d)
ENCODED_PAT=$(echo -n ${GIT_PROVIDER_PAT} | base64)
cat .ci/openshift-ci/pat-secret.yaml > pat-secret.yaml
# patch the pat-secret.yaml file
sed -i "s#che-user-id#${CHE_USER_ID}#g" pat-secret.yaml
sed -i "s#git-provider-name#${GIT_PROVIDER_TYPE}#g" pat-secret.yaml
sed -i "s#git-provider-url#${GIT_PROVIDER_URL}#g" pat-secret.yaml
sed -i "s#encoded-access-token#${ENCODED_PAT}#g" pat-secret.yaml
if [ "${GIT_PROVIDER_TYPE}" == "azure-devops" ]; then
sed -i "s#''#${GIT_PROVIDER_USERNAME}#g" pat-secret.yaml
fi
cat pat-secret.yaml
oc apply -f pat-secret.yaml -n ${USER_CHE_NAMESPACE}
}
setupSSHKeyPairs() {
GIT_PRIVATE_KEY=$1
GIT_PUBLIC_KEY=$2
echo "[INFO] Setup SSH Key Pairs Secret"
oc project ${USER_CHE_NAMESPACE}
ENCODED_GIT_PRIVATE_KEY=$(echo "${GIT_PRIVATE_KEY}" | base64 -w 0)
ENCODED_GIT_PUBLIC_KEY=$(echo "${GIT_PUBLIC_KEY}" | base64 -w 0)
cat .ci/openshift-ci/ssh-secret.yaml > ssh-secret.yaml
# patch the ssh-secret.yaml file
sed -i "s#ssh_private_key#${ENCODED_GIT_PRIVATE_KEY}#g" ssh-secret.yaml
sed -i "s#ssh_public_key#${ENCODED_GIT_PUBLIC_KEY}#g" ssh-secret.yaml
cat ssh-secret.yaml
oc apply -f ssh-secret.yaml -n ${USER_CHE_NAMESPACE}
}
runTestWorkspaceWithGitRepoUrl() {
WS_NAME=$1
PROJECT_NAME=$2
GIT_REPO_URL=$3
OCP_USER_NAMESPACE=$4
oc project ${OCP_USER_NAMESPACE}
cat .ci/openshift-ci/devworkspace-test.yaml > devworkspace-test.yaml
# patch the devworkspace-test.yaml file
sed -i "s#ws-name#${WS_NAME}#g" devworkspace-test.yaml
sed -i "s#project-name#${PROJECT_NAME}#g" devworkspace-test.yaml
sed -i "s#git-repo-url#${GIT_REPO_URL}#g" devworkspace-test.yaml
cat devworkspace-test.yaml
oc apply -f devworkspace-test.yaml -n ${OCP_USER_NAMESPACE}
oc wait -n ${OCP_USER_NAMESPACE} --for=condition=Ready dw ${WS_NAME} --timeout=360s
echo "[INFO] Test workspace is run"
}
testProjectIsCloned() {
PROJECT_NAME=$1
OCP_USER_NAMESPACE=$2
WORKSPACE_POD_NAME=$(oc get pods -n ${OCP_USER_NAMESPACE} | grep workspace | awk '{print $1}')
if oc exec -it -n ${OCP_USER_NAMESPACE} ${WORKSPACE_POD_NAME} -- test -f /projects/${PROJECT_NAME}/${YAML_FILE_NAME}; then
echo "[INFO] Project file /projects/${PROJECT_NAME}/${YAML_FILE_NAME} exists."
else
echo "[INFO] Project file /projects/${PROJECT_NAME}/${YAML_FILE_NAME} is absent."
return 1
fi
}
testGitCredentialsData() {
OCP_USER_NAMESPACE=$1
GIT_PROVIDER_PAT=$2
GIT_PROVIDER_URL=$3
echo "[INFO] Check the 'git credentials' is in a workspace"
hostName="${GIT_PROVIDER_URL#https://}"
if [ "${GIT_PROVIDER_TYPE}" == "azure-devops" ]; then
userName="username"
else
userName=${GIT_PROVIDER_USERNAME}
fi
gitCredentials="https://${userName}:${GIT_PROVIDER_PAT}@${hostName}"
WORKSPACE_POD_NAME=$(oc get pods -n ${OCP_USER_NAMESPACE} | grep workspace | awk '{print $1}')
if oc exec -it -n ${OCP_USER_NAMESPACE} ${WORKSPACE_POD_NAME} -- cat /.git-credentials/credentials | grep -q ${gitCredentials}; then
echo "[INFO] Git credentials file '/.git-credentials/credentials' exists and has the expected content."
else
echo "[ERROR] Git credentials file '/.git-credentials/credentials' does not exist or has incorrect content."
exit 1
fi
}
deleteTestWorkspace() {
WS_NAME=$1
OCP_USER_NAMESPACE=$2
oc delete dw ${WS_NAME} -n ${OCP_USER_NAMESPACE}
}
# Catch the finish of the job and write logs in artifacts.
catchFinish() {
local RESULT=$?
killProcessByPort
if [ "$RESULT" != "0" ]; then
set +e
collectEclipseCheLogs
set -e
fi
[[ "${RESULT}" != "0" ]] && echo "[ERROR] Job failed." || echo "[INFO] Job completed successfully."
exit $RESULT
}
collectEclipseCheLogs() {
mkdir -p ${ARTIFACTS_DIR}/che-logs
# Collect all Eclipse Che logs and cluster CR
chectl server:logs -n $CHE_NAMESPACE --directory ${ARTIFACTS_DIR}/che-logs --telemetry off
oc get checluster -o yaml -n $CHE_NAMESPACE > "${ARTIFACTS_DIR}/che-cluster.yaml"
}
testCloneGitRepoNoProjectExists() {
WS_NAME=$1
PROJECT_NAME=$2
GIT_REPO_URL=$3
OCP_USER_NAMESPACE=$4
runTestWorkspaceWithGitRepoUrl ${WS_NAME} ${PROJECT_NAME} ${GIT_REPO_URL} ${OCP_USER_NAMESPACE}
echo "[INFO] Check the private repository is NOT cloned with NO PAT/OAuth setup"
testProjectIsCloned ${PROJECT_NAME} ${OCP_USER_NAMESPACE} && \
{ echo "[ERROR] Project file /projects/${PROJECT_NAME}/${YAML_FILE_NAME} should NOT be present" && exit 1; }
echo "[INFO] Project file /projects/${PROJECT_NAME}/${YAML_FILE_NAME} is NOT present. This is EXPECTED"
}
# Test that the repository is cloned when PAT, OAuth or SSH is configured
testCloneGitRepoProjectShouldExists() {
WS_NAME=$1
PROJECT_NAME=$2
GIT_REPO_URL=$3
OCP_USER_NAMESPACE=$4
runTestWorkspaceWithGitRepoUrl ${WS_NAME} ${PROJECT_NAME} ${GIT_REPO_URL} ${OCP_USER_NAMESPACE}
echo "[INFO] Check the repository is cloned"
testProjectIsCloned ${PROJECT_NAME} ${OCP_USER_NAMESPACE} || \
{ echo "[ERROR] Project file /projects/${PROJECT_NAME}/${YAML_FILE_NAME} should be present." && exit 1; }
}
setupTestEnvironment() {
OCP_USER_NAME=$1
provisionOpenShiftOAuthUser
createCustomResourcesFile
deployChe
forwardPortToService
initUserNamespace ${OCP_USER_NAME}
}

View File

@ -0,0 +1,21 @@
kind: DevWorkspace
apiVersion: workspace.devfile.io/v1alpha2
metadata:
name: ws-name
spec:
started: true
template:
projects:
- name: project-name
git:
remotes:
origin: git-repo-url
contributions:
- name: che-code
uri: https://eclipse-che.github.io/che-plugin-registry/main/v3/plugins/che-incubator/che-code/latest/devfile.yaml
components:
- name: che-code-runtime-description
container:
env:
- name: CODE_HOST
value: 0.0.0.0

View File

@ -0,0 +1,12 @@
apiVersion: config.openshift.io/v1
kind: OAuth
metadata:
name: cluster
spec:
identityProviders:
- name: htpasswd
mappingMethod: claim
type: HTPasswd
htpasswd:
fileData:
name: htpass-secret

View File

@ -0,0 +1,15 @@
kind: Secret
apiVersion: v1
metadata:
name: personal-access-token
labels:
app.kubernetes.io/component: scm-personal-access-token
app.kubernetes.io/part-of: che.eclipse.org
annotations:
che.eclipse.org/che-userid: che-user-id
che.eclipse.org/scm-personal-access-token-name: git-provider-name
che.eclipse.org/scm-url: git-provider-url
che.eclipse.org/scm-organization: ''
data:
token: encoded-access-token
type: Opaque

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Secret
metadata:
name: git-ssh-key
annotations:
controller.devfile.io/mount-as: subpath
controller.devfile.io/mount-path: /etc/ssh/
labels:
controller.devfile.io/mount-to-devworkspace: "true"
controller.devfile.io/watch-secret: "true"
type: Opaque
data:
dwo_ssh_key: ssh_private_key
dwo_ssh_key.pub: ssh_public_key
ssh_config: aG9zdCAqCiAgSWRlbnRpdHlGaWxlIC9ldGMvc3NoL2R3b19zc2hfa2V5CiAgU3RyaWN0SG9zdEtleUNoZWNraW5nID0gbm8K

View File

@ -0,0 +1,36 @@
#!/bin/bash
#
# Copyright (c) 2023 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
#
# exit immediately when a command fails
set -ex
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
export PUBLIC_REPO_RAW_PATH_URL=${PUBLIC_REPO_RAW_PATH_URL:-"https://dev.azure.com/chepullreq1/che-pr-public/_apis/git/repositories/public-repo/items?path=/devfile.yaml"}
export PRIVATE_REPO_RAW_PATH_URL=${PRIVATE_REPO_URL:-"https://dev.azure.com/chepullreq1/che-pr-private/_apis/git/repositories/private-repo/items?path=/devfile.yaml"}
# import common test functions
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "${SCRIPT_DIR}"/common.sh
trap "catchFinish" EXIT SIGINT
setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME}
testFactoryResolverResponse ${PUBLIC_REPO_RAW_PATH_URL} 200
testFactoryResolverResponse ${PRIVATE_REPO_RAW_PATH_URL} 500
testCloneGitRepoNoProjectExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_RAW_PATH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
testCloneGitRepoNoProjectExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_RAW_PATH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}

View File

@ -0,0 +1,38 @@
#!/bin/bash
#
# Copyright (c) 2023 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
#
# exit immediately when a command fails
set -ex
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
export PUBLIC_REPO_SSH_URL=${PUBLIC_REPO_SSH_URL:-"git@ssh.dev.azure.com:v3/chepullreq1/che-pr-public/public-repo"}
export PRIVATE_REPO_SSH_URL=${PRIVATE_REPO_SSH_URL:-"git@ssh.dev.azure.com:v3/chepullreq1/che-pr-private/private-repo"}
# import common test functions
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "${SCRIPT_DIR}"/common.sh
trap "catchFinish" EXIT SIGINT
setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME}
setupSSHKeyPairs "${AZURE_PRIVATE_KEY}" "${AZURE_PUBLIC_KEY}"
testFactoryResolverResponse ${PUBLIC_REPO_SSH_URL} 200
testFactoryResolverResponse ${PRIVATE_REPO_SSH_URL} 500
testCloneGitRepoProjectShouldExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_SSH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
testCloneGitRepoProjectShouldExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_SSH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}

View File

@ -0,0 +1,34 @@
#!/bin/bash
#
# Copyright (c) 2023 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
#
# exit immediately when a command fails
set -ex
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
export PUBLIC_REPO_URL=${PUBLIC_REPO_URL:-"https://chepullreq1@dev.azure.com/chepullreq1/che-pr-public/_git/public-repo"}
export PRIVATE_REPO_URL=${PRIVATE_REPO_URL:-"https://dev.azure.com/chepullreq1/che-pr-private/_git/private-repo"}
# import common test functions
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "${SCRIPT_DIR}"/common.sh
trap "catchFinish" EXIT SIGINT
setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME}
# due to the issue https://github.com/eclipse/che/issues/22469
# testFactoryResolverNoPatOAuth ${PUBLIC_REPO_URL} ${PRIVATE_REPO_URL}
testCloneGitRepoProjectShouldExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
testCloneGitRepoNoProjectExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}

View File

@ -0,0 +1,57 @@
#!/bin/bash
#
# Copyright (c) 2023 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
#
# exit immediately when a command fails
set -ex
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
export PUBLIC_REPO_URL=${PUBLIC_REPO_URL:-"https://chepullreq1@dev.azure.com/chepullreq1/che-pr-public/_git/public-repo"}
export PRIVATE_REPO_URL=${PRIVATE_REPO_URL:-"https://dev.azure.com/chepullreq1/che-pr-private/_git/private-repo"}
export GIT_PROVIDER_TYPE=${GIT_PROVIDER_TYPE:-"azure-devops"}
export GIT_PROVIDER_URL=${GIT_PROVIDER_URL:-"https://dev.azure.com"}
export PRIVATE_REPO_SSH_URL=${PRIVATE_REPO_SSH_URL:-"git@ssh.dev.azure.com:v3/chepullreq1/che-pr-private/private-repo"}
export PRIVATE_REPO_RAW_PATH_URL=${PRIVATE_REPO_URL:-"https://dev.azure.com/chepullreq1/che-pr-private/_apis/git/repositories/private-repo/items?path=/devfile.yaml"}
# import common test functions
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "${SCRIPT_DIR}"/common.sh
trap "catchFinish" EXIT SIGINT
setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME}
setupPersonalAccessToken ${GIT_PROVIDER_TYPE} ${GIT_PROVIDER_URL} ${AZURE_PAT}
requestProvisionNamespace
testFactoryResolverWithPatOAuth ${PUBLIC_REPO_URL} ${PRIVATE_REPO_URL}
echo "[INFO] Check clone public repository with PAT setup"
testCloneGitRepoProjectShouldExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_URL} ${USER_CHE_NAMESPACE}
testGitCredentialsData ${USER_CHE_NAMESPACE} ${AZURE_PAT} ${GIT_PROVIDER_URL}
deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
echo "[INFO] Check clone private repository with PAT setup"
testCloneGitRepoProjectShouldExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_URL} ${USER_CHE_NAMESPACE}
testGitCredentialsData ${USER_CHE_NAMESPACE} ${AZURE_PAT} ${GIT_PROVIDER_URL}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
echo "[INFO] Check clone private repository by raw devfile URL with PAT setup"
testFactoryResolverResponse ${PRIVATE_REPO_RAW_PATH_URL} 200
testCloneGitRepoProjectShouldExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_RAW_PATH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
setupSSHKeyPairs "${AZURE_PRIVATE_KEY}" "${AZURE_PUBLIC_KEY}"
echo "[INFO] Check clone private repository by SSH URL with PAT setup"
testFactoryResolverResponse ${PRIVATE_REPO_SSH_URL} 200
testCloneGitRepoProjectShouldExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_SSH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}

View File

@ -0,0 +1,35 @@
#!/bin/bash
#
# Copyright (c) 2023 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
#
# exit immediately when a command fails
set -ex
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
export PUBLIC_REPO_RAW_PATH_URL=${PUBLIC_REPO_RAW_PATH_URL:-"https://bitbucket.org/chepullreq/public-repo/raw/746000bd63a54eaf8ea8aba9dfe6620e5c6c61d7/devfile.yaml"}
export PRIVATE_REPO_RAW_PATH_URL=${PRIVATE_REPO_URL:-"https://bitbucket.org/chepullreq/private-repo/raw/80b183d27c6c533462128b0c092208aad73b2906/devfile.yaml"}
# import common test functions
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "${SCRIPT_DIR}"/common.sh
trap "catchFinish" EXIT SIGINT
setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME}
testFactoryResolverNoPatOAuth ${PUBLIC_REPO_RAW_PATH_URL} ${PRIVATE_REPO_RAW_PATH_URL}
testCloneGitRepoProjectShouldExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_RAW_PATH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
testCloneGitRepoNoProjectExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_RAW_PATH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}

View File

@ -0,0 +1,36 @@
#!/bin/bash
#
# Copyright (c) 2023 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
#
# exit immediately when a command fails
set -ex
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
export PUBLIC_REPO_SSH_URL=${PUBLIC_REPO_SSH_URL:-"git@bitbucket.org:chepullreq/public-repo.git"}
export PRIVATE_REPO_SSH_URL=${PRIVATE_REPO_SSH_URL:-"git@bitbucket.org:chepullreq/private-repo.git"}
# import common test functions
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "${SCRIPT_DIR}"/common.sh
trap "catchFinish" EXIT SIGINT
setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME}
setupSSHKeyPairs "${BITBUCKET_PRIVATE_KEY}" "${BITBUCKET_PUBLIC_KEY}"
testFactoryResolverNoPatOAuth ${PUBLIC_REPO_SSH_URL} ${PRIVATE_REPO_SSH_URL}
testCloneGitRepoProjectShouldExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_SSH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
testCloneGitRepoProjectShouldExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_SSH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}

View File

@ -0,0 +1,33 @@
#!/bin/bash
#
# Copyright (c) 2023 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
#
# exit immediately when a command fails
set -ex
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
export PUBLIC_REPO_URL=${PUBLIC_REPO_URL:-"https://chepullreq1@bitbucket.org/chepullreq/public-repo.git"}
export PRIVATE_REPO_URL=${PRIVATE_REPO_URL:-"https://chepullreq1@bitbucket.org/chepullreq/private-repo.git"}
# import common test functions
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "${SCRIPT_DIR}"/common.sh
trap "catchFinish" EXIT SIGINT
setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME}
testFactoryResolverNoPatOAuth ${PUBLIC_REPO_URL} ${PRIVATE_REPO_URL}
testCloneGitRepoProjectShouldExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
testCloneGitRepoNoProjectExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}

View File

@ -0,0 +1,43 @@
#!/bin/bash
#
# Copyright (c) 2023 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
#
# exit immediately when a command fails
set -ex
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
export PUBLIC_REPO_SSH_URL=${PUBLIC_REPO_SSH_URL:-"git@gitea.com:chepullreq1/public-repo.git"}
export PRIVATE_REPO_SSH_URL=${PRIVATE_REPO_SSH_URL:-"git@gitea.com:chepullreq1/private-repo.git"}
export PUBLIC_REPO_RAW_PATH_URL=${PUBLIC_REPO_RAW_PATH_URL:-"https://gitea.com/chepullreq1/public-repo/raw/branch/main/devfile.yaml"}
# import common test functions
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "${SCRIPT_DIR}"/common.sh
trap "catchFinish" EXIT SIGINT
setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME}
setupSSHKeyPairs "${GITEA_PRIVATE_KEY}" "${GITEA_PUBLIC_KEY}"
testFactoryResolverResponse ${PUBLIC_REPO_SSH_URL} 200
testFactoryResolverResponse ${PRIVATE_REPO_SSH_URL} 200
testFactoryResolverResponse ${PUBLIC_REPO_RAW_PATH_URL} 200
testCloneGitRepoProjectShouldExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_SSH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
testCloneGitRepoNoProjectExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_RAW_PATH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
testCloneGitRepoProjectShouldExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_SSH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}

View File

@ -0,0 +1,31 @@
#!/bin/bash
#
# Copyright (c) 2023 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
#
# exit immediately when a command fails
set -ex
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
export PRIVATE_REPO_RAW_PATH_URL=${PRIVATE_REPO_RAW_PATH_URL:-"https://${GITEA_PAT}@gitea.com/chepullreq1/private-repo/raw/branch/main/devfile.yaml"}
# import common test functions
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "${SCRIPT_DIR}"/common.sh
trap "catchFinish" EXIT SIGINT
setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME}
testFactoryResolverResponse ${PRIVATE_REPO_RAW_PATH_URL} 200
testCloneGitRepoNoProjectExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_RAW_PATH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}

View File

@ -0,0 +1,36 @@
#!/bin/bash
#
# Copyright (c) 2023 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
#
# exit immediately when a command fails
set -ex
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
export PUBLIC_REPO_RAW_PATH_URL=${PUBLIC_REPO_RAW_PATH_URL:-"https://raw.githubusercontent.com/chepullreq1/public-repo/main/devfile.yaml"}
export PRIVATE_REPO_RAW_PATH_URL=${PRIVATE_REPO_URL:-"https://raw.githubusercontent.com/chepullreq1/private-repo/main/devfile.yaml"}
# import common test functions
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "${SCRIPT_DIR}"/common.sh
trap "catchFinish" EXIT SIGINT
setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME}
testFactoryResolverNoPatOAuth ${PUBLIC_REPO_RAW_PATH_URL} ${PRIVATE_REPO_RAW_PATH_URL}
testCloneGitRepoNoProjectExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_RAW_PATH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
testCloneGitRepoNoProjectExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_RAW_PATH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}

View File

@ -0,0 +1,36 @@
#!/bin/bash
#
# Copyright (c) 2023 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
#
# exit immediately when a command fails
set -ex
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
export PUBLIC_REPO_SSH_URL=${PUBLIC_REPO_SSH_URL:-"git@github.com:chepullreq1/public-repo.git"}
export PRIVATE_REPO_SSH_URL=${PRIVATE_REPO_SSH_URL:-"git@github.com:chepullreq1/private-repo.git"}
# import common test functions
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "${SCRIPT_DIR}"/common.sh
trap "catchFinish" EXIT SIGINT
setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME}
setupSSHKeyPairs "${GITHUB_PRIVATE_KEY}" "${GITHUB_PUBLIC_KEY}"
testFactoryResolverNoPatOAuth ${PUBLIC_REPO_SSH_URL} ${PRIVATE_REPO_SSH_URL}
testCloneGitRepoProjectShouldExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_SSH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
testCloneGitRepoProjectShouldExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_SSH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}

View File

@ -0,0 +1,33 @@
#!/bin/bash
#
# Copyright (c) 2023 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
#
# exit immediately when a command fails
set -ex
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
export PUBLIC_REPO_URL=${PUBLIC_REPO_URL:-"https://github.com/chepullreq1/public-repo.git"}
export PRIVATE_REPO_URL=${PRIVATE_REPO_URL:-"https://github.com/chepullreq1/private-repo.git"}
# import common test functions
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "${SCRIPT_DIR}"/common.sh
trap "catchFinish" EXIT SIGINT
setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME}
testFactoryResolverNoPatOAuth ${PUBLIC_REPO_URL} ${PRIVATE_REPO_URL}
testCloneGitRepoProjectShouldExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
testCloneGitRepoNoProjectExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}

View File

@ -0,0 +1,57 @@
#!/bin/bash
#
# Copyright (c) 2023 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
#
# exit immediately when a command fails
set -ex
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
export PUBLIC_REPO_URL=${PUBLIC_REPO_URL:-"https://github.com/chepullreq1/public-repo.git"}
export PRIVATE_REPO_URL=${PRIVATE_REPO_URL:-"https://github.com/chepullreq1/private-repo.git"}
export GIT_PROVIDER_TYPE=${GIT_PROVIDER_TYPE:-"github"}
export GIT_PROVIDER_URL=${GIT_PROVIDER_URL:-"https://github.com"}
export PRIVATE_REPO_RAW_PATH_URL=${PRIVATE_REPO_URL:-"https://raw.githubusercontent.com/chepullreq1/private-repo/main/devfile.yaml"}
export PRIVATE_REPO_SSH_URL=${PRIVATE_REPO_SSH_URL:-"git@github.com:chepullreq1/private-repo.git"}
# import common test functions
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "${SCRIPT_DIR}"/common.sh
trap "catchFinish" EXIT SIGINT
setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME}
setupPersonalAccessToken ${GIT_PROVIDER_TYPE} ${GIT_PROVIDER_URL} ${GITHUB_PAT}
requestProvisionNamespace
testFactoryResolverWithPatOAuth ${PUBLIC_REPO_URL} ${PRIVATE_REPO_URL}
echo "[INFO] Check clone public repository with PAT setup"
testCloneGitRepoProjectShouldExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_URL} ${USER_CHE_NAMESPACE}
testGitCredentialsData ${USER_CHE_NAMESPACE} ${GITHUB_PAT} ${GIT_PROVIDER_URL}
deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
echo "[INFO] Check clone private repository with PAT setup"
testCloneGitRepoProjectShouldExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_URL} ${USER_CHE_NAMESPACE}
testGitCredentialsData ${USER_CHE_NAMESPACE} ${GITHUB_PAT} ${GIT_PROVIDER_URL}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
echo "[INFO] Check clone private repository by raw devfile URL with PAT setup"
testFactoryResolverResponse ${PRIVATE_REPO_RAW_PATH_URL} 200
testCloneGitRepoProjectShouldExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_RAW_PATH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
setupSSHKeyPairs "${GITHUB_PRIVATE_KEY}" "${GITHUB_PUBLIC_KEY}"
echo "[INFO] Check clone private repository by SSH URL with PAT setup"
testFactoryResolverResponse ${PRIVATE_REPO_SSH_URL} 200
testCloneGitRepoProjectShouldExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_SSH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}

View File

@ -0,0 +1,35 @@
#!/bin/bash
#
# Copyright (c) 2023 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
#
# exit immediately when a command fails
set -ex
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
export PUBLIC_REPO_RAW_PATH_URL=${PUBLIC_REPO_RAW_PATH_URL:-"https://gitlab.com/chepullreq1/public-repo/-/raw/main/devfile.yaml"}
export PRIVATE_REPO_RAW_PATH_URL=${PRIVATE_REPO_URL:-"https://gitlab.com/chepullreq1/private-repo/-/raw/main/devfile.yaml"}
# import common test functions
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "${SCRIPT_DIR}"/common.sh
trap "catchFinish" EXIT SIGINT
setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME}
testFactoryResolverNoPatOAuth ${PUBLIC_REPO_RAW_PATH_URL} ${PRIVATE_REPO_RAW_PATH_URL}
testCloneGitRepoNoProjectExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_RAW_PATH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
testCloneGitRepoNoProjectExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_RAW_PATH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}

View File

@ -0,0 +1,37 @@
#!/bin/bash
#
# Copyright (c) 2023 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
#
# exit immediately when a command fails
set -ex
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
export PUBLIC_REPO_SSH_URL=${PUBLIC_REPO_SSH_URL:-"git@gitlab.com:chepullreq1/public-repo.git"}
export PRIVATE_REPO_SSH_URL=${PRIVATE_REPO_SSH_URL:-"git@gitlab.com:chepullreq1/private-repo.git"}
# import common test functions
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "${SCRIPT_DIR}"/common.sh
trap "catchFinish" EXIT SIGINT
setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME}
setupSSHKeyPairs "${GITLAB_PRIVATE_KEY}" "${GITLAB_PUBLIC_KEY}"
testFactoryResolverNoPatOAuth ${PUBLIC_REPO_SSH_URL} ${PRIVATE_REPO_SSH_URL}
testCloneGitRepoProjectShouldExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_SSH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
testCloneGitRepoProjectShouldExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_SSH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}

View File

@ -0,0 +1,33 @@
#!/bin/bash
#
# Copyright (c) 2023 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
#
# exit immediately when a command fails
set -ex
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
export PUBLIC_REPO_URL=${PUBLIC_REPO_URL:-"https://gitlab.com/chepullreq1/public-repo.git"}
export PRIVATE_REPO_URL=${PRIVATE_REPO_URL:-"https://gitlab.com/chepullreq1/private-repo.git"}
# import common test functions
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "${SCRIPT_DIR}"/common.sh
trap "catchFinish" EXIT SIGINT
setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME}
testFactoryResolverNoPatOAuth ${PUBLIC_REPO_URL} ${PRIVATE_REPO_URL}
testCloneGitRepoProjectShouldExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
testCloneGitRepoNoProjectExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}

View File

@ -0,0 +1,57 @@
#!/bin/bash
#
# Copyright (c) 2023 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
#
# exit immediately when a command fails
set -ex
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
export PUBLIC_REPO_URL=${PUBLIC_REPO_URL:-"https://gitlab.com/chepullreq1/public-repo.git"}
export PRIVATE_REPO_URL=${PRIVATE_REPO_URL:-"https://gitlab.com/chepullreq1/private-repo.git"}
export GIT_PROVIDER_TYPE=${GIT_PROVIDER_TYPE:-"gitlab"}
export GIT_PROVIDER_URL=${GIT_PROVIDER_URL:-"https://gitlab.com"}
export PRIVATE_REPO_SSH_URL=${PRIVATE_REPO_SSH_URL:-"git@gitlab.com:chepullreq1/private-repo.git"}
export PRIVATE_REPO_RAW_PATH_URL=${PRIVATE_REPO_URL:-"https://gitlab.com/chepullreq1/private-repo/-/raw/main/devfile.yaml"}
# import common test functions
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "${SCRIPT_DIR}"/common.sh
trap "catchFinish" EXIT SIGINT
setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME}
setupPersonalAccessToken ${GIT_PROVIDER_TYPE} ${GIT_PROVIDER_URL} ${GITLAB_PAT}
requestProvisionNamespace
testFactoryResolverWithPatOAuth ${PUBLIC_REPO_URL} ${PRIVATE_REPO_URL}
echo "[INFO] Check clone public repository with PAT setup"
testCloneGitRepoProjectShouldExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_URL} ${USER_CHE_NAMESPACE}
testGitCredentialsData ${USER_CHE_NAMESPACE} ${GITLAB_PAT} ${GIT_PROVIDER_URL}
deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
echo "[INFO] Check clone private repository with PAT setup"
testCloneGitRepoProjectShouldExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_URL} ${USER_CHE_NAMESPACE}
testGitCredentialsData ${USER_CHE_NAMESPACE} ${GITLAB_PAT} ${GIT_PROVIDER_URL}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
echo "[INFO] Check clone private repository by raw devfile URL with PAT setup"
testFactoryResolverResponse ${PRIVATE_REPO_RAW_PATH_URL} 200
testCloneGitRepoProjectShouldExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_RAW_PATH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}
setupSSHKeyPairs "${GITLAB_PRIVATE_KEY}" "${GITLAB_PUBLIC_KEY}"
echo "[INFO] Check clone private repository by SSH URL with PAT setup"
testFactoryResolverResponse ${PRIVATE_REPO_SSH_URL} 200
testCloneGitRepoProjectShouldExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_SSH_URL} ${USER_CHE_NAMESPACE}
deleteTestWorkspace ${PRIVATE_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE}

5
.github/CODEOWNERS vendored
View File

@ -1,5 +1,5 @@
# Global Owners
* @l0rd @ibuziuk @vinokurig @nickboldt
* @l0rd @ibuziuk @vinokurig @nickboldt @tolusha
# che.properties file is quasi-documentation, so in future we might grant access to this
# assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/** @osslate
@ -9,6 +9,3 @@ infrastructures/** @amisevsk
# deploy
deploy/** @tolusha @dkwon17
# e2e tests
tests/e2e/** @musienko-maxim

View File

@ -11,6 +11,9 @@ name: build-pr-check
on: [push, pull_request]
env:
PR_IMAGE_TAG: pr-${{ github.event.pull_request.number }}
jobs:
build:
runs-on: ubuntu-22.04
@ -26,5 +29,25 @@ jobs:
distribution: 'temurin'
java-version: '11'
cache: 'maven'
- name: Login to docker.io
if: github.event_name == 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
registry: docker.io
- name: Login to quay.io
if: github.event_name == 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}
registry: quay.io
- name: Build with Maven
run: mvn -B clean install -U -Pintegration
- name: Build docker images
if: github.event_name == 'pull_request'
run: ./dockerfiles/build.sh --tag:${{ env.PR_IMAGE_TAG }}
- name: Push docker images
if: github.event_name == 'pull_request'
run: docker push quay.io/eclipse/che-server:${{ env.PR_IMAGE_TAG }}

View File

@ -45,8 +45,7 @@ jobs:
- name: Build docker images
id: build
run: |
SHORT_SHA1=$(git rev-parse --short HEAD)
echo ::set-output name=short_sha1::${SHORT_SHA1}
echo "short_sha1=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
./dockerfiles/build.sh --tag:next --sha-tag
- name: Push docker images
run: |

View File

@ -145,17 +145,17 @@ jobs:
prerelease: false
tag: ${{ github.event.inputs.version }}
token: ${{ secrets.CHE_BOT_GITHUB_TOKEN }}
- name: Create failure MM message
if: ${{ failure() }}
run: |
echo "{\"text\":\":no_entry_sign: Che Server ${{ github.event.inputs.version }} release has failed: https://github.com/eclipse-che/che-server/actions/workflows/release.yml\"}" > mattermost.json
- name: Create success MM message
run: |
echo "{\"text\":\":white_check_mark: Che Server ${{ github.event.inputs.version }} has been released: https://quay.io/eclipse/che-server:${{ github.event.inputs.version }}\"}" > mattermost.json
- name: Send MM message
if: ${{ success() }} || ${{ failure() }}
uses: mattermost/action-mattermost-notify@1.1.0
env:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
MATTERMOST_CHANNEL: eclipse-che-releases
MATTERMOST_USERNAME: che-bot
#- name: Create failure MM message
#if: ${{ failure() }}
#run: |
#echo "{\"text\":\":no_entry_sign: Che Server ${{ github.event.inputs.version }} release has failed: https://github.com/eclipse-che/che-server/actions/workflows/release.yml\"}" > mattermost.json
#- name: Create success MM message
#run: |
#echo "{\"text\":\":white_check_mark: Che Server ${{ github.event.inputs.version }} has been released: https://quay.io/eclipse/che-server:${{ github.event.inputs.version }}\"}" > mattermost.json
#- name: Send MM message
#if: ${{ success() }} || ${{ failure() }}
#uses: mattermost/action-mattermost-notify@1.1.0
#env:
#MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
#MATTERMOST_CHANNEL: eclipse-che-releases
#MATTERMOST_USERNAME: che-bot

8
.gitignore vendored
View File

@ -1,3 +1,5 @@
.repository/
# Eclipse #
###################
@ -11,10 +13,6 @@ test-output/
maven-eclipse.xml
.factorypath
# Theia #
##################
.theia/
# Idea #
##################
*.iml
@ -48,8 +46,6 @@ maven-eclipse.xml
*.war
*.ear
# Logs and databases #
######################
*.log

9
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,9 @@
{
"recommendations": [
"redhat.java",
"redhat.fabric8-analytics",
"vscjava.vscode-maven",
"vscjava.vscode-java-debug",
"vscjava.vscode-java-test"
]
}

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

@ -0,0 +1,3 @@
{
"java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx6G -Xms100m -Xlog:disable"
}

82
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,82 @@
## Developing with Eclipse Che
This project contains a [devfile v2](https://github.com/eclipse-che/che-server/blob/main/devfile.yaml) located in the project's root directory.
With this devfile you can perform the following tasks:
- Build project sources with maven.
- Build a container image.
- Push your changes to GitHub.
### Starting a Workspace within Eclipse Che
To start a new workspace within Eclipse Che, navigate to the following URL:
```sh
{CHE-HOST}/#https://github.com/eclipse-che/che-server
```
### Running Devfile Tasks for Downloading Dependencies and Building the Project
For VS Code, execute tasks defined in the devfile with these steps:
1. Open the command palette (Ctrl/Cmd + Shift + P).
2. Execute the `Tasks: Run Task` command.
3. Select the `devfile` folder and select the `1. Build sources` task to build the project sources with maven.
4. Follow steps 1 and 2 again, select the `1. Build image` task to build a container image.
5. In the workspace terminal, tag the **che-server** image with your account: `podman tag quay.io/eclipse/che-server:next <docker registry>/<your account>/che-server:next`.
6. Push the **che-server** image to your account: `podman push <docker registry>/<your account>/che-server:next`.
# Local build requirements
- Apache Maven 3.9 or later
- JDK 11
- Podman or Docker (required for running integration tests)
A Che workspace environment allows to build the image internally, using the workspace terminal.
# Start and debug
1. Deploy Che to a [Red Hat OpenShift](https://www.eclipse.org/che/docs/stable/administration-guide/installing-che-on-openshift-using-cli/) or [Minikube](https://www.eclipse.org/che/docs/stable/administration-guide/installing-che-on-minikube/) cluster by using a previously built image: `chectl server:start --platform=<openshift / minikube> --cheimage=<docker registry>/<your account>/che-server:next`.
2. Enable local debugging of the Eclipse Che server: `chectl server:debug`.
3. In your IDE, create a new Remote JVM Debug configuration on `localhost:8000`.
4. Hit a breakpoint in the code and activate the debug configuration.
# Contributing an SCM provider
An SCM provider support has to be provided by adding new maven modules to the wsmaster directory.
## Implementing che-core-api-oauth-<SCM provider name> module
This module is responsible for Oauth requests to the SCM provider and contans next implementations:
1. `<SCM provider name>OAuthAuthenticator` contains specific implementation of Oauth token requestand authentication endpoint.
2. `<SCM provider name>OAuthAuthenticatorProvider` a provider of the `OAuthAuthenticator`.
## Implementing coresponding che-core-api-factory-<SCM provider name> module
This module is responsible for API operations of the specific SCM provider.
1. `<SCM provider name>ApiClient` contains all necessary HTTP API calls like `getUser()`.
2. `<SCM provider name>AuthorisingFileContentProvider` overrides the common `AuthorisingFileContentProvider` to define the specific `isPublicRepository()` function.
3. `<SCM provider name>FactoryParameterResolver` validates SCM URL if it corresponds to the SCM provider. Also Provides specific Factory Parameters resolver for the SCM repository.
4. `<SCM provider name>PersonalAccessTokenFetcher` fetches Personal Access Token from the SCM provider by reqesting the OAuth API of the provider. Validates the token by comparing the token access scopes with the predefined scope list.
5. `<SCM provider name>FileResolver` Implementation of a resolver that can return particular file content from specified SCM repository.
6. `<SCM provider name>URLParser` Parses the string representation of the URL to an SCM specific object
7. `<SCM provider name>UserDataFetcher` Implementation of a resolver that can return particular file content from specified SCM repository.
# CI
There are several [GitHub Actions](https://github.com/eclipse-che/che-server/actions) workflows implemented for this repository:
- [![build-next](https://github.com/eclipse-che/che-server/actions/workflows/next-build.yml/badge.svg)](https://github.com/eclipse-che/che-server/actions/workflows/next-build.yml)
Builds Maven artifacts, builds container images and pushes them to [quay.io](https://quay.io/organization/eclipse) on each commit to [`main`](https://github.com/eclipse-che/che-server/tree/main) branch.
- [![Release Che Server](https://github.com/eclipse-che/che-server/actions/workflows/release.yml/badge.svg)](https://github.com/eclipse-che/che-server/actions/workflows/release.yml)
Builds Maven artifacts and container images. Images are public and pushed to [quay.io](https://quay.io/organization/eclipse). See [RELEASE.md](https://github.com/eclipse-che/che-server/blob/master/RELEASE.md) for more information about this workflow.
- [![Release Changelog](https://github.com/eclipse-che/che-server/actions/workflows/release-changelog.yml/badge.svg)](https://github.com/eclipse-che/che-server/actions/workflows/release-changelog.yml)
Creates a GitHub release which will include a generated changelog.
- [![Update Che docs variables](https://github.com/eclipse-che/che-server/actions/workflows/che-properties-docs-update.yml/badge.svg)](https://github.com/eclipse-che/che-server/actions/workflows/che-properties-docs-update.yml/badge.svg)
Runs on each commit to [`main`](https://github.com/eclipse-che/che-server/tree/main) branch.
- [![build-pr-check](https://github.com/eclipse-che/che-server/actions/workflows/build-pr-check.yml/badge.svg)](https://github.com/eclipse-che/che-server/actions/workflows/build-pr-check.yml)
Builds Maven artifacts and container images. This workflow is used as a check for all pull requests that are submitted to this project.
- [![Sonar](https://github.com/eclipse-che/che-server/actions/workflows/sonar.yaml/badge.svg)](https://github.com/eclipse-che/che-server/actions/workflows/sonar.yaml)
Runs Sonar against the main branch. The result can be seen [here](https://sonarcloud.io/dashboard?id=org.eclipse.che%3Ache-server).
- [![Try in Web IDE](https://github.com/eclipse-che/che-server/actions/workflows/try-in-web-ide.yaml/badge.svg)](https://github.com/eclipse-che/che-server/actions/workflows/try-in-web-ide.yaml)
Used as a check for pull requests that are submitted to this project.
Downstream builds can be found at the link below, which is _internal to Red Hat_. Stable builds can be found by replacing the 3.x with a specific version like 3.2.
- [server_3.x](https://main-jenkins-csb-crwqe.apps.ocp-c1.prod.psi.redhat.com/job/DS_CI/job/server_3.x/)
# Report issues
Issues are tracked on the main Eclipse Che Repository: https://github.com/eclipse/che/issues

View File

@ -1,49 +1,28 @@
# What is Che server
Che Server is a core component of the [Eclipse Che](https://github.com/eclipse/che/). This component is responsible for creation and managing of Che workspaces, but will some day be replaced by the [Dev Workspace Operator](https://github.com/devfile/devworkspace-operator).
Che Server provides an API for managing Kubernetes namespaces and to retrieve devfile content from repositories,
hosted on GitHub, GitLab, Bitbucket and Microsoft Azure Repos.
# Project structure
Che Server is mostly a Java web application deployed on a Apache Tomcat server in a container.
- ['pom.xml'](https://github.com/eclipse-che/che-server/tree/main/pom.xml) The root Maven module, that lists all dependencies and structure.
- ['assembly'](https://github.com/eclipse-che/che-server/tree/main/assembly) - module for final assemblies of Che web applications
- ['dockerfiles'](https://github.com/eclipse-che/che-server/tree/main/dockerfiles) - directory contains image Dockerfile for Che Server, as well as additional images.
- ['core'](https://github.com/eclipse-che/che-server/tree/main/core) - core and utility modules for Che.
- ['wsmaster'](https://github.com/eclipse-che/che-server/tree/main/wsmaster) - primary modules of the Che Server API.
- ['multiuser'](https://github.com/eclipse-che/che-server/tree/main/multiuser) - modules related to multiuser implementation of Che.
- ['infrastructure'](https://github.com/eclipse-che/che-server/tree/main/infrastructure) - implementations for the underlying infrastructure, on which Che is running (Kubernetes, Openshift, etc.)
- ['deploy'](https://github.com/eclipse-che/che-server/tree/main/deploy) - deployment files for Helm installation.
- ['typescript-dto'](https://github.com/eclipse-che/che-server/tree/main/typescript-dto) module, that provides DTO objects for typescript projects that may depend on Che Server, such as Che Theia.
Che Server is mostly a Java web application deployed on an Apache Tomcat server in a container. Che Server uses the following modules:
### OAuth1 / OAuth2 API implementations
- wsmaster/che-core-api-auth
- wsmaster/che-core-api-azure-devops
- wsmaster/che-core-api-bitbucket
- wsmaster/che-core-api-github
- wsmaster/che-core-api-gitlab
### Factory flow implementations
- wsmaster/che-core-api-factory-azure-devops
- wsmaster/che-core-api-factory-bitbucket
- wsmaster/che-core-api-factory-bitbucket-server
- wsmaster/che-core-api-factory-github
- wsmaster/che-core-api-factory-gitlab
- wsmaster/che-core-api-factory-shared
### Kubernetes namespace provisioning
- infrastructures/kubernetes
- infrastructure/openshift
- infrastructures/infrastructure-factory
# Build requirements
- Apache Maven 3.6.3 or Higher
- JDK Version 11
- Podman or Docker (required for running integration tests)
# Build and debug
Run `mvn clean install` to build
Activate a faster profile build by adding `-Pfast`
To debug, run `mvn clean install -X` and connect your IDE to the debug port
# CI
There are several [GitHub Actions](https://github.com/eclipse-che/che-server/actions) workflows implemented for this repository:
- [![build-next](https://github.com/eclipse-che/che-server/actions/workflows/next-build.yml/badge.svg)](https://github.com/eclipse-che/che-server/actions/workflows/next-build.yml)
Builds Maven artifacts, builds container images and pushes them to [quay.io](https://quay.io/organization/eclipse) on each commit to [`main`](https://github.com/eclipse-che/che-server/tree/main) branch.
- [![Release Che Server](https://github.com/eclipse-che/che-server/actions/workflows/release.yml/badge.svg)](https://github.com/eclipse-che/che-server/actions/workflows/release.yml)
Builds Maven artifacts and container images. Images are public and pushed to [quay.io](https://quay.io/organization/eclipse). See [RELEASE.md](https://github.com/eclipse-che/che-server/blob/master/RELEASE.md) for more information about this workflow.
- [![Release Changelog](https://github.com/eclipse-che/che-server/actions/workflows/release-changelog.yml/badge.svg)](https://github.com/eclipse-che/che-server/actions/workflows/release-changelog.yml)
Creates a GitHub release which will include a generated changelog.
- [![Update Che docs variables](https://github.com/eclipse-che/che-server/actions/workflows/che-properties-docs-update.yml/badge.svg)](https://github.com/eclipse-che/che-server/actions/workflows/che-properties-docs-update.yml/badge.svg)
Runs on each commit to [`main`](https://github.com/eclipse-che/che-server/tree/main) branch.
- [![build-pr-check](https://github.com/eclipse-che/che-server/actions/workflows/build-pr-check.yml/badge.svg)](https://github.com/eclipse-che/che-server/actions/workflows/build-pr-check.yml)
Builds Maven artifacts and container images. This workflow is used as a check for all pull requests that are submitted to this project.
- [![Sonar](https://github.com/eclipse-che/che-server/actions/workflows/sonar.yaml/badge.svg)](https://github.com/eclipse-che/che-server/actions/workflows/sonar.yaml)
Runs Sonar against the main branch. The result can be seen [here](https://sonarcloud.io/dashboard?id=org.eclipse.che%3Ache-server).
- [![Try in Web IDE](https://github.com/eclipse-che/che-server/actions/workflows/try-in-web-ide.yaml/badge.svg)](https://github.com/eclipse-che/che-server/actions/workflows/try-in-web-ide.yaml)
Used as a check for pull requests that are submitted to this project.
Downstream builds can be found at the link below, which is _internal to Red Hat_. Stable builds can be found by replacing the 3.x with a specific version like 3.2.
- [server_3.x](https://main-jenkins-csb-crwqe.apps.ocp-c1.prod.psi.redhat.com/job/DS_CI/job/server_3.x/)
Other modules are deprecated and will be removed in the future.
# License
@ -52,8 +31,4 @@ Downstream builds can be found at the link below, which is _internal to Red Hat_
# Join the community
The Eclipse Che community is globally reachable through public chat rooms, mailing list and weekly calls.
See https://www.eclipse.org/che/docs/che-7/overview/introduction-to-eclipse-che/#_joining_the_community
## Report issues
Issues are tracked on the main Eclipse Che Repository: https://github.com/eclipse/che/issues
See the Eclipse Che Documentation about [how you can join our community](https://www.eclipse.org/che/docs/stable/overview/introduction-to-eclipse-che/#_joining_the_community).

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-assembly-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>assembly-che-tomcat</artifactId>
<packaging>jar</packaging>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-assembly-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>assembly-main</artifactId>
<packaging>pom</packaging>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-assembly-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>assembly-root-war</artifactId>
<packaging>war</packaging>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-assembly-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>assembly-swagger-war</artifactId>
<packaging>war</packaging>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-assembly-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>assembly-wsmaster-war</artifactId>
<packaging>war</packaging>

View File

@ -36,6 +36,7 @@ import org.eclipse.che.api.factory.server.FactoryAcceptValidator;
import org.eclipse.che.api.factory.server.FactoryCreateValidator;
import org.eclipse.che.api.factory.server.FactoryEditValidator;
import org.eclipse.che.api.factory.server.FactoryParametersResolver;
import org.eclipse.che.api.factory.server.RawDevfileUrlFactoryParameterResolver;
import org.eclipse.che.api.factory.server.ScmFileResolver;
import org.eclipse.che.api.factory.server.ScmService;
import org.eclipse.che.api.factory.server.azure.devops.AzureDevOpsFactoryParametersResolver;
@ -176,6 +177,9 @@ public class WsMasterModule extends AbstractModule {
factoryParametersResolverMultibinder
.addBinding()
.to(AzureDevOpsFactoryParametersResolver.class);
factoryParametersResolverMultibinder
.addBinding()
.to(RawDevfileUrlFactoryParameterResolver.class);
factoryParametersResolverMultibinder.addBinding().to(GitSshFactoryParametersResolver.class);
Multibinder<ScmFileResolver> scmFileResolverResolverMultibinder =

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-server</artifactId>
<groupId>org.eclipse.che</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>che-assembly-parent</artifactId>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-api-core</artifactId>
<packaging>jar</packaging>

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* Copyright (c) 2012-2023 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/
@ -11,7 +11,10 @@
*/
package org.eclipse.che.api.core.rest;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.lang.String.format;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toList;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
@ -19,6 +22,7 @@ import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import javax.inject.Singleton;
import org.eclipse.che.api.core.rest.shared.dto.ServiceError;
@ -42,11 +46,18 @@ public class RuntimeExceptionMapper implements ExceptionMapper<RuntimeException>
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
final String utcTime = dateFormat.format(new Date());
final String errorMessage = format("Internal Server Error occurred, error time: %s", utcTime);
String message = exception.getMessage();
final String errorMessage =
isNullOrEmpty(message)
? format("Internal Server Error occurred, error time: %s", utcTime)
: message;
LOG.error(errorMessage, exception);
ServiceError serviceError = DtoFactory.newDto(ServiceError.class).withMessage(errorMessage);
List<String> trace =
stream(exception.getStackTrace()).map(StackTraceElement::toString).collect(toList());
ServiceError serviceError =
DtoFactory.newDto(ServiceError.class).withMessage(errorMessage).withTrace(trace);
return Response.serverError()
.entity(DtoFactory.getInstance().toJson(serviceError))
.type(MediaType.APPLICATION_JSON)

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* Copyright (c) 2012-2023 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/
@ -11,6 +11,7 @@
*/
package org.eclipse.che.api.core.rest.shared.dto;
import java.util.List;
import org.eclipse.che.dto.shared.DTO;
/**
@ -30,8 +31,12 @@ public interface ServiceError {
*/
String getMessage();
List<String> getTrace();
ServiceError withMessage(String message);
ServiceError withTrace(List<String> trace);
/**
* Set error message.
*

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-api-dto-maven-plugin</artifactId>
<packaging>maven-plugin</packaging>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-api-dto</artifactId>
<packaging>jar</packaging>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-api-model</artifactId>
<packaging>jar</packaging>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-db-vendor-h2</artifactId>
<name>Che Core :: Commons :: DB :: Vendor H2</name>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-db-vendor-mysql</artifactId>
<name>Che Core :: Commons :: DB :: Vendor MySQL</name>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-db-vendor-postgresql</artifactId>
<name>Che Core :: Commons :: DB :: Vendor PostgreSQL</name>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-db</artifactId>
<name>Che Core :: Commons :: DB</name>

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* Copyright (c) 2012-2023 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/
@ -77,7 +77,7 @@ public class TracingDataSourceTest {
}
@Test
public void shouldNotWrapDatasourceIfEnvSetToFalseØ() throws Exception {
public void shouldNotWrapDatasourceIfEnvSetToFalse() throws Exception {
setEnv(ImmutableMap.of("CHE_DB_TRACING_ENABLED", "false"));
DataSource actual = TracingDataSource.wrapWithTracingIfEnabled(dataSource);

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-logback</artifactId>
<packaging>jar</packaging>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-metrics-core</artifactId>
<name>Che Core :: Commons :: Metrics :: Core</name>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-tracing-core</artifactId>
<name>Che Core :: Commons :: Tracing :: Core</name>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-tracing-metrics</artifactId>
<name>Che Core :: Commons :: Tracing :: Metrics</name>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-tracing-web</artifactId>
<name>Che Core :: Commons :: Tracing :: Web</name>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-typescript-dto-maven-plugin</artifactId>
<packaging>maven-plugin</packaging>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-commons-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-commons-annotations</artifactId>
<packaging>jar</packaging>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-commons-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-commons-inject</artifactId>
<packaging>jar</packaging>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-commons-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-commons-j2ee</artifactId>
<packaging>jar</packaging>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-commons-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-commons-json</artifactId>
<packaging>jar</packaging>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-commons-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-commons-lang</artifactId>
<packaging>jar</packaging>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-commons-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-commons-observability</artifactId>
<name>Che Core :: Commons :: Tracing and Monitoring wrapper</name>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-commons-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-commons-schedule</artifactId>
<packaging>jar</packaging>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-commons-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-commons-test</artifactId>
<packaging>jar</packaging>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-commons-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-commons-tracing</artifactId>
<name>Che Core :: Commons :: Tracing</name>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>che-core-commons-parent</artifactId>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-server</artifactId>
<groupId>org.eclipse.che</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.eclipse.che.core</groupId>

View File

@ -1,21 +1,25 @@
apiVersion: 1.0.0
schemaVersion: 2.2.0
metadata:
name: che-server-in-che
generateName: che-server
components:
- type: dockerimage
image: quay.io/eclipse/che-java11-maven:nightly
alias: maven
mountSources: true
memoryLimit: 2Gi
volumes:
- name: m2
containerPath: /home/user/.m2
- type: chePlugin
id: redhat/java11/latest
- container:
image: quay.io/devfile/universal-developer-image:ubi8-latest
memoryLimit: 12Gi
mountSources: true
volumeMounts:
- name: m2
path: /home/user/.m2
name: tools
- name: m2
volume: {}
commands:
- name: build without tests
actions:
- type: exec
component: maven
command: mvn clean install -DskipTests
workdir: ${CHE_PROJECTS_ROOT}/che
- id: build
exec:
label: "1. Build"
component: tools
workingDir: ${PROJECT_SOURCE}
commandLine: |
mvn clean install -DskipTests
group:
kind: build
isDefault: true

View File

@ -10,11 +10,11 @@
#
# https://access.redhat.com/containers/?tab=tags#/registry.access.redhat.com/ubi8-minimal
FROM registry.access.redhat.com/ubi8-minimal:8.7-1085
FROM registry.access.redhat.com/ubi8-minimal:8.8-1072
USER root
ENV CHE_HOME=/home/user/eclipse-che
ENV JAVA_HOME=/usr/lib/jvm/jre
RUN microdnf install java-11-openjdk-headless tar gzip shadow-utils findutils && \
RUN microdnf install java-17-openjdk-headless tar gzip shadow-utils findutils && \
microdnf update -y && \
microdnf -y clean all && rm -rf /var/cache/yum && echo "Installed Packages" && rpm -qa | sort -V && echo "End Of Installed Packages" && \
adduser -G root user && mkdir -p /home/user/eclipse-che

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-infrastructures-parent</artifactId>
<groupId>org.eclipse.che.infrastructure</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>infrastructure-distributed</artifactId>
<packaging>jar</packaging>

View File

@ -17,12 +17,16 @@
<parent>
<artifactId>che-infrastructures-parent</artifactId>
<groupId>org.eclipse.che.infrastructure</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>infrastructure-factory</artifactId>
<packaging>jar</packaging>
<name>Infrastructure :: Factory components</name>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
@ -33,7 +37,7 @@
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client</artifactId>
<artifactId>kubernetes-client-api</artifactId>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
@ -43,6 +47,14 @@
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-auth</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-core</artifactId>
@ -55,6 +67,10 @@
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-workspace</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-lang</artifactId>

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* Copyright (c) 2012-2023 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/
@ -12,13 +12,16 @@
package org.eclipse.che.api.factory.server.scm;
import com.google.inject.AbstractModule;
import org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesAuthorisationRequestManager;
import org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesGitCredentialManager;
import org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesPersonalAccessTokenManager;
import org.eclipse.che.security.oauth.AuthorisationRequestManager;
public class KubernetesScmModule extends AbstractModule {
@Override
protected void configure() {
bind(GitCredentialManager.class).to(KubernetesGitCredentialManager.class);
bind(PersonalAccessTokenManager.class).to(KubernetesPersonalAccessTokenManager.class);
bind(AuthorisationRequestManager.class).to(KubernetesAuthorisationRequestManager.class);
}
}

View File

@ -0,0 +1,145 @@
/*
* Copyright (c) 2012-2023 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.eclipse.che.api.factory.server.scm.kubernetes;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.eclipse.che.commons.lang.UrlUtils.getParameter;
import static org.eclipse.che.commons.lang.UrlUtils.getQueryParametersFromState;
import static org.eclipse.che.commons.lang.UrlUtils.getRequestUrl;
import static org.eclipse.che.commons.lang.UrlUtils.getState;
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.PREFERENCES_CONFIGMAP_NAME;
import com.google.gson.Gson;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.client.KubernetesClient;
import jakarta.ws.rs.core.UriInfo;
import java.net.URL;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.factory.server.scm.exception.ScmConfigurationPersistenceException;
import org.eclipse.che.api.factory.server.scm.exception.UnsatisfiedScmPreconditionException;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.security.oauth.AuthorisationRequestManager;
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
/** Store and retrieve rejected authorisation requests in the Kubernetes ConfigMap. */
@Singleton
public class KubernetesAuthorisationRequestManager implements AuthorisationRequestManager {
private final KubernetesNamespaceFactory namespaceFactory;
private final CheServerKubernetesClientFactory cheServerKubernetesClientFactory;
private static final String SKIP_AUTHORISATION_MAP_KEY = "skip-authorisation";
@Inject
public KubernetesAuthorisationRequestManager(
KubernetesNamespaceFactory namespaceFactory,
CheServerKubernetesClientFactory cheServerKubernetesClientFactory) {
this.namespaceFactory = namespaceFactory;
this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory;
}
@Override
public void store(String scmProviderName) {
if (isStored(scmProviderName)) {
return;
}
ConfigMap configMap = getConfigMap();
HashSet<String> fromJson = getSkipAuthorisationValues();
fromJson.add(scmProviderName);
configMap.setData(Map.of(SKIP_AUTHORISATION_MAP_KEY, fromJson.toString()));
patchConfigMap(configMap);
}
@Override
public void remove(String scmProviderName) {
if (!isStored(scmProviderName)) {
return;
}
ConfigMap configMap = getConfigMap();
HashSet<String> fromJson = getSkipAuthorisationValues();
fromJson.remove(scmProviderName);
configMap.setData(Map.of(SKIP_AUTHORISATION_MAP_KEY, fromJson.toString()));
patchConfigMap(configMap);
}
@Override
public boolean isStored(String scmProviderName) {
return getSkipAuthorisationValues().contains(scmProviderName);
}
@Override
public void callback(UriInfo uriInfo, List<String> errorValues) {
URL requestUrl = getRequestUrl(uriInfo);
Map<String, List<String>> params = getQueryParametersFromState(getState(requestUrl));
errorValues = errorValues == null ? uriInfo.getQueryParameters().get("error") : errorValues;
if (errorValues != null && errorValues.contains("access_denied")) {
store(getParameter(params, "oauth_provider"));
}
}
private ConfigMap getConfigMap() {
try (KubernetesClient kubernetesClient = cheServerKubernetesClientFactory.create()) {
String namespace = getFirstNamespace();
return kubernetesClient
.configMaps()
.inNamespace(namespace)
.withName(PREFERENCES_CONFIGMAP_NAME)
.get();
} catch (UnsatisfiedScmPreconditionException
| ScmConfigurationPersistenceException
| InfrastructureException e) {
throw new RuntimeException(e);
}
}
private void patchConfigMap(ConfigMap configMap) {
try (KubernetesClient kubernetesClient = cheServerKubernetesClientFactory.create()) {
kubernetesClient
.configMaps()
.inNamespace(getFirstNamespace())
.withName(PREFERENCES_CONFIGMAP_NAME)
.patch(configMap);
} catch (UnsatisfiedScmPreconditionException
| ScmConfigurationPersistenceException
| InfrastructureException e) {
throw new RuntimeException(e);
}
}
private HashSet<String> getSkipAuthorisationValues() {
String data = getConfigMap().getData().get(SKIP_AUTHORISATION_MAP_KEY);
return new Gson().fromJson(isNullOrEmpty(data) ? "[]" : data, HashSet.class);
}
private String getFirstNamespace()
throws UnsatisfiedScmPreconditionException, ScmConfigurationPersistenceException {
try {
return namespaceFactory.list().stream()
.map(KubernetesNamespaceMeta::getName)
.findFirst()
.orElseThrow(
() ->
new UnsatisfiedScmPreconditionException(
"No user namespace found. Cannot read SCM credentials."));
} catch (InfrastructureException e) {
throw new ScmConfigurationPersistenceException(e.getMessage(), e);
}
}
}

View File

@ -11,6 +11,7 @@
*/
package org.eclipse.che.api.factory.server.scm.kubernetes;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.lang.String.format;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.che.api.factory.server.scm.PersonalAccessTokenFetcher.OAUTH_2_PREFIX;
@ -43,7 +44,7 @@ import org.eclipse.che.api.factory.server.scm.exception.UnsatisfiedScmPreconditi
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.commons.lang.StringUtils;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
@ -86,13 +87,14 @@ public class KubernetesGitCredentialManager implements GitCredentialManager {
ANNOTATION_DEV_WORKSPACE_MOUNT_PATH, CREDENTIALS_MOUNT_PATH);
private final KubernetesNamespaceFactory namespaceFactory;
private final KubernetesClientFactory clientFactory;
private final CheServerKubernetesClientFactory cheServerKubernetesClientFactory;
@Inject
public KubernetesGitCredentialManager(
KubernetesNamespaceFactory namespaceFactory, KubernetesClientFactory clientFactory) {
KubernetesNamespaceFactory namespaceFactory,
CheServerKubernetesClientFactory cheServerKubernetesClientFactory) {
this.namespaceFactory = namespaceFactory;
this.clientFactory = clientFactory;
this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory;
}
@Override
@ -100,7 +102,7 @@ public class KubernetesGitCredentialManager implements GitCredentialManager {
throws UnsatisfiedScmPreconditionException, ScmConfigurationPersistenceException {
try {
final String namespace = getFirstNamespace();
final KubernetesClient client = clientFactory.create();
final KubernetesClient client = cheServerKubernetesClientFactory.create();
// to avoid duplicating secrets we try to reuse existing one by matching
// hostname/username if possible, and update it. Otherwise, create new one.
Optional<Secret> existing =
@ -124,11 +126,7 @@ public class KubernetesGitCredentialManager implements GitCredentialManager {
'/'))
&& personalAccessToken
.getCheUserId()
.equals(s.getMetadata().getAnnotations().get(ANNOTATION_CHE_USERID))
&& personalAccessToken
.getScmUserName()
.equals(
s.getMetadata().getAnnotations().get(ANNOTATION_SCM_USERNAME)))
.equals(s.getMetadata().getAnnotations().get(ANNOTATION_CHE_USERID)))
.findFirst();
Secret secret =
@ -136,7 +134,6 @@ public class KubernetesGitCredentialManager implements GitCredentialManager {
() -> {
Map<String, String> annotations = new HashMap<>(DEFAULT_SECRET_ANNOTATIONS);
annotations.put(ANNOTATION_SCM_URL, personalAccessToken.getScmProviderUrl());
annotations.put(ANNOTATION_SCM_USERNAME, personalAccessToken.getScmUserName());
annotations.put(ANNOTATION_CHE_USERID, personalAccessToken.getCheUserId());
ObjectMeta meta =
new ObjectMetaBuilder()
@ -155,9 +152,7 @@ public class KubernetesGitCredentialManager implements GitCredentialManager {
format(
"%s://%s:%s@%s%s",
scmUrl.getProtocol(),
personalAccessToken.getScmTokenName().startsWith(OAUTH_2_PREFIX)
? "oauth2"
: personalAccessToken.getScmUserName(),
getUsernameSegment(personalAccessToken),
URLEncoder.encode(personalAccessToken.getToken(), UTF_8),
scmUrl.getHost(),
scmUrl.getPort() != 80 && scmUrl.getPort() != -1
@ -170,6 +165,21 @@ public class KubernetesGitCredentialManager implements GitCredentialManager {
}
}
/**
* Returns username URL segment for git credentials. For OAuth2 tokens it is "oauth2", for others
* - {@param personalAccessToken#getScmUserName()} or just "username" string if the token has a
* non-null {@param personalAccessToken#getScmOrganization()}. This is needed to support providers
* that do not have username in their user object. Such providers have an additional organization
* field.
*/
private String getUsernameSegment(PersonalAccessToken personalAccessToken) {
return personalAccessToken.getScmTokenName().startsWith(OAUTH_2_PREFIX)
? "oauth2"
: isNullOrEmpty(personalAccessToken.getScmOrganization())
? personalAccessToken.getScmUserName()
: "username";
}
/**
* It is not guaranteed that this code will always return same namespace, but since credentials
* are now added into manually pre-created user namespace, we can expect always the same result

View File

@ -30,6 +30,7 @@ import javax.inject.Singleton;
import org.eclipse.che.api.factory.server.scm.GitCredentialManager;
import org.eclipse.che.api.factory.server.scm.PersonalAccessToken;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenParams;
import org.eclipse.che.api.factory.server.scm.ScmPersonalAccessTokenFetcher;
import org.eclipse.che.api.factory.server.scm.exception.ScmCommunicationException;
import org.eclipse.che.api.factory.server.scm.exception.ScmConfigurationPersistenceException;
@ -37,11 +38,12 @@ import org.eclipse.che.api.factory.server.scm.exception.ScmUnauthorizedException
import org.eclipse.che.api.factory.server.scm.exception.UnknownScmProviderException;
import org.eclipse.che.api.factory.server.scm.exception.UnsatisfiedScmPreconditionException;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.commons.lang.StringUtils;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
@ -58,8 +60,6 @@ public class KubernetesPersonalAccessTokenManager implements PersonalAccessToken
public static final String NAME_PATTERN = "personal-access-token-";
public static final String ANNOTATION_CHE_USERID = "che.eclipse.org/che-userid";
public static final String ANNOTATION_SCM_USERID = "che.eclipse.org/scm-userid";
public static final String ANNOTATION_SCM_USERNAME = "che.eclipse.org/scm-username";
public static final String ANNOTATION_SCM_ORGANIZATION = "che.eclipse.org/scm-organization";
public static final String ANNOTATION_SCM_PERSONAL_ACCESS_TOKEN_ID =
"che.eclipse.org/scm-personal-access-token-id";
@ -69,18 +69,18 @@ public class KubernetesPersonalAccessTokenManager implements PersonalAccessToken
public static final String TOKEN_DATA_FIELD = "token";
private final KubernetesNamespaceFactory namespaceFactory;
private final KubernetesClientFactory clientFactory;
private final CheServerKubernetesClientFactory cheServerKubernetesClientFactory;
private final ScmPersonalAccessTokenFetcher scmPersonalAccessTokenFetcher;
private final GitCredentialManager gitCredentialManager;
@Inject
public KubernetesPersonalAccessTokenManager(
KubernetesNamespaceFactory namespaceFactory,
KubernetesClientFactory clientFactory,
CheServerKubernetesClientFactory cheServerKubernetesClientFactory,
ScmPersonalAccessTokenFetcher scmPersonalAccessTokenFetcher,
GitCredentialManager gitCredentialManager) {
this.namespaceFactory = namespaceFactory;
this.clientFactory = clientFactory;
this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory;
this.scmPersonalAccessTokenFetcher = scmPersonalAccessTokenFetcher;
this.gitCredentialManager = gitCredentialManager;
}
@ -96,8 +96,6 @@ public class KubernetesPersonalAccessTokenManager implements PersonalAccessToken
.withAnnotations(
new ImmutableMap.Builder<String, String>()
.put(ANNOTATION_CHE_USERID, personalAccessToken.getCheUserId())
.put(ANNOTATION_SCM_USERID, personalAccessToken.getScmUserId())
.put(ANNOTATION_SCM_USERNAME, personalAccessToken.getScmUserName())
.put(ANNOTATION_SCM_URL, personalAccessToken.getScmProviderUrl())
.put(
ANNOTATION_SCM_PERSONAL_ACCESS_TOKEN_ID,
@ -120,7 +118,11 @@ public class KubernetesPersonalAccessTokenManager implements PersonalAccessToken
personalAccessToken.getToken().getBytes(StandardCharsets.UTF_8))))
.build();
clientFactory.create().secrets().inNamespace(namespace).createOrReplace(secret);
cheServerKubernetesClientFactory
.create()
.secrets()
.inNamespace(namespace)
.createOrReplace(secret);
} catch (KubernetesClientException | InfrastructureException e) {
throw new ScmConfigurationPersistenceException(e.getMessage(), e);
}
@ -140,7 +142,36 @@ public class KubernetesPersonalAccessTokenManager implements PersonalAccessToken
public Optional<PersonalAccessToken> get(Subject cheUser, String scmServerUrl)
throws ScmConfigurationPersistenceException, ScmUnauthorizedException,
ScmCommunicationException {
return doGetPersonalAccessToken(cheUser, null, scmServerUrl);
}
@Override
public PersonalAccessToken get(String scmServerUrl)
throws ScmConfigurationPersistenceException, ScmUnauthorizedException,
ScmCommunicationException, UnknownScmProviderException,
UnsatisfiedScmPreconditionException {
Subject subject = EnvironmentContext.getCurrent().getSubject();
Optional<PersonalAccessToken> tokenOptional = get(subject, scmServerUrl);
if (tokenOptional.isPresent()) {
return tokenOptional.get();
} else {
// try to authenticate for the given URL
return fetchAndSave(subject, scmServerUrl);
}
}
@Override
public Optional<PersonalAccessToken> get(
Subject cheUser, String oAuthProviderName, @Nullable String scmServerUrl)
throws ScmConfigurationPersistenceException, ScmUnauthorizedException,
ScmCommunicationException {
return doGetPersonalAccessToken(cheUser, oAuthProviderName, scmServerUrl);
}
private Optional<PersonalAccessToken> doGetPersonalAccessToken(
Subject cheUser, @Nullable String oAuthProviderName, @Nullable String scmServerUrl)
throws ScmConfigurationPersistenceException, ScmUnauthorizedException,
ScmCommunicationException {
try {
for (KubernetesNamespaceMeta namespaceMeta : namespaceFactory.list()) {
List<Secret> secrets =
@ -152,28 +183,41 @@ public class KubernetesPersonalAccessTokenManager implements PersonalAccessToken
Map<String, String> annotations = secret.getMetadata().getAnnotations();
String trimmedUrl = StringUtils.trimEnd(annotations.get(ANNOTATION_SCM_URL), '/');
if (annotations.get(ANNOTATION_CHE_USERID).equals(cheUser.getUserId())
&& trimmedUrl.equals(StringUtils.trimEnd(scmServerUrl, '/'))) {
&& (oAuthProviderName == null
|| oAuthProviderName.equals(
annotations.get(ANNOTATION_SCM_PERSONAL_ACCESS_TOKEN_NAME)))
&& (scmServerUrl == null
|| trimmedUrl.equals(StringUtils.trimEnd(scmServerUrl, '/')))) {
String token =
new String(Base64.getDecoder().decode(secret.getData().get("token"))).trim();
PersonalAccessToken personalAccessToken =
new PersonalAccessToken(
trimmedUrl,
annotations.get(ANNOTATION_CHE_USERID),
annotations.get(ANNOTATION_SCM_ORGANIZATION),
annotations.get(ANNOTATION_SCM_USERNAME),
annotations.get(ANNOTATION_SCM_USERID),
annotations.get(ANNOTATION_SCM_PERSONAL_ACCESS_TOKEN_NAME),
annotations.get(ANNOTATION_SCM_PERSONAL_ACCESS_TOKEN_ID),
token);
if (scmPersonalAccessTokenFetcher.isValid(personalAccessToken)) {
String providerName = annotations.get(ANNOTATION_SCM_PERSONAL_ACCESS_TOKEN_NAME);
String tokenId = annotations.get(ANNOTATION_SCM_PERSONAL_ACCESS_TOKEN_ID);
String organization = annotations.get(ANNOTATION_SCM_ORGANIZATION);
Optional<String> scmUsername =
scmPersonalAccessTokenFetcher.getScmUsername(
new PersonalAccessTokenParams(
trimmedUrl, providerName, tokenId, token, organization));
if (scmUsername.isPresent()) {
PersonalAccessToken personalAccessToken =
new PersonalAccessToken(
trimmedUrl,
annotations.get(ANNOTATION_CHE_USERID),
organization,
scmUsername.get(),
providerName,
tokenId,
token);
return Optional.of(personalAccessToken);
} else {
// Removing token that is no longer valid. If several tokens exist the next one could
// be valid. If no valid token can be found, the caller should react in the same way
// as it reacts if no token exists. Usually, that means that process of new token
// retrieval would be initiated.
clientFactory.create().secrets().inNamespace(namespaceMeta.getName()).delete(secret);
}
// Removing token that is no longer valid. If several tokens exist the next one could
// be valid. If no valid token can be found, the caller should react in the same way
// as it reacts if no token exists. Usually, that means that process of new token
// retrieval would be initiated.
cheServerKubernetesClientFactory
.create()
.secrets()
.inNamespace(namespaceMeta.getName())
.delete(secret);
}
}
}
@ -188,19 +232,23 @@ public class KubernetesPersonalAccessTokenManager implements PersonalAccessToken
throws ScmCommunicationException, ScmConfigurationPersistenceException,
UnknownScmProviderException, UnsatisfiedScmPreconditionException,
ScmUnauthorizedException {
Subject subject = EnvironmentContext.getCurrent().getSubject();
Optional<PersonalAccessToken> tokenOptional = get(subject, scmServerUrl);
PersonalAccessToken personalAccessToken;
if (tokenOptional.isPresent()) {
personalAccessToken = tokenOptional.get();
} else {
// try to authenticate for the given URL
personalAccessToken = fetchAndSave(subject, scmServerUrl);
}
PersonalAccessToken personalAccessToken = get(scmServerUrl);
gitCredentialManager.createOrReplace(personalAccessToken);
return personalAccessToken;
}
@Override
public void store(String scmServerUrl)
throws UnsatisfiedScmPreconditionException, ScmConfigurationPersistenceException,
ScmCommunicationException, ScmUnauthorizedException {
Subject subject = EnvironmentContext.getCurrent().getSubject();
Optional<PersonalAccessToken> tokenOptional = get(subject, scmServerUrl);
if (tokenOptional.isPresent()) {
PersonalAccessToken personalAccessToken = tokenOptional.get();
gitCredentialManager.createOrReplace(personalAccessToken);
}
}
private String getFirstNamespace()
throws UnsatisfiedScmPreconditionException, ScmConfigurationPersistenceException {
try {

View File

@ -0,0 +1,197 @@
/*
* Copyright (c) 2012-2023 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.eclipse.che.api.factory.server.scm.kubernetes;
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.PREFERENCES_CONFIGMAP_NAME;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ConfigMapList;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.dsl.MixedOperation;
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
import io.fabric8.kubernetes.client.dsl.Resource;
import java.util.Collections;
import java.util.Map;
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl;
import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@Listeners(MockitoTestNGListener.class)
public class KubernetesAuthorisationRequestManagerTest {
@Mock private KubernetesNamespaceFactory namespaceFactory;
@Mock
private MixedOperation<ConfigMap, ConfigMapList, Resource<ConfigMap>> configMapsMixedOperation;
@Mock NonNamespaceOperation<ConfigMap, ConfigMapList, Resource<ConfigMap>> nonNamespaceOperation;
@Mock private Resource<ConfigMap> configMapResource;
@Mock private ConfigMap configMap;
@Mock private KubernetesClient kubeClient;
@Mock private CheServerKubernetesClientFactory cheServerKubernetesClientFactory;
private KubernetesAuthorisationRequestManager authorisationRequestManager;
@BeforeMethod
protected void init() throws Exception {
KubernetesNamespaceMeta meta = new KubernetesNamespaceMetaImpl("test");
when(namespaceFactory.list()).thenReturn(Collections.singletonList(meta));
when(cheServerKubernetesClientFactory.create()).thenReturn(kubeClient);
when(kubeClient.configMaps()).thenReturn(configMapsMixedOperation);
when(configMapsMixedOperation.inNamespace(eq(meta.getName())))
.thenReturn(nonNamespaceOperation);
when(nonNamespaceOperation.withName(eq(PREFERENCES_CONFIGMAP_NAME)))
.thenReturn(configMapResource);
when(configMapResource.get()).thenReturn(configMap);
authorisationRequestManager =
new KubernetesAuthorisationRequestManager(
namespaceFactory, cheServerKubernetesClientFactory);
}
@Test
public void shouldStoreAuthorisationRequestToEmptyConfigMap() {
// given
ArgumentCaptor<Map<String, String>> captor = ArgumentCaptor.forClass(Map.class);
// when
authorisationRequestManager.store("test-provider");
// then
verify(configMap).setData(captor.capture());
Map<String, String> value = captor.getValue();
assertEquals(value.get("skip-authorisation"), "[test-provider]");
}
@Test
public void shouldStoreAuthorisationRequestToNonEmptyConfigMap() {
// given
when(configMap.getData()).thenReturn(Map.of("skip-authorisation", "[existing-provider]"));
ArgumentCaptor<Map<String, String>> captor = ArgumentCaptor.forClass(Map.class);
// when
authorisationRequestManager.store("test-provider");
// then
verify(configMap).setData(captor.capture());
Map<String, String> value = captor.getValue();
assertEquals(value.get("skip-authorisation"), "[test-provider, existing-provider]");
}
@Test
public void shouldNotStoreAuthorisationRequestIfAlreadyStored() {
// given
when(configMap.getData()).thenReturn(Map.of("skip-authorisation", "[test-provider]"));
// when
authorisationRequestManager.store("test-provider");
// then
verify(configMap, never()).setData(anyMap());
}
@Test
public void shouldRemoveAuthorisationRequestFromNonEmptyConfigMap() {
// given
when(configMap.getData())
.thenReturn(Map.of("skip-authorisation", "[test-provider, existing-provider]"));
ArgumentCaptor<Map<String, String>> captor = ArgumentCaptor.forClass(Map.class);
// when
authorisationRequestManager.remove("test-provider");
// then
verify(configMap).setData(captor.capture());
Map<String, String> value = captor.getValue();
assertEquals(value.get("skip-authorisation"), "[existing-provider]");
}
@Test
public void shouldRemoveAuthorisationRequestFromSingleValueConfigMap() {
// given
when(configMap.getData()).thenReturn(Map.of("skip-authorisation", "[test-provider]"));
ArgumentCaptor<Map<String, String>> captor = ArgumentCaptor.forClass(Map.class);
// when
authorisationRequestManager.remove("test-provider");
// then
verify(configMap).setData(captor.capture());
Map<String, String> value = captor.getValue();
assertEquals(value.get("skip-authorisation"), "[]");
}
@Test
public void shouldNotRemoveAuthorisationRequestIfAlreadyRemoved() {
// given
when(configMap.getData()).thenReturn(Map.of("skip-authorisation", "[]"));
// when
authorisationRequestManager.remove("test-provider");
// then
verify(configMap, never()).setData(anyMap());
}
@Test
public void shouldReturnFalseAuthorisationRequestStateFromEmptyConfigMap() {
// when
boolean stored = authorisationRequestManager.isStored("test-provider");
// then
assertFalse(stored);
}
@Test
public void shouldReturnFalseAuthorisationRequestStateFromNonEmptyConfigMap() {
// given
when(configMap.getData()).thenReturn(Map.of("skip-authorisation", "[existing-provider]"));
// when
boolean stored = authorisationRequestManager.isStored("test-provider");
// then
assertFalse(stored);
}
@Test
public void shouldReturnTrueAuthorisationRequestStateFromNonEmptyConfigMap() {
// given
when(configMap.getData())
.thenReturn(Map.of("skip-authorisation", "[existing-provider, test-provider]"));
// when
boolean stored = authorisationRequestManager.isStored("test-provider");
// then
assertTrue(stored);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* Copyright (c) 2012-2023 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/
@ -15,7 +15,6 @@ import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesGitCredentialManager.ANNOTATION_CHE_USERID;
import static org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesGitCredentialManager.ANNOTATION_SCM_URL;
import static org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesGitCredentialManager.ANNOTATION_SCM_USERNAME;
import static org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesGitCredentialManager.DEFAULT_SECRET_ANNOTATIONS;
import static org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesGitCredentialManager.NAME_PATTERN;
import static org.mockito.ArgumentMatchers.anyMap;
@ -43,7 +42,7 @@ import java.util.HashMap;
import java.util.Map;
import org.eclipse.che.api.factory.server.scm.PersonalAccessToken;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl;
import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
@ -58,14 +57,14 @@ import org.testng.annotations.Test;
public class KubernetesGitCredentialManagerTest {
@Mock private KubernetesNamespaceFactory namespaceFactory;
@Mock private KubernetesClientFactory clientFactory;
@Mock private CheServerKubernetesClientFactory cheServerKubernetesClientFactory;
@Mock private KubernetesClient kubeClient;
@Mock private MixedOperation<Secret, SecretList, Resource<Secret>> secretsMixedOperation;
@Mock NonNamespaceOperation<Secret, SecretList, Resource<Secret>> nonNamespaceOperation;
@Mock private FilterWatchListDeletable<Secret, SecretList> filterWatchDeletable;
@Mock private FilterWatchListDeletable<Secret, SecretList, Resource<Secret>> filterWatchDeletable;
@Mock private SecretList secretList;
@ -74,7 +73,7 @@ public class KubernetesGitCredentialManagerTest {
@BeforeMethod
protected void init() {
kubernetesGitCredentialManager =
new KubernetesGitCredentialManager(namespaceFactory, clientFactory);
new KubernetesGitCredentialManager(namespaceFactory, cheServerKubernetesClientFactory);
assertNotNull(this.kubernetesGitCredentialManager);
}
@ -83,7 +82,7 @@ public class KubernetesGitCredentialManagerTest {
KubernetesNamespaceMeta meta = new KubernetesNamespaceMetaImpl("test");
when(namespaceFactory.list()).thenReturn(Collections.singletonList(meta));
when(clientFactory.create()).thenReturn(kubeClient);
when(cheServerKubernetesClientFactory.create()).thenReturn(kubeClient);
when(kubeClient.secrets()).thenReturn(secretsMixedOperation);
when(secretsMixedOperation.inNamespace(eq(meta.getName()))).thenReturn(nonNamespaceOperation);
when(nonNamespaceOperation.withLabels(anyMap())).thenReturn(filterWatchDeletable);
@ -93,13 +92,7 @@ public class KubernetesGitCredentialManagerTest {
PersonalAccessToken token =
new PersonalAccessToken(
"https://bitbucket.com",
"cheUser",
"username",
"userId",
"token-name",
"tid-23434",
"token123");
"https://bitbucket.com", "cheUser", "username", "token-name", "tid-23434", "token123");
// when
kubernetesGitCredentialManager.createOrReplace(token);
@ -114,12 +107,63 @@ public class KubernetesGitCredentialManagerTest {
assertFalse(createdSecret.getMetadata().getName().contains(token.getScmUserName()));
}
@Test
public void shouldUseHardcodedUsernameIfScmOrganizationIsDefined() throws Exception {
// given
KubernetesNamespaceMeta namespaceMeta = new KubernetesNamespaceMetaImpl("test");
PersonalAccessToken token =
new PersonalAccessToken(
"https://bitbucket-server.com:5648",
"cheUser",
"cheOrganization",
"username",
"token-name",
"tid-23434",
"token123");
Map<String, String> annotations = new HashMap<>(DEFAULT_SECRET_ANNOTATIONS);
annotations.put(ANNOTATION_SCM_URL, token.getScmProviderUrl() + "/");
annotations.put(ANNOTATION_CHE_USERID, token.getCheUserId());
ObjectMeta objectMeta =
new ObjectMetaBuilder()
.withName(NameGenerator.generate(NAME_PATTERN, 5))
.withAnnotations(annotations)
.build();
Secret existing =
new SecretBuilder()
.withMetadata(objectMeta)
.withData(Map.of("credentials", "foo 123"))
.build();
when(namespaceFactory.list()).thenReturn(Collections.singletonList(namespaceMeta));
when(cheServerKubernetesClientFactory.create()).thenReturn(kubeClient);
when(kubeClient.secrets()).thenReturn(secretsMixedOperation);
when(secretsMixedOperation.inNamespace(eq(namespaceMeta.getName())))
.thenReturn(nonNamespaceOperation);
when(nonNamespaceOperation.withLabels(anyMap())).thenReturn(filterWatchDeletable);
when(filterWatchDeletable.list()).thenReturn(secretList);
when(secretList.getItems()).thenReturn(singletonList(existing));
// when
kubernetesGitCredentialManager.createOrReplace(token);
// then
ArgumentCaptor<Secret> captor = ArgumentCaptor.forClass(Secret.class);
verify(nonNamespaceOperation).createOrReplace(captor.capture());
Secret createdSecret = captor.getValue();
assertNotNull(createdSecret);
assertEquals(
new String(Base64.getDecoder().decode(createdSecret.getData().get("credentials"))),
"https://username:token123@bitbucket-server.com:5648");
}
@Test
public void testCreateAndSaveNewOAuthGitCredential() throws Exception {
KubernetesNamespaceMeta meta = new KubernetesNamespaceMetaImpl("test");
when(namespaceFactory.list()).thenReturn(Collections.singletonList(meta));
when(clientFactory.create()).thenReturn(kubeClient);
when(cheServerKubernetesClientFactory.create()).thenReturn(kubeClient);
when(kubeClient.secrets()).thenReturn(secretsMixedOperation);
when(secretsMixedOperation.inNamespace(eq(meta.getName()))).thenReturn(nonNamespaceOperation);
when(nonNamespaceOperation.withLabels(anyMap())).thenReturn(filterWatchDeletable);
@ -132,7 +176,6 @@ public class KubernetesGitCredentialManagerTest {
"https://bitbucket.com",
"cheUser",
"username",
"userId",
"oauth2-token-name",
"tid-23434",
"token123");
@ -158,7 +201,6 @@ public class KubernetesGitCredentialManagerTest {
"https://bitbucket.com:5648",
"cheUser",
"username",
"userId",
"token-name",
"tid-23434",
"token123");
@ -166,7 +208,6 @@ public class KubernetesGitCredentialManagerTest {
Map<String, String> annotations = new HashMap<>(DEFAULT_SECRET_ANNOTATIONS);
annotations.put(ANNOTATION_SCM_URL, token.getScmProviderUrl() + "/");
annotations.put(ANNOTATION_SCM_USERNAME, token.getScmUserName());
annotations.put(ANNOTATION_CHE_USERID, token.getCheUserId());
ObjectMeta objectMeta =
new ObjectMetaBuilder()
@ -181,7 +222,7 @@ public class KubernetesGitCredentialManagerTest {
when(namespaceFactory.list()).thenReturn(Collections.singletonList(namespaceMeta));
when(clientFactory.create()).thenReturn(kubeClient);
when(cheServerKubernetesClientFactory.create()).thenReturn(kubeClient);
when(kubeClient.secrets()).thenReturn(secretsMixedOperation);
when(secretsMixedOperation.inNamespace(eq(namespaceMeta.getName())))
.thenReturn(nonNamespaceOperation);

View File

@ -12,6 +12,7 @@
package org.eclipse.che.api.factory.server.scm.kubernetes;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.singletonList;
import static org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesPersonalAccessTokenManager.ANNOTATION_CHE_USERID;
import static org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesPersonalAccessTokenManager.ANNOTATION_SCM_PERSONAL_ACCESS_TOKEN_ID;
import static org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesPersonalAccessTokenManager.ANNOTATION_SCM_URL;
@ -37,15 +38,15 @@ import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
import io.fabric8.kubernetes.client.dsl.Resource;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.eclipse.che.api.factory.server.scm.GitCredentialManager;
import org.eclipse.che.api.factory.server.scm.PersonalAccessToken;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenParams;
import org.eclipse.che.api.factory.server.scm.ScmPersonalAccessTokenFetcher;
import org.eclipse.che.commons.subject.SubjectImpl;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl;
import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespace;
@ -64,7 +65,7 @@ import org.testng.annotations.Test;
public class KubernetesPersonalAccessTokenManagerTest {
@Mock private KubernetesNamespaceFactory namespaceFactory;
@Mock private KubernetesClientFactory clientFactory;
@Mock private CheServerKubernetesClientFactory cheServerKubernetesClientFactory;
@Mock private ScmPersonalAccessTokenFetcher scmPersonalAccessTokenFetcher;
@Mock private KubernetesClient kubeClient;
@ -80,19 +81,23 @@ public class KubernetesPersonalAccessTokenManagerTest {
protected void init() {
personalAccessTokenManager =
new KubernetesPersonalAccessTokenManager(
namespaceFactory, clientFactory, scmPersonalAccessTokenFetcher, gitCredentialManager);
namespaceFactory,
cheServerKubernetesClientFactory,
scmPersonalAccessTokenFetcher,
gitCredentialManager);
assertNotNull(this.personalAccessTokenManager);
}
@Test
public void shouldTrimBlankCharsInToken() throws Exception {
KubernetesNamespaceMeta meta = new KubernetesNamespaceMetaImpl("test");
when(namespaceFactory.list()).thenReturn(Collections.singletonList(meta));
when(namespaceFactory.list()).thenReturn(singletonList(meta));
KubernetesNamespace kubernetesnamespace = Mockito.mock(KubernetesNamespace.class);
KubernetesSecrets secrets = Mockito.mock(KubernetesSecrets.class);
when(namespaceFactory.access(eq(null), eq(meta.getName()))).thenReturn(kubernetesnamespace);
when(kubernetesnamespace.secrets()).thenReturn(secrets);
when(scmPersonalAccessTokenFetcher.isValid(any(PersonalAccessToken.class))).thenReturn(true);
when(scmPersonalAccessTokenFetcher.getScmUsername(any(PersonalAccessTokenParams.class)))
.thenReturn(Optional.of("user"));
Map<String, String> data =
Map.of("token", Base64.getEncoder().encodeToString(" token_value \n".getBytes(UTF_8)));
@ -121,22 +126,16 @@ public class KubernetesPersonalAccessTokenManagerTest {
public void testSavingOfPersonalAccessToken() throws Exception {
KubernetesNamespaceMeta meta = new KubernetesNamespaceMetaImpl("test");
when(namespaceFactory.list()).thenReturn(Collections.singletonList(meta));
when(namespaceFactory.list()).thenReturn(singletonList(meta));
when(clientFactory.create()).thenReturn(kubeClient);
when(cheServerKubernetesClientFactory.create()).thenReturn(kubeClient);
when(kubeClient.secrets()).thenReturn(secretsMixedOperation);
when(secretsMixedOperation.inNamespace(eq(meta.getName()))).thenReturn(nonNamespaceOperation);
ArgumentCaptor<Secret> captor = ArgumentCaptor.forClass(Secret.class);
PersonalAccessToken token =
new PersonalAccessToken(
"https://bitbucket.com",
"cheUser",
"username",
"userId",
"token-name",
"tid-24",
"token123");
"https://bitbucket.com", "cheUser", "username", "token-name", "tid-24", "token123");
// when
personalAccessTokenManager.save(token);
@ -159,12 +158,13 @@ public class KubernetesPersonalAccessTokenManagerTest {
public void testGetTokenFromNamespace() throws Exception {
KubernetesNamespaceMeta meta = new KubernetesNamespaceMetaImpl("test");
when(namespaceFactory.list()).thenReturn(Collections.singletonList(meta));
when(namespaceFactory.list()).thenReturn(singletonList(meta));
KubernetesNamespace kubernetesnamespace = Mockito.mock(KubernetesNamespace.class);
KubernetesSecrets secrets = Mockito.mock(KubernetesSecrets.class);
when(namespaceFactory.access(eq(null), eq(meta.getName()))).thenReturn(kubernetesnamespace);
when(kubernetesnamespace.secrets()).thenReturn(secrets);
when(scmPersonalAccessTokenFetcher.isValid(any(PersonalAccessToken.class))).thenReturn(true);
when(scmPersonalAccessTokenFetcher.getScmUsername(any(PersonalAccessTokenParams.class)))
.thenReturn(Optional.of("user"));
Map<String, String> data1 =
Map.of("token", Base64.getEncoder().encodeToString("token1".getBytes(UTF_8)));
@ -209,15 +209,96 @@ public class KubernetesPersonalAccessTokenManagerTest {
}
@Test
public void testGetTokenFromNamespaceWithTrailingSlashMismatch() throws Exception {
public void shouldGetTokenFromASecretWithSCMUsername() throws Exception {
KubernetesNamespaceMeta meta = new KubernetesNamespaceMetaImpl("test");
when(namespaceFactory.list()).thenReturn(Collections.singletonList(meta));
when(namespaceFactory.list()).thenReturn(singletonList(meta));
KubernetesNamespace kubernetesnamespace = Mockito.mock(KubernetesNamespace.class);
KubernetesSecrets secrets = Mockito.mock(KubernetesSecrets.class);
when(namespaceFactory.access(eq(null), eq(meta.getName()))).thenReturn(kubernetesnamespace);
when(kubernetesnamespace.secrets()).thenReturn(secrets);
when(scmPersonalAccessTokenFetcher.isValid(any(PersonalAccessToken.class))).thenReturn(true);
when(scmPersonalAccessTokenFetcher.getScmUsername(any(PersonalAccessTokenParams.class)))
.thenReturn(Optional.of("user"));
Map<String, String> data =
Map.of("token", Base64.getEncoder().encodeToString("token1".getBytes(UTF_8)));
ObjectMeta metaData =
new ObjectMetaBuilder()
.withAnnotations(
Map.of(
ANNOTATION_CHE_USERID,
"user1",
ANNOTATION_SCM_URL,
"http://host1",
"che.eclipse.org/scm-username",
"scm-username"))
.build();
Secret secret = new SecretBuilder().withMetadata(metaData).withData(data).build();
when(secrets.get(any(LabelSelector.class))).thenReturn(singletonList(secret));
// when
Optional<PersonalAccessToken> tokenOptional =
personalAccessTokenManager.get(
new SubjectImpl("user", "user1", "t1", false), "http://host1");
// then
assertTrue(tokenOptional.isPresent());
assertEquals(tokenOptional.get().getCheUserId(), "user1");
assertEquals(tokenOptional.get().getScmProviderUrl(), "http://host1");
assertEquals(tokenOptional.get().getToken(), "token1");
}
@Test
public void shouldGetTokenFromASecretWithoutSCMUsername() throws Exception {
KubernetesNamespaceMeta meta = new KubernetesNamespaceMetaImpl("test");
when(namespaceFactory.list()).thenReturn(singletonList(meta));
KubernetesNamespace kubernetesnamespace = Mockito.mock(KubernetesNamespace.class);
KubernetesSecrets secrets = Mockito.mock(KubernetesSecrets.class);
when(namespaceFactory.access(eq(null), eq(meta.getName()))).thenReturn(kubernetesnamespace);
when(kubernetesnamespace.secrets()).thenReturn(secrets);
when(scmPersonalAccessTokenFetcher.getScmUsername(any(PersonalAccessTokenParams.class)))
.thenReturn(Optional.of("user"));
Map<String, String> data =
Map.of("token", Base64.getEncoder().encodeToString("token1".getBytes(UTF_8)));
ObjectMeta metaData =
new ObjectMetaBuilder()
.withAnnotations(
Map.of(ANNOTATION_CHE_USERID, "user1", ANNOTATION_SCM_URL, "http://host1"))
.build();
Secret secret = new SecretBuilder().withMetadata(metaData).withData(data).build();
when(secrets.get(any(LabelSelector.class))).thenReturn(singletonList(secret));
// when
Optional<PersonalAccessToken> tokenOptional =
personalAccessTokenManager.get(
new SubjectImpl("user", "user1", "t1", false), "http://host1");
// then
assertTrue(tokenOptional.isPresent());
assertEquals(tokenOptional.get().getCheUserId(), "user1");
assertEquals(tokenOptional.get().getScmProviderUrl(), "http://host1");
assertEquals(tokenOptional.get().getToken(), "token1");
}
@Test
public void testGetTokenFromNamespaceWithTrailingSlashMismatch() throws Exception {
KubernetesNamespaceMeta meta = new KubernetesNamespaceMetaImpl("test");
when(namespaceFactory.list()).thenReturn(singletonList(meta));
KubernetesNamespace kubernetesnamespace = Mockito.mock(KubernetesNamespace.class);
KubernetesSecrets secrets = Mockito.mock(KubernetesSecrets.class);
when(namespaceFactory.access(eq(null), eq(meta.getName()))).thenReturn(kubernetesnamespace);
when(kubernetesnamespace.secrets()).thenReturn(secrets);
when(scmPersonalAccessTokenFetcher.getScmUsername(any(PersonalAccessTokenParams.class)))
.thenReturn(Optional.of("user"));
Map<String, String> data1 =
Map.of("token", Base64.getEncoder().encodeToString("token1".getBytes(UTF_8)));
@ -259,13 +340,14 @@ public class KubernetesPersonalAccessTokenManagerTest {
public void shouldDeleteInvalidTokensOnGet() throws Exception {
// given
KubernetesNamespaceMeta meta = new KubernetesNamespaceMetaImpl("test");
when(namespaceFactory.list()).thenReturn(Collections.singletonList(meta));
when(namespaceFactory.list()).thenReturn(singletonList(meta));
KubernetesNamespace kubernetesnamespace = Mockito.mock(KubernetesNamespace.class);
KubernetesSecrets secrets = Mockito.mock(KubernetesSecrets.class);
when(namespaceFactory.access(eq(null), eq(meta.getName()))).thenReturn(kubernetesnamespace);
when(kubernetesnamespace.secrets()).thenReturn(secrets);
when(scmPersonalAccessTokenFetcher.isValid(any(PersonalAccessToken.class))).thenReturn(false);
when(clientFactory.create()).thenReturn(kubeClient);
when(scmPersonalAccessTokenFetcher.getScmUsername(any(PersonalAccessTokenParams.class)))
.thenReturn(Optional.empty());
when(cheServerKubernetesClientFactory.create()).thenReturn(kubeClient);
when(kubeClient.secrets()).thenReturn(secretsMixedOperation);
when(secretsMixedOperation.inNamespace(eq(meta.getName()))).thenReturn(nonNamespaceOperation);
Map<String, String> data1 =
@ -290,19 +372,21 @@ public class KubernetesPersonalAccessTokenManagerTest {
public void shouldReturnFirstValidToken() throws Exception {
// given
KubernetesNamespaceMeta meta = new KubernetesNamespaceMetaImpl("test");
when(namespaceFactory.list()).thenReturn(Collections.singletonList(meta));
when(namespaceFactory.list()).thenReturn(singletonList(meta));
KubernetesNamespace kubernetesnamespace = Mockito.mock(KubernetesNamespace.class);
KubernetesSecrets secrets = Mockito.mock(KubernetesSecrets.class);
when(namespaceFactory.access(eq(null), eq(meta.getName()))).thenReturn(kubernetesnamespace);
when(kubernetesnamespace.secrets()).thenReturn(secrets);
when(scmPersonalAccessTokenFetcher.isValid(any(PersonalAccessToken.class)))
when(scmPersonalAccessTokenFetcher.getScmUsername(any(PersonalAccessTokenParams.class)))
.thenAnswer(
(Answer<Boolean>)
(Answer<Optional<String>>)
invocation -> {
PersonalAccessToken token = invocation.getArgument(0);
return "id2".equals(token.getScmTokenId());
PersonalAccessTokenParams params = invocation.getArgument(0);
return "id2".equals(params.getScmTokenId())
? Optional.of("user")
: Optional.empty();
});
when(clientFactory.create()).thenReturn(kubeClient);
when(cheServerKubernetesClientFactory.create()).thenReturn(kubeClient);
when(kubeClient.secrets()).thenReturn(secretsMixedOperation);
when(secretsMixedOperation.inNamespace(eq(meta.getName()))).thenReturn(nonNamespaceOperation);
Map<String, String> data1 =

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-infrastructures-parent</artifactId>
<groupId>org.eclipse.che.infrastructure</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>infrastructure-metrics</artifactId>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-infrastructures-parent</artifactId>
<groupId>org.eclipse.che.infrastructure</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>infrastructure-permission</artifactId>
<name>Infrastructure :: Kubernetes Permissions</name>

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-infrastructures-parent</artifactId>
<groupId>org.eclipse.che.infrastructure</groupId>
<version>7.64.0-SNAPSHOT</version>
<version>7.76.0-SNAPSHOT</version>
</parent>
<artifactId>infrastructure-kubernetes</artifactId>
<name>Infrastructure :: Kubernetes</name>
@ -63,7 +63,7 @@
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client</artifactId>
<artifactId>kubernetes-client-api</artifactId>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>

View File

@ -11,7 +11,6 @@
*/
package org.eclipse.che.workspace.infrastructure.kubernetes;
import io.fabric8.kubernetes.client.BaseKubernetesClient;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClient;
@ -19,7 +18,6 @@ import io.fabric8.kubernetes.client.http.HttpClient;
import io.fabric8.kubernetes.client.utils.HttpClientUtils;
import javax.inject.Inject;
import javax.inject.Singleton;
import okhttp3.OkHttpClient;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.commons.annotation.Nullable;
@ -94,12 +92,12 @@ public class KubernetesClientFactory {
}
/**
* Creates instance of {@link KubernetesClient} that uses an {@link OkHttpClient} instance derived
* Creates instance of {@link KubernetesClient} that uses an {@link HttpClient} instance derived
* from the shared {@code httpClient} instance in which interceptors are overridden to
* authenticate with the credentials (user/password or Oauth token) contained in the {@code
* config} parameter.
*/
protected BaseKubernetesClient<?> create(Config config) {
protected KubernetesClient create(Config config) {
return new UnclosableKubernetesClient(httpClient, config);
}

View File

@ -41,7 +41,6 @@ public class KubernetesInfrastructure extends RuntimeInfrastructure {
private final KubernetesRuntimeContextFactory runtimeContextFactory;
private final KubernetesRuntimeStateCache runtimeStatusesCache;
private final KubernetesNamespaceFactory namespaceFactory;
private final KubernetesClientFactory kubernetesClientFactory;
@Inject
public KubernetesInfrastructure(
@ -49,8 +48,7 @@ public class KubernetesInfrastructure extends RuntimeInfrastructure {
KubernetesRuntimeContextFactory runtimeContextFactory,
Set<InternalEnvironmentProvisioner> internalEnvProvisioners,
KubernetesRuntimeStateCache runtimeStatusesCache,
KubernetesNamespaceFactory namespaceFactory,
KubernetesClientFactory kubernetesClientFactory) {
KubernetesNamespaceFactory namespaceFactory) {
super(
NAME,
ImmutableSet.of(KubernetesEnvironment.TYPE, Constants.NO_ENVIRONMENT_RECIPE_TYPE),
@ -59,7 +57,6 @@ public class KubernetesInfrastructure extends RuntimeInfrastructure {
this.runtimeContextFactory = runtimeContextFactory;
this.runtimeStatusesCache = runtimeStatusesCache;
this.namespaceFactory = namespaceFactory;
this.kubernetesClientFactory = kubernetesClientFactory;
}
@Override

View File

@ -37,6 +37,7 @@ import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodSpec;
import io.fabric8.kubernetes.api.model.PodStatus;
import io.fabric8.kubernetes.api.model.ServiceAccount;
import io.fabric8.kubernetes.api.model.StatusDetails;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
import io.fabric8.kubernetes.client.KubernetesClientException;
@ -71,7 +72,6 @@ import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import okhttp3.Response;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
@ -301,7 +301,7 @@ public class KubernetesDeployments {
CompletableFuture<Pod> future = new CompletableFuture<>();
try {
PodResource<Pod> podResource =
PodResource podResource =
clientFactory.create(workspaceId).pods().inNamespace(namespace).withName(podName);
Watch watch =
@ -366,7 +366,7 @@ public class KubernetesDeployments {
final CompletableFuture<Void> podRunningFuture = new CompletableFuture<>();
try {
final String podName = getPodName(name);
final PodResource<Pod> podResource =
final PodResource podResource =
clientFactory.create(workspaceId).pods().inNamespace(namespace).withName(podName);
final Watch watch =
podResource.watch(
@ -941,18 +941,18 @@ public class KubernetesDeployments {
// If we have a Pod, we have to watch to make sure it is deleted, otherwise, we watch the
// Deployment we are deleting.
if (!Strings.isNullOrEmpty(podName)) {
PodResource<Pod> podResource =
PodResource podResource =
clientFactory.create(workspaceId).pods().inNamespace(namespace).withName(podName);
watch = podResource.watch(new DeleteWatcher<>(deleteFuture));
toCloseOnException = watch;
} else {
watch = deploymentResource.watch(new DeleteWatcher<Deployment>(deleteFuture));
watch = deploymentResource.watch(new DeleteWatcher<>(deleteFuture));
toCloseOnException = watch;
}
Boolean deleteSucceeded = deploymentResource.withPropagationPolicy(BACKGROUND).delete();
List<StatusDetails> deleted = deploymentResource.withPropagationPolicy(BACKGROUND).delete();
if (deleteSucceeded == null || !deleteSucceeded) {
if (deleted == null || deleted.isEmpty()) {
deleteFuture.complete(null);
}
return deleteFuture.whenComplete(
@ -978,7 +978,7 @@ public class KubernetesDeployments {
protected CompletableFuture<Void> doDeletePod(String podName) throws InfrastructureException {
Watch toCloseOnException = null;
try {
PodResource<Pod> podResource =
PodResource podResource =
clientFactory.create(workspaceId).pods().inNamespace(namespace).withName(podName);
if (podResource.get() == null) {
throw new InfrastructureException(format("No pod found to delete for name %s", podName));
@ -988,8 +988,8 @@ public class KubernetesDeployments {
final Watch watch = podResource.watch(new DeleteWatcher<>(deleteFuture));
toCloseOnException = watch;
Boolean deleteSucceeded = podResource.withPropagationPolicy(BACKGROUND).delete();
if (deleteSucceeded == null || !deleteSucceeded) {
List<StatusDetails> deleted = podResource.withPropagationPolicy(BACKGROUND).delete();
if (deleted == null || deleted.isEmpty()) {
deleteFuture.complete(null);
}
return deleteFuture.whenComplete(

View File

@ -41,7 +41,7 @@ import java.util.concurrent.TimeoutException;
import java.util.function.Predicate;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.InternalInfrastructureException;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesInfrastructureException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -73,18 +73,15 @@ public class KubernetesNamespace {
private final KubernetesServices services;
private final KubernetesPersistentVolumeClaims pvcs;
private final KubernetesIngresses ingresses;
/** Factory for workspace related operations clients */
private final KubernetesClientFactory clientFactory;
/** Factory for cluster related operations clients (like labeling the namespaces) */
private final KubernetesClientFactory cheSAClientFactory;
private final CheServerKubernetesClientFactory cheSAClientFactory;
private final KubernetesSecrets secrets;
private final KubernetesConfigsMaps configMaps;
@VisibleForTesting
protected KubernetesNamespace(
KubernetesClientFactory clientFactory,
KubernetesClientFactory cheSAClientFactory,
CheServerKubernetesClientFactory cheSAClientFactory,
String workspaceId,
String name,
KubernetesDeployments deployments,
@ -93,7 +90,6 @@ public class KubernetesNamespace {
KubernetesIngresses kubernetesIngresses,
KubernetesSecrets secrets,
KubernetesConfigsMaps configMaps) {
this.clientFactory = clientFactory;
this.cheSAClientFactory = cheSAClientFactory;
this.workspaceId = workspaceId;
this.name = name;
@ -106,21 +102,19 @@ public class KubernetesNamespace {
}
public KubernetesNamespace(
KubernetesClientFactory clientFactory,
KubernetesClientFactory cheSAClientFactory,
CheServerKubernetesClientFactory cheSAClientFactory,
Executor executor,
String name,
String workspaceId) {
this.clientFactory = clientFactory;
this.cheSAClientFactory = cheSAClientFactory;
this.workspaceId = workspaceId;
this.name = name;
this.deployments = new KubernetesDeployments(name, workspaceId, clientFactory, executor);
this.services = new KubernetesServices(name, workspaceId, clientFactory);
this.pvcs = new KubernetesPersistentVolumeClaims(name, workspaceId, clientFactory);
this.ingresses = new KubernetesIngresses(name, workspaceId, clientFactory);
this.secrets = new KubernetesSecrets(name, workspaceId, clientFactory);
this.configMaps = new KubernetesConfigsMaps(name, workspaceId, clientFactory);
this.deployments = new KubernetesDeployments(name, workspaceId, cheSAClientFactory, executor);
this.services = new KubernetesServices(name, workspaceId, cheSAClientFactory);
this.pvcs = new KubernetesPersistentVolumeClaims(name, workspaceId, cheSAClientFactory);
this.ingresses = new KubernetesIngresses(name, workspaceId, cheSAClientFactory);
this.secrets = new KubernetesSecrets(name, workspaceId, cheSAClientFactory);
this.configMaps = new KubernetesConfigsMaps(name, workspaceId, cheSAClientFactory);
}
/**
@ -270,7 +264,7 @@ public class KubernetesNamespace {
* @throws InfrastructureException if any unexpected exception occurs during namespace deletion
*/
void delete() throws InfrastructureException {
KubernetesClient client = clientFactory.create(workspaceId);
KubernetesClient client = cheSAClientFactory.create(workspaceId);
try {
delete(name, client);

View File

@ -46,7 +46,6 @@ import org.eclipse.che.api.core.ValidationException;
import org.eclipse.che.api.core.model.workspace.Workspace;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.api.user.server.PreferenceManager;
import org.eclipse.che.api.user.server.UserManager;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
import org.eclipse.che.commons.annotation.Nullable;
@ -56,7 +55,6 @@ import org.eclipse.che.commons.lang.Pair;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.inject.ConfigurationException;
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl;
import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator;
@ -95,10 +93,8 @@ public class KubernetesNamespaceFactory {
protected final Map<String, String> namespaceLabels;
protected final Map<String, String> namespaceAnnotations;
private final KubernetesClientFactory clientFactory;
private final KubernetesClientFactory cheClientFactory;
private final CheServerKubernetesClientFactory cheServerKubernetesClientFactory;
private final boolean namespaceCreationAllowed;
private final UserManager userManager;
private final PreferenceManager preferenceManager;
protected final Set<NamespaceConfigurator> namespaceConfigurators;
protected final KubernetesSharedPool sharedPool;
@ -112,16 +108,12 @@ public class KubernetesNamespaceFactory {
@Named("che.infra.kubernetes.namespace.labels") String namespaceLabels,
@Named("che.infra.kubernetes.namespace.annotations") String namespaceAnnotations,
Set<NamespaceConfigurator> namespaceConfigurators,
KubernetesClientFactory clientFactory,
CheServerKubernetesClientFactory cheClientFactory,
UserManager userManager,
CheServerKubernetesClientFactory cheServerKubernetesClientFactory,
PreferenceManager preferenceManager,
KubernetesSharedPool sharedPool)
throws ConfigurationException {
this.namespaceCreationAllowed = namespaceCreationAllowed;
this.userManager = userManager;
this.clientFactory = clientFactory;
this.cheClientFactory = cheClientFactory;
this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory;
this.defaultNamespaceName = defaultNamespaceName;
this.preferenceManager = preferenceManager;
this.sharedPool = sharedPool;
@ -168,7 +160,7 @@ public class KubernetesNamespaceFactory {
@VisibleForTesting
KubernetesNamespace doCreateNamespaceAccess(String workspaceId, String name) {
return new KubernetesNamespace(
clientFactory, cheClientFactory, sharedPool.getExecutor(), name, workspaceId);
cheServerKubernetesClientFactory, sharedPool.getExecutor(), name, workspaceId);
}
/**
@ -241,7 +233,8 @@ public class KubernetesNamespaceFactory {
public Optional<KubernetesNamespaceMeta> fetchNamespace(String name)
throws InfrastructureException {
try {
Namespace namespace = cheClientFactory.create().namespaces().withName(name).get();
Namespace namespace =
cheServerKubernetesClientFactory.create().namespaces().withName(name).get();
if (namespace == null) {
return Optional.empty();
} else {
@ -501,7 +494,12 @@ public class KubernetesNamespaceFactory {
NamespaceResolutionContext namespaceCtx) throws InfrastructureException {
try {
List<Namespace> workspaceNamespaces =
cheClientFactory.create().namespaces().withLabels(namespaceLabels).list().getItems();
cheServerKubernetesClientFactory
.create()
.namespaces()
.withLabels(namespaceLabels)
.list()
.getItems();
if (!workspaceNamespaces.isEmpty()) {
Map<String, String> evaluatedAnnotations = evaluateAnnotationPlaceholders(namespaceCtx);
return workspaceNamespaces.stream()

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* Copyright (c) 2012-2023 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/
@ -22,7 +22,7 @@ import io.fabric8.kubernetes.api.model.LabelSelector;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
import io.fabric8.kubernetes.api.model.PersistentVolumeClaimBuilder;
import io.fabric8.kubernetes.api.model.PersistentVolumeClaimFluent.SpecNested;
import io.fabric8.kubernetes.api.model.PersistentVolumeClaimFluent;
import io.fabric8.kubernetes.api.model.PersistentVolumeClaimVolumeSource;
import io.fabric8.kubernetes.api.model.PersistentVolumeClaimVolumeSourceBuilder;
import io.fabric8.kubernetes.api.model.Quantity;
@ -199,13 +199,15 @@ public class KubernetesObjectUtil {
*/
public static PersistentVolumeClaim newPVC(
String name, String accessMode, String quantity, String storageClassName) {
SpecNested<PersistentVolumeClaimBuilder> specs =
new PersistentVolumeClaimBuilder()
.withNewMetadata()
.withName(name)
.endMetadata()
.withNewSpec()
.withAccessModes(accessMode);
PersistentVolumeClaimFluent<PersistentVolumeClaimBuilder>.SpecNested<
PersistentVolumeClaimBuilder>
specs =
new PersistentVolumeClaimBuilder()
.withNewMetadata()
.withName(name)
.endMetadata()
.withNewSpec()
.withAccessModes(accessMode);
if (!isNullOrEmpty(storageClassName)) {
specs.withStorageClassName(storageClassName);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* Copyright (c) 2012-2023 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/
@ -11,15 +11,20 @@
*/
package org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator;
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.CREDENTIALS_SECRET_NAME;
import com.google.common.collect.ImmutableMap;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.api.model.SecretBuilder;
import java.util.Base64;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
import org.eclipse.che.api.factory.server.scm.exception.ScmCommunicationException;
import org.eclipse.che.api.factory.server.scm.exception.ScmConfigurationPersistenceException;
import org.eclipse.che.api.factory.server.scm.exception.ScmUnauthorizedException;
import org.eclipse.che.api.factory.server.scm.exception.UnsatisfiedScmPreconditionException;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
/**
* This {@link NamespaceConfigurator} ensures that Secret {@link
@ -29,27 +34,57 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFacto
@Singleton
public class CredentialsSecretConfigurator implements NamespaceConfigurator {
private final KubernetesClientFactory clientFactory;
private final CheServerKubernetesClientFactory cheServerKubernetesClientFactory;
private final PersonalAccessTokenManager personalAccessTokenManager;
private static final Map<String, String> SEARCH_LABELS =
ImmutableMap.of(
"app.kubernetes.io/part-of", "che.eclipse.org",
"app.kubernetes.io/component", "scm-personal-access-token");
private static final String ANNOTATION_SCM_URL = "che.eclipse.org/scm-url";
private static final String MERGED_GIT_CREDENTIALS_SECRET_NAME =
"devworkspace-merged-git-credentials";
@Inject
public CredentialsSecretConfigurator(KubernetesClientFactory clientFactory) {
this.clientFactory = clientFactory;
public CredentialsSecretConfigurator(
CheServerKubernetesClientFactory cheServerKubernetesClientFactory,
PersonalAccessTokenManager personalAccessTokenManager) {
this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory;
this.personalAccessTokenManager = personalAccessTokenManager;
}
@Override
public void configure(NamespaceResolutionContext namespaceResolutionContext, String namespaceName)
throws InfrastructureException {
var client = clientFactory.create();
if (client.secrets().inNamespace(namespaceName).withName(CREDENTIALS_SECRET_NAME).get()
== null) {
Secret secret =
new SecretBuilder()
.withType("opaque")
.withNewMetadata()
.withName(CREDENTIALS_SECRET_NAME)
.endMetadata()
.build();
client.secrets().inNamespace(namespaceName).create(secret);
}
var client = cheServerKubernetesClientFactory.create();
Secret mergedCredentialsSecret =
client
.secrets()
.inNamespace(namespaceName)
.withName(MERGED_GIT_CREDENTIALS_SECRET_NAME)
.get();
client.secrets().inNamespace(namespaceName).withLabels(SEARCH_LABELS).list().getItems().stream()
.filter(
s ->
mergedCredentialsSecret == null
|| !getSecretData(mergedCredentialsSecret, "credentials")
.contains(getSecretData(s, "token")))
.forEach(
s -> {
try {
personalAccessTokenManager.store(
s.getMetadata().getAnnotations().get(ANNOTATION_SCM_URL));
} catch (ScmCommunicationException
| ScmConfigurationPersistenceException
| UnsatisfiedScmPreconditionException
| ScmUnauthorizedException e) {
throw new RuntimeException(e);
}
});
}
private String getSecretData(Secret secret, String key) {
return new String(Base64.getDecoder().decode(secret.getData().get(key).getBytes()));
}
}

View File

@ -22,42 +22,41 @@ import java.util.Set;
import javax.inject.Inject;
import org.eclipse.che.api.factory.server.scm.GitUserData;
import org.eclipse.che.api.factory.server.scm.GitUserDataFetcher;
import org.eclipse.che.api.factory.server.scm.exception.ScmCommunicationException;
import org.eclipse.che.api.factory.server.scm.exception.ScmUnauthorizedException;
import org.eclipse.che.api.user.server.UserManager;
import org.eclipse.che.api.factory.server.scm.exception.*;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GitconfigUserDataConfigurator implements NamespaceConfigurator {
private static final Logger LOG = LoggerFactory.getLogger(GitconfigUserDataConfigurator.class);
private final KubernetesClientFactory clientFactory;
private final CheServerKubernetesClientFactory cheServerKubernetesClientFactory;
private final Set<GitUserDataFetcher> gitUserDataFetchers;
private static final String CONFIGMAP_DATA_KEY = "gitconfig";
private final UserManager userManager;
@Inject
public GitconfigUserDataConfigurator(
KubernetesClientFactory clientFactory,
Set<GitUserDataFetcher> gitUserDataFetchers,
UserManager userManager) {
this.clientFactory = clientFactory;
CheServerKubernetesClientFactory cheServerKubernetesClientFactory,
Set<GitUserDataFetcher> gitUserDataFetchers) {
this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory;
this.gitUserDataFetchers = gitUserDataFetchers;
this.userManager = userManager;
}
@Override
public void configure(NamespaceResolutionContext namespaceResolutionContext, String namespaceName)
throws InfrastructureException {
var client = clientFactory.create();
var client = cheServerKubernetesClientFactory.create();
GitUserData gitUserData = null;
for (GitUserDataFetcher fetcher : gitUserDataFetchers) {
try {
gitUserData = fetcher.fetchGitUserData();
break;
} catch (ScmUnauthorizedException | ScmCommunicationException e) {
} catch (ScmUnauthorizedException
| ScmCommunicationException
| ScmConfigurationPersistenceException
| ScmItemNotFoundException
| ScmBadRequestException e) {
LOG.debug("No GitUserDataFetcher is configured. " + e.getMessage());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* Copyright (c) 2012-2023 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/
@ -18,7 +18,7 @@ import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import javax.inject.Inject;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
/**
* This {@link NamespaceConfigurator} ensures that ConfigMap {@link
@ -27,17 +27,18 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFacto
*/
public class PreferencesConfigMapConfigurator implements NamespaceConfigurator {
private final KubernetesClientFactory clientFactory;
private final CheServerKubernetesClientFactory cheServerKubernetesClientFactory;
@Inject
public PreferencesConfigMapConfigurator(KubernetesClientFactory clientFactory) {
this.clientFactory = clientFactory;
public PreferencesConfigMapConfigurator(
CheServerKubernetesClientFactory cheServerKubernetesClientFactory) {
this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory;
}
@Override
public void configure(NamespaceResolutionContext namespaceResolutionContext, String namespaceName)
throws InfrastructureException {
var client = clientFactory.create();
var client = cheServerKubernetesClientFactory.create();
if (client.configMaps().inNamespace(namespaceName).withName(PREFERENCES_CONFIGMAP_NAME).get()
== null) {
ConfigMap configMap =

View File

@ -38,7 +38,7 @@ import org.eclipse.che.api.ssh.server.model.impl.SshPairImpl;
import org.eclipse.che.api.ssh.shared.model.SshPair;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -55,7 +55,7 @@ public class SshKeysConfigurator implements NamespaceConfigurator {
private final SshManager sshManager;
private final KubernetesClientFactory clientFactory;
private final CheServerKubernetesClientFactory cheServerKubernetesClientFactory;
private static final Logger LOG = LoggerFactory.getLogger(SshKeysConfigurator.class);
@ -63,16 +63,17 @@ public class SshKeysConfigurator implements NamespaceConfigurator {
Pattern.compile("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$");
@Inject
public SshKeysConfigurator(SshManager sshManager, KubernetesClientFactory clientFactory) {
public SshKeysConfigurator(
SshManager sshManager, CheServerKubernetesClientFactory cheServerKubernetesClientFactory) {
this.sshManager = sshManager;
this.clientFactory = clientFactory;
this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory;
}
@Override
public void configure(NamespaceResolutionContext namespaceResolutionContext, String namespaceName)
throws InfrastructureException {
var client = clientFactory.create();
var client = cheServerKubernetesClientFactory.create();
List<SshPairImpl> vcsSshPairs = getVcsSshPairs(namespaceResolutionContext);
List<String> invalidSshKeyNames =

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* Copyright (c) 2012-2023 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/
@ -26,7 +26,6 @@ import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
/**
* This {@link NamespaceConfigurator} ensures that User has assigned configured ClusterRoles from
@ -37,13 +36,13 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFacto
public class UserPermissionConfigurator implements NamespaceConfigurator {
private final Set<String> userClusterRoles;
private final KubernetesClientFactory clientFactory;
private final CheServerKubernetesClientFactory cheServerKubernetesClientFactory;
@Inject
public UserPermissionConfigurator(
@Nullable @Named("che.infra.kubernetes.user_cluster_roles") String userClusterRoles,
CheServerKubernetesClientFactory cheClientFactory) {
this.clientFactory = cheClientFactory;
CheServerKubernetesClientFactory cheServerKubernetesClientFactory) {
this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory;
if (!isNullOrEmpty(userClusterRoles)) {
this.userClusterRoles =
Sets.newHashSet(
@ -58,7 +57,7 @@ public class UserPermissionConfigurator implements NamespaceConfigurator {
throws InfrastructureException {
if (!userClusterRoles.isEmpty()) {
bindRoles(
clientFactory.create(),
cheServerKubernetesClientFactory.create(),
namespaceName,
namespaceResolutionContext.getUserName(),
userClusterRoles);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2022 Red Hat, Inc.
* Copyright (c) 2012-2023 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/
@ -15,10 +15,7 @@ import com.google.common.annotations.VisibleForTesting;
import io.fabric8.kubernetes.api.model.Secret;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.user.server.PreferenceManager;
import org.eclipse.che.api.user.server.UserManager;
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -36,19 +33,8 @@ public class UserPreferencesConfigurator implements NamespaceConfigurator {
private static final String USER_PREFERENCES_SECRET_MOUNT_PATH = "/config/user/preferences";
private static final int PREFERENCE_NAME_MAX_LENGTH = 253;
private final KubernetesClientFactory clientFactory;
private final UserManager userManager;
private final PreferenceManager preferenceManager;
@Inject
public UserPreferencesConfigurator(
KubernetesClientFactory clientFactory,
UserManager userManager,
PreferenceManager preferenceManager) {
this.clientFactory = clientFactory;
this.userManager = userManager;
this.preferenceManager = preferenceManager;
}
public UserPreferencesConfigurator() {}
@Override
public void configure(

View File

@ -24,10 +24,9 @@ import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.user.server.UserManager;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
/**
* Creates {@link Secret} with user profile information such as his id, name and email. This serves
@ -40,13 +39,12 @@ public class UserProfileConfigurator implements NamespaceConfigurator {
private static final String USER_PROFILE_SECRET_NAME = "user-profile";
private static final String USER_PROFILE_SECRET_MOUNT_PATH = "/config/user/profile";
private final KubernetesClientFactory clientFactory;
private final UserManager userManager;
private final CheServerKubernetesClientFactory cheServerKubernetesClientFactory;
@Inject
public UserProfileConfigurator(KubernetesClientFactory clientFactory, UserManager userManager) {
this.clientFactory = clientFactory;
this.userManager = userManager;
public UserProfileConfigurator(
CheServerKubernetesClientFactory cheServerKubernetesClientFactory) {
this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory;
}
@Override
@ -54,7 +52,7 @@ public class UserProfileConfigurator implements NamespaceConfigurator {
throws InfrastructureException {
Secret userProfileSecret = prepareProfileSecret(namespaceResolutionContext);
try {
clientFactory
cheServerKubernetesClientFactory
.create()
.secrets()
.inNamespace(namespaceName)
@ -83,6 +81,7 @@ public class UserProfileConfigurator implements NamespaceConfigurator {
.withName(USER_PROFILE_SECRET_NAME)
.addToLabels(DEV_WORKSPACE_MOUNT_LABEL, "true")
.addToLabels(DEV_WORKSPACE_WATCH_SECRET_LABEL, "true")
.addToLabels("app.kubernetes.io/part-of", "che.eclipse.org")
.addToAnnotations(DEV_WORKSPACE_MOUNT_AS_ANNOTATION, "file")
.addToAnnotations(DEV_WORKSPACE_MOUNT_PATH_ANNOTATION, USER_PROFILE_SECRET_MOUNT_PATH)
.endMetadata()

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* Copyright (c) 2012-2023 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/
@ -24,7 +24,7 @@ import javax.inject.Singleton;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesWorkspaceServiceAccount;
/**
@ -34,7 +34,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesW
@Singleton
public class WorkspaceServiceAccountConfigurator implements NamespaceConfigurator {
private final KubernetesClientFactory clientFactory;
private final CheServerKubernetesClientFactory cheServerKubernetesClientFactory;
private final String serviceAccountName;
private final Set<String> clusterRoleNames;
@ -43,8 +43,8 @@ public class WorkspaceServiceAccountConfigurator implements NamespaceConfigurato
public WorkspaceServiceAccountConfigurator(
@Nullable @Named("che.infra.kubernetes.service_account_name") String serviceAccountName,
@Nullable @Named("che.infra.kubernetes.workspace_sa_cluster_roles") String clusterRoleNames,
KubernetesClientFactory clientFactory) {
this.clientFactory = clientFactory;
CheServerKubernetesClientFactory cheServerKubernetesClientFactory) {
this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory;
this.serviceAccountName = serviceAccountName;
if (!isNullOrEmpty(clusterRoleNames)) {
this.clusterRoleNames =
@ -69,6 +69,10 @@ public class WorkspaceServiceAccountConfigurator implements NamespaceConfigurato
public KubernetesWorkspaceServiceAccount doCreateServiceAccount(
String workspaceId, String namespaceName) {
return new KubernetesWorkspaceServiceAccount(
workspaceId, namespaceName, serviceAccountName, clusterRoleNames, clientFactory);
workspaceId,
namespaceName,
serviceAccountName,
clusterRoleNames,
cheServerKubernetesClientFactory);
}
}

View File

@ -12,14 +12,11 @@
package org.eclipse.che.workspace.infrastructure.kubernetes;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import io.fabric8.kubernetes.client.Config;
import java.util.Collections;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.workspace.infrastructure.kubernetes.cache.KubernetesRuntimeStateCache;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
@ -27,7 +24,6 @@ import org.testng.annotations.Listeners;
@Listeners(MockitoTestNGListener.class)
public class KubernetesInfrastructureTest {
@Mock private KubernetesClientFactory factory;
private KubernetesInfrastructure infra;
@BeforeMethod
@ -38,9 +34,6 @@ public class KubernetesInfrastructureTest {
mock(KubernetesRuntimeContextFactory.class),
Collections.emptySet(),
mock(KubernetesRuntimeStateCache.class),
mock(KubernetesNamespaceFactory.class),
factory);
when(factory.getDefaultConfig()).thenReturn(mock(Config.class));
mock(KubernetesNamespaceFactory.class));
}
}

View File

@ -843,7 +843,7 @@ public class KubernetesInternalRuntimeTest {
}
private static IntOrString intOrString(int port) {
return new IntOrStringBuilder().withIntVal(port).withStrVal(String.valueOf(port)).build();
return new IntOrStringBuilder().withValue(port).withValue(String.valueOf(port)).build();
}
private static class MapBasedRuntimeStateCache implements KubernetesRuntimeStateCache {

View File

@ -25,8 +25,8 @@ import com.google.common.collect.ImmutableMap;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.api.model.ConfigMapList;
import io.fabric8.kubernetes.client.GracePeriodConfigurable;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.dsl.EditReplacePatchDeletable;
import io.fabric8.kubernetes.client.dsl.MixedOperation;
import io.fabric8.kubernetes.client.dsl.Resource;
import java.util.Arrays;
@ -70,7 +70,7 @@ public class CheNamespaceTest {
@Mock
private MixedOperation<ConfigMap, ConfigMapList, Resource<ConfigMap>> kubeConfigMapsWithLabel;
@Mock private EditReplacePatchDeletable<ConfigMap> kubeConfigMapsWithPropagationPolicy;
@Mock private GracePeriodConfigurable gracePeriodConfigurable;
@Mock private InternalRuntime internalRuntime;
@ -237,12 +237,12 @@ public class CheNamespaceTest {
when(kubeConfigMapsInNamespace.withLabel(CHE_WORKSPACE_ID_LABEL, WORKSPACE_ID))
.thenReturn(kubeConfigMapsWithLabel);
when(kubeConfigMapsWithLabel.withPropagationPolicy(BACKGROUND))
.thenReturn(kubeConfigMapsWithPropagationPolicy);
.thenReturn(gracePeriodConfigurable);
// when
cheNamespace.cleanUp(WORKSPACE_ID);
// then
verify(kubeConfigMapsWithPropagationPolicy).delete();
verify(gracePeriodConfigurable).delete();
}
}

View File

@ -13,6 +13,7 @@ package org.eclipse.che.workspace.infrastructure.kubernetes.namespace;
import static io.fabric8.kubernetes.api.model.DeletionPropagation.BACKGROUND;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.POD_STATUS_PHASE_FAILED;
import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.POD_STATUS_PHASE_RUNNING;
@ -55,12 +56,12 @@ import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
import io.fabric8.kubernetes.api.model.apps.DeploymentList;
import io.fabric8.kubernetes.api.model.apps.DeploymentSpec;
import io.fabric8.kubernetes.api.model.apps.DeploymentSpecBuilder;
import io.fabric8.kubernetes.client.GracePeriodConfigurable;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.Watch;
import io.fabric8.kubernetes.client.Watcher;
import io.fabric8.kubernetes.client.dsl.AppsAPIGroupDSL;
import io.fabric8.kubernetes.client.dsl.EditReplacePatchDeletable;
import io.fabric8.kubernetes.client.dsl.FilterWatchListDeletable;
import io.fabric8.kubernetes.client.dsl.MixedOperation;
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
@ -119,12 +120,12 @@ public class KubernetesDeploymentsTest {
@Mock private Deployment deployment;
@Mock private ObjectMeta deploymentMetadata;
@Mock private DeploymentSpec deploymentSpec;
@Mock private EditReplacePatchDeletable<Deployment> deploymentEditReplacePatchDeletable;
@Mock private GracePeriodConfigurable gracePeriodConfigurable;
// Pod Mocks
@Mock private Pod pod;
@Mock private PodStatus status;
@Mock private PodResource<Pod> podResource;
@Mock private PodResource podResource;
@Mock private ObjectMeta metadata;
@Mock private MixedOperation podsMixedOperation;
@Mock private NonNamespaceOperation podsNamespaceOperation;
@ -491,7 +492,7 @@ public class KubernetesDeploymentsTest {
public void testDeleteNonExistingPodBeforeWatch() throws Exception {
final String POD_NAME = "nonExistingPod";
doReturn(Boolean.FALSE).when(podResource).delete();
doReturn(emptyList()).when(podResource).delete();
doReturn(podResource).when(podResource).withPropagationPolicy(eq(BACKGROUND));
Watch watch = mock(Watch.class);
doReturn(watch).when(podResource).watch(any());
@ -527,7 +528,7 @@ public class KubernetesDeploymentsTest {
public void testDeleteNonExistingDeploymentBeforeWatch() throws Exception {
final String DEPLOYMENT_NAME = "nonExistingPod";
doReturn(deploymentResource).when(deploymentResource).withPropagationPolicy(eq(BACKGROUND));
doReturn(Boolean.FALSE).when(deploymentResource).delete();
doReturn(emptyList()).when(deploymentResource).delete();
Watch watch = mock(Watch.class);
doReturn(watch).when(podResource).watch(any());
@ -587,9 +588,9 @@ public class KubernetesDeploymentsTest {
public void testDeleteDeploymentThrowingAnyExceptionShouldCloseWatch() throws Exception {
final String DEPLOYMENT_NAME = "nonExistingPod";
when(deploymentResource.withPropagationPolicy(eq(BACKGROUND)))
.thenReturn(deploymentEditReplacePatchDeletable);
.thenReturn(gracePeriodConfigurable);
doThrow(new RuntimeException("testDeleteDeploymentThrowingAnyExceptionShouldCloseWatch msg"))
.when(deploymentEditReplacePatchDeletable)
.when(gracePeriodConfigurable)
.delete();
Watch watch = mock(Watch.class);
doReturn(watch).when(podResource).watch(any());

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