feat: Use a pre-created ServiceAccount for workspace Pods (#1569)
* feat: Use a pre-created ServiceAccount for workspace Pods Signed-off-by: Anatolii Bazko <abazko@redhat.com>pull/1577/head
parent
8ef15f9489
commit
783d35046c
|
|
@ -19,7 +19,7 @@
|
||||||
| [github.com/BurntSushi/xgb@27f122750802c950b2c869a5b63dafcf590ced95](https://github.com/BurntSushi/xgb.git) | BSD-3-Clause AND WTFPL | [CQ](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=23291) |
|
| [github.com/BurntSushi/xgb@27f122750802c950b2c869a5b63dafcf590ced95](https://github.com/BurntSushi/xgb.git) | BSD-3-Clause AND WTFPL | [CQ](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=23291) |
|
||||||
| [sigs.k8s.io/structured-merge-diff/v4@v4.1.0](https://github.com/kubernetes-sigs/structured-merge-diff/releases/tag/v4.1.0.git) | Apache-2.0 | [CQ](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=23293) |
|
| [sigs.k8s.io/structured-merge-diff/v4@v4.1.0](https://github.com/kubernetes-sigs/structured-merge-diff/releases/tag/v4.1.0.git) | Apache-2.0 | [CQ](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=23293) |
|
||||||
| [sigs.k8s.io/yaml@v1.2.0](https://github.com/kubernetes-sigs/yaml.git) | BSD-3-Clause AND MIT | [CQ](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=23295) |
|
| [sigs.k8s.io/yaml@v1.2.0](https://github.com/kubernetes-sigs/yaml.git) | BSD-3-Clause AND MIT | [CQ](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=23295) |
|
||||||
| [k8s.io/utils@67b214c5f920060488eda08cd2b5c30597f2f1d7](https://github.com/kubernetes/utils.git) | Apache-2.0 | [clearlydefined](https://clearlydefined.io/definitions/git/github/kubernetes/utils/67b214c5f920060488eda08cd2b5c30597f2f1d7) |
|
| [k8s.io/utils@7f3ee0f3147123c62f9b8a204651aa38fc772d20](https://github.com/kubernetes/utils.git) | Apache-2.0 | [clearlydefined](https://clearlydefined.io/definitions/go/golang/k8s.io/utils/v0.0.0-20210722164352-7f3ee0f31471) |
|
||||||
| [k8s.io/gengo@0689ccc1d7d65d9dd1bedcc3b0b1ed7df91ba266](https://github.com/kubernetes/gengo.git) | Apache-2.0 | [clearlydefined](https://clearlydefined.io/definitions/git/github/kubernetes/gengo/0689ccc1d7d65d9dd1bedcc3b0b1ed7df91ba266) |
|
| [k8s.io/gengo@0689ccc1d7d65d9dd1bedcc3b0b1ed7df91ba266](https://github.com/kubernetes/gengo.git) | Apache-2.0 | [clearlydefined](https://clearlydefined.io/definitions/git/github/kubernetes/gengo/0689ccc1d7d65d9dd1bedcc3b0b1ed7df91ba266) |
|
||||||
| [github.com/PuerkitoBio/urlesc@5bd2802263f21d8788851d5305584c82a5c75d7e](https://github.com/puerkitobio/urlesc.git) | BSD-3-Clause | [clearlydefined](https://clearlydefined.io/definitions/git/github/puerkitobio/urlesc/5bd2802263f21d8788851d5305584c82a5c75d7e) |
|
| [github.com/PuerkitoBio/urlesc@5bd2802263f21d8788851d5305584c82a5c75d7e](https://github.com/puerkitobio/urlesc.git) | BSD-3-Clause | [clearlydefined](https://clearlydefined.io/definitions/git/github/puerkitobio/urlesc/5bd2802263f21d8788851d5305584c82a5c75d7e) |
|
||||||
| [github.com/census-instrumentation/opencensus-proto@v0.2.1](https://github.com/census-instrumentation/opencensus-proto.git) | Apache-2.0 | [clearlydefined](https://clearlydefined.io/definitions/git/github/census-instrumentation/opencensus-proto/d89fa54de508111353cb0b06403c00569be780d8) |
|
| [github.com/census-instrumentation/opencensus-proto@v0.2.1](https://github.com/census-instrumentation/opencensus-proto.git) | Apache-2.0 | [clearlydefined](https://clearlydefined.io/definitions/git/github/census-instrumentation/opencensus-proto/d89fa54de508111353cb0b06403c00569be780d8) |
|
||||||
|
|
@ -458,6 +458,6 @@
|
||||||
| [golang.org/x/xerrors@5ec99f83aff198f5fbd629d6c8d8eb38a04218ca](https://cs.opensource.google/go) | BSD-3-Clause | N/A |
|
| [golang.org/x/xerrors@5ec99f83aff198f5fbd629d6c8d8eb38a04218ca](https://cs.opensource.google/go) | BSD-3-Clause | N/A |
|
||||||
| [golang.org/x/oauth2@bf48bf16ab8d622ce64ec6ce98d2c98f916b6303](https://cs.opensource.google/go) | BSD-3-Clause | N/A |
|
| [golang.org/x/oauth2@bf48bf16ab8d622ce64ec6ce98d2c98f916b6303](https://cs.opensource.google/go) | BSD-3-Clause | N/A |
|
||||||
| [golang.org/x/mobile@d3739f865fa66d07c1f506505c18aac71a8ead6e](https://cs.opensource.google/go) | BSD-3-Clause | N/A |
|
| [golang.org/x/mobile@d3739f865fa66d07c1f506505c18aac71a8ead6e](https://cs.opensource.google/go) | BSD-3-Clause | N/A |
|
||||||
| [github.com/devfile/api/v2@32cae1f8e42c22035138ef6ee93080bc47d751c6](https://github.com/devfile/api.git) | Apache-2.0 | [clearlydefined](https://clearlydefined.io/definitions/git/github/devfile/api/32cae1f8e42c22035138ef6ee93080bc47d751c6) |
|
| [github.com/devfile/api/v2@fe7c10eaa530b12b19cfb0e22e221e753391304c](https://github.com/devfile/api.git) | Apache-2.0 | [clearlydefined](https://clearlydefined.io/definitions/git/github/devfile/api/fe7c10eaa530b12b19cfb0e22e221e753391304c) |
|
||||||
| [github.com/che-incubator/kubernetes-image-puller-operator@0128446f5af78587c0427a35d693bbb8d24036bc](https://github.com/che-incubator/kubernetes-image-puller-operator.git) | EPL-2.0 | todo |
|
| [github.com/che-incubator/kubernetes-image-puller-operator@0128446f5af78587c0427a35d693bbb8d24036bc](https://github.com/che-incubator/kubernetes-image-puller-operator.git) | EPL-2.0 | todo |
|
||||||
| [github.com/devfile/devworkspace-operator@0224ca56f4deaaaa342fa0ab4d098303e3ac53e6](https://github.com/devfile/devworkspace-operator.git) | Apache-2.0 | [clearlydefined](https://clearlydefined.io/definitions/git/github/devfile/devworkspace-operator/0224ca56f4deaaaa342fa0ab4d098303e3ac53e6) |
|
| [github.com/devfile/devworkspace-operator@bd6bffe44a9cc064688214059dfe05865cdfe491](https://github.com/devfile/devworkspace-operator.git) | Apache-2.0 | [clearlydefined](https://clearlydefined.io/definitions/git/github/devfile/devworkspace-operator/bd6bffe44a9cc064688214059dfe05865cdfe491) |
|
||||||
|
|
|
||||||
6
Makefile
6
Makefile
|
|
@ -322,7 +322,7 @@ ENVTEST_ASSETS_DIR=$(shell pwd)/testbin
|
||||||
test: download-gateway-resources ## Run tests.
|
test: download-gateway-resources ## Run tests.
|
||||||
export MOCK_API=true; go test -mod=vendor ./... -coverprofile cover.out
|
export MOCK_API=true; go test -mod=vendor ./... -coverprofile cover.out
|
||||||
|
|
||||||
update-go-dependencies: update-go-dependencies ## Update golang dependencies
|
update-go-dependencies: ## Update golang dependencies
|
||||||
go mod tidy
|
go mod tidy
|
||||||
go mod vendor
|
go mod vendor
|
||||||
|
|
||||||
|
|
@ -762,12 +762,12 @@ install-devworkspace: ## Install Dev Workspace operator, available channels: nex
|
||||||
IMAGE="quay.io/devfile/devworkspace-operator-index:next"
|
IMAGE="quay.io/devfile/devworkspace-operator-index:next"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
$(MAKE) create-catalogsource IMAGE="$${IMAGE}" NAME="devworkspace-operator"
|
$(MAKE) create-catalogsource IMAGE="$${IMAGE}" NAME="devworkspace-operator" NAMESPACE="openshift-marketplace"
|
||||||
$(MAKE) create-subscription \
|
$(MAKE) create-subscription \
|
||||||
NAME="devworkspace-operator" \
|
NAME="devworkspace-operator" \
|
||||||
NAMESPACE="openshift-operators" \
|
NAMESPACE="openshift-operators" \
|
||||||
PACKAGE_NAME="devworkspace-operator" \
|
PACKAGE_NAME="devworkspace-operator" \
|
||||||
CHANNEL="$(CHANNEL)" \
|
CHANNEL=$(CHANNEL) \
|
||||||
SOURCE="devworkspace-operator" \
|
SOURCE="devworkspace-operator" \
|
||||||
SOURCE_NAMESPACE="openshift-marketplace" \
|
SOURCE_NAMESPACE="openshift-marketplace" \
|
||||||
INSTALL_PLAN_APPROVAL="Auto"
|
INSTALL_PLAN_APPROVAL="Auto"
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,11 @@ type CheClusterDevEnvironments struct {
|
||||||
// Container build configuration.
|
// Container build configuration.
|
||||||
// +optional
|
// +optional
|
||||||
ContainerBuildConfiguration *ContainerBuildConfiguration `json:"containerBuildConfiguration,omitempty"`
|
ContainerBuildConfiguration *ContainerBuildConfiguration `json:"containerBuildConfiguration,omitempty"`
|
||||||
|
// ServiceAccount to use by the DevWorkspace operator when starting the workspaces.
|
||||||
|
// +optional
|
||||||
|
// +kubebuilder:validation:Pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||||
|
// +kubebuilder:validation:MaxLength=63
|
||||||
|
ServiceAccount string `json:"serviceAccount,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Che components configuration.
|
// Che components configuration.
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ runTest() {
|
||||||
--k8spodwaittimeout=120000 \
|
--k8spodwaittimeout=120000 \
|
||||||
--k8spodreadytimeout=120000 \
|
--k8spodreadytimeout=120000 \
|
||||||
--templates "${CURRENT_OPERATOR_VERSION_TEMPLATE_PATH}" \
|
--templates "${CURRENT_OPERATOR_VERSION_TEMPLATE_PATH}" \
|
||||||
|
--k8spodwaittimeout=120000 \
|
||||||
|
--k8spodreadytimeout=120000 \
|
||||||
--che-operator-cr-patch-yaml "${OPERATOR_REPO}/build/scripts/minikube-tests/minikube-checluster-patch.yaml"
|
--che-operator-cr-patch-yaml "${OPERATOR_REPO}/build/scripts/minikube-tests/minikube-checluster-patch.yaml"
|
||||||
|
|
||||||
make wait-devworkspace-running NAMESPACE="devworkspace-controller" VERBOSE=1
|
make wait-devworkspace-running NAMESPACE="devworkspace-controller" VERBOSE=1
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ runTest() {
|
||||||
--k8spodwaittimeout=120000 \
|
--k8spodwaittimeout=120000 \
|
||||||
--k8spodreadytimeout=120000 \
|
--k8spodreadytimeout=120000 \
|
||||||
--templates ${LAST_OPERATOR_VERSION_TEMPLATE_PATH} \
|
--templates ${LAST_OPERATOR_VERSION_TEMPLATE_PATH} \
|
||||||
|
--k8spodwaittimeout=120000 \
|
||||||
|
--k8spodreadytimeout=120000 \
|
||||||
--che-operator-cr-patch-yaml "${OPERATOR_REPO}/build/scripts/minikube-tests/minikube-checluster-patch.yaml"
|
--che-operator-cr-patch-yaml "${OPERATOR_REPO}/build/scripts/minikube-tests/minikube-checluster-patch.yaml"
|
||||||
|
|
||||||
# Free up some cpu resources
|
# Free up some cpu resources
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@ catchFinish() {
|
||||||
exit ${RESULT}
|
exit ${RESULT}
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForRemovedEclipseCheSubscription() {
|
waitForRemovedSubscription() {
|
||||||
while [[ $(oc get subscription -A -o json | jq -r '.items | .[] | select(.spec.name == "'${ECLIPSE_CHE_PACKAGE_NAME}'")') != "" ]]; do
|
while [[ $(oc get subscription -A -o json | jq -r '.items | .[] | select(.spec.name == "'$1'")') != "" ]]; do
|
||||||
sleep 5s
|
sleep 5s
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,16 +31,30 @@ deleteEclipseCheStableVersionOperator() {
|
||||||
oc delete csv ${ECLIPSE_CHE_INSTALLED_CSV} -n ${ECLIPSE_CHE_SUBSCRIPTION_NAMESPACE}
|
oc delete csv ${ECLIPSE_CHE_INSTALLED_CSV} -n ${ECLIPSE_CHE_SUBSCRIPTION_NAMESPACE}
|
||||||
oc delete subscription ${ECLIPSE_CHE_SUBSCRIPTION_NAME} -n ${ECLIPSE_CHE_SUBSCRIPTION_NAMESPACE}
|
oc delete subscription ${ECLIPSE_CHE_SUBSCRIPTION_NAME} -n ${ECLIPSE_CHE_SUBSCRIPTION_NAMESPACE}
|
||||||
|
|
||||||
waitForRemovedEclipseCheSubscription
|
waitForRemovedSubscription ${ECLIPSE_CHE_PACKAGE_NAME}
|
||||||
|
|
||||||
# Hack, since we remove operator pod, webhook won't work.
|
# Hack, since we remove operator pod, webhook won't work.
|
||||||
# We have to disable it for a while.
|
# We have to disable it for a while.
|
||||||
oc patch crd checlusters.org.eclipse.che --patch '{"spec": {"conversion": null}}' --type=merge
|
oc patch crd checlusters.org.eclipse.che --patch '{"spec": {"conversion": null}}' --type=merge
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deleteDevWorkspaceStableVersionOperator() {
|
||||||
|
DEV_WORKSPACE_PACKAGE_NAME="devworkspace-operator"
|
||||||
|
DEV_WORKSPACE_SUBSCRIPTION_RECORD=$(oc get subscription -A -o json | jq -r '.items | .[] | select(.spec.name == "'${DEV_WORKSPACE_PACKAGE_NAME}'")')
|
||||||
|
DEV_WORKSPACE_SUBSCRIPTION_NAME=$(echo ${DEV_WORKSPACE_SUBSCRIPTION_RECORD} | jq -r '.metadata.name')
|
||||||
|
DEV_WORKSPACE_SUBSCRIPTION_NAMESPACE=$(echo ${DEV_WORKSPACE_SUBSCRIPTION_RECORD} | jq -r '.metadata.namespace')
|
||||||
|
DEV_WORKSPACE_INSTALLED_CSV=$(echo ${DEV_WORKSPACE_SUBSCRIPTION_RECORD} | jq -r '.status.installedCSV')
|
||||||
|
|
||||||
|
oc delete csv ${DEV_WORKSPACE_INSTALLED_CSV} -n ${DEV_WORKSPACE_SUBSCRIPTION_NAMESPACE}
|
||||||
|
oc delete subscription ${DEV_WORKSPACE_SUBSCRIPTION_NAME} -n ${DEV_WORKSPACE_SUBSCRIPTION_NAMESPACE}
|
||||||
|
|
||||||
|
waitForRemovedSubscription ${DEV_WORKSPACE_PACKAGE_NAME}
|
||||||
|
}
|
||||||
|
|
||||||
runTests() {
|
runTests() {
|
||||||
. ${OPERATOR_REPO}/build/scripts/olm/test-catalog.sh -i quay.io/eclipse/eclipse-che-olm-catalog:stable -c stable --verbose
|
. ${OPERATOR_REPO}/build/scripts/olm/test-catalog.sh -i quay.io/eclipse/eclipse-che-olm-catalog:stable -c stable --verbose
|
||||||
deleteEclipseCheStableVersionOperator
|
deleteEclipseCheStableVersionOperator
|
||||||
|
deleteDevWorkspaceStableVersionOperator
|
||||||
. ${OPERATOR_REPO}/build/scripts/olm/test-catalog-from-sources.sh --verbose
|
. ${OPERATOR_REPO}/build/scripts/olm/test-catalog-from-sources.sh --verbose
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,9 @@ createEclipseCheCatalogFromSources() {
|
||||||
run() {
|
run() {
|
||||||
make create-namespace NAMESPACE="${NAMESPACE}" VERBOSE=${VERBOSE}
|
make create-namespace NAMESPACE="${NAMESPACE}" VERBOSE=${VERBOSE}
|
||||||
|
|
||||||
|
# Install Dev Workspace operator (next version as well)
|
||||||
|
make install-devworkspace CHANNEL="next"
|
||||||
|
|
||||||
exposeOpenShiftRegistry
|
exposeOpenShiftRegistry
|
||||||
createEclipseCheCatalogFromSources
|
createEclipseCheCatalogFromSources
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ usage () {
|
||||||
|
|
||||||
run() {
|
run() {
|
||||||
make create-namespace NAMESPACE="${NAMESPACE}" VERBOSE=${VERBOSE}
|
make create-namespace NAMESPACE="${NAMESPACE}" VERBOSE=${VERBOSE}
|
||||||
make create-catalogsource NAME="${ECLIPSE_CHE_CATALOG_SOURCE_NAME}" IMAGE="${CATALOG_IMAGE}" VERBOSE=${VERBOSE}
|
make create-catalogsource NAME="${ECLIPSE_CHE_CATALOG_SOURCE_NAME}" IMAGE="${CATALOG_IMAGE}" VERBOSE=${VERBOSE} NAMESPACE="openshift-marketplace"
|
||||||
|
|
||||||
discoverEclipseCheBundles "${CHANNEL}"
|
discoverEclipseCheBundles "${CHANNEL}"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ usage () {
|
||||||
|
|
||||||
run() {
|
run() {
|
||||||
make create-namespace NAMESPACE="${NAMESPACE}" VERBOSE=${VERBOSE}
|
make create-namespace NAMESPACE="${NAMESPACE}" VERBOSE=${VERBOSE}
|
||||||
make create-catalogsource NAME="${ECLIPSE_CHE_CATALOG_SOURCE_NAME}" IMAGE="${CATALOG_IMAGE}" VERBOSE=${VERBOSE}
|
make create-catalogsource NAME="${ECLIPSE_CHE_CATALOG_SOURCE_NAME}" IMAGE="${CATALOG_IMAGE}" VERBOSE=${VERBOSE} NAMESPACE="openshift-marketplace"
|
||||||
|
|
||||||
discoverEclipseCheBundles "${CHANNEL}"
|
discoverEclipseCheBundles "${CHANNEL}"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6995,6 +6995,12 @@ spec:
|
||||||
run timeout, set this value to -1.
|
run timeout, set this value to -1.
|
||||||
format: int32
|
format: int32
|
||||||
type: integer
|
type: integer
|
||||||
|
serviceAccount:
|
||||||
|
description: ServiceAccount to use by the DevWorkspace operator
|
||||||
|
when starting the workspaces.
|
||||||
|
maxLength: 63
|
||||||
|
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||||
|
type: string
|
||||||
storage:
|
storage:
|
||||||
default:
|
default:
|
||||||
pvcStrategy: per-user
|
pvcStrategy: per-user
|
||||||
|
|
|
||||||
|
|
@ -6805,6 +6805,12 @@ spec:
|
||||||
run timeout, set this value to -1.
|
run timeout, set this value to -1.
|
||||||
format: int32
|
format: int32
|
||||||
type: integer
|
type: integer
|
||||||
|
serviceAccount:
|
||||||
|
description: ServiceAccount to use by the DevWorkspace operator
|
||||||
|
when starting the workspaces.
|
||||||
|
maxLength: 63
|
||||||
|
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||||
|
type: string
|
||||||
storage:
|
storage:
|
||||||
default:
|
default:
|
||||||
pvcStrategy: per-user
|
pvcStrategy: per-user
|
||||||
|
|
|
||||||
|
|
@ -4600,6 +4600,11 @@ spec:
|
||||||
description: Run timeout for workspaces in seconds. This timeout is the maximum duration a workspace runs. To disable workspace run timeout, set this value to -1.
|
description: Run timeout for workspaces in seconds. This timeout is the maximum duration a workspace runs. To disable workspace run timeout, set this value to -1.
|
||||||
format: int32
|
format: int32
|
||||||
type: integer
|
type: integer
|
||||||
|
serviceAccount:
|
||||||
|
description: ServiceAccount to use by the DevWorkspace operator when starting the workspaces.
|
||||||
|
maxLength: 63
|
||||||
|
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||||
|
type: string
|
||||||
storage:
|
storage:
|
||||||
default:
|
default:
|
||||||
pvcStrategy: per-user
|
pvcStrategy: per-user
|
||||||
|
|
|
||||||
|
|
@ -4595,6 +4595,11 @@ spec:
|
||||||
description: Run timeout for workspaces in seconds. This timeout is the maximum duration a workspace runs. To disable workspace run timeout, set this value to -1.
|
description: Run timeout for workspaces in seconds. This timeout is the maximum duration a workspace runs. To disable workspace run timeout, set this value to -1.
|
||||||
format: int32
|
format: int32
|
||||||
type: integer
|
type: integer
|
||||||
|
serviceAccount:
|
||||||
|
description: ServiceAccount to use by the DevWorkspace operator when starting the workspaces.
|
||||||
|
maxLength: 63
|
||||||
|
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||||
|
type: string
|
||||||
storage:
|
storage:
|
||||||
default:
|
default:
|
||||||
pvcStrategy: per-user
|
pvcStrategy: per-user
|
||||||
|
|
|
||||||
|
|
@ -4600,6 +4600,11 @@ spec:
|
||||||
description: Run timeout for workspaces in seconds. This timeout is the maximum duration a workspace runs. To disable workspace run timeout, set this value to -1.
|
description: Run timeout for workspaces in seconds. This timeout is the maximum duration a workspace runs. To disable workspace run timeout, set this value to -1.
|
||||||
format: int32
|
format: int32
|
||||||
type: integer
|
type: integer
|
||||||
|
serviceAccount:
|
||||||
|
description: ServiceAccount to use by the DevWorkspace operator when starting the workspaces.
|
||||||
|
maxLength: 63
|
||||||
|
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||||
|
type: string
|
||||||
storage:
|
storage:
|
||||||
default:
|
default:
|
||||||
pvcStrategy: per-user
|
pvcStrategy: per-user
|
||||||
|
|
|
||||||
|
|
@ -4595,6 +4595,11 @@ spec:
|
||||||
description: Run timeout for workspaces in seconds. This timeout is the maximum duration a workspace runs. To disable workspace run timeout, set this value to -1.
|
description: Run timeout for workspaces in seconds. This timeout is the maximum duration a workspace runs. To disable workspace run timeout, set this value to -1.
|
||||||
format: int32
|
format: int32
|
||||||
type: integer
|
type: integer
|
||||||
|
serviceAccount:
|
||||||
|
description: ServiceAccount to use by the DevWorkspace operator when starting the workspaces.
|
||||||
|
maxLength: 63
|
||||||
|
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||||
|
type: string
|
||||||
storage:
|
storage:
|
||||||
default:
|
default:
|
||||||
pvcStrategy: per-user
|
pvcStrategy: per-user
|
||||||
|
|
|
||||||
5
go.mod
5
go.mod
|
|
@ -5,8 +5,8 @@ go 1.16
|
||||||
require (
|
require (
|
||||||
github.com/Shopify/logrus-bugsnag v0.0.0-00010101000000-000000000000 // indirect
|
github.com/Shopify/logrus-bugsnag v0.0.0-00010101000000-000000000000 // indirect
|
||||||
github.com/che-incubator/kubernetes-image-puller-operator v0.0.0-20210929175054-0128446f5af7
|
github.com/che-incubator/kubernetes-image-puller-operator v0.0.0-20210929175054-0128446f5af7
|
||||||
github.com/devfile/api/v2 v2.0.0-20220414122024-32cae1f8e42c
|
github.com/devfile/api/v2 v2.0.0-20220928161623-fe7c10eaa530
|
||||||
github.com/devfile/devworkspace-operator v0.15.2
|
github.com/devfile/devworkspace-operator v0.17.0
|
||||||
github.com/go-logr/logr v0.4.0
|
github.com/go-logr/logr v0.4.0
|
||||||
github.com/golang/mock v1.5.0
|
github.com/golang/mock v1.5.0
|
||||||
github.com/google/go-cmp v0.5.6
|
github.com/google/go-cmp v0.5.6
|
||||||
|
|
@ -388,7 +388,6 @@ replace (
|
||||||
k8s.io/klog/v2 => k8s.io/klog/v2 v2.8.0
|
k8s.io/klog/v2 => k8s.io/klog/v2 v2.8.0
|
||||||
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.0.0-20180912235703-14b8d2d93fcb
|
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.0.0-20180912235703-14b8d2d93fcb
|
||||||
k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20200923105717-7eba4cbaebdf
|
k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20200923105717-7eba4cbaebdf
|
||||||
k8s.io/utils => k8s.io/utils v0.0.0-20201110183641-67b214c5f920
|
|
||||||
kubernetes/klog => kubernetes/klog v1.0.0
|
kubernetes/klog => kubernetes/klog v1.0.0
|
||||||
modernc.org/b => modernc.org/b v1.0.0
|
modernc.org/b => modernc.org/b v1.0.0
|
||||||
modernc.org/db => modernc.org/db v1.0.0
|
modernc.org/db => modernc.org/db v1.0.0
|
||||||
|
|
|
||||||
12
go.sum
12
go.sum
|
|
@ -112,10 +112,10 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||||
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
|
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
|
||||||
github.com/deislabs/oras v0.8.1/go.mod h1:Mx0rMSbBNaNfY9hjpccEnxkOqJL6KGjtxNHPLC4G4As=
|
github.com/deislabs/oras v0.8.1/go.mod h1:Mx0rMSbBNaNfY9hjpccEnxkOqJL6KGjtxNHPLC4G4As=
|
||||||
github.com/denisenkom/go-mssqldb v0.0.0-20190204142019-df6d76eb9289/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
|
github.com/denisenkom/go-mssqldb v0.0.0-20190204142019-df6d76eb9289/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
|
||||||
github.com/devfile/api/v2 v2.0.0-20220414122024-32cae1f8e42c h1:yyidoxal8ngJWDxRuVZMNh4PBwqDIzOkTeOagtmRiy0=
|
github.com/devfile/api/v2 v2.0.0-20220928161623-fe7c10eaa530 h1:pZvf4AZrf/ZwV2AwQnTInlUpns+Wj9JYtPRtBDiFHzk=
|
||||||
github.com/devfile/api/v2 v2.0.0-20220414122024-32cae1f8e42c/go.mod h1:kLX/nW93gigOHXK3NLeJL2fSS/sgEe+OHu8bo3aoOi4=
|
github.com/devfile/api/v2 v2.0.0-20220928161623-fe7c10eaa530/go.mod h1:dN7xFrOVG+iPqn4UKGibXLd5oVsdE8XyK9OEb5JL3aI=
|
||||||
github.com/devfile/devworkspace-operator v0.15.2 h1:CcLGHtuBOKdwpeYV8Iy7ZwUSgavcMbjuPA9ejupM7BE=
|
github.com/devfile/devworkspace-operator v0.17.0 h1:Ml8ncEQAVf/d+DTttQ76oe/btnFQpicRG/XncdflOZo=
|
||||||
github.com/devfile/devworkspace-operator v0.15.2/go.mod h1:fM3/GhPWEL8JZOEImCnpyxTYEf9dN6PsjzJq+ffcD1k=
|
github.com/devfile/devworkspace-operator v0.17.0/go.mod h1:xLELAolfebwROqGSvOWhdC0eH7S+V7iVFzHxtm3Jf2A=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dhui/dktest v0.3.2/go.mod h1:l1/ib23a/CmxAe7yixtrYPc8Iy90Zy2udyaHINM5p58=
|
github.com/dhui/dktest v0.3.2/go.mod h1:l1/ib23a/CmxAe7yixtrYPc8Iy90Zy2udyaHINM5p58=
|
||||||
|
|
@ -706,8 +706,10 @@ k8s.io/kube-openapi v0.0.0-20200923105717-7eba4cbaebdf h1:7RCqblb9HTvcWeOYwrt1SV
|
||||||
k8s.io/kube-openapi v0.0.0-20200923105717-7eba4cbaebdf/go.mod h1:bfCVj+qXcEaE5SCvzBaqpOySr6tuCcpPKqF6HD8nyCw=
|
k8s.io/kube-openapi v0.0.0-20200923105717-7eba4cbaebdf/go.mod h1:bfCVj+qXcEaE5SCvzBaqpOySr6tuCcpPKqF6HD8nyCw=
|
||||||
k8s.io/kubectl v0.0.0-20201218185502-10b66c3fd14b/go.mod h1:2bE0JLYTRDVKDiTREFsjLAx4R2GvUtL/mGYFXfFFMzY=
|
k8s.io/kubectl v0.0.0-20201218185502-10b66c3fd14b/go.mod h1:2bE0JLYTRDVKDiTREFsjLAx4R2GvUtL/mGYFXfFFMzY=
|
||||||
k8s.io/metrics v0.20.2/go.mod h1:yTck5nl5wt/lIeLcU6g0b8/AKJf2girwe0PQiaM4Mwk=
|
k8s.io/metrics v0.20.2/go.mod h1:yTck5nl5wt/lIeLcU6g0b8/AKJf2girwe0PQiaM4Mwk=
|
||||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
|
|
||||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||||
|
k8s.io/utils v0.0.0-20210111153108-fddb29f9d009/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||||
|
k8s.io/utils v0.0.0-20210722164352-7f3ee0f31471 h1:DnzUXII7sVg1FJ/4JX6YDRJfLNAC7idRatPwe07suiI=
|
||||||
|
k8s.io/utils v0.0.0-20210722164352-7f3ee0f31471/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||||
modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg=
|
modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg=
|
||||||
modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8=
|
modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8=
|
||||||
modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw=
|
modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw=
|
||||||
|
|
|
||||||
|
|
@ -4595,6 +4595,11 @@ spec:
|
||||||
description: Run timeout for workspaces in seconds. This timeout is the maximum duration a workspace runs. To disable workspace run timeout, set this value to -1.
|
description: Run timeout for workspaces in seconds. This timeout is the maximum duration a workspace runs. To disable workspace run timeout, set this value to -1.
|
||||||
format: int32
|
format: int32
|
||||||
type: integer
|
type: integer
|
||||||
|
serviceAccount:
|
||||||
|
description: ServiceAccount to use by the DevWorkspace operator when starting the workspaces.
|
||||||
|
maxLength: 63
|
||||||
|
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||||
|
type: string
|
||||||
storage:
|
storage:
|
||||||
default:
|
default:
|
||||||
pvcStrategy: per-user
|
pvcStrategy: per-user
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/utils/pointer"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -54,13 +55,12 @@ func (d *DevWorkspaceConfigReconciler) Reconcile(ctx *chetypes.DeployContext) (r
|
||||||
if dwoc.Config == nil {
|
if dwoc.Config == nil {
|
||||||
dwoc.Config = &controllerv1alpha1.OperatorConfiguration{}
|
dwoc.Config = &controllerv1alpha1.OperatorConfiguration{}
|
||||||
}
|
}
|
||||||
err := updateOperatorConfig(ctx.CheCluster.Spec.DevEnvironments.Storage, dwoc.Config)
|
|
||||||
if err != nil {
|
if err := updateWorkspaceConfig(&ctx.CheCluster.Spec.DevEnvironments, dwoc.Config); err != nil {
|
||||||
return reconcile.Result{}, false, err
|
return reconcile.Result{}, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
done, err := deploy.Sync(ctx, dwoc)
|
if done, err := deploy.Sync(ctx, dwoc); !done {
|
||||||
if !done {
|
|
||||||
return reconcile.Result{}, false, err
|
return reconcile.Result{}, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,52 +71,62 @@ func (d *DevWorkspaceConfigReconciler) Finalize(ctx *chetypes.DeployContext) boo
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateOperatorConfig(storage chev2.WorkspaceStorage, operatorConfig *controllerv1alpha1.OperatorConfiguration) error {
|
func updateWorkspaceConfig(devEnvironments *chev2.CheClusterDevEnvironments, operatorConfig *controllerv1alpha1.OperatorConfiguration) error {
|
||||||
var pvc *chev2.PVC
|
if operatorConfig.Workspace == nil {
|
||||||
|
operatorConfig.Workspace = &controllerv1alpha1.WorkspaceConfig{}
|
||||||
pvcStrategy := utils.GetValue(storage.PvcStrategy, constants.DefaultPvcStorageStrategy)
|
|
||||||
switch pvcStrategy {
|
|
||||||
case constants.CommonPVCStorageStrategy:
|
|
||||||
fallthrough
|
|
||||||
case constants.PerUserPVCStorageStrategy:
|
|
||||||
if storage.PerUserStrategyPvcConfig != nil {
|
|
||||||
pvc = storage.PerUserStrategyPvcConfig
|
|
||||||
}
|
|
||||||
case constants.PerWorkspacePVCStorageStrategy:
|
|
||||||
if storage.PerWorkspaceStrategyPvcConfig != nil {
|
|
||||||
pvc = storage.PerWorkspaceStrategyPvcConfig
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := updateWorkspaceStorageConfig(devEnvironments, operatorConfig.Workspace); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := updateWorkspaceServiceAccountConfig(devEnvironments, operatorConfig.Workspace); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateWorkspaceStorageConfig(devEnvironments *chev2.CheClusterDevEnvironments, workspaceConfig *controllerv1alpha1.WorkspaceConfig) error {
|
||||||
|
pvcStrategy := utils.GetValue(devEnvironments.Storage.PvcStrategy, constants.DefaultPvcStorageStrategy)
|
||||||
|
isPerWorkspacePVCStorageStrategy := pvcStrategy == constants.PerWorkspacePVCStorageStrategy
|
||||||
|
pvc := map[bool]*chev2.PVC{
|
||||||
|
true: devEnvironments.Storage.PerWorkspaceStrategyPvcConfig,
|
||||||
|
false: devEnvironments.Storage.PerUserStrategyPvcConfig,
|
||||||
|
}[isPerWorkspacePVCStorageStrategy]
|
||||||
|
|
||||||
if pvc != nil {
|
if pvc != nil {
|
||||||
if operatorConfig.Workspace == nil {
|
if pvc.StorageClass != "" {
|
||||||
operatorConfig.Workspace = &controllerv1alpha1.WorkspaceConfig{}
|
workspaceConfig.StorageClassName = &pvc.StorageClass
|
||||||
}
|
|
||||||
return updateWorkspaceConfig(pvc, pvcStrategy == constants.PerWorkspacePVCStorageStrategy, operatorConfig.Workspace)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateWorkspaceConfig(pvc *chev2.PVC, isPerWorkspacePVCStorageStrategy bool, workspaceConfig *controllerv1alpha1.WorkspaceConfig) error {
|
|
||||||
if pvc.StorageClass != "" {
|
|
||||||
workspaceConfig.StorageClassName = &pvc.StorageClass
|
|
||||||
}
|
|
||||||
|
|
||||||
if pvc.ClaimSize != "" {
|
|
||||||
if workspaceConfig.DefaultStorageSize == nil {
|
|
||||||
workspaceConfig.DefaultStorageSize = &controllerv1alpha1.StorageSizes{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pvcSize, err := resource.ParseQuantity(pvc.ClaimSize)
|
if pvc.ClaimSize != "" {
|
||||||
if err != nil {
|
if workspaceConfig.DefaultStorageSize == nil {
|
||||||
return err
|
workspaceConfig.DefaultStorageSize = &controllerv1alpha1.StorageSizes{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isPerWorkspacePVCStorageStrategy {
|
pvcSize, err := resource.ParseQuantity(pvc.ClaimSize)
|
||||||
workspaceConfig.DefaultStorageSize.PerWorkspace = &pvcSize
|
if err != nil {
|
||||||
} else {
|
return err
|
||||||
workspaceConfig.DefaultStorageSize.Common = &pvcSize
|
}
|
||||||
|
|
||||||
|
if isPerWorkspacePVCStorageStrategy {
|
||||||
|
workspaceConfig.DefaultStorageSize.PerWorkspace = &pvcSize
|
||||||
|
} else {
|
||||||
|
workspaceConfig.DefaultStorageSize.Common = &pvcSize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateWorkspaceServiceAccountConfig(devEnvironments *chev2.CheClusterDevEnvironments, workspaceConfig *controllerv1alpha1.WorkspaceConfig) error {
|
||||||
|
isNamespaceAutoProvisioned := pointer.BoolPtrDerefOr(devEnvironments.DefaultNamespace.AutoProvision, constants.DefaultAutoProvision)
|
||||||
|
|
||||||
|
workspaceConfig.ServiceAccount = &controllerv1alpha1.ServiceAccountConfig{
|
||||||
|
ServiceAccountName: devEnvironments.ServiceAccount,
|
||||||
|
// If user's Namespace is not auto provisioned (is pre-created by admin), then ServiceAccount must be pre-created as well
|
||||||
|
DisableCreation: pointer.BoolPtr(!isNamespaceAutoProvisioned && devEnvironments.ServiceAccount != ""),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,9 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
|
|
||||||
"github.com/eclipse-che/che-operator/pkg/common/constants"
|
"github.com/eclipse-che/che-operator/pkg/common/constants"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
"k8s.io/utils/pointer"
|
"k8s.io/utils/pointer"
|
||||||
|
|
@ -85,7 +88,7 @@ func TestReconcileDevWorkspaceConfigPerUserStorage(t *testing.T) {
|
||||||
DevEnvironments: chev2.CheClusterDevEnvironments{},
|
DevEnvironments: chev2.CheClusterDevEnvironments{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{},
|
expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{Workspace: &controllerv1alpha1.WorkspaceConfig{}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Create DevWorkspaceOperatorConfig with StorageClassName only",
|
name: "Create DevWorkspaceOperatorConfig with StorageClassName only",
|
||||||
|
|
@ -330,7 +333,8 @@ func TestReconcileDevWorkspaceConfigPerUserStorage(t *testing.T) {
|
||||||
err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: devWorkspaceConfigName, Namespace: testCase.cheCluster.Namespace}, dwoc)
|
err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: devWorkspaceConfigName, Namespace: testCase.cheCluster.Namespace}, dwoc)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, testCase.expectedOperatorConfig, dwoc.Config)
|
diff := cmp.Diff(testCase.expectedOperatorConfig, dwoc.Config, cmp.Options{cmpopts.IgnoreFields(controllerv1alpha1.WorkspaceConfig{}, "ServiceAccount")})
|
||||||
|
assert.Empty(t, diff)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -346,3 +350,120 @@ func TestReconcileDevWorkspaceConfigPerUserStorage(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReconcileServiceAccountConfig(t *testing.T) {
|
||||||
|
type testCase struct {
|
||||||
|
name string
|
||||||
|
cheCluster *chev2.CheCluster
|
||||||
|
expectedOperatorConfig *controllerv1alpha1.OperatorConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCases = []testCase{
|
||||||
|
{
|
||||||
|
name: "Case #1",
|
||||||
|
cheCluster: &chev2.CheCluster{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: "eclipse-che",
|
||||||
|
Name: "eclipse-che",
|
||||||
|
},
|
||||||
|
Spec: chev2.CheClusterSpec{
|
||||||
|
DevEnvironments: chev2.CheClusterDevEnvironments{
|
||||||
|
ServiceAccount: "service-account",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{
|
||||||
|
Workspace: &controllerv1alpha1.WorkspaceConfig{
|
||||||
|
ServiceAccount: &controllerv1alpha1.ServiceAccountConfig{
|
||||||
|
ServiceAccountName: "service-account",
|
||||||
|
DisableCreation: pointer.BoolPtr(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Case #2",
|
||||||
|
cheCluster: &chev2.CheCluster{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: "eclipse-che",
|
||||||
|
Name: "eclipse-che",
|
||||||
|
},
|
||||||
|
Spec: chev2.CheClusterSpec{
|
||||||
|
DevEnvironments: chev2.CheClusterDevEnvironments{
|
||||||
|
DefaultNamespace: chev2.DefaultNamespace{
|
||||||
|
AutoProvision: pointer.BoolPtr(false),
|
||||||
|
},
|
||||||
|
ServiceAccount: "service-account",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{
|
||||||
|
Workspace: &controllerv1alpha1.WorkspaceConfig{
|
||||||
|
ServiceAccount: &controllerv1alpha1.ServiceAccountConfig{
|
||||||
|
ServiceAccountName: "service-account",
|
||||||
|
DisableCreation: pointer.BoolPtr(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Case #3",
|
||||||
|
cheCluster: &chev2.CheCluster{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: "eclipse-che",
|
||||||
|
Name: "eclipse-che",
|
||||||
|
},
|
||||||
|
Spec: chev2.CheClusterSpec{
|
||||||
|
DevEnvironments: chev2.CheClusterDevEnvironments{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{
|
||||||
|
Workspace: &controllerv1alpha1.WorkspaceConfig{
|
||||||
|
ServiceAccount: &controllerv1alpha1.ServiceAccountConfig{
|
||||||
|
DisableCreation: pointer.BoolPtr(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Case #4",
|
||||||
|
cheCluster: &chev2.CheCluster{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: "eclipse-che",
|
||||||
|
Name: "eclipse-che",
|
||||||
|
},
|
||||||
|
Spec: chev2.CheClusterSpec{
|
||||||
|
DevEnvironments: chev2.CheClusterDevEnvironments{
|
||||||
|
DefaultNamespace: chev2.DefaultNamespace{
|
||||||
|
AutoProvision: pointer.BoolPtr(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{
|
||||||
|
Workspace: &controllerv1alpha1.WorkspaceConfig{
|
||||||
|
ServiceAccount: &controllerv1alpha1.ServiceAccountConfig{
|
||||||
|
DisableCreation: pointer.BoolPtr(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
deployContext := test.GetDeployContext(testCase.cheCluster, []runtime.Object{})
|
||||||
|
infrastructure.InitializeForTesting(infrastructure.OpenShiftv4)
|
||||||
|
|
||||||
|
devWorkspaceConfigReconciler := NewDevWorkspaceConfigReconciler()
|
||||||
|
_, _, err := devWorkspaceConfigReconciler.Reconcile(deployContext)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
dwoc := &controllerv1alpha1.DevWorkspaceOperatorConfig{}
|
||||||
|
err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: devWorkspaceConfigName, Namespace: testCase.cheCluster.Namespace}, dwoc)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, testCase.expectedOperatorConfig.Workspace.ServiceAccount, dwoc.Config.Workspace.ServiceAccount)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
21
vendor/github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2/component_contribution.go
generated
vendored
Normal file
21
vendor/github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2/component_contribution.go
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
package v1alpha2
|
||||||
|
|
||||||
|
import attributes "github.com/devfile/api/v2/pkg/attributes"
|
||||||
|
|
||||||
|
type ComponentContribution struct {
|
||||||
|
// Mandatory name that allows referencing the component
|
||||||
|
// from other elements (such as commands) or from an external
|
||||||
|
// devfile that may reference this component through a parent or a plugin.
|
||||||
|
// +kubebuilder:validation:Pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||||
|
// +kubebuilder:validation:MaxLength=63
|
||||||
|
Name string `json:"name"`
|
||||||
|
// Map of implementation-dependant free-form YAML attributes.
|
||||||
|
// +optional
|
||||||
|
// +kubebuilder:validation:Type=object
|
||||||
|
// +kubebuilder:pruning:PreserveUnknownFields
|
||||||
|
// +kubebuilder:validation:Schemaless
|
||||||
|
Attributes attributes.Attributes `json:"attributes,omitempty"`
|
||||||
|
// Reference to a remote Devfile or DevWorkspace resource to be included
|
||||||
|
// in the DevWorkspace.
|
||||||
|
PluginComponent `json:",inline"`
|
||||||
|
}
|
||||||
7
vendor/github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2/devworkspace_types.go
generated
vendored
7
vendor/github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2/devworkspace_types.go
generated
vendored
|
|
@ -7,9 +7,10 @@ import (
|
||||||
|
|
||||||
// DevWorkspaceSpec defines the desired state of DevWorkspace
|
// DevWorkspaceSpec defines the desired state of DevWorkspace
|
||||||
type DevWorkspaceSpec struct {
|
type DevWorkspaceSpec struct {
|
||||||
Started bool `json:"started"`
|
Started bool `json:"started"`
|
||||||
RoutingClass string `json:"routingClass,omitempty"`
|
RoutingClass string `json:"routingClass,omitempty"`
|
||||||
Template DevWorkspaceTemplateSpec `json:"template,omitempty"`
|
Template DevWorkspaceTemplateSpec `json:"template,omitempty"`
|
||||||
|
Contributions []ComponentContribution `json:"contributions,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DevWorkspaceStatus defines the observed state of DevWorkspace
|
// DevWorkspaceStatus defines the observed state of DevWorkspace
|
||||||
|
|
|
||||||
30
vendor/github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2/zz_generated.deepcopy.go
generated
vendored
30
vendor/github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2/zz_generated.deepcopy.go
generated
vendored
|
|
@ -830,6 +830,29 @@ func (in *Component) DeepCopy() *Component {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ComponentContribution) DeepCopyInto(out *ComponentContribution) {
|
||||||
|
*out = *in
|
||||||
|
if in.Attributes != nil {
|
||||||
|
in, out := &in.Attributes, &out.Attributes
|
||||||
|
*out = make(attributes.Attributes, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
(*out)[key] = *val.DeepCopy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in.PluginComponent.DeepCopyInto(&out.PluginComponent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ComponentContribution.
|
||||||
|
func (in *ComponentContribution) DeepCopy() *ComponentContribution {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ComponentContribution)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *ComponentParentOverride) DeepCopyInto(out *ComponentParentOverride) {
|
func (in *ComponentParentOverride) DeepCopyInto(out *ComponentParentOverride) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
|
@ -1637,6 +1660,13 @@ func (in *DevWorkspaceList) DeepCopyObject() runtime.Object {
|
||||||
func (in *DevWorkspaceSpec) DeepCopyInto(out *DevWorkspaceSpec) {
|
func (in *DevWorkspaceSpec) DeepCopyInto(out *DevWorkspaceSpec) {
|
||||||
*out = *in
|
*out = *in
|
||||||
in.Template.DeepCopyInto(&out.Template)
|
in.Template.DeepCopyInto(&out.Template)
|
||||||
|
if in.Contributions != nil {
|
||||||
|
in, out := &in.Contributions, &out.Contributions
|
||||||
|
*out = make([]ComponentContribution, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevWorkspaceSpec.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevWorkspaceSpec.
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
package v1alpha1
|
package v1alpha1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
@ -78,6 +79,21 @@ type StorageSizes struct {
|
||||||
PerWorkspace *resource.Quantity `json:"perWorkspace,omitempty"`
|
PerWorkspace *resource.Quantity `json:"perWorkspace,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ServiceAccountConfig struct {
|
||||||
|
// ServiceAccountName defines a fixed name to be used for all DevWorkspaces. If set, the DevWorkspace
|
||||||
|
// Operator will not generate a separate ServiceAccount for each DevWorkspace, and will instead create
|
||||||
|
// a ServiceAccount with the specified name in each namespace where DevWorkspaces are created. If specified,
|
||||||
|
// the created ServiceAccount will not be removed when DevWorkspaces are deleted and must be cleaned up manually.
|
||||||
|
// +kubebuilder:validation:Pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||||
|
// +kubebuilder:validation:MaxLength=63
|
||||||
|
ServiceAccountName string `json:"serviceAccountName,omitempty"`
|
||||||
|
// Disable creation of DevWorkspace ServiceAccounts by the DevWorkspace Operator. If set to true, the serviceAccountName
|
||||||
|
// field must also be set. If ServiceAccount creation is disabled, it is assumed that the specified ServiceAccount already
|
||||||
|
// exists in any namespace where a workspace is created. If a suitable ServiceAccount does not exist, starting DevWorkspaces
|
||||||
|
// will fail.
|
||||||
|
DisableCreation *bool `json:"disableCreation,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type WorkspaceConfig struct {
|
type WorkspaceConfig struct {
|
||||||
// ImagePullPolicy defines the imagePullPolicy used for containers in a DevWorkspace
|
// ImagePullPolicy defines the imagePullPolicy used for containers in a DevWorkspace
|
||||||
// For additional information, see Kubernetes documentation for imagePullPolicy. If
|
// For additional information, see Kubernetes documentation for imagePullPolicy. If
|
||||||
|
|
@ -94,6 +110,9 @@ type WorkspaceConfig struct {
|
||||||
// +kubebuilder:validation:Pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
// +kubebuilder:validation:Pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||||
// +kubebuilder:validation:MaxLength=63
|
// +kubebuilder:validation:MaxLength=63
|
||||||
PVCName string `json:"pvcName,omitempty"`
|
PVCName string `json:"pvcName,omitempty"`
|
||||||
|
// ServiceAccount defines configuration options for the ServiceAccount used for
|
||||||
|
// DevWorkspaces.
|
||||||
|
ServiceAccount *ServiceAccountConfig `json:"serviceAccount,omitempty"`
|
||||||
// StorageClassName defines an optional storageClass to use for persistent
|
// StorageClassName defines an optional storageClass to use for persistent
|
||||||
// volume claims created to support DevWorkspaces
|
// volume claims created to support DevWorkspaces
|
||||||
StorageClassName *string `json:"storageClassName,omitempty"`
|
StorageClassName *string `json:"storageClassName,omitempty"`
|
||||||
|
|
@ -123,10 +142,17 @@ type WorkspaceConfig struct {
|
||||||
// but the objects will be left on the cluster). The default value is false.
|
// but the objects will be left on the cluster). The default value is false.
|
||||||
CleanupOnStop *bool `json:"cleanupOnStop,omitempty"`
|
CleanupOnStop *bool `json:"cleanupOnStop,omitempty"`
|
||||||
// PodSecurityContext overrides the default PodSecurityContext used for all workspace-related
|
// PodSecurityContext overrides the default PodSecurityContext used for all workspace-related
|
||||||
// pods created by the DevWorkspace Operator when running on Kubernetes. On OpenShift, this
|
// pods created by the DevWorkspace Operator. If set, defined values are merged into the default
|
||||||
// configuration option is ignored. If set, the entire pod security context is overridden;
|
// configuration
|
||||||
// values are not merged.
|
|
||||||
PodSecurityContext *corev1.PodSecurityContext `json:"podSecurityContext,omitempty"`
|
PodSecurityContext *corev1.PodSecurityContext `json:"podSecurityContext,omitempty"`
|
||||||
|
// ContainerSecurityContext overrides the default ContainerSecurityContext used for all
|
||||||
|
// workspace-related containers created by the DevWorkspace Operator. If set, defined
|
||||||
|
// values are merged into the default configuration
|
||||||
|
ContainerSecurityContext *corev1.SecurityContext `json:"containerSecurityContext,omitempty"`
|
||||||
|
// DefaultTemplate defines an optional DevWorkspace Spec Template which gets applied to the workspace
|
||||||
|
// if the workspace's Template Spec Components are not defined. The DefaultTemplate will overwrite the existing
|
||||||
|
// Template Spec, with the exception of Projects (if any are defined).
|
||||||
|
DefaultTemplate *dw.DevWorkspaceTemplateSpecContent `json:"defaultTemplate,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DevWorkspaceOperatorConfig is the Schema for the devworkspaceoperatorconfigs API
|
// DevWorkspaceOperatorConfig is the Schema for the devworkspaceoperatorconfigs API
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
//go:build !ignore_autogenerated
|
|
||||||
// +build !ignore_autogenerated
|
// +build !ignore_autogenerated
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
@ -21,6 +20,7 @@
|
||||||
package v1alpha1
|
package v1alpha1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
@ -478,6 +478,26 @@ func (in *RoutingConfig) DeepCopy() *RoutingConfig {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ServiceAccountConfig) DeepCopyInto(out *ServiceAccountConfig) {
|
||||||
|
*out = *in
|
||||||
|
if in.DisableCreation != nil {
|
||||||
|
in, out := &in.DisableCreation, &out.DisableCreation
|
||||||
|
*out = new(bool)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountConfig.
|
||||||
|
func (in *ServiceAccountConfig) DeepCopy() *ServiceAccountConfig {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ServiceAccountConfig)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *StorageSizes) DeepCopyInto(out *StorageSizes) {
|
func (in *StorageSizes) DeepCopyInto(out *StorageSizes) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
|
@ -506,6 +526,11 @@ func (in *StorageSizes) DeepCopy() *StorageSizes {
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *WorkspaceConfig) DeepCopyInto(out *WorkspaceConfig) {
|
func (in *WorkspaceConfig) DeepCopyInto(out *WorkspaceConfig) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
if in.ServiceAccount != nil {
|
||||||
|
in, out := &in.ServiceAccount, &out.ServiceAccount
|
||||||
|
*out = new(ServiceAccountConfig)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
if in.StorageClassName != nil {
|
if in.StorageClassName != nil {
|
||||||
in, out := &in.StorageClassName, &out.StorageClassName
|
in, out := &in.StorageClassName, &out.StorageClassName
|
||||||
*out = new(string)
|
*out = new(string)
|
||||||
|
|
@ -531,6 +556,16 @@ func (in *WorkspaceConfig) DeepCopyInto(out *WorkspaceConfig) {
|
||||||
*out = new(v1.PodSecurityContext)
|
*out = new(v1.PodSecurityContext)
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
|
if in.ContainerSecurityContext != nil {
|
||||||
|
in, out := &in.ContainerSecurityContext, &out.ContainerSecurityContext
|
||||||
|
*out = new(v1.SecurityContext)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.DefaultTemplate != nil {
|
||||||
|
in, out := &in.DefaultTemplate, &out.DefaultTemplate
|
||||||
|
*out = new(v1alpha2.DevWorkspaceTemplateSpecContent)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceConfig.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceConfig.
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,8 @@ func (s *BasicSolver) Finalize(*controllerv1alpha1.DevWorkspaceRouting) error {
|
||||||
func (s *BasicSolver) GetSpecObjects(routing *controllerv1alpha1.DevWorkspaceRouting, workspaceMeta DevWorkspaceMetadata) (RoutingObjects, error) {
|
func (s *BasicSolver) GetSpecObjects(routing *controllerv1alpha1.DevWorkspaceRouting, workspaceMeta DevWorkspaceMetadata) (RoutingObjects, error) {
|
||||||
routingObjects := RoutingObjects{}
|
routingObjects := RoutingObjects{}
|
||||||
|
|
||||||
routingSuffix := config.Routing.ClusterHostSuffix
|
// TODO: Use workspace-scoped ClusterHostSuffix to allow overriding
|
||||||
|
routingSuffix := config.GetGlobalConfig().Routing.ClusterHostSuffix
|
||||||
if routingSuffix == "" {
|
if routingSuffix == "" {
|
||||||
return routingObjects, &RoutingInvalid{"basic routing requires .config.routing.clusterHostSuffix to be set in operator config"}
|
return routingObjects, &RoutingInvalid{"basic routing requires .config.routing.clusterHostSuffix to be set in operator config"}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,25 @@ func ServiceName(workspaceId string) string {
|
||||||
return fmt.Sprintf("%s-%s", workspaceId, "service")
|
return fmt.Sprintf("%s-%s", workspaceId, "service")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ServiceAccountName(workspaceId string) string {
|
func ServiceAccountName(workspace *DevWorkspaceWithConfig) string {
|
||||||
return fmt.Sprintf("%s-%s", workspaceId, "sa")
|
if workspace.Config.Workspace.ServiceAccount.ServiceAccountName != "" {
|
||||||
|
return workspace.Config.Workspace.ServiceAccount.ServiceAccountName
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s-%s", workspace.Status.DevWorkspaceId, "sa")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServiceAccountLabels(workspace *DevWorkspaceWithConfig) map[string]string {
|
||||||
|
if workspace.Config.Workspace.ServiceAccount.ServiceAccountName != "" {
|
||||||
|
// One SA used for multiple workspaces; do not add specific DevWorkspace ID. We still need the
|
||||||
|
// devworkspace ID label in order to cache the ServiceAccount.
|
||||||
|
return map[string]string{
|
||||||
|
constants.DevWorkspaceIDLabel: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map[string]string{
|
||||||
|
constants.DevWorkspaceIDLabel: workspace.Status.DevWorkspaceId,
|
||||||
|
constants.DevWorkspaceNameLabel: workspace.Name,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func EndpointHostname(routingSuffix, workspaceId, endpointName string, endpointPort int) string {
|
func EndpointHostname(routingSuffix, workspaceId, endpointName string, endpointPort int) string {
|
||||||
|
|
|
||||||
24
vendor/github.com/devfile/devworkspace-operator/pkg/common/types.go
generated
vendored
Normal file
24
vendor/github.com/devfile/devworkspace-operator/pkg/common/types.go
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright (c) 2019-2022 Red Hat, Inc.
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||||
|
controllerv1alpha1 "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DevWorkspaceWithConfig struct {
|
||||||
|
*dw.DevWorkspace `json:",inline"`
|
||||||
|
Config *controllerv1alpha1.OperatorConfiguration `json:"resolvedConfig"`
|
||||||
|
}
|
||||||
|
|
@ -16,9 +16,13 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
|
"github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
|
||||||
|
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
"k8s.io/utils/pointer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// defaultConfig represents the default configuration for the DevWorkspace Operator.
|
// defaultConfig represents the default configuration for the DevWorkspace Operator.
|
||||||
|
|
@ -30,28 +34,45 @@ var defaultConfig = &v1alpha1.OperatorConfiguration{
|
||||||
Workspace: &v1alpha1.WorkspaceConfig{
|
Workspace: &v1alpha1.WorkspaceConfig{
|
||||||
ImagePullPolicy: "Always",
|
ImagePullPolicy: "Always",
|
||||||
PVCName: "claim-devworkspace",
|
PVCName: "claim-devworkspace",
|
||||||
|
ServiceAccount: &v1alpha1.ServiceAccountConfig{
|
||||||
|
DisableCreation: pointer.Bool(false),
|
||||||
|
},
|
||||||
DefaultStorageSize: &v1alpha1.StorageSizes{
|
DefaultStorageSize: &v1alpha1.StorageSizes{
|
||||||
Common: &commonStorageSize,
|
Common: &commonStorageSize,
|
||||||
PerWorkspace: &perWorkspaceStorageSize,
|
PerWorkspace: &perWorkspaceStorageSize,
|
||||||
},
|
},
|
||||||
IdleTimeout: "15m",
|
IdleTimeout: "15m",
|
||||||
ProgressTimeout: "5m",
|
ProgressTimeout: "5m",
|
||||||
CleanupOnStop: &boolFalse,
|
CleanupOnStop: pointer.BoolPtr(false),
|
||||||
PodSecurityContext: &corev1.PodSecurityContext{
|
PodSecurityContext: nil,
|
||||||
RunAsUser: &int64UID,
|
ContainerSecurityContext: &corev1.SecurityContext{},
|
||||||
RunAsGroup: &int64GID,
|
DefaultTemplate: nil,
|
||||||
RunAsNonRoot: &boolTrue,
|
|
||||||
FSGroup: &int64UID,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var defaultKubernetesPodSecurityContext = &corev1.PodSecurityContext{
|
||||||
|
RunAsUser: pointer.Int64(1234),
|
||||||
|
RunAsGroup: pointer.Int64(0),
|
||||||
|
RunAsNonRoot: pointer.Bool(true),
|
||||||
|
FSGroup: pointer.Int64(1234),
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultOpenShiftPodSecurityContext = &corev1.PodSecurityContext{}
|
||||||
|
|
||||||
// Necessary variables for setting pointer values
|
// Necessary variables for setting pointer values
|
||||||
var (
|
var (
|
||||||
boolTrue = true
|
|
||||||
boolFalse = false
|
|
||||||
int64UID = int64(1234)
|
|
||||||
int64GID = int64(0)
|
|
||||||
commonStorageSize = resource.MustParse("10Gi")
|
commonStorageSize = resource.MustParse("10Gi")
|
||||||
perWorkspaceStorageSize = resource.MustParse("5Gi")
|
perWorkspaceStorageSize = resource.MustParse("5Gi")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func setDefaultPodSecurityContext() error {
|
||||||
|
if !infrastructure.IsInitialized() {
|
||||||
|
return fmt.Errorf("can not set default pod security context, infrastructure not detected")
|
||||||
|
}
|
||||||
|
if infrastructure.IsOpenShift() {
|
||||||
|
defaultConfig.Workspace.PodSecurityContext = defaultOpenShiftPodSecurityContext
|
||||||
|
} else {
|
||||||
|
defaultConfig.Workspace.PodSecurityContext = defaultKubernetesPodSecurityContext
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/utils/pointer"
|
||||||
crclient "sigs.k8s.io/controller-runtime/pkg/client"
|
crclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
dw "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
|
dw "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
|
||||||
|
|
@ -128,8 +129,7 @@ func convertConfigMapToConfigCRD(client crclient.Client) (*dw.DevWorkspaceOperat
|
||||||
var experimentalFeatures *bool
|
var experimentalFeatures *bool
|
||||||
experimentalFeaturesStr := configmap.ControllerCfg.GetExperimentalFeaturesEnabled()
|
experimentalFeaturesStr := configmap.ControllerCfg.GetExperimentalFeaturesEnabled()
|
||||||
if experimentalFeaturesStr != nil && *experimentalFeaturesStr == "true" {
|
if experimentalFeaturesStr != nil && *experimentalFeaturesStr == "true" {
|
||||||
trueBool := true
|
experimentalFeatures = pointer.Bool(true)
|
||||||
experimentalFeatures = &trueBool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !setRoutingConfig && !setWorkspaceConfig && experimentalFeatures == nil {
|
if !setRoutingConfig && !setWorkspaceConfig && experimentalFeatures == nil {
|
||||||
|
|
|
||||||
|
|
@ -17,19 +17,26 @@ package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||||
"github.com/devfile/devworkspace-operator/pkg/config/proxy"
|
"github.com/devfile/devworkspace-operator/pkg/config/proxy"
|
||||||
routeV1 "github.com/openshift/api/route/v1"
|
routeV1 "github.com/openshift/api/route/v1"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
|
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||||
|
"k8s.io/utils/pointer"
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
crclient "sigs.k8s.io/controller-runtime/pkg/client"
|
crclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
controller "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
|
controller "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
|
||||||
|
"github.com/devfile/devworkspace-operator/pkg/constants"
|
||||||
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
|
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -39,26 +46,64 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Routing *controller.RoutingConfig
|
|
||||||
Workspace *controller.WorkspaceConfig
|
|
||||||
internalConfig *controller.OperatorConfiguration
|
internalConfig *controller.OperatorConfiguration
|
||||||
configMutex sync.Mutex
|
configMutex sync.Mutex
|
||||||
configNamespace string
|
configNamespace string
|
||||||
log = ctrl.Log.WithName("operator-configuration")
|
log = ctrl.Log.WithName("operator-configuration")
|
||||||
)
|
)
|
||||||
|
|
||||||
func SetConfigForTesting(config *controller.OperatorConfiguration) {
|
func GetGlobalConfig() *controller.OperatorConfiguration {
|
||||||
|
return internalConfig.DeepCopy()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolveConfigForWorkspace returns the resulting config from merging the global DevWorkspaceOperatorConfig with the
|
||||||
|
// DevWorkspaceOperatorConfig specified by the optional workspace attribute `controller.devfile.io/devworkspace-config`.
|
||||||
|
// If the `controller.devfile.io/devworkspace-config` is not set, the global DevWorkspaceOperatorConfig is returned.
|
||||||
|
// If the `controller.devfile.io/devworkspace-config` attribute is incorrectly set, or the specified DevWorkspaceOperatorConfig
|
||||||
|
// does not exist on the cluster, an error is returned.
|
||||||
|
func ResolveConfigForWorkspace(workspace *dw.DevWorkspace, client crclient.Client) (*controller.OperatorConfiguration, error) {
|
||||||
|
if !workspace.Spec.Template.Attributes.Exists(constants.ExternalDevWorkspaceConfiguration) {
|
||||||
|
return GetGlobalConfig(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
namespacedName := types.NamespacedName{}
|
||||||
|
err := workspace.Spec.Template.Attributes.GetInto(constants.ExternalDevWorkspaceConfiguration, &namespacedName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read attribute %s in DevWorkspace attributes: %w", constants.ExternalDevWorkspaceConfiguration, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if namespacedName.Name == "" {
|
||||||
|
return nil, fmt.Errorf("'name' must be set for attribute %s in DevWorkspace attributes", constants.ExternalDevWorkspaceConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
if namespacedName.Namespace == "" {
|
||||||
|
return nil, fmt.Errorf("'namespace' must be set for attribute %s in DevWorkspace attributes", constants.ExternalDevWorkspaceConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
externalDWOC := &controller.DevWorkspaceOperatorConfig{}
|
||||||
|
err = client.Get(context.TODO(), namespacedName, externalDWOC)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not fetch external DWOC with name %s in namespace %s: %w", namespacedName.Name, namespacedName.Namespace, err)
|
||||||
|
}
|
||||||
|
return getMergedConfig(externalDWOC.Config, internalConfig), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetConfigForTesting(customConfig *controller.OperatorConfiguration) *controller.OperatorConfiguration {
|
||||||
configMutex.Lock()
|
configMutex.Lock()
|
||||||
defer configMutex.Unlock()
|
defer configMutex.Unlock()
|
||||||
internalConfig = defaultConfig.DeepCopy()
|
testConfig := defaultConfig.DeepCopy()
|
||||||
mergeConfig(config, internalConfig)
|
mergeConfig(customConfig, testConfig)
|
||||||
updatePublicConfig()
|
return testConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupControllerConfig(client crclient.Client) error {
|
func SetupControllerConfig(client crclient.Client) error {
|
||||||
if internalConfig != nil {
|
if internalConfig != nil {
|
||||||
return fmt.Errorf("internal controller configuration is already set up")
|
return fmt.Errorf("internal controller configuration is already set up")
|
||||||
}
|
}
|
||||||
|
if err := setDefaultPodSecurityContext(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
internalConfig = &controller.OperatorConfiguration{}
|
internalConfig = &controller.OperatorConfiguration{}
|
||||||
|
|
||||||
namespace, err := infrastructure.GetNamespace()
|
namespace, err := infrastructure.GetNamespace()
|
||||||
|
|
@ -93,7 +138,7 @@ func SetupControllerConfig(client crclient.Client) error {
|
||||||
defaultConfig.Routing.ProxyConfig = clusterProxy
|
defaultConfig.Routing.ProxyConfig = clusterProxy
|
||||||
internalConfig.Routing.ProxyConfig = proxy.MergeProxyConfigs(clusterProxy, internalConfig.Routing.ProxyConfig)
|
internalConfig.Routing.ProxyConfig = proxy.MergeProxyConfigs(clusterProxy, internalConfig.Routing.ProxyConfig)
|
||||||
|
|
||||||
updatePublicConfig()
|
logCurrentConfig()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,6 +164,13 @@ func getClusterConfig(namespace string, client crclient.Client) (*controller.Dev
|
||||||
return clusterConfig, nil
|
return clusterConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMergedConfig(from, to *controller.OperatorConfiguration) *controller.OperatorConfiguration {
|
||||||
|
mergedConfig := to.DeepCopy()
|
||||||
|
fromCopy := from.DeepCopy()
|
||||||
|
mergeConfig(fromCopy, mergedConfig)
|
||||||
|
return mergedConfig
|
||||||
|
}
|
||||||
|
|
||||||
func syncConfigFrom(newConfig *controller.DevWorkspaceOperatorConfig) {
|
func syncConfigFrom(newConfig *controller.DevWorkspaceOperatorConfig) {
|
||||||
if newConfig == nil || newConfig.Name != OperatorConfigName || newConfig.Namespace != configNamespace {
|
if newConfig == nil || newConfig.Name != OperatorConfigName || newConfig.Namespace != configNamespace {
|
||||||
return
|
return
|
||||||
|
|
@ -127,19 +179,13 @@ func syncConfigFrom(newConfig *controller.DevWorkspaceOperatorConfig) {
|
||||||
defer configMutex.Unlock()
|
defer configMutex.Unlock()
|
||||||
internalConfig = defaultConfig.DeepCopy()
|
internalConfig = defaultConfig.DeepCopy()
|
||||||
mergeConfig(newConfig.Config, internalConfig)
|
mergeConfig(newConfig.Config, internalConfig)
|
||||||
updatePublicConfig()
|
logCurrentConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
func restoreDefaultConfig() {
|
func restoreDefaultConfig() {
|
||||||
configMutex.Lock()
|
configMutex.Lock()
|
||||||
defer configMutex.Unlock()
|
defer configMutex.Unlock()
|
||||||
internalConfig = defaultConfig.DeepCopy()
|
internalConfig = defaultConfig.DeepCopy()
|
||||||
updatePublicConfig()
|
|
||||||
}
|
|
||||||
|
|
||||||
func updatePublicConfig() {
|
|
||||||
Routing = internalConfig.Routing.DeepCopy()
|
|
||||||
Workspace = internalConfig.Workspace.DeepCopy()
|
|
||||||
logCurrentConfig()
|
logCurrentConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -222,6 +268,17 @@ func mergeConfig(from, to *controller.OperatorConfiguration) {
|
||||||
if from.Workspace.PVCName != "" {
|
if from.Workspace.PVCName != "" {
|
||||||
to.Workspace.PVCName = from.Workspace.PVCName
|
to.Workspace.PVCName = from.Workspace.PVCName
|
||||||
}
|
}
|
||||||
|
if from.Workspace.ServiceAccount != nil {
|
||||||
|
if to.Workspace.ServiceAccount == nil {
|
||||||
|
to.Workspace.ServiceAccount = &controller.ServiceAccountConfig{}
|
||||||
|
}
|
||||||
|
if from.Workspace.ServiceAccount.ServiceAccountName != "" {
|
||||||
|
to.Workspace.ServiceAccount.ServiceAccountName = from.Workspace.ServiceAccount.ServiceAccountName
|
||||||
|
}
|
||||||
|
if from.Workspace.ServiceAccount.DisableCreation != nil {
|
||||||
|
to.Workspace.ServiceAccount.DisableCreation = pointer.BoolPtr(*from.Workspace.ServiceAccount.DisableCreation)
|
||||||
|
}
|
||||||
|
}
|
||||||
if from.Workspace.ImagePullPolicy != "" {
|
if from.Workspace.ImagePullPolicy != "" {
|
||||||
to.Workspace.ImagePullPolicy = from.Workspace.ImagePullPolicy
|
to.Workspace.ImagePullPolicy = from.Workspace.ImagePullPolicy
|
||||||
}
|
}
|
||||||
|
|
@ -238,7 +295,10 @@ func mergeConfig(from, to *controller.OperatorConfiguration) {
|
||||||
to.Workspace.CleanupOnStop = from.Workspace.CleanupOnStop
|
to.Workspace.CleanupOnStop = from.Workspace.CleanupOnStop
|
||||||
}
|
}
|
||||||
if from.Workspace.PodSecurityContext != nil {
|
if from.Workspace.PodSecurityContext != nil {
|
||||||
to.Workspace.PodSecurityContext = from.Workspace.PodSecurityContext
|
to.Workspace.PodSecurityContext = mergePodSecurityContext(to.Workspace.PodSecurityContext, from.Workspace.PodSecurityContext)
|
||||||
|
}
|
||||||
|
if from.Workspace.ContainerSecurityContext != nil {
|
||||||
|
to.Workspace.ContainerSecurityContext = mergeContainerSecurityContext(to.Workspace.ContainerSecurityContext, from.Workspace.ContainerSecurityContext)
|
||||||
}
|
}
|
||||||
if from.Workspace.DefaultStorageSize != nil {
|
if from.Workspace.DefaultStorageSize != nil {
|
||||||
if to.Workspace.DefaultStorageSize == nil {
|
if to.Workspace.DefaultStorageSize == nil {
|
||||||
|
|
@ -253,57 +313,143 @@ func mergeConfig(from, to *controller.OperatorConfiguration) {
|
||||||
to.Workspace.DefaultStorageSize.PerWorkspace = &perWorkspaceSizeCopy
|
to.Workspace.DefaultStorageSize.PerWorkspace = &perWorkspaceSizeCopy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if from.Workspace.DefaultTemplate != nil {
|
||||||
|
templateSpecContentCopy := from.Workspace.DefaultTemplate.DeepCopy()
|
||||||
|
to.Workspace.DefaultTemplate = templateSpecContentCopy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergePodSecurityContext(base, patch *corev1.PodSecurityContext) *corev1.PodSecurityContext {
|
||||||
|
baseBytes, err := json.Marshal(base)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Failed to serialize base pod security context: %s", err)
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
patchBytes, err := json.Marshal(patch)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Failed to serialize configured pod security context: %s", err)
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
patchedBytes, err := strategicpatch.StrategicMergePatch(baseBytes, patchBytes, &corev1.PodSecurityContext{})
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Failed to merge configured pod security context: %s", err)
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
patched := &corev1.PodSecurityContext{}
|
||||||
|
if err := json.Unmarshal(patchedBytes, patched); err != nil {
|
||||||
|
log.Info("Failed to deserialize patched pod security context: %s", patched)
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
return patched
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeContainerSecurityContext(base, patch *corev1.SecurityContext) *corev1.SecurityContext {
|
||||||
|
baseBytes, err := json.Marshal(base)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Failed to serialize base container security context: %s", err)
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
patchBytes, err := json.Marshal(patch)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Failed to serialize configured container security context: %s", err)
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
patchedBytes, err := strategicpatch.StrategicMergePatch(baseBytes, patchBytes, &corev1.SecurityContext{})
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Failed to merge configured container security context: %s", err)
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
patched := &corev1.SecurityContext{}
|
||||||
|
if err := json.Unmarshal(patchedBytes, patched); err != nil {
|
||||||
|
log.Info("Failed to deserialize patched container security context: %s", patched)
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
return patched
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCurrentConfigString(currConfig *controller.OperatorConfiguration) string {
|
||||||
|
if currConfig == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
routing := currConfig.Routing
|
||||||
|
var config []string
|
||||||
|
if routing != nil {
|
||||||
|
if routing.ClusterHostSuffix != "" && routing.ClusterHostSuffix != defaultConfig.Routing.ClusterHostSuffix {
|
||||||
|
config = append(config, fmt.Sprintf("routing.clusterHostSuffix=%s", routing.ClusterHostSuffix))
|
||||||
|
}
|
||||||
|
if routing.DefaultRoutingClass != defaultConfig.Routing.DefaultRoutingClass {
|
||||||
|
config = append(config, fmt.Sprintf("routing.defaultRoutingClass=%s", routing.DefaultRoutingClass))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
workspace := currConfig.Workspace
|
||||||
|
if workspace != nil {
|
||||||
|
if workspace.ImagePullPolicy != defaultConfig.Workspace.ImagePullPolicy {
|
||||||
|
config = append(config, fmt.Sprintf("workspace.imagePullPolicy=%s", workspace.ImagePullPolicy))
|
||||||
|
}
|
||||||
|
if workspace.PVCName != defaultConfig.Workspace.PVCName {
|
||||||
|
config = append(config, fmt.Sprintf("workspace.pvcName=%s", workspace.PVCName))
|
||||||
|
}
|
||||||
|
if workspace.ServiceAccount != nil {
|
||||||
|
if workspace.ServiceAccount.ServiceAccountName != defaultConfig.Workspace.ServiceAccount.ServiceAccountName {
|
||||||
|
config = append(config, fmt.Sprintf("workspace.serviceAccount.serviceAccountName=%s", workspace.ServiceAccount.ServiceAccountName))
|
||||||
|
}
|
||||||
|
if workspace.ServiceAccount.DisableCreation != nil && *workspace.ServiceAccount.DisableCreation != *defaultConfig.Workspace.ServiceAccount.DisableCreation {
|
||||||
|
config = append(config, fmt.Sprintf("workspace.serviceAccount.disableCreation=%t", *workspace.ServiceAccount.DisableCreation))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if workspace.StorageClassName != nil && workspace.StorageClassName != defaultConfig.Workspace.StorageClassName {
|
||||||
|
config = append(config, fmt.Sprintf("workspace.storageClassName=%s", *workspace.StorageClassName))
|
||||||
|
}
|
||||||
|
if workspace.IdleTimeout != defaultConfig.Workspace.IdleTimeout {
|
||||||
|
config = append(config, fmt.Sprintf("workspace.idleTimeout=%s", workspace.IdleTimeout))
|
||||||
|
}
|
||||||
|
if workspace.ProgressTimeout != "" && workspace.ProgressTimeout != defaultConfig.Workspace.ProgressTimeout {
|
||||||
|
config = append(config, fmt.Sprintf("workspace.progressTimeout=%s", workspace.ProgressTimeout))
|
||||||
|
}
|
||||||
|
if workspace.IgnoredUnrecoverableEvents != nil {
|
||||||
|
config = append(config, fmt.Sprintf("workspace.ignoredUnrecoverableEvents=%s",
|
||||||
|
strings.Join(workspace.IgnoredUnrecoverableEvents, ";")))
|
||||||
|
}
|
||||||
|
if workspace.CleanupOnStop != nil && *workspace.CleanupOnStop != *defaultConfig.Workspace.CleanupOnStop {
|
||||||
|
config = append(config, fmt.Sprintf("workspace.cleanupOnStop=%t", *workspace.CleanupOnStop))
|
||||||
|
}
|
||||||
|
if workspace.DefaultStorageSize != nil {
|
||||||
|
if workspace.DefaultStorageSize.Common != nil && workspace.DefaultStorageSize.Common.String() != defaultConfig.Workspace.DefaultStorageSize.Common.String() {
|
||||||
|
config = append(config, fmt.Sprintf("workspace.defaultStorageSize.common=%s", workspace.DefaultStorageSize.Common.String()))
|
||||||
|
}
|
||||||
|
if workspace.DefaultStorageSize.PerWorkspace != nil && workspace.DefaultStorageSize.PerWorkspace.String() != defaultConfig.Workspace.DefaultStorageSize.PerWorkspace.String() {
|
||||||
|
config = append(config, fmt.Sprintf("workspace.defaultStorageSize.perWorkspace=%s", workspace.DefaultStorageSize.PerWorkspace.String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(workspace.PodSecurityContext, defaultConfig.Workspace.PodSecurityContext) {
|
||||||
|
config = append(config, "workspace.podSecurityContext is set")
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(workspace.ContainerSecurityContext, defaultConfig.Workspace.ContainerSecurityContext) {
|
||||||
|
config = append(config, "workspace.containerSecurityContext is set")
|
||||||
|
}
|
||||||
|
if workspace.DefaultTemplate != nil {
|
||||||
|
config = append(config, "workspace.defaultTemplate is set")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if currConfig.EnableExperimentalFeatures != nil && *currConfig.EnableExperimentalFeatures {
|
||||||
|
config = append(config, "enableExperimentalFeatures=true")
|
||||||
|
}
|
||||||
|
if len(config) == 0 {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
return strings.Join(config, ",")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// logCurrentConfig formats the current operator configuration as a plain string
|
// logCurrentConfig formats the current operator configuration as a plain string
|
||||||
func logCurrentConfig() {
|
func logCurrentConfig() {
|
||||||
if internalConfig == nil {
|
currConfig := GetCurrentConfigString(internalConfig)
|
||||||
return
|
if len(currConfig) == 0 {
|
||||||
}
|
|
||||||
var config []string
|
|
||||||
if Routing != nil {
|
|
||||||
if Routing.ClusterHostSuffix != "" && Routing.ClusterHostSuffix != defaultConfig.Routing.ClusterHostSuffix {
|
|
||||||
config = append(config, fmt.Sprintf("routing.clusterHostSuffix=%s", Routing.ClusterHostSuffix))
|
|
||||||
}
|
|
||||||
if Routing.DefaultRoutingClass != defaultConfig.Routing.DefaultRoutingClass {
|
|
||||||
config = append(config, fmt.Sprintf("routing.defaultRoutingClass=%s", Routing.DefaultRoutingClass))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if Workspace != nil {
|
|
||||||
if Workspace.ImagePullPolicy != defaultConfig.Workspace.ImagePullPolicy {
|
|
||||||
config = append(config, fmt.Sprintf("workspace.imagePullPolicy=%s", Workspace.ImagePullPolicy))
|
|
||||||
}
|
|
||||||
if Workspace.PVCName != defaultConfig.Workspace.PVCName {
|
|
||||||
config = append(config, fmt.Sprintf("workspace.pvcName=%s", Workspace.PVCName))
|
|
||||||
}
|
|
||||||
if Workspace.StorageClassName != nil && Workspace.StorageClassName != defaultConfig.Workspace.StorageClassName {
|
|
||||||
config = append(config, fmt.Sprintf("workspace.storageClassName=%s", *Workspace.StorageClassName))
|
|
||||||
}
|
|
||||||
if Workspace.IdleTimeout != defaultConfig.Workspace.IdleTimeout {
|
|
||||||
config = append(config, fmt.Sprintf("workspace.idleTimeout=%s", Workspace.IdleTimeout))
|
|
||||||
}
|
|
||||||
if Workspace.IgnoredUnrecoverableEvents != nil {
|
|
||||||
config = append(config, fmt.Sprintf("workspace.ignoredUnrecoverableEvents=%s",
|
|
||||||
strings.Join(Workspace.IgnoredUnrecoverableEvents, ";")))
|
|
||||||
}
|
|
||||||
if Workspace.DefaultStorageSize != nil {
|
|
||||||
if Workspace.DefaultStorageSize.Common != nil {
|
|
||||||
config = append(config, fmt.Sprintf("workspace.defaultStorageSize.common=%s", Workspace.DefaultStorageSize.Common.String()))
|
|
||||||
}
|
|
||||||
if Workspace.DefaultStorageSize.PerWorkspace != nil {
|
|
||||||
config = append(config, fmt.Sprintf("workspace.defaultStorageSize.perWorkspace=%s", Workspace.DefaultStorageSize.PerWorkspace.String()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if internalConfig.EnableExperimentalFeatures != nil && *internalConfig.EnableExperimentalFeatures {
|
|
||||||
config = append(config, "enableExperimentalFeatures=true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(config) == 0 {
|
|
||||||
log.Info("Updated config to [(default config)]")
|
log.Info("Updated config to [(default config)]")
|
||||||
} else {
|
} else {
|
||||||
log.Info(fmt.Sprintf("Updated config to [%s]", strings.Join(config, ",")))
|
log.Info(fmt.Sprintf("Updated config to [%s]", currConfig))
|
||||||
}
|
}
|
||||||
|
|
||||||
if internalConfig.Routing.ProxyConfig != nil {
|
if internalConfig.Routing.ProxyConfig != nil {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,21 @@ const (
|
||||||
// stopped.
|
// stopped.
|
||||||
DevWorkspaceStorageTypeAttribute = "controller.devfile.io/storage-type"
|
DevWorkspaceStorageTypeAttribute = "controller.devfile.io/storage-type"
|
||||||
|
|
||||||
|
// ExternalDevWorkspaceConfiguration is an attribute that allows for specifying an (optional) external DevWorkspaceOperatorConfig
|
||||||
|
// which will merged with the internal/global DevWorkspaceOperatorConfig. The DevWorkspaceOperatorConfig resulting from the merge will be used for the workspace.
|
||||||
|
// The fields which are set in the external DevWorkspaceOperatorConfig will overwrite those existing in the
|
||||||
|
// internal/global DevWorkspaceOperatorConfig during the merge.
|
||||||
|
// The structure of the attribute value should contain two strings: name and namespace.
|
||||||
|
// 'name' specifies the metadata.name of the external operator configuration.
|
||||||
|
// 'namespace' specifies the metadata.namespace of the external operator configuration .
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// attributes:
|
||||||
|
// controller.devfile.io/devworkspace-config:
|
||||||
|
// name: external-dwoc-name
|
||||||
|
// namespace: some-namespace
|
||||||
|
ExternalDevWorkspaceConfiguration = "controller.devfile.io/devworkspace-config"
|
||||||
|
|
||||||
// RuntimeClassNameAttribute is an attribute added to a DevWorkspace to specify a runtimeClassName for container
|
// RuntimeClassNameAttribute is an attribute added to a DevWorkspace to specify a runtimeClassName for container
|
||||||
// components in the DevWorkspace (pod.spec.runtimeClassName). If empty, no runtimeClassName is added.
|
// components in the DevWorkspace (pod.spec.runtimeClassName). If empty, no runtimeClassName is added.
|
||||||
RuntimeClassNameAttribute = "controller.devfile.io/runtime-class"
|
RuntimeClassNameAttribute = "controller.devfile.io/runtime-class"
|
||||||
|
|
@ -74,4 +89,56 @@ const (
|
||||||
// EndpointURLAttribute is an attribute added to endpoints to denote the endpoint on the cluster that
|
// EndpointURLAttribute is an attribute added to endpoints to denote the endpoint on the cluster that
|
||||||
// was created to route to this endpoint
|
// was created to route to this endpoint
|
||||||
EndpointURLAttribute = "controller.devfile.io/endpoint-url"
|
EndpointURLAttribute = "controller.devfile.io/endpoint-url"
|
||||||
|
|
||||||
|
// ContainerContributionAttribute defines a container component as a container contribution that should be merged
|
||||||
|
// into an existing container in the devfile if possible. If no suitable container exists, this component
|
||||||
|
// is treated as a regular container component
|
||||||
|
ContainerContributionAttribute = "controller.devfile.io/container-contribution"
|
||||||
|
|
||||||
|
// MergeContributionAttribute defines a container component as a target for merging a container contribution. If
|
||||||
|
// present on a container component, any container contributions will be merged into that container. If multiple
|
||||||
|
// container components have the merge-contribution attribute, the first one will be used and all others ignored.
|
||||||
|
MergeContributionAttribute = "controller.devfile.io/merge-contribution"
|
||||||
|
|
||||||
|
// MergedContributionsAttribute is applied as an attribute onto a component to list the components from the unflattened
|
||||||
|
// DevWorkspace that have been merged into the current component. The contributions are listed in a comma-separated list.
|
||||||
|
MergedContributionsAttribute = "controller.devfile.io/merged-contributions"
|
||||||
|
|
||||||
|
// PodOverridesAttribute is an attribute applied to a container component or in global attributes to specify overrides
|
||||||
|
// for the pod spec used in the main workspace deployment. The format of the field is the same as the Kubernetes
|
||||||
|
// PodSpec API. Overrides are applied over the default pod template spec used via strategic merge patch.
|
||||||
|
//
|
||||||
|
// If this attribute is used multiple times, all overrides are applied in the order they are defined in the DevWorkspace,
|
||||||
|
// with later values overriding previous ones. Overrides defined in the top-level attributes field are applied last and
|
||||||
|
// override any overrides from container components.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// kind: DevWorkspace
|
||||||
|
// apiVersion: workspace.devfile.io/v1alpha2
|
||||||
|
// spec:
|
||||||
|
// template:
|
||||||
|
// attributes:
|
||||||
|
// pod-overrides:
|
||||||
|
// metadata:
|
||||||
|
// annotations:
|
||||||
|
// io.openshift.userns: "true"
|
||||||
|
// io.kubernetes.cri-o.userns-mode: "auto:size=65536;map-to-root=true" # <-- user namespace
|
||||||
|
// openshift.io/scc: container-build
|
||||||
|
// spec:
|
||||||
|
// runtimeClassName: kata
|
||||||
|
// schedulerName: stork
|
||||||
|
PodOverridesAttribute = "pod-overrides"
|
||||||
|
|
||||||
|
// ContainerOverridesAttribute is an attribute applied to a container component to specify arbitrary fields in that
|
||||||
|
// container. This attribute should only be used to set fields that are not configurable in the container component
|
||||||
|
// itself. Any values specified in the overrides attribute overwrite fields on the container.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// components:
|
||||||
|
// - name: go
|
||||||
|
// attributes:
|
||||||
|
// container-overrides: {"resources":{"limits":{"nvidia.com/gpu": "1"}}}
|
||||||
|
// container:
|
||||||
|
// image: ...
|
||||||
|
ContainerOverridesAttribute = "container-overrides"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,12 @@ const (
|
||||||
|
|
||||||
// Constants describing storage classes supported by the controller
|
// Constants describing storage classes supported by the controller
|
||||||
|
|
||||||
// CommonStorageClassType defines the 'common' storage policy -- one PVC is provisioned per namespace and all devworkspace storage
|
// CommonStorageClassType defines the 'common' storage policy, which is an alias of the 'per-user' storage policy, and operates in the same fashion as the 'per-user' storage policy.
|
||||||
// is mounted in it on subpaths according to devworkspace ID.
|
// The 'common' storage policy exists only for legacy compatibility.
|
||||||
CommonStorageClassType = "common"
|
CommonStorageClassType = "common"
|
||||||
|
// PerUserStorageClassType defines the 'per-user' storage policy -- one PVC is provisioned per namespace and all devworkspace storage
|
||||||
|
// is mounted in it on subpaths according to devworkspace ID.
|
||||||
|
PerUserStorageClassType = "per-user"
|
||||||
// AsyncStorageClassType defines the 'asynchronous' storage policy. An rsync sidecar is added to devworkspaces that uses SSH to connect
|
// AsyncStorageClassType defines the 'asynchronous' storage policy. An rsync sidecar is added to devworkspaces that uses SSH to connect
|
||||||
// to a storage deployment that mounts a common PVC for the namespace.
|
// to a storage deployment that mounts a common PVC for the namespace.
|
||||||
AsyncStorageClassType = "async"
|
AsyncStorageClassType = "async"
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,10 @@ func InitializeForTesting(currentInfrastructure Type) {
|
||||||
initialized = true
|
initialized = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsInitialized() bool {
|
||||||
|
return initialized
|
||||||
|
}
|
||||||
|
|
||||||
// IsOpenShift returns true if the current cluster is an OpenShift (v4.x) cluster.
|
// IsOpenShift returns true if the current cluster is an OpenShift (v4.x) cluster.
|
||||||
func IsOpenShift() bool {
|
func IsOpenShift() bool {
|
||||||
if !initialized {
|
if !initialized {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
networkingv1 "k8s.io/api/networking/v1"
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
crclient "sigs.k8s.io/controller-runtime/pkg/client"
|
crclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -37,7 +38,7 @@ type diffFunc func(spec crclient.Object, cluster crclient.Object) (delete, updat
|
||||||
var diffFuncs = map[reflect.Type]diffFunc{
|
var diffFuncs = map[reflect.Type]diffFunc{
|
||||||
reflect.TypeOf(rbacv1.Role{}): allDiffFuncs(labelsAndAnnotationsDiffFunc, basicDiffFunc(roleDiffOpts)),
|
reflect.TypeOf(rbacv1.Role{}): allDiffFuncs(labelsAndAnnotationsDiffFunc, basicDiffFunc(roleDiffOpts)),
|
||||||
reflect.TypeOf(rbacv1.RoleBinding{}): allDiffFuncs(labelsAndAnnotationsDiffFunc, basicDiffFunc(rolebindingDiffOpts)),
|
reflect.TypeOf(rbacv1.RoleBinding{}): allDiffFuncs(labelsAndAnnotationsDiffFunc, basicDiffFunc(rolebindingDiffOpts)),
|
||||||
reflect.TypeOf(corev1.ServiceAccount{}): labelsAndAnnotationsDiffFunc,
|
reflect.TypeOf(corev1.ServiceAccount{}): allDiffFuncs(labelsAndAnnotationsDiffFunc, ownerrefsDiffFunc),
|
||||||
reflect.TypeOf(appsv1.Deployment{}): allDiffFuncs(deploymentDiffFunc, labelsAndAnnotationsDiffFunc, basicDiffFunc(deploymentDiffOpts)),
|
reflect.TypeOf(appsv1.Deployment{}): allDiffFuncs(deploymentDiffFunc, labelsAndAnnotationsDiffFunc, basicDiffFunc(deploymentDiffOpts)),
|
||||||
reflect.TypeOf(corev1.ConfigMap{}): allDiffFuncs(labelsAndAnnotationsDiffFunc, basicDiffFunc(configmapDiffOpts)),
|
reflect.TypeOf(corev1.ConfigMap{}): allDiffFuncs(labelsAndAnnotationsDiffFunc, basicDiffFunc(configmapDiffOpts)),
|
||||||
reflect.TypeOf(corev1.Secret{}): allDiffFuncs(labelsAndAnnotationsDiffFunc, basicDiffFunc(secretDiffOpts)),
|
reflect.TypeOf(corev1.Secret{}): allDiffFuncs(labelsAndAnnotationsDiffFunc, basicDiffFunc(secretDiffOpts)),
|
||||||
|
|
@ -73,6 +74,16 @@ func labelsAndAnnotationsDiffFunc(spec, cluster crclient.Object) (delete, update
|
||||||
return false, false
|
return false, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ownerrefsDiffFunc(spec, cluster crclient.Object) (delete, update bool) {
|
||||||
|
clusterRefs := cluster.GetOwnerReferences()
|
||||||
|
for _, ownerref := range spec.GetOwnerReferences() {
|
||||||
|
if !containsOwnerRef(ownerref, clusterRefs) {
|
||||||
|
return false, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, false
|
||||||
|
}
|
||||||
|
|
||||||
// allDiffFuncs represents an 'and' condition across specified diffFuncs. Functions are checked in provided order,
|
// allDiffFuncs represents an 'and' condition across specified diffFuncs. Functions are checked in provided order,
|
||||||
// returning the result of the first function to require an update/deletion.
|
// returning the result of the first function to require an update/deletion.
|
||||||
func allDiffFuncs(funcs ...diffFunc) diffFunc {
|
func allDiffFuncs(funcs ...diffFunc) diffFunc {
|
||||||
|
|
@ -135,3 +146,26 @@ func serviceDiffFunc(spec, cluster crclient.Object) (delete, update bool) {
|
||||||
}
|
}
|
||||||
return false, specCopy.Spec.Type != clusterCopy.Spec.Type
|
return false, specCopy.Spec.Type != clusterCopy.Spec.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func containsOwnerRef(toCheck metav1.OwnerReference, listRefs []metav1.OwnerReference) bool {
|
||||||
|
boolPtrsEqual := func(a, b *bool) bool {
|
||||||
|
// If either is nil, assume check other is nil or false; otherwise, compare actual values
|
||||||
|
switch {
|
||||||
|
case a == nil:
|
||||||
|
return b == nil || !*b
|
||||||
|
case b == nil:
|
||||||
|
return a == nil || !*a
|
||||||
|
default:
|
||||||
|
return *a == *b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, ref := range listRefs {
|
||||||
|
if toCheck.Kind == ref.Kind &&
|
||||||
|
toCheck.Name == ref.Name &&
|
||||||
|
toCheck.UID == ref.UID &&
|
||||||
|
boolPtrsEqual(toCheck.Controller, ref.Controller) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ func createObjectGeneric(specObj crclient.Object, api ClusterAPI) error {
|
||||||
// Need to try to update the object to address an edge case where removing a labelselector
|
// Need to try to update the object to address an edge case where removing a labelselector
|
||||||
// results in the object not being tracked by the controller's cache.
|
// results in the object not being tracked by the controller's cache.
|
||||||
return updateObjectGeneric(specObj, nil, api)
|
return updateObjectGeneric(specObj, nil, api)
|
||||||
case k8sErrors.IsInvalid(err):
|
case k8sErrors.IsInvalid(err), k8sErrors.IsForbidden(err):
|
||||||
return &UnrecoverableSyncError{err}
|
return &UnrecoverableSyncError{err}
|
||||||
default:
|
default:
|
||||||
return err
|
return err
|
||||||
|
|
@ -108,7 +108,7 @@ func updateObjectGeneric(specObj, clusterObj crclient.Object, api ClusterAPI) er
|
||||||
case k8sErrors.IsConflict(err), k8sErrors.IsNotFound(err):
|
case k8sErrors.IsConflict(err), k8sErrors.IsNotFound(err):
|
||||||
// Need to catch IsNotFound here because we attempt to update when creation fails with AlreadyExists
|
// Need to catch IsNotFound here because we attempt to update when creation fails with AlreadyExists
|
||||||
return NewNotInSync(specObj, NeedRetryReason)
|
return NewNotInSync(specObj, NeedRetryReason)
|
||||||
case k8sErrors.IsInvalid(err):
|
case k8sErrors.IsInvalid(err), k8sErrors.IsForbidden(err):
|
||||||
return &UnrecoverableSyncError{err}
|
return &UnrecoverableSyncError{err}
|
||||||
default:
|
default:
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -50,11 +50,29 @@ func serviceUpdateFunc(spec, cluster crclient.Object) (crclient.Object, error) {
|
||||||
return specService, nil
|
return specService, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func serviceAccountUpdateFunc(spec, cluster crclient.Object) (crclient.Object, error) {
|
||||||
|
if cluster == nil {
|
||||||
|
// May occur if ServiceAccount is not cached by the operator
|
||||||
|
return spec, nil
|
||||||
|
}
|
||||||
|
spec.SetResourceVersion(cluster.GetResourceVersion())
|
||||||
|
ownerrefs := spec.GetOwnerReferences()
|
||||||
|
for _, clusterOwnerref := range cluster.GetOwnerReferences() {
|
||||||
|
if !containsOwnerRef(clusterOwnerref, ownerrefs) {
|
||||||
|
ownerrefs = append(ownerrefs, clusterOwnerref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spec.SetOwnerReferences(ownerrefs)
|
||||||
|
return spec, nil
|
||||||
|
}
|
||||||
|
|
||||||
func getUpdateFunc(obj crclient.Object) updateFunc {
|
func getUpdateFunc(obj crclient.Object) updateFunc {
|
||||||
objType := reflect.TypeOf(obj).Elem()
|
objType := reflect.TypeOf(obj).Elem()
|
||||||
switch objType {
|
switch objType {
|
||||||
case reflect.TypeOf(corev1.Service{}):
|
case reflect.TypeOf(corev1.Service{}):
|
||||||
return serviceUpdateFunc
|
return serviceUpdateFunc
|
||||||
|
case reflect.TypeOf(corev1.ServiceAccount{}):
|
||||||
|
return serviceAccountUpdateFunc
|
||||||
default:
|
default:
|
||||||
return defaultUpdateFunc
|
return defaultUpdateFunc
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,86 +46,182 @@ func AllPtrFieldsNil(obj interface{}) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int32Ptr returns a pointer to an int32
|
// Int32 returns a pointer to an int32.
|
||||||
func Int32Ptr(i int32) *int32 {
|
func Int32(i int32) *int32 {
|
||||||
return &i
|
return &i
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int32PtrDerefOr dereference the int32 ptr and returns it if not nil,
|
var Int32Ptr = Int32 // for back-compat
|
||||||
// else returns def.
|
|
||||||
func Int32PtrDerefOr(ptr *int32, def int32) int32 {
|
// Int32Deref dereferences the int32 ptr and returns it if not nil, or else
|
||||||
|
// returns def.
|
||||||
|
func Int32Deref(ptr *int32, def int32) int32 {
|
||||||
if ptr != nil {
|
if ptr != nil {
|
||||||
return *ptr
|
return *ptr
|
||||||
}
|
}
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int64Ptr returns a pointer to an int64
|
var Int32PtrDerefOr = Int32Deref // for back-compat
|
||||||
func Int64Ptr(i int64) *int64 {
|
|
||||||
|
// Int32Equal returns true if both arguments are nil or both arguments
|
||||||
|
// dereference to the same value.
|
||||||
|
func Int32Equal(a, b *int32) bool {
|
||||||
|
if (a == nil) != (b == nil) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return *a == *b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 returns a pointer to an int64.
|
||||||
|
func Int64(i int64) *int64 {
|
||||||
return &i
|
return &i
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int64PtrDerefOr dereference the int64 ptr and returns it if not nil,
|
var Int64Ptr = Int64 // for back-compat
|
||||||
// else returns def.
|
|
||||||
func Int64PtrDerefOr(ptr *int64, def int64) int64 {
|
// Int64Deref dereferences the int64 ptr and returns it if not nil, or else
|
||||||
|
// returns def.
|
||||||
|
func Int64Deref(ptr *int64, def int64) int64 {
|
||||||
if ptr != nil {
|
if ptr != nil {
|
||||||
return *ptr
|
return *ptr
|
||||||
}
|
}
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
|
|
||||||
// BoolPtr returns a pointer to a bool
|
var Int64PtrDerefOr = Int64Deref // for back-compat
|
||||||
func BoolPtr(b bool) *bool {
|
|
||||||
|
// Int64Equal returns true if both arguments are nil or both arguments
|
||||||
|
// dereference to the same value.
|
||||||
|
func Int64Equal(a, b *int64) bool {
|
||||||
|
if (a == nil) != (b == nil) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return *a == *b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool returns a pointer to a bool.
|
||||||
|
func Bool(b bool) *bool {
|
||||||
return &b
|
return &b
|
||||||
}
|
}
|
||||||
|
|
||||||
// BoolPtrDerefOr dereference the bool ptr and returns it if not nil,
|
var BoolPtr = Bool // for back-compat
|
||||||
// else returns def.
|
|
||||||
func BoolPtrDerefOr(ptr *bool, def bool) bool {
|
// BoolDeref dereferences the bool ptr and returns it if not nil, or else
|
||||||
|
// returns def.
|
||||||
|
func BoolDeref(ptr *bool, def bool) bool {
|
||||||
if ptr != nil {
|
if ptr != nil {
|
||||||
return *ptr
|
return *ptr
|
||||||
}
|
}
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringPtr returns a pointer to the passed string.
|
var BoolPtrDerefOr = BoolDeref // for back-compat
|
||||||
func StringPtr(s string) *string {
|
|
||||||
|
// BoolEqual returns true if both arguments are nil or both arguments
|
||||||
|
// dereference to the same value.
|
||||||
|
func BoolEqual(a, b *bool) bool {
|
||||||
|
if (a == nil) != (b == nil) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return *a == *b
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a pointer to a string.
|
||||||
|
func String(s string) *string {
|
||||||
return &s
|
return &s
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringPtrDerefOr dereference the string ptr and returns it if not nil,
|
var StringPtr = String // for back-compat
|
||||||
// else returns def.
|
|
||||||
func StringPtrDerefOr(ptr *string, def string) string {
|
// StringDeref dereferences the string ptr and returns it if not nil, or else
|
||||||
|
// returns def.
|
||||||
|
func StringDeref(ptr *string, def string) string {
|
||||||
if ptr != nil {
|
if ptr != nil {
|
||||||
return *ptr
|
return *ptr
|
||||||
}
|
}
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float32Ptr returns a pointer to the passed float32.
|
var StringPtrDerefOr = StringDeref // for back-compat
|
||||||
func Float32Ptr(i float32) *float32 {
|
|
||||||
|
// StringEqual returns true if both arguments are nil or both arguments
|
||||||
|
// dereference to the same value.
|
||||||
|
func StringEqual(a, b *string) bool {
|
||||||
|
if (a == nil) != (b == nil) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return *a == *b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32 returns a pointer to the a float32.
|
||||||
|
func Float32(i float32) *float32 {
|
||||||
return &i
|
return &i
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float32PtrDerefOr dereference the float32 ptr and returns it if not nil,
|
var Float32Ptr = Float32
|
||||||
// else returns def.
|
|
||||||
func Float32PtrDerefOr(ptr *float32, def float32) float32 {
|
// Float32Deref dereferences the float32 ptr and returns it if not nil, or else
|
||||||
|
// returns def.
|
||||||
|
func Float32Deref(ptr *float32, def float32) float32 {
|
||||||
if ptr != nil {
|
if ptr != nil {
|
||||||
return *ptr
|
return *ptr
|
||||||
}
|
}
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float64Ptr returns a pointer to the passed float64.
|
var Float32PtrDerefOr = Float32Deref // for back-compat
|
||||||
func Float64Ptr(i float64) *float64 {
|
|
||||||
|
// Float32Equal returns true if both arguments are nil or both arguments
|
||||||
|
// dereference to the same value.
|
||||||
|
func Float32Equal(a, b *float32) bool {
|
||||||
|
if (a == nil) != (b == nil) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return *a == *b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 returns a pointer to the a float64.
|
||||||
|
func Float64(i float64) *float64 {
|
||||||
return &i
|
return &i
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float64PtrDerefOr dereference the float64 ptr and returns it if not nil,
|
var Float64Ptr = Float64
|
||||||
// else returns def.
|
|
||||||
func Float64PtrDerefOr(ptr *float64, def float64) float64 {
|
// Float64Deref dereferences the float64 ptr and returns it if not nil, or else
|
||||||
|
// returns def.
|
||||||
|
func Float64Deref(ptr *float64, def float64) float64 {
|
||||||
if ptr != nil {
|
if ptr != nil {
|
||||||
return *ptr
|
return *ptr
|
||||||
}
|
}
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var Float64PtrDerefOr = Float64Deref // for back-compat
|
||||||
|
|
||||||
|
// Float64Equal returns true if both arguments are nil or both arguments
|
||||||
|
// dereference to the same value.
|
||||||
|
func Float64Equal(a, b *float64) bool {
|
||||||
|
if (a == nil) != (b == nil) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return *a == *b
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ func writeTraceItemSummary(b *bytes.Buffer, msg string, totalTime time.Duration,
|
||||||
b.WriteString(" ")
|
b.WriteString(" ")
|
||||||
}
|
}
|
||||||
|
|
||||||
b.WriteString(fmt.Sprintf("%vms (%v)", durationToMilliseconds(totalTime), startTime.Format("15:04:00.000")))
|
b.WriteString(fmt.Sprintf("%vms (%v)", durationToMilliseconds(totalTime), startTime.Format("15:04:05.000")))
|
||||||
}
|
}
|
||||||
|
|
||||||
func durationToMilliseconds(timeDuration time.Duration) int64 {
|
func durationToMilliseconds(timeDuration time.Duration) int64 {
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,12 @@ github.com/cespare/xxhash/v2
|
||||||
github.com/che-incubator/kubernetes-image-puller-operator/api/v1alpha1
|
github.com/che-incubator/kubernetes-image-puller-operator/api/v1alpha1
|
||||||
# github.com/davecgh/go-spew v1.1.1 => github.com/davecgh/go-spew v1.1.1
|
# github.com/davecgh/go-spew v1.1.1 => github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/davecgh/go-spew/spew
|
github.com/davecgh/go-spew/spew
|
||||||
# github.com/devfile/api/v2 v2.0.0-20220414122024-32cae1f8e42c
|
# github.com/devfile/api/v2 v2.0.0-20220928161623-fe7c10eaa530
|
||||||
## explicit
|
## explicit
|
||||||
github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2
|
github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2
|
||||||
github.com/devfile/api/v2/pkg/attributes
|
github.com/devfile/api/v2/pkg/attributes
|
||||||
github.com/devfile/api/v2/pkg/devfile
|
github.com/devfile/api/v2/pkg/devfile
|
||||||
# github.com/devfile/devworkspace-operator v0.15.2
|
# github.com/devfile/devworkspace-operator v0.17.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/devfile/devworkspace-operator/apis/controller/v1alpha1
|
github.com/devfile/devworkspace-operator/apis/controller/v1alpha1
|
||||||
github.com/devfile/devworkspace-operator/controllers/controller/devworkspacerouting
|
github.com/devfile/devworkspace-operator/controllers/controller/devworkspacerouting
|
||||||
|
|
@ -67,7 +67,7 @@ github.com/golang/groupcache/lru
|
||||||
# github.com/golang/mock v1.5.0 => github.com/golang/mock v1.5.0
|
# github.com/golang/mock v1.5.0 => github.com/golang/mock v1.5.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/golang/mock/gomock
|
github.com/golang/mock/gomock
|
||||||
# github.com/golang/protobuf v1.4.3 => github.com/golang/protobuf v1.4.3
|
# github.com/golang/protobuf v1.5.2 => github.com/golang/protobuf v1.4.3
|
||||||
github.com/golang/protobuf/proto
|
github.com/golang/protobuf/proto
|
||||||
github.com/golang/protobuf/ptypes
|
github.com/golang/protobuf/ptypes
|
||||||
github.com/golang/protobuf/ptypes/any
|
github.com/golang/protobuf/ptypes/any
|
||||||
|
|
@ -296,7 +296,7 @@ google.golang.org/grpc/serviceconfig
|
||||||
google.golang.org/grpc/stats
|
google.golang.org/grpc/stats
|
||||||
google.golang.org/grpc/status
|
google.golang.org/grpc/status
|
||||||
google.golang.org/grpc/tap
|
google.golang.org/grpc/tap
|
||||||
# google.golang.org/protobuf v1.26.0-rc.1 => google.golang.org/protobuf v1.25.0
|
# google.golang.org/protobuf v1.26.0 => google.golang.org/protobuf v1.25.0
|
||||||
google.golang.org/protobuf/encoding/prototext
|
google.golang.org/protobuf/encoding/prototext
|
||||||
google.golang.org/protobuf/encoding/protowire
|
google.golang.org/protobuf/encoding/protowire
|
||||||
google.golang.org/protobuf/internal/descfmt
|
google.golang.org/protobuf/internal/descfmt
|
||||||
|
|
@ -648,7 +648,7 @@ k8s.io/component-base/config/v1alpha1
|
||||||
k8s.io/klog/v2
|
k8s.io/klog/v2
|
||||||
# k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 => k8s.io/kube-openapi v0.0.0-20200923105717-7eba4cbaebdf
|
# k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 => k8s.io/kube-openapi v0.0.0-20200923105717-7eba4cbaebdf
|
||||||
k8s.io/kube-openapi/pkg/util/proto
|
k8s.io/kube-openapi/pkg/util/proto
|
||||||
# k8s.io/utils v0.0.0-20210722164352-7f3ee0f31471 => k8s.io/utils v0.0.0-20201110183641-67b214c5f920
|
# k8s.io/utils v0.0.0-20210722164352-7f3ee0f31471
|
||||||
## explicit
|
## explicit
|
||||||
k8s.io/utils/buffer
|
k8s.io/utils/buffer
|
||||||
k8s.io/utils/integer
|
k8s.io/utils/integer
|
||||||
|
|
@ -1064,7 +1064,6 @@ sigs.k8s.io/yaml
|
||||||
# k8s.io/klog/v2 => k8s.io/klog/v2 v2.8.0
|
# k8s.io/klog/v2 => k8s.io/klog/v2 v2.8.0
|
||||||
# k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.0.0-20180912235703-14b8d2d93fcb
|
# k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.0.0-20180912235703-14b8d2d93fcb
|
||||||
# k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20200923105717-7eba4cbaebdf
|
# k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20200923105717-7eba4cbaebdf
|
||||||
# k8s.io/utils => k8s.io/utils v0.0.0-20201110183641-67b214c5f920
|
|
||||||
# kubernetes/klog => kubernetes/klog v1.0.0
|
# kubernetes/klog => kubernetes/klog v1.0.0
|
||||||
# modernc.org/b => modernc.org/b v1.0.0
|
# modernc.org/b => modernc.org/b v1.0.0
|
||||||
# modernc.org/db => modernc.org/db v1.0.0
|
# modernc.org/db => modernc.org/db v1.0.0
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue