diff --git a/api/v2/checluster_types.go b/api/v2/checluster_types.go index dee18f4a1..61c535e71 100644 --- a/api/v2/checluster_types.go +++ b/api/v2/checluster_types.go @@ -25,6 +25,8 @@ import ( "github.com/devfile/devworkspace-operator/pkg/infrastructure" "github.com/eclipse-che/che-operator/pkg/common/constants" + + controllerv1alpha1 "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" "k8s.io/apimachinery/pkg/api/resource" imagepullerv1alpha1 "github.com/che-incubator/kubernetes-image-puller-operator/api/v1alpha1" @@ -121,6 +123,9 @@ type CheClusterDevEnvironments struct { // +kubebuilder:validation:Pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ // +kubebuilder:validation:MaxLength=63 ServiceAccount string `json:"serviceAccount,omitempty"` + // List of ServiceAccount tokens that will be mounted into workspace pods as projected volumes. + // +optional + ServiceAccountTokens []controllerv1alpha1.ServiceAccountToken `json:"serviceAccountTokens,omitempty"` // Pod scheduler for the workspace pods. // If not specified, the pod scheduler is set to the default scheduler on the cluster. // +optional diff --git a/api/v2/zz_generated.deepcopy.go b/api/v2/zz_generated.deepcopy.go index f79544555..668147350 100644 --- a/api/v2/zz_generated.deepcopy.go +++ b/api/v2/zz_generated.deepcopy.go @@ -19,6 +19,7 @@ package v2 import ( "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -202,6 +203,11 @@ func (in *CheClusterDevEnvironments) DeepCopyInto(out *CheClusterDevEnvironments *out = new(ContainerBuildConfiguration) **out = **in } + if in.ServiceAccountTokens != nil { + in, out := &in.ServiceAccountTokens, &out.ServiceAccountTokens + *out = make([]v1alpha1.ServiceAccountToken, len(*in)) + copy(*out, *in) + } if in.StartTimeoutSeconds != nil { in, out := &in.StartTimeoutSeconds, &out.StartTimeoutSeconds *out = new(int32) diff --git a/bundle/next/eclipse-che/manifests/che-operator.clusterserviceversion.yaml b/bundle/next/eclipse-che/manifests/che-operator.clusterserviceversion.yaml index e0ebbca1c..a9f5b6d6d 100644 --- a/bundle/next/eclipse-che/manifests/che-operator.clusterserviceversion.yaml +++ b/bundle/next/eclipse-che/manifests/che-operator.clusterserviceversion.yaml @@ -77,7 +77,7 @@ metadata: operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 repository: https://github.com/eclipse-che/che-operator support: Eclipse Foundation - name: eclipse-che.v7.64.0-785.next + name: eclipse-che.v7.64.0-786.next namespace: placeholder spec: apiservicedefinitions: {} @@ -1243,7 +1243,7 @@ spec: minKubeVersion: 1.19.0 provider: name: Eclipse Foundation - version: 7.64.0-785.next + version: 7.64.0-786.next webhookdefinitions: - admissionReviewVersions: - v1 diff --git a/bundle/next/eclipse-che/manifests/org.eclipse.che_checlusters.yaml b/bundle/next/eclipse-che/manifests/org.eclipse.che_checlusters.yaml index e63a57d41..0e6d4643a 100644 --- a/bundle/next/eclipse-che/manifests/org.eclipse.che_checlusters.yaml +++ b/bundle/next/eclipse-che/manifests/org.eclipse.che_checlusters.yaml @@ -7092,6 +7092,51 @@ spec: maxLength: 63 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string + serviceAccountTokens: + description: List of ServiceAccount tokens that will be mounted + into workspace pods as projected volumes. + items: + properties: + audience: + description: Audience is the intended audience of the + token. A recipient of a token must identify itself with + an identifier specified in the audience of the token, + and otherwise should reject the token. The audience + defaults to the identifier of the apiserver. + type: string + expirationSeconds: + default: 3600 + description: ExpirationSeconds is the requested duration + of validity of the service account token. As the token + approaches expiration, the kubelet volume plugin will + proactively rotate the service account token. The kubelet + will start trying to rotate the token if the token is + older than 80 percent of its time to live or if the + token is older than 24 hours. Defaults to 1 hour and + must be at least 10 minutes. + format: int64 + minimum: 600 + type: integer + mountPath: + description: Path within the workspace container at which + the token should be mounted. Must not contain ':'. + type: string + name: + description: Identifiable name of the ServiceAccount token. + If multiple ServiceAccount tokens use the same mount + path, a generic name will be used for the projected + volume instead. + type: string + path: + description: Path is the path relative to the mount point + of the file to project the token into. + type: string + required: + - mountPath + - name + - path + type: object + type: array startTimeoutSeconds: default: 300 description: StartTimeoutSeconds determines the maximum duration diff --git a/config/crd/bases/org.eclipse.che_checlusters.yaml b/config/crd/bases/org.eclipse.che_checlusters.yaml index afae45a9e..8da7571fb 100644 --- a/config/crd/bases/org.eclipse.che_checlusters.yaml +++ b/config/crd/bases/org.eclipse.che_checlusters.yaml @@ -6896,6 +6896,50 @@ spec: maxLength: 63 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string + serviceAccountTokens: + description: List of ServiceAccount tokens that will be mounted + into workspace pods as projected volumes. + items: + properties: + audience: + description: Audience is the intended audience of the token. + A recipient of a token must identify itself with an identifier + specified in the audience of the token, and otherwise + should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + default: 3600 + description: ExpirationSeconds is the requested duration + of validity of the service account token. As the token + approaches expiration, the kubelet volume plugin will + proactively rotate the service account token. The kubelet + will start trying to rotate the token if the token is + older than 80 percent of its time to live or if the token + is older than 24 hours. Defaults to 1 hour and must be + at least 10 minutes. + format: int64 + minimum: 600 + type: integer + mountPath: + description: Path within the workspace container at which + the token should be mounted. Must not contain ':'. + type: string + name: + description: Identifiable name of the ServiceAccount token. + If multiple ServiceAccount tokens use the same mount path, + a generic name will be used for the projected volume instead. + type: string + path: + description: Path is the path relative to the mount point + of the file to project the token into. + type: string + required: + - mountPath + - name + - path + type: object + type: array startTimeoutSeconds: default: 300 description: StartTimeoutSeconds determines the maximum duration diff --git a/deploy/deployment/kubernetes/combined.yaml b/deploy/deployment/kubernetes/combined.yaml index 86b1e1f1c..3174ba69b 100644 --- a/deploy/deployment/kubernetes/combined.yaml +++ b/deploy/deployment/kubernetes/combined.yaml @@ -6915,6 +6915,50 @@ spec: maxLength: 63 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string + serviceAccountTokens: + description: List of ServiceAccount tokens that will be mounted + into workspace pods as projected volumes. + items: + properties: + audience: + description: Audience is the intended audience of the token. + A recipient of a token must identify itself with an identifier + specified in the audience of the token, and otherwise + should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + default: 3600 + description: ExpirationSeconds is the requested duration + of validity of the service account token. As the token + approaches expiration, the kubelet volume plugin will + proactively rotate the service account token. The kubelet + will start trying to rotate the token if the token is + older than 80 percent of its time to live or if the token + is older than 24 hours. Defaults to 1 hour and must be + at least 10 minutes. + format: int64 + minimum: 600 + type: integer + mountPath: + description: Path within the workspace container at which + the token should be mounted. Must not contain ':'. + type: string + name: + description: Identifiable name of the ServiceAccount token. + If multiple ServiceAccount tokens use the same mount path, + a generic name will be used for the projected volume instead. + type: string + path: + description: Path is the path relative to the mount point + of the file to project the token into. + type: string + required: + - mountPath + - name + - path + type: object + type: array startTimeoutSeconds: default: 300 description: StartTimeoutSeconds determines the maximum duration diff --git a/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml b/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml index 9e4e0594c..b5e910f07 100644 --- a/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml +++ b/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml @@ -6910,6 +6910,50 @@ spec: maxLength: 63 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string + serviceAccountTokens: + description: List of ServiceAccount tokens that will be mounted + into workspace pods as projected volumes. + items: + properties: + audience: + description: Audience is the intended audience of the token. + A recipient of a token must identify itself with an identifier + specified in the audience of the token, and otherwise + should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + default: 3600 + description: ExpirationSeconds is the requested duration + of validity of the service account token. As the token + approaches expiration, the kubelet volume plugin will + proactively rotate the service account token. The kubelet + will start trying to rotate the token if the token is + older than 80 percent of its time to live or if the token + is older than 24 hours. Defaults to 1 hour and must be + at least 10 minutes. + format: int64 + minimum: 600 + type: integer + mountPath: + description: Path within the workspace container at which + the token should be mounted. Must not contain ':'. + type: string + name: + description: Identifiable name of the ServiceAccount token. + If multiple ServiceAccount tokens use the same mount path, + a generic name will be used for the projected volume instead. + type: string + path: + description: Path is the path relative to the mount point + of the file to project the token into. + type: string + required: + - mountPath + - name + - path + type: object + type: array startTimeoutSeconds: default: 300 description: StartTimeoutSeconds determines the maximum duration diff --git a/deploy/deployment/openshift/combined.yaml b/deploy/deployment/openshift/combined.yaml index e459878c2..47d4c0d02 100644 --- a/deploy/deployment/openshift/combined.yaml +++ b/deploy/deployment/openshift/combined.yaml @@ -6915,6 +6915,50 @@ spec: maxLength: 63 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string + serviceAccountTokens: + description: List of ServiceAccount tokens that will be mounted + into workspace pods as projected volumes. + items: + properties: + audience: + description: Audience is the intended audience of the token. + A recipient of a token must identify itself with an identifier + specified in the audience of the token, and otherwise + should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + default: 3600 + description: ExpirationSeconds is the requested duration + of validity of the service account token. As the token + approaches expiration, the kubelet volume plugin will + proactively rotate the service account token. The kubelet + will start trying to rotate the token if the token is + older than 80 percent of its time to live or if the token + is older than 24 hours. Defaults to 1 hour and must be + at least 10 minutes. + format: int64 + minimum: 600 + type: integer + mountPath: + description: Path within the workspace container at which + the token should be mounted. Must not contain ':'. + type: string + name: + description: Identifiable name of the ServiceAccount token. + If multiple ServiceAccount tokens use the same mount path, + a generic name will be used for the projected volume instead. + type: string + path: + description: Path is the path relative to the mount point + of the file to project the token into. + type: string + required: + - mountPath + - name + - path + type: object + type: array startTimeoutSeconds: default: 300 description: StartTimeoutSeconds determines the maximum duration diff --git a/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml b/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml index 269d27898..bce902ea1 100644 --- a/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml +++ b/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml @@ -6910,6 +6910,50 @@ spec: maxLength: 63 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string + serviceAccountTokens: + description: List of ServiceAccount tokens that will be mounted + into workspace pods as projected volumes. + items: + properties: + audience: + description: Audience is the intended audience of the token. + A recipient of a token must identify itself with an identifier + specified in the audience of the token, and otherwise + should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + default: 3600 + description: ExpirationSeconds is the requested duration + of validity of the service account token. As the token + approaches expiration, the kubelet volume plugin will + proactively rotate the service account token. The kubelet + will start trying to rotate the token if the token is + older than 80 percent of its time to live or if the token + is older than 24 hours. Defaults to 1 hour and must be + at least 10 minutes. + format: int64 + minimum: 600 + type: integer + mountPath: + description: Path within the workspace container at which + the token should be mounted. Must not contain ':'. + type: string + name: + description: Identifiable name of the ServiceAccount token. + If multiple ServiceAccount tokens use the same mount path, + a generic name will be used for the projected volume instead. + type: string + path: + description: Path is the path relative to the mount point + of the file to project the token into. + type: string + required: + - mountPath + - name + - path + type: object + type: array startTimeoutSeconds: default: 300 description: StartTimeoutSeconds determines the maximum duration diff --git a/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml b/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml index 9e4e0594c..b5e910f07 100644 --- a/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml +++ b/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml @@ -6910,6 +6910,50 @@ spec: maxLength: 63 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string + serviceAccountTokens: + description: List of ServiceAccount tokens that will be mounted + into workspace pods as projected volumes. + items: + properties: + audience: + description: Audience is the intended audience of the token. + A recipient of a token must identify itself with an identifier + specified in the audience of the token, and otherwise + should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + default: 3600 + description: ExpirationSeconds is the requested duration + of validity of the service account token. As the token + approaches expiration, the kubelet volume plugin will + proactively rotate the service account token. The kubelet + will start trying to rotate the token if the token is + older than 80 percent of its time to live or if the token + is older than 24 hours. Defaults to 1 hour and must be + at least 10 minutes. + format: int64 + minimum: 600 + type: integer + mountPath: + description: Path within the workspace container at which + the token should be mounted. Must not contain ':'. + type: string + name: + description: Identifiable name of the ServiceAccount token. + If multiple ServiceAccount tokens use the same mount path, + a generic name will be used for the projected volume instead. + type: string + path: + description: Path is the path relative to the mount point + of the file to project the token into. + type: string + required: + - mountPath + - name + - path + type: object + type: array startTimeoutSeconds: default: 300 description: StartTimeoutSeconds determines the maximum duration diff --git a/pkg/deploy/dev-workspace-config/dev_workspace_config.go b/pkg/deploy/dev-workspace-config/dev_workspace_config.go index 94d461b4f..3f80c6c35 100644 --- a/pkg/deploy/dev-workspace-config/dev_workspace_config.go +++ b/pkg/deploy/dev-workspace-config/dev_workspace_config.go @@ -147,7 +147,8 @@ func updateWorkspaceServiceAccountConfig(devEnvironments *chev2.CheClusterDevEnv isNamespaceAutoProvisioned := pointer.BoolPtrDerefOr(devEnvironments.DefaultNamespace.AutoProvision, constants.DefaultAutoProvision) workspaceConfig.ServiceAccount = &controllerv1alpha1.ServiceAccountConfig{ - ServiceAccountName: devEnvironments.ServiceAccount, + ServiceAccountName: devEnvironments.ServiceAccount, + ServiceAccountTokens: devEnvironments.ServiceAccountTokens, // 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 != ""), } diff --git a/pkg/deploy/dev-workspace-config/dev_workspace_config_test.go b/pkg/deploy/dev-workspace-config/dev_workspace_config_test.go index b1fe6e339..15c15b135 100644 --- a/pkg/deploy/dev-workspace-config/dev_workspace_config_test.go +++ b/pkg/deploy/dev-workspace-config/dev_workspace_config_test.go @@ -940,3 +940,248 @@ func TestReconcileDevWorkspaceConfigPodSchedulerName(t *testing.T) { }) } } + +func TestReconcileDevWorkspaceConfigServiceAccountTokens(t *testing.T) { + type testCase struct { + name string + cheCluster *chev2.CheCluster + existedObjects []runtime.Object + expectedOperatorConfig *controllerv1alpha1.OperatorConfiguration + } + + var testCases = []testCase{ + { + name: "Create DevWorkspaceOperatorConfig with ServiceAccountTokens", + cheCluster: &chev2.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "eclipse-che", + Name: "eclipse-che", + }, + Spec: chev2.CheClusterSpec{ + DevEnvironments: chev2.CheClusterDevEnvironments{ + ServiceAccountTokens: []controllerv1alpha1.ServiceAccountToken{ + { + Name: "test-token-1", + MountPath: "/var/run/secrets/tokens", + Audience: "openshift", + ExpirationSeconds: 3600, + }, + { + Name: "test-token-2", + MountPath: "/var/run/secrets/tokens", + Audience: "kubernetes", + ExpirationSeconds: 180, + }, + }, + }, + }, + }, + expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{ + Workspace: &controllerv1alpha1.WorkspaceConfig{ + ServiceAccount: &controllerv1alpha1.ServiceAccountConfig{ + ServiceAccountTokens: []controllerv1alpha1.ServiceAccountToken{ + { + Name: "test-token-1", + MountPath: "/var/run/secrets/tokens", + Audience: "openshift", + ExpirationSeconds: 3600, + }, + { + Name: "test-token-2", + MountPath: "/var/run/secrets/tokens", + Audience: "kubernetes", + ExpirationSeconds: 180, + }, + }}, + }, + }, + }, + { + name: "Update existing DevWorkspaceOperatorConfig when ServiceAccountTokens are added", + cheCluster: &chev2.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "eclipse-che", + Name: "eclipse-che", + }, + Spec: chev2.CheClusterSpec{ + DevEnvironments: chev2.CheClusterDevEnvironments{ + ServiceAccountTokens: []controllerv1alpha1.ServiceAccountToken{ + { + Name: "test-token", + MountPath: "/var/run/secrets/tokens", + Audience: "openshift", + ExpirationSeconds: 3600, + }, + { + Name: "test-token-2", + MountPath: "/var/run/secrets/tokens", + Audience: "kubernetes", + ExpirationSeconds: 180, + }, + }, + }, + }, + }, + existedObjects: []runtime.Object{ + &controllerv1alpha1.DevWorkspaceOperatorConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: devWorkspaceConfigName, + Namespace: "eclipse-che", + }, + TypeMeta: metav1.TypeMeta{ + Kind: "DevWorkspaceOperatorConfig", + APIVersion: controllerv1alpha1.GroupVersion.String(), + }, + }, + }, + expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{ + Workspace: &controllerv1alpha1.WorkspaceConfig{ + ServiceAccount: &controllerv1alpha1.ServiceAccountConfig{ + ServiceAccountTokens: []controllerv1alpha1.ServiceAccountToken{ + { + Name: "test-token", + MountPath: "/var/run/secrets/tokens", + Audience: "openshift", + ExpirationSeconds: 3600, + }, + { + Name: "test-token-2", + MountPath: "/var/run/secrets/tokens", + Audience: "kubernetes", + ExpirationSeconds: 180, + }, + }, + }, + }, + }, + }, + { + name: "Update existing DevWorkspaceOperatorConfig when ServiceAccountTokens are changed", + cheCluster: &chev2.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "eclipse-che", + Name: "eclipse-che", + }, + Spec: chev2.CheClusterSpec{ + DevEnvironments: chev2.CheClusterDevEnvironments{ + ServiceAccountTokens: []controllerv1alpha1.ServiceAccountToken{ + { + Name: "new-token", + MountPath: "/var/run/secrets/tokens", + Audience: "openshift", + ExpirationSeconds: 3600, + }, + }, + }, + }, + }, + existedObjects: []runtime.Object{ + &controllerv1alpha1.DevWorkspaceOperatorConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: devWorkspaceConfigName, + Namespace: "eclipse-che", + }, + TypeMeta: metav1.TypeMeta{ + Kind: "DevWorkspaceOperatorConfig", + APIVersion: controllerv1alpha1.GroupVersion.String(), + }, + Config: &controllerv1alpha1.OperatorConfiguration{ + Workspace: &controllerv1alpha1.WorkspaceConfig{ + ServiceAccount: &controllerv1alpha1.ServiceAccountConfig{ + ServiceAccountTokens: []controllerv1alpha1.ServiceAccountToken{ + { + Name: "old-token", + MountPath: "/var/run/secrets/tokens", + Audience: "openshift", + ExpirationSeconds: 60, + }, + }, + }, + }, + }, + }, + }, + expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{ + Workspace: &controllerv1alpha1.WorkspaceConfig{ + ServiceAccount: &controllerv1alpha1.ServiceAccountConfig{ + ServiceAccountTokens: []controllerv1alpha1.ServiceAccountToken{ + { + Name: "new-token", + MountPath: "/var/run/secrets/tokens", + Audience: "openshift", + ExpirationSeconds: 3600, + }, + }, + }, + }, + }, + }, + { + name: "Update existing DevWorkspaceOperatorConfig when ServiceAccountTokens are removed", + cheCluster: &chev2.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "eclipse-che", + Name: "eclipse-che", + }, + Spec: chev2.CheClusterSpec{ + DevEnvironments: chev2.CheClusterDevEnvironments{ + ServiceAccountTokens: []controllerv1alpha1.ServiceAccountToken{}, + }, + }, + }, + existedObjects: []runtime.Object{ + &controllerv1alpha1.DevWorkspaceOperatorConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: devWorkspaceConfigName, + Namespace: "eclipse-che", + }, + TypeMeta: metav1.TypeMeta{ + Kind: "DevWorkspaceOperatorConfig", + APIVersion: controllerv1alpha1.GroupVersion.String(), + }, + Config: &controllerv1alpha1.OperatorConfiguration{ + Workspace: &controllerv1alpha1.WorkspaceConfig{ + ServiceAccount: &controllerv1alpha1.ServiceAccountConfig{ + ServiceAccountTokens: []controllerv1alpha1.ServiceAccountToken{ + { + Name: "test-token", + MountPath: "/var/run/secrets/tokens", + Audience: "openshift", + ExpirationSeconds: 3600, + }, + { + Name: "test-token-2", + MountPath: "/var/run/secrets/tokens", + Audience: "kubernetes", + ExpirationSeconds: 180, + }, + }, + }, + }, + }, + }, + }, + expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{ + Workspace: &controllerv1alpha1.WorkspaceConfig{ + ServiceAccount: &controllerv1alpha1.ServiceAccountConfig{}, + }, + }, + }, + } + + 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.ServiceAccountTokens, dwoc.Config.Workspace.ServiceAccount.ServiceAccountTokens) + }) + } +}