You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datalab.apache.org by of...@apache.org on 2020/10/02 07:21:13 UTC

[incubator-datalab] branch develop updated: [DATALAB-1542] Added possibility to recreate edge node after edge termination/failing

This is an automated email from the ASF dual-hosted git repository.

ofuks pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git


The following commit(s) were added to refs/heads/develop by this push:
     new 26f204b  [DATALAB-1542] Added possibility to recreate edge node after edge termination/failing
26f204b is described below

commit 26f204b4e7d409c10d81143aad92471f5f5a76c8
Author: Oleh Fuks <ol...@gmail.com>
AuthorDate: Fri Oct 2 10:15:50 2020 +0300

    [DATALAB-1542] Added possibility to recreate edge node after edge termination/failing
---
 .../core/commands/CommandExecutorMockAsync.java    |  1 +
 .../backendapi/core/commands/DockerAction.java     | 25 +++++-----
 .../response/handlers/ProjectCallbackHandler.java  | 25 +++++-----
 .../response/handlers/ResourceCallbackHandler.java |  3 +-
 .../backendapi/resources/ProjectResource.java      | 39 +++++++++------
 .../datalab/backendapi/service/ProjectService.java | 10 ++--
 .../service/impl/ProjectServiceImpl.java           | 39 ++++++++-------
 .../mock_response/aws/project_recreate.json        | 57 ++++++++++++++++++++++
 .../mock_response/azure/project_recreate.json      | 57 ++++++++++++++++++++++
 .../mock_response/gcp/project_recreate.json        | 57 ++++++++++++++++++++++
 .../datalab/backendapi/domain/AuditActionEnum.java |  3 +-
 .../backendapi/resources/ProjectResource.java      | 18 +++++++
 .../datalab/backendapi/service/ProjectService.java | 22 +++++----
 .../service/impl/ProjectServiceImpl.java           | 34 ++++++++++---
 .../backendapi/resources/ProjectResourceTest.java  | 21 ++++++--
 .../backendapi/service/ProjectServiceImplTest.java | 51 +++++++++++++------
 16 files changed, 363 insertions(+), 99 deletions(-)

diff --git a/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/core/commands/CommandExecutorMockAsync.java b/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/core/commands/CommandExecutorMockAsync.java
index 6d6c224..38d6ca8 100644
--- a/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/core/commands/CommandExecutorMockAsync.java
+++ b/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/core/commands/CommandExecutorMockAsync.java
@@ -135,6 +135,7 @@ public class CommandExecutorMockAsync implements Supplier<Boolean> {
                         describe();
                         break;
                     case CREATE:
+                    case RECREATE:
                     case START:
                     case STOP:
                     case TERMINATE:
diff --git a/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/core/commands/DockerAction.java b/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/core/commands/DockerAction.java
index f06c94d..d61bac6 100644
--- a/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/core/commands/DockerAction.java
+++ b/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/core/commands/DockerAction.java
@@ -20,18 +20,19 @@
 package com.epam.datalab.backendapi.core.commands;
 
 public enum DockerAction {
-    DESCRIBE,
-    CREATE,
-    START,
-    CONFIGURE,
-    RUN,
-    STOP,
-    TERMINATE,
-    LIB_LIST,
-    LIB_INSTALL,
-    GIT_CREDS,
-    CREATE_IMAGE,
-    STATUS,
+	DESCRIBE,
+	CREATE,
+	RECREATE,
+	START,
+	CONFIGURE,
+	RUN,
+	STOP,
+	TERMINATE,
+	LIB_LIST,
+	LIB_INSTALL,
+	GIT_CREDS,
+	CREATE_IMAGE,
+	STATUS,
     REUPLOAD_KEY,
     RECONFIGURE_SPARK,
     CHECK_INACTIVITY;
diff --git a/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/core/response/handlers/ProjectCallbackHandler.java b/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/core/response/handlers/ProjectCallbackHandler.java
index 738a2fc..cad5635 100644
--- a/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/core/response/handlers/ProjectCallbackHandler.java
+++ b/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/core/response/handlers/ProjectCallbackHandler.java
@@ -28,6 +28,7 @@ import com.epam.datalab.rest.client.RESTService;
 import com.fasterxml.jackson.databind.JsonNode;
 
 import java.io.IOException;
+import java.util.Arrays;
 
 public class ProjectCallbackHandler extends ResourceCallbackHandler<ProjectResult> {
 
@@ -54,18 +55,18 @@ public class ProjectCallbackHandler extends ResourceCallbackHandler<ProjectResul
 
     @Override
     protected ProjectResult parseOutResponse(JsonNode resultNode, ProjectResult baseStatus) {
-        baseStatus.setProjectName(projectName);
-        baseStatus.setEndpointName(endpointName);
-        if (resultNode != null && getAction() == DockerAction.CREATE
-                && UserInstanceStatus.of(baseStatus.getStatus()) != UserInstanceStatus.FAILED) {
-            try {
-                final EdgeInfo projectEdgeInfo = mapper.readValue(resultNode.toString(), clazz);
-                baseStatus.setEdgeInfo(projectEdgeInfo);
-            } catch (IOException e) {
-                throw new DatalabException("Cannot parse the EDGE info in JSON: " + e.getLocalizedMessage(), e);
-            }
-        }
+	    baseStatus.setProjectName(projectName);
+	    baseStatus.setEndpointName(endpointName);
+	    if (resultNode != null && Arrays.asList(DockerAction.CREATE, DockerAction.RECREATE).contains(getAction()) &&
+			    UserInstanceStatus.of(baseStatus.getStatus()) != UserInstanceStatus.FAILED) {
+		    try {
+			    final EdgeInfo projectEdgeInfo = mapper.readValue(resultNode.toString(), clazz);
+			    baseStatus.setEdgeInfo(projectEdgeInfo);
+		    } catch (IOException e) {
+			    throw new DatalabException("Cannot parse the EDGE info in JSON: " + e.getLocalizedMessage(), e);
+		    }
+	    }
 
-        return baseStatus;
+	    return baseStatus;
     }
 }
diff --git a/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/core/response/handlers/ResourceCallbackHandler.java b/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/core/response/handlers/ResourceCallbackHandler.java
index 623ca4f..5690657 100644
--- a/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/core/response/handlers/ResourceCallbackHandler.java
+++ b/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/core/response/handlers/ResourceCallbackHandler.java
@@ -176,7 +176,8 @@ public abstract class ResourceCallbackHandler<T extends StatusBaseDTO<?>> implem
                 case CREATE_IMAGE:
                     return UserInstanceStatus.CREATED; // Any status besides failed
                 case CREATE:
-                case CONFIGURE:
+	            case RECREATE:
+	            case CONFIGURE:
                 case START:
                 case RECONFIGURE_SPARK:
                     return UserInstanceStatus.RUNNING;
diff --git a/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/resources/ProjectResource.java b/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/resources/ProjectResource.java
index f8c50cf..ae4c3e0 100644
--- a/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/resources/ProjectResource.java
+++ b/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/resources/ProjectResource.java
@@ -35,6 +35,7 @@ import javax.ws.rs.core.Response;
 
 @Path("infrastructure/project")
 public class ProjectResource {
+
     private final ProjectService projectService;
 
     @Inject
@@ -42,23 +43,31 @@ public class ProjectResource {
         this.projectService = projectService;
     }
 
-    @Path("/create")
-    @POST
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Produces(MediaType.APPLICATION_JSON)
-    public Response createProject(@Auth UserInfo userInfo, ProjectCreateDTO dto) {
-        return Response.ok(projectService.create(userInfo, dto)).build();
-    }
+	@Path("/create")
+	@POST
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response createProject(@Auth UserInfo userInfo, ProjectCreateDTO dto) {
+		return Response.ok(projectService.create(userInfo, dto)).build();
+	}
 
-    @Path("/terminate")
-    @POST
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Produces(MediaType.APPLICATION_JSON)
-    public Response terminateProject(@Auth UserInfo userInfo, ProjectActionDTO dto) {
-        return Response.ok(projectService.terminate(userInfo, dto)).build();
-    }
+	@Path("/recreate")
+	@POST
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response recreateProject(@Auth UserInfo userInfo, ProjectCreateDTO dto) {
+		return Response.ok(projectService.recreate(userInfo, dto)).build();
+	}
+
+	@Path("/terminate")
+	@POST
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response terminateProject(@Auth UserInfo userInfo, ProjectActionDTO dto) {
+		return Response.ok(projectService.terminate(userInfo, dto)).build();
+	}
 
-    @Path("/start")
+	@Path("/start")
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
diff --git a/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/service/ProjectService.java b/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/service/ProjectService.java
index d919d87..1f20c52 100644
--- a/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/service/ProjectService.java
+++ b/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/service/ProjectService.java
@@ -25,11 +25,13 @@ import com.epam.datalab.dto.project.ProjectCreateDTO;
 
 public interface ProjectService {
 
-    String create(UserInfo userInfo, ProjectCreateDTO projectCreateDTO);
+	String create(UserInfo userInfo, ProjectCreateDTO projectCreateDTO);
 
-    String terminate(UserInfo userInfo, ProjectActionDTO dto);
+	String recreate(UserInfo userInfo, ProjectCreateDTO dto);
 
-    String start(UserInfo userInfo, ProjectActionDTO dto);
+	String terminate(UserInfo userInfo, ProjectActionDTO dto);
 
-    String stop(UserInfo userInfo, ProjectActionDTO dto);
+	String start(UserInfo userInfo, ProjectActionDTO dto);
+
+	String stop(UserInfo userInfo, ProjectActionDTO dto);
 }
diff --git a/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/service/impl/ProjectServiceImpl.java b/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/service/impl/ProjectServiceImpl.java
index 7754286..d7a94e1 100644
--- a/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/service/impl/ProjectServiceImpl.java
+++ b/services/provisioning-service/src/main/java/com/epam/datalab/backendapi/service/impl/ProjectServiceImpl.java
@@ -58,28 +58,31 @@ public class ProjectServiceImpl implements ProjectService {
     @Inject
     public ProjectServiceImpl(RESTService selfService, ProvisioningServiceApplicationConfiguration configuration,
                               FolderListenerExecutor folderListenerExecutor, ICommandExecutor commandExecutor, CommandBuilder commandBuilder) {
-        this.selfService = selfService;
-        this.configuration = configuration;
-        this.folderListenerExecutor = folderListenerExecutor;
-        this.commandExecutor = commandExecutor;
-        this.commandBuilder = commandBuilder;
+	    this.selfService = selfService;
+	    this.configuration = configuration;
+	    this.folderListenerExecutor = folderListenerExecutor;
+	    this.commandExecutor = commandExecutor;
+	    this.commandBuilder = commandBuilder;
     }
 
-    @Override
-    public String create(UserInfo userInfo, ProjectCreateDTO dto) {
-        return executeDocker(userInfo, dto, DockerAction.CREATE, dto.getName(), "project", PROJECT_IMAGE,
-                dto.getEndpoint());
-    }
+	@Override
+	public String create(UserInfo userInfo, ProjectCreateDTO dto) {
+		return executeDocker(userInfo, dto, DockerAction.CREATE, dto.getName(), "project", PROJECT_IMAGE, dto.getEndpoint());
+	}
 
-    @Override
-    public String terminate(UserInfo userInfo, ProjectActionDTO dto) {
-        return executeDocker(userInfo, dto, DockerAction.TERMINATE, dto.getName(), "project", PROJECT_IMAGE,
-                dto.getEndpoint());
-    }
+	@Override
+	public String recreate(UserInfo userInfo, ProjectCreateDTO dto) {
+		return executeDocker(userInfo, dto, DockerAction.RECREATE, dto.getName(), "project", PROJECT_IMAGE, dto.getEndpoint());
+	}
 
-    @Override
-    public String start(UserInfo userInfo, ProjectActionDTO dto) {
-        return executeDocker(userInfo, dto, DockerAction.START, dto.getName(), "edge", EDGE_IMAGE, dto.getEndpoint());
+	@Override
+	public String terminate(UserInfo userInfo, ProjectActionDTO dto) {
+		return executeDocker(userInfo, dto, DockerAction.TERMINATE, dto.getName(), "project", PROJECT_IMAGE, dto.getEndpoint());
+	}
+
+	@Override
+	public String start(UserInfo userInfo, ProjectActionDTO dto) {
+		return executeDocker(userInfo, dto, DockerAction.START, dto.getName(), "edge", EDGE_IMAGE, dto.getEndpoint());
     }
 
     @Override
diff --git a/services/provisioning-service/src/main/resources/mock_response/aws/project_recreate.json b/services/provisioning-service/src/main/resources/mock_response/aws/project_recreate.json
new file mode 100644
index 0000000..e63315f
--- /dev/null
+++ b/services/provisioning-service/src/main/resources/mock_response/aws/project_recreate.json
@@ -0,0 +1,57 @@
+{
+  "status": "ok",
+  "response": {
+    "result": {
+      "tunnel_port": "22",
+      "full_edge_conf": {
+        "edge_service_account_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge",
+        "fw_edge_egress_internal": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge-egress-internal",
+        "datalab_ssh_user": "datalab-user",
+        "ps_role_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ps",
+        "fw_ps_egress_public": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ps-egress-public",
+        "private_ip": "10.10.0.3",
+        "fw_ps_ingress": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ps-ingress",
+        "ssh_key_path": "/root/keys/BDCC-DSS-POC.pem",
+        "zone": "us-west1-a ",
+        "fw_edge_egress_public": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge-egress-public",
+        "ami_name": "/projects/ubuntu-os-cloud/global/images/ubuntu-1604-xenial-v20170721",
+        "edge_role_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge",
+        "private_subnet_prefix": "24",
+        "subnet_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-subnet",
+        "vpc_cidr": "10.10.0.0/16",
+        "key_name": "${CONF_KEY_NAME}",
+        "service_base_name": "${CONF_SERVICE_BASE_NAME}",
+        "static_address_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ip",
+        "fw_ps_egress_private": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ps-egress-private",
+        "vpc_name": "${CONF_SERVICE_BASE_NAME}-ssn-vpc",
+        "static_ip": "104.198.5.3",
+        "bucket_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-bucket",
+        "fw_edge_ingress_public": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge-ingress-public",
+        "ps_service_account_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ps",
+        "firewall_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge-firewall",
+        "region": "us-west1",
+        "fw_common_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ps",
+        "instance_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge",
+        "user_keyname": "${EDGE_USER_NAME}",
+        "edge_user_name": "${EDGE_USER_NAME}",
+        "private_subnet_cidr": "10.10.16.0/24",
+        "fw_edge_ingress_internal": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge-ingress-internal",
+        "vpc_selflink": "https://www.googleapis.com/compute/v1/projects/or2-msq-epmc-datalab-t1iylu/global/networks/${CONF_SERVICE_BASE_NAME}-ssn-vpc",
+        "instance_size": "n1-standard-1",
+        "notebook_firewall_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-nb-firewall"
+      },
+      "key_name": "BDCC-DSS-POC",
+      "hostname": "104.198.5.3",
+      "public_ip": "104.198.5.3",
+      "ip": "10.10.0.3",
+      "Action": "Create new EDGE server",
+      "user_own_bucket_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-bucket",
+      "socks_port": "1080",
+      "notebook_subnet": "10.10.16.0/24",
+      "project_name": "${PROJECT_NAME}",
+      "@class": "com.epam.datalab.dto.aws.edge.EdgeInfoAws"
+    },
+    "log": "/var/log/datalab/edge/edge_${EDGE_USER_NAME}_${REQUEST_ID}.log"
+  },
+  "request_id": "${REQUEST_ID}"
+}
\ No newline at end of file
diff --git a/services/provisioning-service/src/main/resources/mock_response/azure/project_recreate.json b/services/provisioning-service/src/main/resources/mock_response/azure/project_recreate.json
new file mode 100644
index 0000000..79a1cc5
--- /dev/null
+++ b/services/provisioning-service/src/main/resources/mock_response/azure/project_recreate.json
@@ -0,0 +1,57 @@
+{
+  "status": "ok",
+  "response": {
+    "result": {
+      "tunnel_port": "22",
+      "full_edge_conf": {
+        "edge_service_account_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge",
+        "fw_edge_egress_internal": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge-egress-internal",
+        "datalab_ssh_user": "datalab-user",
+        "ps_role_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ps",
+        "fw_ps_egress_public": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ps-egress-public",
+        "private_ip": "10.10.0.3",
+        "fw_ps_ingress": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ps-ingress",
+        "ssh_key_path": "/root/keys/BDCC-DSS-POC.pem",
+        "zone": "us-west1-a ",
+        "fw_edge_egress_public": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge-egress-public",
+        "ami_name": "/projects/ubuntu-os-cloud/global/images/ubuntu-1604-xenial-v20170721",
+        "edge_role_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge",
+        "private_subnet_prefix": "24",
+        "subnet_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-subnet",
+        "vpc_cidr": "10.10.0.0/16",
+        "key_name": "${CONF_KEY_NAME}",
+        "service_base_name": "${CONF_SERVICE_BASE_NAME}",
+        "static_address_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ip",
+        "fw_ps_egress_private": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ps-egress-private",
+        "vpc_name": "${CONF_SERVICE_BASE_NAME}-ssn-vpc",
+        "static_ip": "104.198.5.3",
+        "bucket_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-bucket",
+        "fw_edge_ingress_public": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge-ingress-public",
+        "ps_service_account_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ps",
+        "firewall_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge-firewall",
+        "region": "us-west1",
+        "fw_common_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ps",
+        "instance_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge",
+        "user_keyname": "${EDGE_USER_NAME}",
+        "edge_user_name": "${EDGE_USER_NAME}",
+        "private_subnet_cidr": "10.10.16.0/24",
+        "fw_edge_ingress_internal": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge-ingress-internal",
+        "vpc_selflink": "https://www.googleapis.com/compute/v1/projects/or2-msq-epmc-datalab-t1iylu/global/networks/${CONF_SERVICE_BASE_NAME}-ssn-vpc",
+        "instance_size": "n1-standard-1",
+        "notebook_firewall_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-nb-firewall"
+      },
+      "key_name": "BDCC-DSS-POC",
+      "hostname": "104.198.5.3",
+      "public_ip": "104.198.5.3",
+      "ip": "10.10.0.3",
+      "Action": "Create new EDGE server",
+      "user_own_bucket_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-bucket",
+      "socks_port": "1080",
+      "notebook_subnet": "10.10.16.0/24",
+      "project_name": "${PROJECT_NAME}",
+      "@class": "com.epam.datalab.dto.azure.edge.EdgeInfoAzure"
+    },
+    "log": "/var/log/datalab/edge/edge_${EDGE_USER_NAME}_${REQUEST_ID}.log"
+  },
+  "request_id": "${REQUEST_ID}"
+}
\ No newline at end of file
diff --git a/services/provisioning-service/src/main/resources/mock_response/gcp/project_recreate.json b/services/provisioning-service/src/main/resources/mock_response/gcp/project_recreate.json
new file mode 100644
index 0000000..f8ddb72
--- /dev/null
+++ b/services/provisioning-service/src/main/resources/mock_response/gcp/project_recreate.json
@@ -0,0 +1,57 @@
+{
+  "status": "ok",
+  "response": {
+    "result": {
+      "tunnel_port": "22",
+      "full_edge_conf": {
+        "edge_service_account_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge",
+        "fw_edge_egress_internal": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge-egress-internal",
+        "datalab_ssh_user": "datalab-user",
+        "ps_role_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ps",
+        "fw_ps_egress_public": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ps-egress-public",
+        "private_ip": "10.10.0.3",
+        "fw_ps_ingress": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ps-ingress",
+        "ssh_key_path": "/root/keys/BDCC-DSS-POC.pem",
+        "zone": "us-west1-a ",
+        "fw_edge_egress_public": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge-egress-public",
+        "ami_name": "/projects/ubuntu-os-cloud/global/images/ubuntu-1604-xenial-v20170721",
+        "edge_role_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge",
+        "private_subnet_prefix": "24",
+        "subnet_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-subnet",
+        "vpc_cidr": "10.10.0.0/16",
+        "key_name": "${CONF_KEY_NAME}",
+        "service_base_name": "${CONF_SERVICE_BASE_NAME}",
+        "static_address_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ip",
+        "fw_ps_egress_private": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ps-egress-private",
+        "vpc_name": "${CONF_SERVICE_BASE_NAME}-ssn-vpc",
+        "static_ip": "104.198.5.3",
+        "bucket_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-bucket",
+        "fw_edge_ingress_public": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge-ingress-public",
+        "ps_service_account_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ps",
+        "firewall_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge-firewall",
+        "region": "us-west1",
+        "fw_common_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-ps",
+        "instance_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge",
+        "user_keyname": "${EDGE_USER_NAME}",
+        "edge_user_name": "${EDGE_USER_NAME}",
+        "private_subnet_cidr": "10.10.16.0/24",
+        "fw_edge_ingress_internal": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-edge-ingress-internal",
+        "vpc_selflink": "https://www.googleapis.com/compute/v1/projects/or2-msq-epmc-datalab-t1iylu/global/networks/${CONF_SERVICE_BASE_NAME}-ssn-vpc",
+        "instance_size": "n1-standard-1",
+        "notebook_firewall_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-nb-firewall"
+      },
+      "key_name": "BDCC-DSS-POC",
+      "hostname": "104.198.5.3",
+      "public_ip": "104.198.5.3",
+      "ip": "10.10.0.3",
+      "Action": "Recreate new EDGE server",
+      "user_own_bucket_name": "${CONF_SERVICE_BASE_NAME}-${EDGE_USER_NAME}-bucket",
+      "socks_port": "1080",
+      "notebook_subnet": "10.10.16.0/24",
+      "project_name": "${PROJECT_NAME}",
+      "@class": "com.epam.datalab.dto.gcp.edge.EdgeInfoGcp"
+    },
+    "log": "/var/log/datalab/edge/edge_${EDGE_USER_NAME}_${REQUEST_ID}.log"
+  },
+  "request_id": "${REQUEST_ID}"
+}
\ No newline at end of file
diff --git a/services/self-service/src/main/java/com/epam/datalab/backendapi/domain/AuditActionEnum.java b/services/self-service/src/main/java/com/epam/datalab/backendapi/domain/AuditActionEnum.java
index ed18240..65fdcda 100644
--- a/services/self-service/src/main/java/com/epam/datalab/backendapi/domain/AuditActionEnum.java
+++ b/services/self-service/src/main/java/com/epam/datalab/backendapi/domain/AuditActionEnum.java
@@ -20,5 +20,6 @@
 package com.epam.datalab.backendapi.domain;
 
 public enum AuditActionEnum {
-    CREATE, SET_UP_SCHEDULER, START, STOP, TERMINATE, RECONFIGURE, UPDATE, CONNECT, DISCONNECT, UPLOAD, DOWNLOAD, DELETE, INSTALL_LIBS, FOLLOW_LINK, LOG_IN
+	CREATE, RECREATE, SET_UP_SCHEDULER, START, STOP, TERMINATE, RECONFIGURE, UPDATE, CONNECT, DISCONNECT, UPLOAD,
+	DOWNLOAD, DELETE, INSTALL_LIBS, FOLLOW_LINK, LOG_IN
 }
diff --git a/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/ProjectResource.java b/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/ProjectResource.java
index 9ff6ee0..a0f3407 100644
--- a/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/ProjectResource.java
+++ b/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/ProjectResource.java
@@ -104,6 +104,24 @@ public class ProjectResource {
                 .build();
     }
 
+    @Operation(summary = "Recreate project edge", tags = "project")
+    @ApiResponse(responseCode = "202", description = "Project edge is recreating")
+    @ApiResponse(responseCode = "400", description = "Validation error", content = @Content(mediaType =
+            MediaType.APPLICATION_JSON,
+            schema = @Schema(implementation = ErrorDTO.class)))
+    @Path("recreate")
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @RolesAllowed("/api/project")
+    public Response recreateProject(@Parameter(hidden = true) @Auth UserInfo userInfo,
+                                    @NotNull @Valid ProjectActionFormDTO startProjectDto) {
+        startProjectDto.getEndpoints()
+                .forEach(endpoint -> projectService.recreate(userInfo, endpoint, startProjectDto.getProjectName()));
+        return Response
+                .accepted()
+                .build();
+    }
+
     @Operation(summary = "Start project", tags = "project")
     @ApiResponse(responseCode = "202", description = "Project is starting")
     @ApiResponse(responseCode = "400", description = "Validation error", content = @Content(mediaType =
diff --git a/services/self-service/src/main/java/com/epam/datalab/backendapi/service/ProjectService.java b/services/self-service/src/main/java/com/epam/datalab/backendapi/service/ProjectService.java
index 46ceaa7..01b9028 100644
--- a/services/self-service/src/main/java/com/epam/datalab/backendapi/service/ProjectService.java
+++ b/services/self-service/src/main/java/com/epam/datalab/backendapi/service/ProjectService.java
@@ -27,25 +27,27 @@ import com.epam.datalab.backendapi.domain.UpdateProjectDTO;
 import java.util.List;
 
 public interface ProjectService {
-    List<ProjectDTO> getProjects();
+	List<ProjectDTO> getProjects();
 
-    List<ProjectDTO> getProjects(UserInfo user);
+	List<ProjectDTO> getProjects(UserInfo user);
 
-    List<ProjectDTO> getUserProjects(UserInfo userInfo, boolean active);
+	List<ProjectDTO> getUserProjects(UserInfo userInfo, boolean active);
 
-    List<ProjectDTO> getProjectsByEndpoint(String endpointName);
+	List<ProjectDTO> getProjectsByEndpoint(String endpointName);
 
-    void create(UserInfo userInfo, ProjectDTO projectDTO, String resourceName);
+	void create(UserInfo userInfo, ProjectDTO projectDTO, String resourceName);
 
-    ProjectDTO get(String name);
+	void recreate(UserInfo userInfo, String endpoint, String projectName);
 
-    void terminateEndpoint(UserInfo userInfo, String endpoint, String name);
+	ProjectDTO get(String name);
 
-    void terminateEndpoint(UserInfo userInfo, List<String> endpoints, String name);
+	void terminateEndpoint(UserInfo userInfo, String endpoint, String name);
 
-    void start(UserInfo userInfo, String endpoint, String name);
+	void terminateEndpoint(UserInfo userInfo, List<String> endpoints, String name);
 
-    void start(UserInfo userInfo, List<String> endpoints, String name);
+	void start(UserInfo userInfo, String endpoint, String name);
+
+	void start(UserInfo userInfo, List<String> endpoints, String name);
 
     void stop(UserInfo userInfo, String endpoint, String name, String auditInfo);
 
diff --git a/services/self-service/src/main/java/com/epam/datalab/backendapi/service/impl/ProjectServiceImpl.java b/services/self-service/src/main/java/com/epam/datalab/backendapi/service/impl/ProjectServiceImpl.java
index 54d0495..bc66131 100644
--- a/services/self-service/src/main/java/com/epam/datalab/backendapi/service/impl/ProjectServiceImpl.java
+++ b/services/self-service/src/main/java/com/epam/datalab/backendapi/service/impl/ProjectServiceImpl.java
@@ -61,6 +61,7 @@ import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
 import static com.epam.datalab.backendapi.domain.AuditActionEnum.CREATE;
+import static com.epam.datalab.backendapi.domain.AuditActionEnum.RECREATE;
 import static com.epam.datalab.backendapi.domain.AuditActionEnum.START;
 import static com.epam.datalab.backendapi.domain.AuditActionEnum.STOP;
 import static com.epam.datalab.backendapi.domain.AuditActionEnum.TERMINATE;
@@ -77,6 +78,7 @@ public class ProjectServiceImpl implements ProjectService {
     private static final String TERMINATE_PRJ_API = "infrastructure/project/terminate";
     private static final String START_PRJ_API = "infrastructure/project/start";
     private static final String STOP_PRJ_API = "infrastructure/project/stop";
+    private static final String RECREATE_PRJ_API = "infrastructure/project/recreate";
     private static final String STOP_ACTION = "stop";
     private static final String TERMINATE_ACTION = "terminate";
     private static final String TOTAL_BUDGET_PERIOD = "Total";
@@ -87,6 +89,7 @@ public class ProjectServiceImpl implements ProjectService {
     private static final String AUDIT_REMOVE_GROUP = "Remove group(s): %s\n";
     private static final String AUDIT_UPDATE_BUDGET = "Update quota: %d->%d\nUpdate period: %s->%s";
     private static final String AUDIT_ADD_EDGE_NODE = "Create edge node for endpoint %s, requested in project %s";
+    private static final String AUDIT_RECREATE_EDGE_NODE = "Recreate edge node for endpoint %s, requested in project %s";
 
     private final ProjectDAO projectDAO;
     private final ExploratoryService exploratoryService;
@@ -139,9 +142,8 @@ public class ProjectServiceImpl implements ProjectService {
         return projectDAO.getProjectsByEndpoint(endpointName);
     }
 
-    @BudgetLimited
     @Override
-    public void create(UserInfo user, ProjectDTO projectDTO, String resourceName) {
+    public void create(UserInfo user, ProjectDTO projectDTO, String projectName) {
         if (!projectDAO.get(projectDTO.getName()).isPresent()) {
             projectDAO.create(projectDTO);
             createProjectOnCloud(user, projectDTO);
@@ -150,6 +152,13 @@ public class ProjectServiceImpl implements ProjectService {
         }
     }
 
+    @BudgetLimited
+    @Override
+    public void recreate(UserInfo userInfo, String endpoint, @Project String name) {
+        projectDAO.updateEdgeStatus(name, endpoint, UserInstanceStatus.CREATING);
+        recreateEndpoint(userInfo, get(name), endpoint, name, String.format(AUDIT_RECREATE_EDGE_NODE, endpoint, name));
+    }
+
     @Override
     public ProjectDTO get(String name) {
         return projectDAO.get(name)
@@ -237,7 +246,7 @@ public class ProjectServiceImpl implements ProjectService {
         project.getEndpoints().addAll(endpointsToBeCreated);
         projectDAO.update(new ProjectDTO(project.getName(), projectDTO.getGroups(), project.getKey(),
                 project.getTag(), project.getBudget(), project.getEndpoints(), projectDTO.isSharedImageEnabled()));
-        endpointsToBeCreated.forEach(e -> createEndpoint(userInfo, projectName, project, e.getName(), String.format(AUDIT_ADD_EDGE_NODE, e.getName(), project.getName())));
+        endpointsToBeCreated.forEach(e -> createEndpoint(userInfo, project, e.getName(), projectName, String.format(AUDIT_ADD_EDGE_NODE, e.getName(), project.getName())));
     }
 
     @Override
@@ -276,7 +285,7 @@ public class ProjectServiceImpl implements ProjectService {
 
     private void createProjectOnCloud(UserInfo user, ProjectDTO project) {
         try {
-            project.getEndpoints().forEach(e -> createEndpoint(user, project.getName(), project, e.getName(), String.format(AUDIT_ADD_EDGE_NODE, e.getName(), project.getName())));
+            project.getEndpoints().forEach(e -> createEndpoint(user, project, e.getName(), project.getName(), String.format(AUDIT_ADD_EDGE_NODE, e.getName(), project.getName())));
         } catch (Exception e) {
             log.error("Can not create project due to: {}", e.getMessage(), e);
             projectDAO.updateStatus(project.getName(), ProjectDTO.Status.FAILED);
@@ -284,9 +293,20 @@ public class ProjectServiceImpl implements ProjectService {
     }
 
     @Audit(action = CREATE, type = EDGE_NODE)
-    public void createEndpoint(@User UserInfo user, @Project String projectName, ProjectDTO projectDTO, @ResourceName String endpointName, @Info String auditInfo) {
+    public void createEndpoint(@User UserInfo user, ProjectDTO projectDTO, @ResourceName String endpointName, @Project String projectName,
+                               @Info String auditInfo) {
+        createEdgeNode(user, projectDTO, endpointName, CREATE_PRJ_API);
+    }
+
+    @Audit(action = RECREATE, type = EDGE_NODE)
+    public void recreateEndpoint(@User UserInfo user, ProjectDTO projectDTO, @ResourceName String endpointName, @Project String projectName,
+                                 @Info String auditInfo) {
+        createEdgeNode(user, projectDTO, endpointName, RECREATE_PRJ_API);
+    }
+
+    private void createEdgeNode(UserInfo user, ProjectDTO projectDTO, String endpointName, String provisioningApiUri) {
         EndpointDTO endpointDTO = endpointService.get(endpointName);
-        String uuid = provisioningService.post(endpointDTO.getUrl() + CREATE_PRJ_API, user.getAccessToken(),
+        String uuid = provisioningService.post(endpointDTO.getUrl() + provisioningApiUri, user.getAccessToken(),
                 requestBuilder.newProjectCreate(user, projectDTO, endpointDTO), String.class);
         requestId.put(user.getName(), uuid);
     }
@@ -298,7 +318,7 @@ public class ProjectServiceImpl implements ProjectService {
                     requestBuilder.newProjectAction(user, projectName, endpointDTO), String.class);
             requestId.put(user.getName(), uuid);
         } catch (Exception e) {
-            log.error("Can not terminate project due to: {}", e.getMessage(), e);
+            log.error("Can not post to {} project due to: {}", provisioningApiUri, e.getMessage(), e);
             projectDAO.updateStatus(projectName, ProjectDTO.Status.FAILED);
         }
     }
diff --git a/services/self-service/src/test/java/com/epam/datalab/backendapi/resources/ProjectResourceTest.java b/services/self-service/src/test/java/com/epam/datalab/backendapi/resources/ProjectResourceTest.java
index 47e0c4c..0dee149 100644
--- a/services/self-service/src/test/java/com/epam/datalab/backendapi/resources/ProjectResourceTest.java
+++ b/services/self-service/src/test/java/com/epam/datalab/backendapi/resources/ProjectResourceTest.java
@@ -106,6 +106,19 @@ public class ProjectResourceTest extends TestBase {
     }
 
     @Test
+    public void recreateProject() {
+        final Response response = resources.getJerseyTest()
+                .target("project/recreate")
+                .request()
+                .header("Authorization", "Bearer " + TOKEN)
+                .post(Entity.json(getProjectActionDTO()));
+
+        assertEquals(HttpStatus.SC_ACCEPTED, response.getStatus());
+        verify(projectService).recreate(getUserInfo(), ENDPOINT_NAME, PROJECT_NAME);
+        verifyNoMoreInteractions(projectService);
+    }
+
+    @Test
     public void startProject() {
         final Response response = resources.getJerseyTest()
                 .target("project/start")
@@ -114,7 +127,7 @@ public class ProjectResourceTest extends TestBase {
                 .post(Entity.json(getProjectActionDTO()));
 
         assertEquals(HttpStatus.SC_ACCEPTED, response.getStatus());
-        verify(projectService).start(getUserInfo(), Collections.singletonList("https://localhost:8083/"), PROJECT_NAME);
+        verify(projectService).start(getUserInfo(), Collections.singletonList(ENDPOINT_NAME), PROJECT_NAME);
         verifyNoMoreInteractions(projectService);
     }
 
@@ -127,7 +140,7 @@ public class ProjectResourceTest extends TestBase {
                 .post(Entity.json(getProjectActionDTO()));
 
         assertEquals(HttpStatus.SC_ACCEPTED, response.getStatus());
-        verify(projectService).stopWithResources(getUserInfo(), Collections.singletonList("https://localhost:8083/"), PROJECT_NAME);
+        verify(projectService).stopWithResources(getUserInfo(), Collections.singletonList(ENDPOINT_NAME), PROJECT_NAME);
         verifyNoMoreInteractions(projectService);
     }
 
@@ -275,7 +288,7 @@ public class ProjectResourceTest extends TestBase {
     }
 
     private ProjectActionFormDTO getProjectActionDTO() {
-        return new ProjectActionFormDTO(PROJECT_NAME, Collections.singletonList("https://localhost:8083/"));
+        return new ProjectActionFormDTO(PROJECT_NAME, Collections.singletonList(ENDPOINT_NAME));
     }
 
     private UpdateProjectDTO prepareUpdateProjectDTO() {
@@ -283,7 +296,7 @@ public class ProjectResourceTest extends TestBase {
     }
 
     private ProjectActionFormDTO prepareProjectActionFormDTO() {
-        return new ProjectActionFormDTO(PROJECT_NAME, Collections.singletonList("https://localhost:8083/"));
+        return new ProjectActionFormDTO(PROJECT_NAME, Collections.singletonList(ENDPOINT_NAME));
     }
 
     private List<UpdateProjectBudgetDTO> prepareUpdateProjectBudgetDTOs() {
diff --git a/services/self-service/src/test/java/com/epam/datalab/backendapi/service/ProjectServiceImplTest.java b/services/self-service/src/test/java/com/epam/datalab/backendapi/service/ProjectServiceImplTest.java
index c52abf7..d7752d7 100644
--- a/services/self-service/src/test/java/com/epam/datalab/backendapi/service/ProjectServiceImplTest.java
+++ b/services/self-service/src/test/java/com/epam/datalab/backendapi/service/ProjectServiceImplTest.java
@@ -64,8 +64,10 @@ import static org.mockito.Mockito.when;
 
 @RunWith(MockitoJUnitRunner.class)
 public class ProjectServiceImplTest extends TestBase {
+
     private static final String CREATE_PRJ_API = "infrastructure/project/create";
-    private static final String TERMINATE_PRJ_API = "infrastructure/project/terminate";
+	private static final String RECREATE_PRJ_API = "infrastructure/project/recreate";
+	private static final String TERMINATE_PRJ_API = "infrastructure/project/terminate";
     private static final String START_PRJ_API = "infrastructure/project/start";
     private static final String STOP_PRJ_API = "infrastructure/project/stop";
 
@@ -172,24 +174,43 @@ public class ProjectServiceImplTest extends TestBase {
 
     @Test(expected = ResourceConflictException.class)
     public void createWithException() {
-        when(projectDAO.get(anyString())).thenReturn(Optional.of(getProjectCreatingDTO()));
+	    when(projectDAO.get(anyString())).thenReturn(Optional.of(getProjectCreatingDTO()));
 
-        ProjectDTO projectDTO = getProjectCreatingDTO();
-        projectService.create(getUserInfo(), projectDTO, projectDTO.getName());
+	    ProjectDTO projectDTO = getProjectCreatingDTO();
+	    projectService.create(getUserInfo(), projectDTO, projectDTO.getName());
 
-        verify(projectDAO).get(NAME1);
-        verifyNoMoreInteractions(projectDAO);
+	    verify(projectDAO).get(NAME1);
+	    verifyNoMoreInteractions(projectDAO);
     }
 
-    @Test
-    public void get() {
-        ProjectDTO projectMock = getProjectCreatingDTO();
-        when(projectDAO.get(anyString())).thenReturn(Optional.of(projectMock));
-
-        ProjectDTO project = projectService.get(NAME1);
-
-        assertEquals(projectMock, project);
-        verify(projectDAO).get(NAME1);
+	@Test
+	public void recreate() {
+		ProjectDTO projectDTO = getProjectCreatingDTO();
+		when(projectDAO.get(anyString())).thenReturn(Optional.of(projectDTO));
+		when(endpointService.get(anyString())).thenReturn(getEndpointDTO());
+		when(provisioningService.post(anyString(), anyString(), any(), any())).thenReturn(UUID);
+		when(requestBuilder.newProjectCreate(any(UserInfo.class), any(ProjectDTO.class), any(EndpointDTO.class))).thenReturn(newProjectCreate());
+
+		projectService.recreate(getUserInfo(), ENDPOINT_NAME, projectDTO.getName());
+
+		verify(projectDAO).get(NAME1);
+		verify(projectDAO).updateEdgeStatus(NAME1, ENDPOINT_NAME, UserInstanceStatus.CREATING);
+		verify(endpointService).get(ENDPOINT_NAME);
+		verify(requestBuilder).newProjectCreate(getUserInfo(), projectDTO, getEndpointDTO());
+		verify(provisioningService).post(ENDPOINT_URL + RECREATE_PRJ_API, TOKEN, newProjectCreate(), String.class);
+		verify(requestId).put(USER.toLowerCase(), UUID);
+		verifyNoMoreInteractions(projectDAO, endpointService, provisioningService, requestBuilder);
+	}
+
+	@Test
+	public void get() {
+		ProjectDTO projectMock = getProjectCreatingDTO();
+		when(projectDAO.get(anyString())).thenReturn(Optional.of(projectMock));
+
+		ProjectDTO project = projectService.get(NAME1);
+
+		assertEquals(projectMock, project);
+		verify(projectDAO).get(NAME1);
         verifyNoMoreInteractions(projectDAO);
     }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org