Merge pull request #11178 from sleshchenko/workspaceServiceAccount

Added an ability to specify service account to be bound to workspaces pods
6.19.x
Sergii Leshchenko 2018-09-13 11:03:28 +03:00 committed by GitHub
commit 2dd5bb84b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 265 additions and 10 deletions

View File

@ -353,6 +353,9 @@ che.infra.kubernetes.ingress.domain=
# Ignored for OpenShift infra. Use `che.infra.openshift.project` instead
che.infra.kubernetes.namespace=
# Defines Kubernetes Service Account name which should be specified to be bound to all workspaces pods.
# Note that Che Server won't create the service account and it should exist.
che.infra.kubernetes.service_account_name=NULL
# Defines time frame that limits the Kubernetes workspace start time
che.infra.kubernetes.workspace_start_timeout_min=8

View File

@ -36,6 +36,7 @@ public abstract class CheMethodInvokerFilter implements MethodInvokerFilter {
public void accept(GenericResourceMethod genericMethodResource, Object[] arguments)
throws WebApplicationException {
try {
filter(genericMethodResource, arguments);
} catch (ApiException exception) {
Response response;

View File

@ -0,0 +1,5 @@
{{- define "workspaceServiceAccountName" }}
{{- if (.Values.global.cheWorkspacesNamespace) }}
{{- printf "che-workspace" }}
{{- end }}
{{- end }}

View File

@ -49,6 +49,7 @@ data:
CHE_KEYCLOAK_USERNAME__CLAIM: {{ .Values.customOidcUsernameClaim }}
{{- end }}
CHE_INFRA_KUBERNETES_NAMESPACE: {{ .Values.global.cheWorkspacesNamespace }}
CHE_INFRA_KUBERNETES_SERVICE__ACCOUNT__NAME: {{ template "workspaceServiceAccountName" . }}
CHE_INFRA_KUBERNETES_TRUST__CERTS: "false"
CHE_INFRA_KUBERNETES_PVC_STRATEGY: "common"
CHE_INFRA_KUBERNETES_PVC_QUANTITY: {{ .Values.global.pvcClaim }}

View File

@ -186,6 +186,11 @@ spec:
configMapKeyRef:
key: CHE_INFRA_KUBERNETES_NAMESPACE
name: che
- name: CHE_INFRA_KUBERNETES_SERVICE__ACCOUNT__NAME
valueFrom:
configMapKeyRef:
key: CHE_INFRA_KUBERNETES_SERVICE__ACCOUNT__NAME
name: che
- name: CHE_LOCAL_CONF_DIR
valueFrom:
configMapKeyRef:

View File

@ -0,0 +1,24 @@
#
# Copyright (c) 2012-2017 Red Hat, Inc.
# This program and the accompanying materials are made
# available under the terms of the Eclipse Public License 2.0
# which is available at https://www.eclipse.org/legal/epl-2.0/
#
# SPDX-License-Identifier: EPL-2.0
#
{{- if (.Values.global.cheWorkspacesNamespace) }}
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: exec
namespace: {{ .Values.global.cheWorkspacesNamespace }}
rules:
- apiGroups:
- ""
attributeRestrictions: null
resources:
- pods/exec
verbs:
- create
{{- end }}

View File

@ -0,0 +1,25 @@
#
# Copyright (c) 2012-2017 Red Hat, Inc.
# This program and the accompanying materials are made
# available under the terms of the Eclipse Public License 2.0
# which is available at https://www.eclipse.org/legal/epl-2.0/
#
# SPDX-License-Identifier: EPL-2.0
#
{{- if (.Values.global.cheWorkspacesNamespace) }}
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: che-workspace-exec
namespace: {{ .Values.global.cheWorkspacesNamespace }}
roleRef:
kind: Role
name: exec
namespace: {{ .Values.global.cheWorkspacesNamespace }}
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: che-workspace
namespace: {{ .Values.global.cheWorkspacesNamespace }}
{{- end }}

View File

@ -0,0 +1,16 @@
#
# Copyright (c) 2012-2017 Red Hat, Inc.
# This program and the accompanying materials are made
# available under the terms of the Eclipse Public License 2.0
# which is available at https://www.eclipse.org/legal/epl-2.0/
#
# SPDX-License-Identifier: EPL-2.0
#
{{- if (.Values.global.cheWorkspacesNamespace) }}
kind: ServiceAccount
apiVersion: v1
metadata:
name: "che-workspace"
namespace: {{ .Values.global.cheWorkspacesNamespace }}
{{- end }}

View File

@ -0,0 +1,23 @@
#
# Copyright (c) 2012-2017 Red Hat, Inc.
# This program and the accompanying materials are made
# available under the terms of the Eclipse Public License 2.0
# which is available at https://www.eclipse.org/legal/epl-2.0/
#
# SPDX-License-Identifier: EPL-2.0
#
{{- if (.Values.global.cheWorkspacesNamespace) }}
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: che-workspace-view
namespace: {{ .Values.global.cheWorkspacesNamespace }}
roleRef:
kind: Role
name: view
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: che-workspace
{{- end }}

View File

@ -445,6 +445,13 @@ ${CHE_VAR_ARRAY}"
PLUGIN__REGISTRY__URL="${HTTP_PROTOCOL}://${PLUGIN_REGISTRY_ROUTE}"
fi
if [ ! -z ${CHE_INFRA_OPENSHIFT_PROJECT} ]; then
${OC_BINARY} new-app -f ${BASE_DIR}/templates/che-workspace-service-account.yaml \
-p SERVICE_ACCOUNT_NAME='che-workspace' \
-p SERVICE_ACCOUNT_NAMESPACE=${CHE_INFRA_OPENSHIFT_PROJECT}
WORKSPACE_SERVICE_ACCOUNT_NAME="che-workspace"
fi
${OC_BINARY} new-app -f ${BASE_DIR}/templates/che-server-template.yaml \
-p ROUTING_SUFFIX=${OPENSHIFT_ROUTING_SUFFIX} \
-p IMAGE_CHE=${CHE_IMAGE_REPO} \
@ -458,6 +465,7 @@ ${CHE_VAR_ARRAY}"
-p CHE_INFRA_OPENSHIFT_OAUTH__IDENTITY__PROVIDER=${CHE_INFRA_OPENSHIFT_OAUTH__IDENTITY__PROVIDER} \
-p TLS=${TLS} \
-p CHE_WORKSPACE_PLUGIN__REGISTRY__URL=${PLUGIN__REGISTRY__URL} \
-p CHE_INFRA_KUBERNETES_SERVICE__ACCOUNT__NAME=${WORKSPACE_SERVICE_ACCOUNT_NAME} \
${ENV}
if [ ${UPDATE_STRATEGY} == "Recreate" ]; then

View File

@ -102,6 +102,8 @@ objects:
value: "${CHE_INFRA_KUBERNETES_MASTER__URL}"
- name: CHE_INFRA_OPENSHIFT_PROJECT
value: "${CHE_INFRA_OPENSHIFT_PROJECT}"
- name: CHE_INFRA_KUBERNETES_SERVICE__ACCOUNT__NAME
value: "${CHE_INFRA_KUBERNETES_SERVICE__ACCOUNT__NAME}"
- name: CHE_INFRA_KUBERNETES_PVC_STRATEGY
value: "${CHE_INFRA_KUBERNETES_PVC_STRATEGY}"
- name: CHE_INFRA_KUBERNETES_PVC_PRECREATE__SUBPATHS
@ -241,6 +243,10 @@ parameters:
displayName: PVC strategy
description: PVC strategy. Unique implies creating PVC per workspace. Common uses one PVC with subpaths in PV. Defaults to unique
value: 'unique'
- name: CHE_INFRA_KUBERNETES_SERVICE__ACCOUNT__NAME
displayName: Workspace service account name
description: Service accont name that should be specified to be bound to workspaces pods. Defauls to 'NULL' that means that Che Server won't specify any and default one will be bound.
value: 'NULL'
- name: CHE_KEYCLOAK_ADMIN_REQUIRE_UPDATE_PASSWORD
displayName: Admin password update
description: Force an admin to update password after 1st login. True by default

View File

@ -0,0 +1,63 @@
#
# Copyright (c) 2012-2018 Red Hat, Inc.
# This program and the accompanying materials are made
# available under the terms of the Eclipse Public License 2.0
# which is available at https://www.eclipse.org/legal/epl-2.0/
#
# SPDX-License-Identifier: EPL-2.0
#
---
apiVersion: v1
kind: Template
metadata:
name: che-workspace-service-account
objects:
- apiVersion: v1
kind: ServiceAccount
metadata:
name: ${SERVICE_ACCOUNT_NAME}
namespace: ${SERVICE_ACCOUNT_NAMESPACE}
- apiVersion: v1
kind: Role
metadata:
name: exec
namespace: ${SERVICE_ACCOUNT_NAMESPACE}
rules:
- apiGroups:
- ""
attributeRestrictions: null
resources:
- pods/exec
verbs:
- create
- apiVersion: v1
kind: RoleBinding
metadata:
name: che-workspace-exec
namespace: ${SERVICE_ACCOUNT_NAMESPACE}
roleRef:
name: exec
namespace: ${SERVICE_ACCOUNT_NAMESPACE}
subjects:
- kind: ServiceAccount
name: ${SERVICE_ACCOUNT_NAME}
namespace: ${SERVICE_ACCOUNT_NAMESPACE}
- apiVersion: v1
kind: RoleBinding
metadata:
name: che-workspace-view
namespace: ${SERVICE_ACCOUNT_NAMESPACE}
roleRef:
name: view
subjects:
- kind: ServiceAccount
name: ${SERVICE_ACCOUNT_NAME}
parameters:
- name: SERVICE_ACCOUNT_NAME
value: che-workspace
displayName: Eclipse Che plugin registry image
description: Service Account name that should be bound to workspaces. Defaults to 'che-workspace'
- name: SERVICE_ACCOUNT_NAMESPACE
displayName: Service account namespace
description: Namespace where service account should be created
required: true

View File

@ -25,6 +25,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.provision.LogsVolumeM
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.PodTerminationGracePeriodProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ProxySettingsProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.SecurityContextProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ServiceAccountProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.UniqueNamesProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.env.EnvVarsConverter;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.limits.ram.RamLimitProvisioner;
@ -60,6 +61,7 @@ public interface KubernetesEnvironmentProvisioner<T extends KubernetesEnvironmen
private IngressTlsProvisioner externalServerIngressTlsProvisioner;
private ImagePullSecretProvisioner imagePullSecretProvisioner;
private ProxySettingsProvisioner proxySettingsProvisioner;
private ServiceAccountProvisioner serviceAccountProvisioner;
@Inject
public KubernetesEnvironmentProvisionerImpl(
@ -76,7 +78,8 @@ public interface KubernetesEnvironmentProvisioner<T extends KubernetesEnvironmen
PodTerminationGracePeriodProvisioner podTerminationGracePeriodProvisioner,
IngressTlsProvisioner externalServerIngressTlsProvisioner,
ImagePullSecretProvisioner imagePullSecretProvisioner,
ProxySettingsProvisioner proxySettingsProvisioner) {
ProxySettingsProvisioner proxySettingsProvisioner,
ServiceAccountProvisioner serviceAccountProvisioner) {
this.pvcEnabled = pvcEnabled;
this.volumesStrategy = volumesStrategy;
this.uniqueNamesProvisioner = uniqueNamesProvisioner;
@ -91,6 +94,7 @@ public interface KubernetesEnvironmentProvisioner<T extends KubernetesEnvironmen
this.externalServerIngressTlsProvisioner = externalServerIngressTlsProvisioner;
this.imagePullSecretProvisioner = imagePullSecretProvisioner;
this.proxySettingsProvisioner = proxySettingsProvisioner;
this.serviceAccountProvisioner = serviceAccountProvisioner;
}
public void provision(KubernetesEnvironment k8sEnv, RuntimeIdentity identity)
@ -117,6 +121,7 @@ public interface KubernetesEnvironmentProvisioner<T extends KubernetesEnvironmen
podTerminationGracePeriodProvisioner.provision(k8sEnv, identity);
imagePullSecretProvisioner.provision(k8sEnv, identity);
proxySettingsProvisioner.provision(k8sEnv, identity);
serviceAccountProvisioner.provision(k8sEnv, identity);
}
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.workspace.infrastructure.kubernetes.provision;
import static com.google.common.base.Strings.isNullOrEmpty;
import io.fabric8.kubernetes.api.model.Pod;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
/**
* Sets the service account to workspace pods if configured.
*
* <p>Service account won't be set to pods if property value is `NULL` and then Kubernetes
* infrastructure will set default one.
*
* @author Sergii Leshchenko
*/
@Singleton
public class ServiceAccountProvisioner implements ConfigurationProvisioner {
private final String serviceAccount;
@Inject
public ServiceAccountProvisioner(
@Nullable @Named("che.infra.kubernetes.service_account_name") String serviceAccount) {
this.serviceAccount = serviceAccount;
}
@Override
public void provision(KubernetesEnvironment k8sEnv, RuntimeIdentity identity)
throws InfrastructureException {
if (!isNullOrEmpty(serviceAccount)) {
for (Pod pod : k8sEnv.getPods().values()) {
pod.getSpec().setServiceAccountName(serviceAccount);
pod.getSpec().setAutomountServiceAccountToken(true);
}
}
}
}

View File

@ -25,6 +25,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.provision.LogsVolumeM
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.PodTerminationGracePeriodProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ProxySettingsProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.SecurityContextProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ServiceAccountProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.UniqueNamesProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.env.EnvVarsConverter;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.limits.ram.RamLimitProvisioner;
@ -51,7 +52,7 @@ public class KubernetesEnvironmentProvisionerTest {
@Mock private KubernetesEnvironment k8sEnv;
@Mock private RuntimeIdentity runtimeIdentity;
@Mock private EnvVarsConverter envVarsProvisioner;
@Mock private ServersConverter serversProvisioner;
@Mock private ServersConverter<KubernetesEnvironment> serversProvisioner;
@Mock private RestartPolicyRewriter restartPolicyRewriter;
@Mock private RamLimitProvisioner ramLimitProvisioner;
@Mock private LogsVolumeMachineProvisioner logsVolumeMachineProvisioner;
@ -60,14 +61,15 @@ public class KubernetesEnvironmentProvisionerTest {
@Mock private IngressTlsProvisioner externalServerIngressTlsProvisioner;
@Mock private ImagePullSecretProvisioner imagePullSecretProvisioner;
@Mock private ProxySettingsProvisioner proxySettingsProvisioner;
@Mock private ServiceAccountProvisioner serviceAccountProvisioner;
private KubernetesEnvironmentProvisioner osInfraProvisioner;
private KubernetesEnvironmentProvisioner<KubernetesEnvironment> k8sInfraProvisioner;
private InOrder provisionOrder;
@BeforeMethod
public void setUp() {
osInfraProvisioner =
k8sInfraProvisioner =
new KubernetesEnvironmentProvisionerImpl(
true,
uniqueNamesProvisioner,
@ -82,7 +84,8 @@ public class KubernetesEnvironmentProvisionerTest {
podTerminationGracePeriodProvisioner,
externalServerIngressTlsProvisioner,
imagePullSecretProvisioner,
proxySettingsProvisioner);
proxySettingsProvisioner,
serviceAccountProvisioner);
provisionOrder =
inOrder(
installerServersPortProvisioner,
@ -97,12 +100,13 @@ public class KubernetesEnvironmentProvisionerTest {
podTerminationGracePeriodProvisioner,
externalServerIngressTlsProvisioner,
imagePullSecretProvisioner,
proxySettingsProvisioner);
proxySettingsProvisioner,
serviceAccountProvisioner);
}
@Test
public void performsOrderedProvisioning() throws Exception {
osInfraProvisioner.provision(k8sEnv, runtimeIdentity);
k8sInfraProvisioner.provision(k8sEnv, runtimeIdentity);
provisionOrder
.verify(installerServersPortProvisioner)
@ -123,6 +127,7 @@ public class KubernetesEnvironmentProvisionerTest {
.provision(eq(k8sEnv), eq(runtimeIdentity));
provisionOrder.verify(imagePullSecretProvisioner).provision(eq(k8sEnv), eq(runtimeIdentity));
provisionOrder.verify(proxySettingsProvisioner).provision(eq(k8sEnv), eq(runtimeIdentity));
provisionOrder.verify(serviceAccountProvisioner).provision(eq(k8sEnv), eq(runtimeIdentity));
provisionOrder.verifyNoMoreInteractions();
}
}

View File

@ -23,6 +23,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.provision.InstallerSe
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.LogsVolumeMachineProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.PodTerminationGracePeriodProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ProxySettingsProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ServiceAccountProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.UniqueNamesProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.env.EnvVarsConverter;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.limits.ram.RamLimitProvisioner;
@ -56,6 +57,7 @@ public class OpenShiftEnvironmentProvisioner
private final PodTerminationGracePeriodProvisioner podTerminationGracePeriodProvisioner;
private final ImagePullSecretProvisioner imagePullSecretProvisioner;
private final ProxySettingsProvisioner proxySettingsProvisioner;
private final ServiceAccountProvisioner serviceAccountProvisioner;
@Inject
public OpenShiftEnvironmentProvisioner(
@ -71,7 +73,8 @@ public class OpenShiftEnvironmentProvisioner
LogsVolumeMachineProvisioner logsVolumeMachineProvisioner,
PodTerminationGracePeriodProvisioner podTerminationGracePeriodProvisioner,
ImagePullSecretProvisioner imagePullSecretProvisioner,
ProxySettingsProvisioner proxySettingsProvisioner) {
ProxySettingsProvisioner proxySettingsProvisioner,
ServiceAccountProvisioner serviceAccountProvisioner) {
this.pvcEnabled = pvcEnabled;
this.volumesStrategy = volumesStrategy;
this.uniqueNamesProvisioner = uniqueNamesProvisioner;
@ -85,6 +88,7 @@ public class OpenShiftEnvironmentProvisioner
this.podTerminationGracePeriodProvisioner = podTerminationGracePeriodProvisioner;
this.imagePullSecretProvisioner = imagePullSecretProvisioner;
this.proxySettingsProvisioner = proxySettingsProvisioner;
this.serviceAccountProvisioner = serviceAccountProvisioner;
}
@Override
@ -111,5 +115,6 @@ public class OpenShiftEnvironmentProvisioner
podTerminationGracePeriodProvisioner.provision(osEnv, identity);
imagePullSecretProvisioner.provision(osEnv, identity);
proxySettingsProvisioner.provision(osEnv, identity);
serviceAccountProvisioner.provision(osEnv, identity);
}
}

View File

@ -21,6 +21,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.provision.InstallerSe
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.LogsVolumeMachineProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.PodTerminationGracePeriodProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ProxySettingsProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ServiceAccountProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.env.EnvVarsConverter;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.limits.ram.RamLimitProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.restartpolicy.RestartPolicyRewriter;
@ -57,6 +58,7 @@ public class OpenShiftEnvironmentProvisionerTest {
@Mock private PodTerminationGracePeriodProvisioner podTerminationGracePeriodProvisioner;
@Mock private ImagePullSecretProvisioner imagePullSecretProvisioner;
@Mock private ProxySettingsProvisioner proxySettingsProvisioner;
@Mock private ServiceAccountProvisioner serviceAccountProvisioner;
private OpenShiftEnvironmentProvisioner osInfraProvisioner;
@ -78,7 +80,8 @@ public class OpenShiftEnvironmentProvisionerTest {
logsVolumeMachineProvisioner,
podTerminationGracePeriodProvisioner,
imagePullSecretProvisioner,
proxySettingsProvisioner);
proxySettingsProvisioner,
serviceAccountProvisioner);
provisionOrder =
inOrder(
installerServersPortProvisioner,
@ -92,7 +95,8 @@ public class OpenShiftEnvironmentProvisionerTest {
ramLimitProvisioner,
podTerminationGracePeriodProvisioner,
imagePullSecretProvisioner,
proxySettingsProvisioner);
proxySettingsProvisioner,
serviceAccountProvisioner);
}
@Test
@ -115,6 +119,7 @@ public class OpenShiftEnvironmentProvisionerTest {
.provision(eq(osEnv), eq(runtimeIdentity));
provisionOrder.verify(imagePullSecretProvisioner).provision(eq(osEnv), eq(runtimeIdentity));
provisionOrder.verify(proxySettingsProvisioner).provision(eq(osEnv), eq(runtimeIdentity));
provisionOrder.verify(serviceAccountProvisioner).provision(eq(osEnv), eq(runtimeIdentity));
provisionOrder.verifyNoMoreInteractions();
}
}