CommonPVCStrategy implements WorkspaceVolumesStrategy { @Override @Traced public void prepare( KubernetesEnvironment k8sEnv, RuntimeIdentity identity, long timeoutMillis, Map<String, String> startOptions) throws InfrastructureException { String workspaceId = identity.getWorkspaceId(); TracingTags.WORKSPACE_ID.set(workspaceId); if (EphemeralWorkspaceUtility.isEphemeral(k8sEnv.getAttributes())) { return; } log.debug("Preparing PVC started for workspace '{}'", workspaceId); Map<String, PersistentVolumeClaim> claims = k8sEnv.getPersistentVolumeClaims(); if (claims.isEmpty()) { return; } if (claims.size() > 1) { throw new InfrastructureException( format( "The only one PVC MUST be present in common strategy while it contains: %s.", claims.keySet().stream().collect(joining(", ")))); } PersistentVolumeClaim commonPVC = claims.values().iterator().next(); final KubernetesNamespace namespace = factory.getOrCreate(identity); final KubernetesPersistentVolumeClaims pvcs = namespace.persistentVolumeClaims(); final Set<String> existing = pvcs.get().stream().map(p -> p.getMetadata().getName()).collect(toSet()); if (!existing.contains(commonPVC.getMetadata().getName())) { log.debug("Creating PVC for workspace '{}'", workspaceId); pvcs.create(commonPVC); if (waitBound) { log.debug("Waiting for PVC for workspace '{}' to be bound", workspaceId); pvcs.waitBound(commonPVC.getMetadata().getName(), timeoutMillis); } } final String[] subpaths = (String[]) commonPVC.getAdditionalProperties().remove(format(SUBPATHS_PROPERTY_FMT, workspaceId)); if (preCreateDirs && subpaths != null) { pvcSubPathHelper.createDirs( identity, workspaceId, commonPVC.getMetadata().getName(), startOptions, subpaths); } log.debug("Preparing PVC done for workspace '{}'", workspaceId); } @Inject  CommonPVCStrategy(
      @Named("che.infra.kubernetes.pvc.name") String configuredPVCName,
      @Named("che.infra.kubernetes.pvc.quantity") String pvcQuantity,
      @Named("che.infra.kubernetes.pvc.access_mode") String pvcAccessMode,
      @Named("che.infra.kubernetes.pvc.precreate_subpaths") boolean preCreateDirs,
      @Named("che.infra.kubernetes.pvc.storage_class_name") String pvcStorageClassName,
      @Named("che.infra.kubernetes.pvc.wait_bound") boolean waitBound,
      PVCSubPathHelper pvcSubPathHelper,
      KubernetesNamespaceFactory factory,
      EphemeralWorkspaceAdapter ephemeralWorkspaceAdapter,
      PVCProvisioner pvcProvisioner,
      PodsVolumes podsVolumes,
      SubPathPrefixes subpathPrefixes); @Override void provision(KubernetesEnvironment k8sEnv, RuntimeIdentity identity); @Override @Traced void prepare(
      KubernetesEnvironment k8sEnv,
      RuntimeIdentity identity,
      long timeoutMillis,
      Map<String, String> startOptions); @Override void cleanup(Workspace workspace); static final String COMMON_STRATEGY; }
@Test public void testCreatesPVCsWithSubpathsOnPrepare() throws Exception { final PersistentVolumeClaim pvc = newPVC(PVC_NAME); pvc.getAdditionalProperties() .put(format(SUBPATHS_PROPERTY_FMT, WORKSPACE_ID), WORKSPACE_SUBPATHS); k8sEnv.getPersistentVolumeClaims().put(PVC_NAME, pvc); doNothing() .when(pvcSubPathHelper) .createDirs(IDENTITY, WORKSPACE_ID, PVC_NAME, emptyMap(), WORKSPACE_SUBPATHS); commonPVCStrategy.prepare(k8sEnv, IDENTITY, 100, emptyMap()); verify(pvcs).get(); verify(pvcs).create(pvc); verify(pvcs).waitBound(PVC_NAME, 100); verify(pvcSubPathHelper) .createDirs(IDENTITY, WORKSPACE_ID, PVC_NAME, emptyMap(), WORKSPACE_SUBPATHS); }
@Test public void testCreatesPVCsWithSubpathsOnPrepareIfWaitIsDisabled() throws Exception { commonPVCStrategy = new CommonPVCStrategy( PVC_NAME, PVC_QUANTITY, PVC_ACCESS_MODE, true, PVC_STORAGE_CLASS_NAME, false, pvcSubPathHelper, factory, ephemeralWorkspaceAdapter, volumeConverter, podsVolumes, subpathPrefixes); final PersistentVolumeClaim pvc = newPVC(PVC_NAME); pvc.getAdditionalProperties() .put(format(SUBPATHS_PROPERTY_FMT, WORKSPACE_ID), WORKSPACE_SUBPATHS); k8sEnv.getPersistentVolumeClaims().put(PVC_NAME, pvc); doNothing() .when(pvcSubPathHelper) .createDirs(IDENTITY, WORKSPACE_ID, PVC_NAME, emptyMap(), WORKSPACE_SUBPATHS); commonPVCStrategy.prepare(k8sEnv, IDENTITY, 100, emptyMap()); verify(pvcs).get(); verify(pvcs).create(pvc); verify(pvcs, never()).waitBound(anyString(), anyLong()); verify(pvcSubPathHelper) .createDirs(IDENTITY, WORKSPACE_ID, PVC_NAME, emptyMap(), WORKSPACE_SUBPATHS); }
@Test( expectedExceptions = InfrastructureException.class, expectedExceptionsMessageRegExp = "The only one PVC MUST be present in common strategy while it contains: pvc1, pvc2\\.") public void shouldThrowExceptionIfK8sEnvHasMoreThanOnePVCOnPreparing() throws Exception { final PersistentVolumeClaim pvc1 = newPVC("pvc1"); final PersistentVolumeClaim pvc2 = newPVC("pvc2"); k8sEnv.getPersistentVolumeClaims().put("pvc1", pvc1); k8sEnv.getPersistentVolumeClaims().put("pvc2", pvc2); commonPVCStrategy.prepare(k8sEnv, IDENTITY, 100, emptyMap()); }
@Test(expectedExceptions = InfrastructureException.class) public void throwsInfrastructureExceptionWhenFailedToGetExistingPVCs() throws Exception { k8sEnv.getPersistentVolumeClaims().put(PVC_NAME, mock(PersistentVolumeClaim.class)); doThrow(InfrastructureException.class).when(pvcs).get(); commonPVCStrategy.prepare(k8sEnv, IDENTITY, 100, emptyMap()); }
@Test(expectedExceptions = InfrastructureException.class) public void throwsInfrastructureExceptionWhenPVCCreationFailed() throws Exception { final PersistentVolumeClaim claim = newPVC(PVC_NAME); k8sEnv.getPersistentVolumeClaims().put(PVC_NAME, claim); when(pvcs.get()).thenReturn(emptyList()); doThrow(InfrastructureException.class).when(pvcs).create(any()); commonPVCStrategy.prepare(k8sEnv, IDENTITY, 100, emptyMap()); }