feat: configure DWOC ServiceAccount tokens from devEnvironments.serviceAccountTokens (#1643)

* feat: configure DWOC ServiceAccount tokens from devEnvironments.serviceAccountTokens

Signed-off-by: Andrew Obuchowicz <aobuchow@redhat.com>
pull/1665/head
Andrew O 2023-04-17 09:49:09 -04:00 committed by GitHub
parent dbde06742d
commit 5bd0cdc19a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 569 additions and 3 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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