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) |
|
||||
| [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) |
|
||||
| [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) |
|
||||
| [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) |
|
||||
|
|
@ -458,6 +458,6 @@
|
|||
| [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/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/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.
|
||||
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 vendor
|
||||
|
||||
|
|
@ -762,12 +762,12 @@ install-devworkspace: ## Install Dev Workspace operator, available channels: nex
|
|||
IMAGE="quay.io/devfile/devworkspace-operator-index:next"
|
||||
fi
|
||||
|
||||
$(MAKE) create-catalogsource IMAGE="$${IMAGE}" NAME="devworkspace-operator"
|
||||
$(MAKE) create-catalogsource IMAGE="$${IMAGE}" NAME="devworkspace-operator" NAMESPACE="openshift-marketplace"
|
||||
$(MAKE) create-subscription \
|
||||
NAME="devworkspace-operator" \
|
||||
NAMESPACE="openshift-operators" \
|
||||
PACKAGE_NAME="devworkspace-operator" \
|
||||
CHANNEL="$(CHANNEL)" \
|
||||
CHANNEL=$(CHANNEL) \
|
||||
SOURCE="devworkspace-operator" \
|
||||
SOURCE_NAMESPACE="openshift-marketplace" \
|
||||
INSTALL_PLAN_APPROVAL="Auto"
|
||||
|
|
|
|||
|
|
@ -114,6 +114,11 @@ type CheClusterDevEnvironments struct {
|
|||
// Container build configuration.
|
||||
// +optional
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ runTest() {
|
|||
--k8spodwaittimeout=120000 \
|
||||
--k8spodreadytimeout=120000 \
|
||||
--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"
|
||||
|
||||
make wait-devworkspace-running NAMESPACE="devworkspace-controller" VERBOSE=1
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ runTest() {
|
|||
--k8spodwaittimeout=120000 \
|
||||
--k8spodreadytimeout=120000 \
|
||||
--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"
|
||||
|
||||
# Free up some cpu resources
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ catchFinish() {
|
|||
exit ${RESULT}
|
||||
}
|
||||
|
||||
waitForRemovedEclipseCheSubscription() {
|
||||
while [[ $(oc get subscription -A -o json | jq -r '.items | .[] | select(.spec.name == "'${ECLIPSE_CHE_PACKAGE_NAME}'")') != "" ]]; do
|
||||
waitForRemovedSubscription() {
|
||||
while [[ $(oc get subscription -A -o json | jq -r '.items | .[] | select(.spec.name == "'$1'")') != "" ]]; do
|
||||
sleep 5s
|
||||
done
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,16 +31,30 @@ deleteEclipseCheStableVersionOperator() {
|
|||
oc delete csv ${ECLIPSE_CHE_INSTALLED_CSV} -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.
|
||||
# We have to disable it for a while.
|
||||
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() {
|
||||
. ${OPERATOR_REPO}/build/scripts/olm/test-catalog.sh -i quay.io/eclipse/eclipse-che-olm-catalog:stable -c stable --verbose
|
||||
deleteEclipseCheStableVersionOperator
|
||||
deleteDevWorkspaceStableVersionOperator
|
||||
. ${OPERATOR_REPO}/build/scripts/olm/test-catalog-from-sources.sh --verbose
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -145,6 +145,9 @@ createEclipseCheCatalogFromSources() {
|
|||
run() {
|
||||
make create-namespace NAMESPACE="${NAMESPACE}" VERBOSE=${VERBOSE}
|
||||
|
||||
# Install Dev Workspace operator (next version as well)
|
||||
make install-devworkspace CHANNEL="next"
|
||||
|
||||
exposeOpenShiftRegistry
|
||||
createEclipseCheCatalogFromSources
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ usage () {
|
|||
|
||||
run() {
|
||||
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}"
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ usage () {
|
|||
|
||||
run() {
|
||||
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}"
|
||||
|
||||
|
|
|
|||
|
|
@ -6995,6 +6995,12 @@ spec:
|
|||
run timeout, set this value to -1.
|
||||
format: int32
|
||||
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:
|
||||
default:
|
||||
pvcStrategy: per-user
|
||||
|
|
|
|||
|
|
@ -6805,6 +6805,12 @@ spec:
|
|||
run timeout, set this value to -1.
|
||||
format: int32
|
||||
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:
|
||||
default:
|
||||
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.
|
||||
format: int32
|
||||
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:
|
||||
default:
|
||||
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.
|
||||
format: int32
|
||||
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:
|
||||
default:
|
||||
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.
|
||||
format: int32
|
||||
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:
|
||||
default:
|
||||
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.
|
||||
format: int32
|
||||
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:
|
||||
default:
|
||||
pvcStrategy: per-user
|
||||
|
|
|
|||
5
go.mod
5
go.mod
|
|
@ -5,8 +5,8 @@ go 1.16
|
|||
require (
|
||||
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/devfile/api/v2 v2.0.0-20220414122024-32cae1f8e42c
|
||||
github.com/devfile/devworkspace-operator v0.15.2
|
||||
github.com/devfile/api/v2 v2.0.0-20220928161623-fe7c10eaa530
|
||||
github.com/devfile/devworkspace-operator v0.17.0
|
||||
github.com/go-logr/logr v0.4.0
|
||||
github.com/golang/mock v1.5.0
|
||||
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/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/utils => k8s.io/utils v0.0.0-20201110183641-67b214c5f920
|
||||
kubernetes/klog => kubernetes/klog v1.0.0
|
||||
modernc.org/b => modernc.org/b 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/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/devfile/api/v2 v2.0.0-20220414122024-32cae1f8e42c h1:yyidoxal8ngJWDxRuVZMNh4PBwqDIzOkTeOagtmRiy0=
|
||||
github.com/devfile/api/v2 v2.0.0-20220414122024-32cae1f8e42c/go.mod h1:kLX/nW93gigOHXK3NLeJL2fSS/sgEe+OHu8bo3aoOi4=
|
||||
github.com/devfile/devworkspace-operator v0.15.2 h1:CcLGHtuBOKdwpeYV8Iy7ZwUSgavcMbjuPA9ejupM7BE=
|
||||
github.com/devfile/devworkspace-operator v0.15.2/go.mod h1:fM3/GhPWEL8JZOEImCnpyxTYEf9dN6PsjzJq+ffcD1k=
|
||||
github.com/devfile/api/v2 v2.0.0-20220928161623-fe7c10eaa530 h1:pZvf4AZrf/ZwV2AwQnTInlUpns+Wj9JYtPRtBDiFHzk=
|
||||
github.com/devfile/api/v2 v2.0.0-20220928161623-fe7c10eaa530/go.mod h1:dN7xFrOVG+iPqn4UKGibXLd5oVsdE8XyK9OEb5JL3aI=
|
||||
github.com/devfile/devworkspace-operator v0.17.0 h1:Ml8ncEQAVf/d+DTttQ76oe/btnFQpicRG/XncdflOZo=
|
||||
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/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
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/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/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-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/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8=
|
||||
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.
|
||||
format: int32
|
||||
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:
|
||||
default:
|
||||
pvcStrategy: per-user
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
|
|
@ -54,13 +55,12 @@ func (d *DevWorkspaceConfigReconciler) Reconcile(ctx *chetypes.DeployContext) (r
|
|||
if dwoc.Config == nil {
|
||||
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
|
||||
}
|
||||
|
||||
done, err := deploy.Sync(ctx, dwoc)
|
||||
if !done {
|
||||
if done, err := deploy.Sync(ctx, dwoc); !done {
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
|
||||
|
|
@ -71,52 +71,62 @@ func (d *DevWorkspaceConfigReconciler) Finalize(ctx *chetypes.DeployContext) boo
|
|||
return true
|
||||
}
|
||||
|
||||
func updateOperatorConfig(storage chev2.WorkspaceStorage, operatorConfig *controllerv1alpha1.OperatorConfiguration) error {
|
||||
var pvc *chev2.PVC
|
||||
|
||||
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
|
||||
}
|
||||
func updateWorkspaceConfig(devEnvironments *chev2.CheClusterDevEnvironments, operatorConfig *controllerv1alpha1.OperatorConfiguration) error {
|
||||
if operatorConfig.Workspace == nil {
|
||||
operatorConfig.Workspace = &controllerv1alpha1.WorkspaceConfig{}
|
||||
}
|
||||
|
||||
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 operatorConfig.Workspace == nil {
|
||||
operatorConfig.Workspace = &controllerv1alpha1.WorkspaceConfig{}
|
||||
}
|
||||
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{}
|
||||
if pvc.StorageClass != "" {
|
||||
workspaceConfig.StorageClassName = &pvc.StorageClass
|
||||
}
|
||||
|
||||
pvcSize, err := resource.ParseQuantity(pvc.ClaimSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pvc.ClaimSize != "" {
|
||||
if workspaceConfig.DefaultStorageSize == nil {
|
||||
workspaceConfig.DefaultStorageSize = &controllerv1alpha1.StorageSizes{}
|
||||
}
|
||||
|
||||
if isPerWorkspacePVCStorageStrategy {
|
||||
workspaceConfig.DefaultStorageSize.PerWorkspace = &pvcSize
|
||||
} else {
|
||||
workspaceConfig.DefaultStorageSize.Common = &pvcSize
|
||||
pvcSize, err := resource.ParseQuantity(pvc.ClaimSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isPerWorkspacePVCStorageStrategy {
|
||||
workspaceConfig.DefaultStorageSize.PerWorkspace = &pvcSize
|
||||
} else {
|
||||
workspaceConfig.DefaultStorageSize.Common = &pvcSize
|
||||
}
|
||||
}
|
||||
}
|
||||
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"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
|
||||
"github.com/eclipse-che/che-operator/pkg/common/constants"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/utils/pointer"
|
||||
|
|
@ -85,7 +88,7 @@ func TestReconcileDevWorkspaceConfigPerUserStorage(t *testing.T) {
|
|||
DevEnvironments: chev2.CheClusterDevEnvironments{},
|
||||
},
|
||||
},
|
||||
expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{},
|
||||
expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{Workspace: &controllerv1alpha1.WorkspaceConfig{}},
|
||||
},
|
||||
{
|
||||
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)
|
||||
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
|
||||
type DevWorkspaceSpec struct {
|
||||
Started bool `json:"started"`
|
||||
RoutingClass string `json:"routingClass,omitempty"`
|
||||
Template DevWorkspaceTemplateSpec `json:"template,omitempty"`
|
||||
Started bool `json:"started"`
|
||||
RoutingClass string `json:"routingClass,omitempty"`
|
||||
Template DevWorkspaceTemplateSpec `json:"template,omitempty"`
|
||||
Contributions []ComponentContribution `json:"contributions,omitempty"`
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (in *ComponentParentOverride) DeepCopyInto(out *ComponentParentOverride) {
|
||||
*out = *in
|
||||
|
|
@ -1637,6 +1660,13 @@ func (in *DevWorkspaceList) DeepCopyObject() runtime.Object {
|
|||
func (in *DevWorkspaceSpec) DeepCopyInto(out *DevWorkspaceSpec) {
|
||||
*out = *in
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
package v1alpha1
|
||||
|
||||
import (
|
||||
dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
|
@ -78,6 +79,21 @@ type StorageSizes struct {
|
|||
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 {
|
||||
// ImagePullPolicy defines the imagePullPolicy used for containers in a DevWorkspace
|
||||
// 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:MaxLength=63
|
||||
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
|
||||
// volume claims created to support DevWorkspaces
|
||||
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.
|
||||
CleanupOnStop *bool `json:"cleanupOnStop,omitempty"`
|
||||
// PodSecurityContext overrides the default PodSecurityContext used for all workspace-related
|
||||
// pods created by the DevWorkspace Operator when running on Kubernetes. On OpenShift, this
|
||||
// configuration option is ignored. If set, the entire pod security context is overridden;
|
||||
// values are not merged.
|
||||
// pods created by the DevWorkspace Operator. If set, defined values are merged into the default
|
||||
// configuration
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
//
|
||||
|
|
@ -21,6 +20,7 @@
|
|||
package v1alpha1
|
||||
|
||||
import (
|
||||
"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
|
@ -478,6 +478,26 @@ func (in *RoutingConfig) DeepCopy() *RoutingConfig {
|
|||
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.
|
||||
func (in *StorageSizes) DeepCopyInto(out *StorageSizes) {
|
||||
*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.
|
||||
func (in *WorkspaceConfig) DeepCopyInto(out *WorkspaceConfig) {
|
||||
*out = *in
|
||||
if in.ServiceAccount != nil {
|
||||
in, out := &in.ServiceAccount, &out.ServiceAccount
|
||||
*out = new(ServiceAccountConfig)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.StorageClassName != nil {
|
||||
in, out := &in.StorageClassName, &out.StorageClassName
|
||||
*out = new(string)
|
||||
|
|
@ -531,6 +556,16 @@ func (in *WorkspaceConfig) DeepCopyInto(out *WorkspaceConfig) {
|
|||
*out = new(v1.PodSecurityContext)
|
||||
(*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.
|
||||
|
|
|
|||
|
|
@ -57,7 +57,8 @@ func (s *BasicSolver) Finalize(*controllerv1alpha1.DevWorkspaceRouting) error {
|
|||
func (s *BasicSolver) GetSpecObjects(routing *controllerv1alpha1.DevWorkspaceRouting, workspaceMeta DevWorkspaceMetadata) (RoutingObjects, error) {
|
||||
routingObjects := RoutingObjects{}
|
||||
|
||||
routingSuffix := config.Routing.ClusterHostSuffix
|
||||
// TODO: Use workspace-scoped ClusterHostSuffix to allow overriding
|
||||
routingSuffix := config.GetGlobalConfig().Routing.ClusterHostSuffix
|
||||
if routingSuffix == "" {
|
||||
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")
|
||||
}
|
||||
|
||||
func ServiceAccountName(workspaceId string) string {
|
||||
return fmt.Sprintf("%s-%s", workspaceId, "sa")
|
||||
func ServiceAccountName(workspace *DevWorkspaceWithConfig) string {
|
||||
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 {
|
||||
|
|
|
|||
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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
|
||||
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
// defaultConfig represents the default configuration for the DevWorkspace Operator.
|
||||
|
|
@ -30,28 +34,45 @@ var defaultConfig = &v1alpha1.OperatorConfiguration{
|
|||
Workspace: &v1alpha1.WorkspaceConfig{
|
||||
ImagePullPolicy: "Always",
|
||||
PVCName: "claim-devworkspace",
|
||||
ServiceAccount: &v1alpha1.ServiceAccountConfig{
|
||||
DisableCreation: pointer.Bool(false),
|
||||
},
|
||||
DefaultStorageSize: &v1alpha1.StorageSizes{
|
||||
Common: &commonStorageSize,
|
||||
PerWorkspace: &perWorkspaceStorageSize,
|
||||
},
|
||||
IdleTimeout: "15m",
|
||||
ProgressTimeout: "5m",
|
||||
CleanupOnStop: &boolFalse,
|
||||
PodSecurityContext: &corev1.PodSecurityContext{
|
||||
RunAsUser: &int64UID,
|
||||
RunAsGroup: &int64GID,
|
||||
RunAsNonRoot: &boolTrue,
|
||||
FSGroup: &int64UID,
|
||||
},
|
||||
IdleTimeout: "15m",
|
||||
ProgressTimeout: "5m",
|
||||
CleanupOnStop: pointer.BoolPtr(false),
|
||||
PodSecurityContext: nil,
|
||||
ContainerSecurityContext: &corev1.SecurityContext{},
|
||||
DefaultTemplate: nil,
|
||||
},
|
||||
}
|
||||
|
||||
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
|
||||
var (
|
||||
boolTrue = true
|
||||
boolFalse = false
|
||||
int64UID = int64(1234)
|
||||
int64GID = int64(0)
|
||||
commonStorageSize = resource.MustParse("10Gi")
|
||||
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"
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/pointer"
|
||||
crclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
dw "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
|
||||
|
|
@ -128,8 +129,7 @@ func convertConfigMapToConfigCRD(client crclient.Client) (*dw.DevWorkspaceOperat
|
|||
var experimentalFeatures *bool
|
||||
experimentalFeaturesStr := configmap.ControllerCfg.GetExperimentalFeaturesEnabled()
|
||||
if experimentalFeaturesStr != nil && *experimentalFeaturesStr == "true" {
|
||||
trueBool := true
|
||||
experimentalFeatures = &trueBool
|
||||
experimentalFeatures = pointer.Bool(true)
|
||||
}
|
||||
|
||||
if !setRoutingConfig && !setWorkspaceConfig && experimentalFeatures == nil {
|
||||
|
|
|
|||
|
|
@ -17,19 +17,26 @@ package config
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||
"github.com/devfile/devworkspace-operator/pkg/config/proxy"
|
||||
routeV1 "github.com/openshift/api/route/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||
"k8s.io/utils/pointer"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
crclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
controller "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
|
||||
"github.com/devfile/devworkspace-operator/pkg/constants"
|
||||
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
|
||||
)
|
||||
|
||||
|
|
@ -39,26 +46,64 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
Routing *controller.RoutingConfig
|
||||
Workspace *controller.WorkspaceConfig
|
||||
internalConfig *controller.OperatorConfiguration
|
||||
configMutex sync.Mutex
|
||||
configNamespace string
|
||||
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()
|
||||
defer configMutex.Unlock()
|
||||
internalConfig = defaultConfig.DeepCopy()
|
||||
mergeConfig(config, internalConfig)
|
||||
updatePublicConfig()
|
||||
testConfig := defaultConfig.DeepCopy()
|
||||
mergeConfig(customConfig, testConfig)
|
||||
return testConfig
|
||||
}
|
||||
|
||||
func SetupControllerConfig(client crclient.Client) error {
|
||||
if internalConfig != nil {
|
||||
return fmt.Errorf("internal controller configuration is already set up")
|
||||
}
|
||||
if err := setDefaultPodSecurityContext(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
internalConfig = &controller.OperatorConfiguration{}
|
||||
|
||||
namespace, err := infrastructure.GetNamespace()
|
||||
|
|
@ -93,7 +138,7 @@ func SetupControllerConfig(client crclient.Client) error {
|
|||
defaultConfig.Routing.ProxyConfig = clusterProxy
|
||||
internalConfig.Routing.ProxyConfig = proxy.MergeProxyConfigs(clusterProxy, internalConfig.Routing.ProxyConfig)
|
||||
|
||||
updatePublicConfig()
|
||||
logCurrentConfig()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -119,6 +164,13 @@ func getClusterConfig(namespace string, client crclient.Client) (*controller.Dev
|
|||
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) {
|
||||
if newConfig == nil || newConfig.Name != OperatorConfigName || newConfig.Namespace != configNamespace {
|
||||
return
|
||||
|
|
@ -127,19 +179,13 @@ func syncConfigFrom(newConfig *controller.DevWorkspaceOperatorConfig) {
|
|||
defer configMutex.Unlock()
|
||||
internalConfig = defaultConfig.DeepCopy()
|
||||
mergeConfig(newConfig.Config, internalConfig)
|
||||
updatePublicConfig()
|
||||
logCurrentConfig()
|
||||
}
|
||||
|
||||
func restoreDefaultConfig() {
|
||||
configMutex.Lock()
|
||||
defer configMutex.Unlock()
|
||||
internalConfig = defaultConfig.DeepCopy()
|
||||
updatePublicConfig()
|
||||
}
|
||||
|
||||
func updatePublicConfig() {
|
||||
Routing = internalConfig.Routing.DeepCopy()
|
||||
Workspace = internalConfig.Workspace.DeepCopy()
|
||||
logCurrentConfig()
|
||||
}
|
||||
|
||||
|
|
@ -222,6 +268,17 @@ func mergeConfig(from, to *controller.OperatorConfiguration) {
|
|||
if 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 != "" {
|
||||
to.Workspace.ImagePullPolicy = from.Workspace.ImagePullPolicy
|
||||
}
|
||||
|
|
@ -238,7 +295,10 @@ func mergeConfig(from, to *controller.OperatorConfiguration) {
|
|||
to.Workspace.CleanupOnStop = from.Workspace.CleanupOnStop
|
||||
}
|
||||
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 to.Workspace.DefaultStorageSize == nil {
|
||||
|
|
@ -253,57 +313,143 @@ func mergeConfig(from, to *controller.OperatorConfiguration) {
|
|||
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
|
||||
func logCurrentConfig() {
|
||||
if internalConfig == nil {
|
||||
return
|
||||
}
|
||||
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 {
|
||||
currConfig := GetCurrentConfigString(internalConfig)
|
||||
if len(currConfig) == 0 {
|
||||
log.Info("Updated config to [(default config)]")
|
||||
} 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 {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,21 @@ const (
|
|||
// stopped.
|
||||
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
|
||||
// components in the DevWorkspace (pod.spec.runtimeClassName). If empty, no runtimeClassName is added.
|
||||
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
|
||||
// was created to route to this endpoint
|
||||
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
|
||||
|
||||
// CommonStorageClassType defines the 'common' storage policy -- one PVC is provisioned per namespace and all devworkspace storage
|
||||
// is mounted in it on subpaths according to devworkspace ID.
|
||||
// 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.
|
||||
// The 'common' storage policy exists only for legacy compatibility.
|
||||
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
|
||||
// to a storage deployment that mounts a common PVC for the namespace.
|
||||
AsyncStorageClassType = "async"
|
||||
|
|
|
|||
|
|
@ -60,6 +60,10 @@ func InitializeForTesting(currentInfrastructure Type) {
|
|||
initialized = true
|
||||
}
|
||||
|
||||
func IsInitialized() bool {
|
||||
return initialized
|
||||
}
|
||||
|
||||
// IsOpenShift returns true if the current cluster is an OpenShift (v4.x) cluster.
|
||||
func IsOpenShift() bool {
|
||||
if !initialized {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
networkingv1 "k8s.io/api/networking/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
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{
|
||||
reflect.TypeOf(rbacv1.Role{}): allDiffFuncs(labelsAndAnnotationsDiffFunc, basicDiffFunc(roleDiffOpts)),
|
||||
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(corev1.ConfigMap{}): allDiffFuncs(labelsAndAnnotationsDiffFunc, basicDiffFunc(configmapDiffOpts)),
|
||||
reflect.TypeOf(corev1.Secret{}): allDiffFuncs(labelsAndAnnotationsDiffFunc, basicDiffFunc(secretDiffOpts)),
|
||||
|
|
@ -73,6 +74,16 @@ func labelsAndAnnotationsDiffFunc(spec, cluster crclient.Object) (delete, update
|
|||
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,
|
||||
// returning the result of the first function to require an update/deletion.
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
// results in the object not being tracked by the controller's cache.
|
||||
return updateObjectGeneric(specObj, nil, api)
|
||||
case k8sErrors.IsInvalid(err):
|
||||
case k8sErrors.IsInvalid(err), k8sErrors.IsForbidden(err):
|
||||
return &UnrecoverableSyncError{err}
|
||||
default:
|
||||
return err
|
||||
|
|
@ -108,7 +108,7 @@ func updateObjectGeneric(specObj, clusterObj crclient.Object, api ClusterAPI) er
|
|||
case k8sErrors.IsConflict(err), k8sErrors.IsNotFound(err):
|
||||
// Need to catch IsNotFound here because we attempt to update when creation fails with AlreadyExists
|
||||
return NewNotInSync(specObj, NeedRetryReason)
|
||||
case k8sErrors.IsInvalid(err):
|
||||
case k8sErrors.IsInvalid(err), k8sErrors.IsForbidden(err):
|
||||
return &UnrecoverableSyncError{err}
|
||||
default:
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -50,11 +50,29 @@ func serviceUpdateFunc(spec, cluster crclient.Object) (crclient.Object, error) {
|
|||
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 {
|
||||
objType := reflect.TypeOf(obj).Elem()
|
||||
switch objType {
|
||||
case reflect.TypeOf(corev1.Service{}):
|
||||
return serviceUpdateFunc
|
||||
case reflect.TypeOf(corev1.ServiceAccount{}):
|
||||
return serviceAccountUpdateFunc
|
||||
default:
|
||||
return defaultUpdateFunc
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,86 +46,182 @@ func AllPtrFieldsNil(obj interface{}) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Int32Ptr returns a pointer to an int32
|
||||
func Int32Ptr(i int32) *int32 {
|
||||
// Int32 returns a pointer to an int32.
|
||||
func Int32(i int32) *int32 {
|
||||
return &i
|
||||
}
|
||||
|
||||
// Int32PtrDerefOr dereference the int32 ptr and returns it if not nil,
|
||||
// else returns def.
|
||||
func Int32PtrDerefOr(ptr *int32, def int32) int32 {
|
||||
var Int32Ptr = Int32 // for back-compat
|
||||
|
||||
// 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 {
|
||||
return *ptr
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
// Int64Ptr returns a pointer to an int64
|
||||
func Int64Ptr(i int64) *int64 {
|
||||
var Int32PtrDerefOr = Int32Deref // for back-compat
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Int64PtrDerefOr dereference the int64 ptr and returns it if not nil,
|
||||
// else returns def.
|
||||
func Int64PtrDerefOr(ptr *int64, def int64) int64 {
|
||||
var Int64Ptr = Int64 // for back-compat
|
||||
|
||||
// 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 {
|
||||
return *ptr
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
// BoolPtr returns a pointer to a bool
|
||||
func BoolPtr(b bool) *bool {
|
||||
var Int64PtrDerefOr = Int64Deref // for back-compat
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// BoolPtrDerefOr dereference the bool ptr and returns it if not nil,
|
||||
// else returns def.
|
||||
func BoolPtrDerefOr(ptr *bool, def bool) bool {
|
||||
var BoolPtr = Bool // for back-compat
|
||||
|
||||
// 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 {
|
||||
return *ptr
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
// StringPtr returns a pointer to the passed string.
|
||||
func StringPtr(s string) *string {
|
||||
var BoolPtrDerefOr = BoolDeref // for back-compat
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// StringPtrDerefOr dereference the string ptr and returns it if not nil,
|
||||
// else returns def.
|
||||
func StringPtrDerefOr(ptr *string, def string) string {
|
||||
var StringPtr = String // for back-compat
|
||||
|
||||
// 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 {
|
||||
return *ptr
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
// Float32Ptr returns a pointer to the passed float32.
|
||||
func Float32Ptr(i float32) *float32 {
|
||||
var StringPtrDerefOr = StringDeref // for back-compat
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Float32PtrDerefOr dereference the float32 ptr and returns it if not nil,
|
||||
// else returns def.
|
||||
func Float32PtrDerefOr(ptr *float32, def float32) float32 {
|
||||
var Float32Ptr = 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 {
|
||||
return *ptr
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
// Float64Ptr returns a pointer to the passed float64.
|
||||
func Float64Ptr(i float64) *float64 {
|
||||
var Float32PtrDerefOr = Float32Deref // for back-compat
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Float64PtrDerefOr dereference the float64 ptr and returns it if not nil,
|
||||
// else returns def.
|
||||
func Float64PtrDerefOr(ptr *float64, def float64) float64 {
|
||||
var Float64Ptr = 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 {
|
||||
return *ptr
|
||||
}
|
||||
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(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 {
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ github.com/cespare/xxhash/v2
|
|||
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/spew
|
||||
# github.com/devfile/api/v2 v2.0.0-20220414122024-32cae1f8e42c
|
||||
# github.com/devfile/api/v2 v2.0.0-20220928161623-fe7c10eaa530
|
||||
## explicit
|
||||
github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2
|
||||
github.com/devfile/api/v2/pkg/attributes
|
||||
github.com/devfile/api/v2/pkg/devfile
|
||||
# github.com/devfile/devworkspace-operator v0.15.2
|
||||
# github.com/devfile/devworkspace-operator v0.17.0
|
||||
## explicit
|
||||
github.com/devfile/devworkspace-operator/apis/controller/v1alpha1
|
||||
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
|
||||
## explicit
|
||||
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/ptypes
|
||||
github.com/golang/protobuf/ptypes/any
|
||||
|
|
@ -296,7 +296,7 @@ google.golang.org/grpc/serviceconfig
|
|||
google.golang.org/grpc/stats
|
||||
google.golang.org/grpc/status
|
||||
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/protowire
|
||||
google.golang.org/protobuf/internal/descfmt
|
||||
|
|
@ -648,7 +648,7 @@ k8s.io/component-base/config/v1alpha1
|
|||
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/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
|
||||
k8s.io/utils/buffer
|
||||
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/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/utils => k8s.io/utils v0.0.0-20201110183641-67b214c5f920
|
||||
# kubernetes/klog => kubernetes/klog v1.0.0
|
||||
# modernc.org/b => modernc.org/b v1.0.0
|
||||
# modernc.org/db => modernc.org/db v1.0.0
|
||||
|
|
|
|||
Loading…
Reference in New Issue