chore: Remote workspace-stop roles (#659)
Signed-off-by: Anatolii Bazko <abazko@redhat.com>pull/661/head
parent
60262e3a72
commit
7dc7a61511
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (c) 2012-2023 Red Hat, Inc.
|
||||
# Copyright (c) 2012-2024 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/
|
||||
|
|
@ -91,10 +91,6 @@ che.workspace.server.liveness_probes=wsagent/http,exec-agent/http,terminal,theia
|
|||
# The default is: 10MB=10485760.
|
||||
che.workspace.startup_debug_log_limit_bytes=10485760
|
||||
|
||||
# If set to `true`, 'stop-workspace' role with the edit privileges is granted to the 'che' ServiceAccount if OpenShift OAuth is enabled.
|
||||
# This configuration is mainly required for workspace idling when the OpenShift OAuth is enabled.
|
||||
che.workspace.stop.role.enabled=true
|
||||
|
||||
# Specifies whether {prod-short} is deployed with DevWorkspaces enabled.
|
||||
# This property is set by the {prod-short} Operator if it also installed the support for DevWorkspaces.
|
||||
# This property is used to advertise this fact to the {prod-short} dashboard.
|
||||
|
|
|
|||
|
|
@ -50,10 +50,6 @@
|
|||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>kubernetes-model-networking</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>kubernetes-model-rbac</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>openshift-client</artifactId>
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@ import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftE
|
|||
import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironmentFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.openshift.project.OpenShiftProjectFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.openshift.project.RemoveProjectOnWorkspaceRemove;
|
||||
import org.eclipse.che.workspace.infrastructure.openshift.project.configurator.OpenShiftStopWorkspaceRoleConfigurator;
|
||||
import org.eclipse.che.workspace.infrastructure.openshift.project.configurator.OpenShiftWorkspaceServiceAccountConfigurator;
|
||||
import org.eclipse.che.workspace.infrastructure.openshift.provision.OpenShiftPreviewUrlCommandProvisioner;
|
||||
import org.eclipse.che.workspace.infrastructure.openshift.provision.OpenshiftTrustedCAProvisioner;
|
||||
|
|
@ -116,7 +115,6 @@ public class OpenShiftInfraModule extends AbstractModule {
|
|||
namespaceConfigurators.addBinding().to(OAuthTokenSecretsConfigurator.class);
|
||||
namespaceConfigurators.addBinding().to(PreferencesConfigMapConfigurator.class);
|
||||
namespaceConfigurators.addBinding().to(OpenShiftWorkspaceServiceAccountConfigurator.class);
|
||||
namespaceConfigurators.addBinding().to(OpenShiftStopWorkspaceRoleConfigurator.class);
|
||||
namespaceConfigurators.addBinding().to(GitconfigUserDataConfigurator.class);
|
||||
|
||||
bind(AuthorizationChecker.class).to(OpenShiftAuthorizationCheckerImpl.class);
|
||||
|
|
|
|||
|
|
@ -1,140 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2023 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.openshift.project.configurator;
|
||||
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.rbac.*;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import io.fabric8.kubernetes.client.KubernetesClientException;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||
import org.eclipse.che.commons.annotation.Nullable;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.CheInstallationLocation;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This class creates the necessary role and rolebindings to allow the che serviceaccount to stop
|
||||
* user workspaces.
|
||||
*
|
||||
* @author Tom George
|
||||
*/
|
||||
@Singleton
|
||||
public class OpenShiftStopWorkspaceRoleConfigurator implements NamespaceConfigurator {
|
||||
|
||||
private final CheServerKubernetesClientFactory cheClientFactory;
|
||||
private final String installationLocation;
|
||||
private final boolean stopWorkspaceRoleEnabled;
|
||||
private final String oAuthIdentityProvider;
|
||||
|
||||
private static final Logger LOG =
|
||||
LoggerFactory.getLogger(OpenShiftStopWorkspaceRoleConfigurator.class);
|
||||
|
||||
@Inject
|
||||
public OpenShiftStopWorkspaceRoleConfigurator(
|
||||
CheServerKubernetesClientFactory cheClientFactory,
|
||||
CheInstallationLocation installationLocation,
|
||||
@Named("che.workspace.stop.role.enabled") boolean stopWorkspaceRoleEnabled,
|
||||
@Nullable @Named("che.infra.openshift.oauth_identity_provider") String oAuthIdentityProvider)
|
||||
throws InfrastructureException {
|
||||
this.cheClientFactory = cheClientFactory;
|
||||
this.installationLocation = installationLocation.getInstallationLocationNamespace();
|
||||
this.stopWorkspaceRoleEnabled = stopWorkspaceRoleEnabled;
|
||||
this.oAuthIdentityProvider = oAuthIdentityProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(NamespaceResolutionContext namespaceResolutionContext, String projectName)
|
||||
throws InfrastructureException {
|
||||
if (isNullOrEmpty(oAuthIdentityProvider)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (stopWorkspaceRoleEnabled && installationLocation != null) {
|
||||
KubernetesClient client = cheClientFactory.create();
|
||||
String stopWorkspacesRoleName = "workspace-stop";
|
||||
|
||||
client
|
||||
.rbac()
|
||||
.roles()
|
||||
.inNamespace(projectName)
|
||||
.createOrReplace(createStopWorkspacesRole(stopWorkspacesRoleName));
|
||||
|
||||
client
|
||||
.rbac()
|
||||
.roleBindings()
|
||||
.inNamespace(projectName)
|
||||
.createOrReplace(createStopWorkspacesRoleBinding(stopWorkspacesRoleName));
|
||||
}
|
||||
} catch (KubernetesClientException e) {
|
||||
LOG.warn(
|
||||
"Stop workspace Role and RoleBinding will not be provisioned to the '{}' namespace. 'che.workspace.stop.role.enabled' property is set to '{}', {}",
|
||||
installationLocation,
|
||||
stopWorkspaceRoleEnabled,
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected Role createStopWorkspacesRole(String name) {
|
||||
return new RoleBuilder()
|
||||
.withNewMetadata()
|
||||
.withName(name)
|
||||
.endMetadata()
|
||||
.withRules(
|
||||
new PolicyRuleBuilder()
|
||||
.withApiGroups("")
|
||||
.withResources("pods")
|
||||
.withVerbs("get", "list", "watch", "delete")
|
||||
.build(),
|
||||
new PolicyRuleBuilder()
|
||||
.withApiGroups("")
|
||||
.withResources("configmaps", "services", "secrets")
|
||||
.withVerbs("delete", "list", "get")
|
||||
.build(),
|
||||
new PolicyRuleBuilder()
|
||||
.withApiGroups("route.openshift.io")
|
||||
.withResources("routes")
|
||||
.withVerbs("delete", "list")
|
||||
.build(),
|
||||
new PolicyRuleBuilder()
|
||||
.withApiGroups("apps")
|
||||
.withResources("deployments", "replicasets")
|
||||
.withVerbs("delete", "list", "get", "patch")
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
protected RoleBinding createStopWorkspacesRoleBinding(String name) {
|
||||
return new RoleBindingBuilder()
|
||||
.withNewMetadata()
|
||||
.withName(name)
|
||||
.endMetadata()
|
||||
.withNewRoleRef()
|
||||
.withKind("Role")
|
||||
.withName(name)
|
||||
.endRoleRef()
|
||||
.withSubjects(
|
||||
new SubjectBuilder()
|
||||
.withKind("ServiceAccount")
|
||||
.withName("che")
|
||||
.withNamespace(installationLocation)
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,199 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2023 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.openshift.project.configurator;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyString;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.rbac.*;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import io.fabric8.kubernetes.client.dsl.MixedOperation;
|
||||
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
|
||||
import io.fabric8.kubernetes.client.dsl.RbacAPIGroupDSL;
|
||||
import io.fabric8.kubernetes.client.dsl.Resource;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.CheInstallationLocation;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Listeners;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* Test for {@link
|
||||
* org.eclipse.che.workspace.infrastructure.openshift.project.configurator.OpenShiftStopWorkspaceRoleConfigurator}
|
||||
*
|
||||
* <p>#author Tom George
|
||||
*/
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class OpenShiftStopWorkspaceRoleConfiguratorTest {
|
||||
|
||||
@Mock private CheInstallationLocation cheInstallationLocation;
|
||||
private OpenShiftStopWorkspaceRoleConfigurator stopWorkspaceRoleProvisioner;
|
||||
|
||||
@Mock private CheServerKubernetesClientFactory cheClientFactory;
|
||||
@Mock private KubernetesClient client;
|
||||
|
||||
@Mock private MixedOperation<Role, RoleList, Resource<Role>> mixedRoleOperation;
|
||||
|
||||
@Mock
|
||||
private MixedOperation<RoleBinding, RoleBindingList, Resource<RoleBinding>>
|
||||
mixedRoleBindingOperation;
|
||||
|
||||
@Mock private NonNamespaceOperation<Role, RoleList, Resource<Role>> nonNamespaceRoleOperation;
|
||||
|
||||
@Mock
|
||||
private NonNamespaceOperation<RoleBinding, RoleBindingList, Resource<RoleBinding>>
|
||||
nonNamespaceRoleBindingOperation;
|
||||
|
||||
@Mock private Resource<Role> roleResource;
|
||||
@Mock private Resource<RoleBinding> roleBindingResource;
|
||||
@Mock private Role mockRole;
|
||||
@Mock private RoleBinding mockRoleBinding;
|
||||
@Mock private RbacAPIGroupDSL rbacAPIGroupDSL;
|
||||
|
||||
private final Role expectedRole =
|
||||
new RoleBuilder()
|
||||
.withNewMetadata()
|
||||
.withName("workspace-stop")
|
||||
.endMetadata()
|
||||
.withRules(
|
||||
new PolicyRuleBuilder()
|
||||
.withApiGroups("")
|
||||
.withResources("pods")
|
||||
.withVerbs("get", "list", "watch", "delete")
|
||||
.build(),
|
||||
new PolicyRuleBuilder()
|
||||
.withApiGroups("")
|
||||
.withResources("configmaps", "services", "secrets")
|
||||
.withVerbs("delete", "list", "get")
|
||||
.build(),
|
||||
new PolicyRuleBuilder()
|
||||
.withApiGroups("route.openshift.io")
|
||||
.withResources("routes")
|
||||
.withVerbs("delete", "list")
|
||||
.build(),
|
||||
new PolicyRuleBuilder()
|
||||
.withApiGroups("apps")
|
||||
.withResources("deployments", "replicasets")
|
||||
.withVerbs("delete", "list", "get", "patch")
|
||||
.build())
|
||||
.build();
|
||||
|
||||
private final RoleBinding expectedRoleBinding =
|
||||
new RoleBindingBuilder()
|
||||
.withNewMetadata()
|
||||
.withName("workspace-stop")
|
||||
.endMetadata()
|
||||
.withNewRoleRef()
|
||||
.withKind("Role")
|
||||
.withName("workspace-stop")
|
||||
.endRoleRef()
|
||||
.withSubjects(
|
||||
new SubjectBuilder()
|
||||
.withKind("ServiceAccount")
|
||||
.withName("che")
|
||||
.withNamespace("che")
|
||||
.build())
|
||||
.build();
|
||||
|
||||
@BeforeMethod
|
||||
public void setUp() throws Exception {
|
||||
lenient().when(cheInstallationLocation.getInstallationLocationNamespace()).thenReturn("che");
|
||||
stopWorkspaceRoleProvisioner =
|
||||
new OpenShiftStopWorkspaceRoleConfigurator(
|
||||
cheClientFactory, cheInstallationLocation, true, "yes");
|
||||
lenient().when(cheClientFactory.create()).thenReturn(client);
|
||||
lenient().when(client.rbac()).thenReturn(rbacAPIGroupDSL);
|
||||
lenient().when(rbacAPIGroupDSL.roles()).thenReturn(mixedRoleOperation);
|
||||
lenient().when(rbacAPIGroupDSL.roleBindings()).thenReturn(mixedRoleBindingOperation);
|
||||
lenient()
|
||||
.when(mixedRoleOperation.inNamespace(anyString()))
|
||||
.thenReturn(nonNamespaceRoleOperation);
|
||||
lenient()
|
||||
.when(mixedRoleBindingOperation.inNamespace(anyString()))
|
||||
.thenReturn(nonNamespaceRoleBindingOperation);
|
||||
lenient().when(nonNamespaceRoleOperation.withName(anyString())).thenReturn(roleResource);
|
||||
lenient()
|
||||
.when(nonNamespaceRoleBindingOperation.withName(anyString()))
|
||||
.thenReturn(roleBindingResource);
|
||||
lenient().when(roleResource.get()).thenReturn(null);
|
||||
lenient().when(nonNamespaceRoleOperation.createOrReplace(any())).thenReturn(mockRole);
|
||||
lenient()
|
||||
.when(nonNamespaceRoleBindingOperation.createOrReplace(any()))
|
||||
.thenReturn(mockRoleBinding);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateRole() {
|
||||
assertEquals(
|
||||
stopWorkspaceRoleProvisioner.createStopWorkspacesRole("workspace-stop"), expectedRole);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateRoleBinding() throws InfrastructureException {
|
||||
assertEquals(
|
||||
stopWorkspaceRoleProvisioner.createStopWorkspacesRoleBinding("workspace-stop"),
|
||||
expectedRoleBinding);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateRoleAndRoleBindingWhenRoleDoesNotYetExist()
|
||||
throws InfrastructureException {
|
||||
stopWorkspaceRoleProvisioner.configure(null, "developer-che");
|
||||
verify(client.rbac().roles().inNamespace("developer-che")).createOrReplace(expectedRole);
|
||||
verify(client.rbac().roleBindings().inNamespace("developer-che"))
|
||||
.createOrReplace(expectedRoleBinding);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotCreateRoleBindingWhenStopWorkspaceRolePropertyIsDisabled()
|
||||
throws InfrastructureException {
|
||||
OpenShiftStopWorkspaceRoleConfigurator disabledStopWorkspaceRoleProvisioner =
|
||||
new OpenShiftStopWorkspaceRoleConfigurator(
|
||||
cheClientFactory, cheInstallationLocation, false, "yes");
|
||||
disabledStopWorkspaceRoleProvisioner.configure(null, "developer-che");
|
||||
verify(client, never()).rbac();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotCreateRoleBindingWhenInstallationLocationIsNull()
|
||||
throws InfrastructureException {
|
||||
lenient().when(cheInstallationLocation.getInstallationLocationNamespace()).thenReturn(null);
|
||||
OpenShiftStopWorkspaceRoleConfigurator
|
||||
stopWorkspaceRoleProvisionerWithoutValidInstallationLocation =
|
||||
new OpenShiftStopWorkspaceRoleConfigurator(
|
||||
cheClientFactory, cheInstallationLocation, true, "yes");
|
||||
stopWorkspaceRoleProvisionerWithoutValidInstallationLocation.configure(null, "developer-che");
|
||||
verify(client, never()).rbac();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotCallStopWorkspaceRoleProvisionWhenIdentityProviderIsDefined()
|
||||
throws Exception {
|
||||
when(cheInstallationLocation.getInstallationLocationNamespace()).thenReturn("something");
|
||||
OpenShiftStopWorkspaceRoleConfigurator configurator =
|
||||
new OpenShiftStopWorkspaceRoleConfigurator(
|
||||
cheClientFactory, cheInstallationLocation, true, null);
|
||||
|
||||
configurator.configure(null, "something");
|
||||
|
||||
verify(cheClientFactory, times(0)).create();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue