diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/main/java/org/apache/hadoop/yarn/service/webapp/ApiServer.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/main/java/org/apache/hadoop/yarn/service/webapp/ApiServer.java index 9537e42a8e9..40e889b526d 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/main/java/org/apache/hadoop/yarn/service/webapp/ApiServer.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/main/java/org/apache/hadoop/yarn/service/webapp/ApiServer.java @@ -60,6 +60,8 @@ import static org.apache.hadoop.yarn.service.api.records.ServiceState.ACCEPTED; import static org.apache.hadoop.yarn.service.api.records.ServiceState.CANCEL_UPGRADING; import static org.apache.hadoop.yarn.service.conf.RestApiConstants.*; +import static org.apache.hadoop.yarn.service.conf.YarnServiceConf.DEFAULT_DESTROY_FORCEFULLY_ENABLED; +import static org.apache.hadoop.yarn.service.conf.YarnServiceConf.DESTROY_FORCEFULLY_ENABLED; import static org.apache.hadoop.yarn.service.exceptions.LauncherExitCodes.*; /** @@ -76,6 +78,8 @@ public ApiServer() { @Inject public ApiServer(Configuration conf) { super(); + destroyForcefully = conf.getBoolean(DESTROY_FORCEFULLY_ENABLED, + DEFAULT_DESTROY_FORCEFULLY_ENABLED); } private static final Logger LOG = @@ -83,6 +87,8 @@ public ApiServer(Configuration conf) { private static Configuration YARN_CONFIG = new YarnConfiguration(); private ServiceClient serviceClientUnitTest; private boolean unitTest = false; + private boolean destroyForcefully = YARN_CONFIG.getBoolean( + DESTROY_FORCEFULLY_ENABLED, DEFAULT_DESTROY_FORCEFULLY_ENABLED); static { init(); @@ -226,6 +232,15 @@ public Response deleteService(@Context HttpServletRequest request, UserGroupInformation ugi = getProxyUser(request); LOG.info("DELETE: deleteService for appName = {} user = {}", appName, ugi); + if (!destroyForcefully) { + Service app = getServiceFromClient(ugi, appName); + if (app.getState() != ServiceState.STOPPED && + app.getState() != ServiceState.FAILED) { + LOG.info("Cannot be deleted. {} state is {}", appName, app.getState().name()); + return formatResponse(Status.BAD_REQUEST, "Running service cannot be deleted"); + } + } + return stopService(appName, true, ugi); } catch (AccessControlException e) { return formatResponse(Status.FORBIDDEN, e.getMessage()); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/test/java/org/apache/hadoop/yarn/service/ServiceClientTest.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/test/java/org/apache/hadoop/yarn/service/ServiceClientTest.java index 89366b43889..e00bf026414 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/test/java/org/apache/hadoop/yarn/service/ServiceClientTest.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/test/java/org/apache/hadoop/yarn/service/ServiceClientTest.java @@ -27,6 +27,7 @@ import org.apache.hadoop.yarn.service.api.records.ContainerState; import org.apache.hadoop.yarn.service.api.records.Resource; import org.apache.hadoop.yarn.service.api.records.Service; +import org.apache.hadoop.yarn.service.api.records.ServiceState; import org.apache.hadoop.yarn.service.client.ServiceClient; import org.apache.hadoop.yarn.service.utils.ServiceApiUtil; import org.apache.hadoop.yarn.service.utils.SliderFileSystem; @@ -97,6 +98,10 @@ public ApplicationId actionCreate(Service service) throws IOException { public Service getStatus(String appName) throws FileNotFoundException { if ("jenkins".equals(appName)) { return goodServiceStatus; + } else if ("jenkins-error-cleaning-registry".equals(appName) || + "jenkins-already-stopped".equals(appName) || + "no-jenkins".equals(appName)) { + return buildStoppedService(appName); } else { throw new FileNotFoundException("Service " + appName + " not found"); } @@ -184,6 +189,7 @@ static Service buildGoodService() { Service service = new Service(); service.setName("jenkins"); service.setVersion("v1"); + service.setState(ServiceState.STABLE); Artifact artifact = new Artifact(); artifact.setType(Artifact.TypeEnum.DOCKER); artifact.setId("jenkins:latest"); @@ -223,4 +229,11 @@ public synchronized ApplicationId getAppId(String serviceName) throws IOException, YarnException { return serviceAppId.get(serviceName); } + + static Service buildStoppedService(String appName) { + Service service = new Service(); + service.setName(appName); + service.setState(ServiceState.STOPPED); + return service; + } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/test/java/org/apache/hadoop/yarn/service/TestApiServer.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/test/java/org/apache/hadoop/yarn/service/TestApiServer.java index 733b9bcffaf..e189dbd431a 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/test/java/org/apache/hadoop/yarn/service/TestApiServer.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/test/java/org/apache/hadoop/yarn/service/TestApiServer.java @@ -238,6 +238,26 @@ public void testDeleteStoppedService() { Response.status(Status.OK).build().getStatus(), actual.getStatus()); } + @Test + public void testDeleteServiceWhenDisabledDestroyForcefully() { + Configuration conf = new Configuration(); + conf.set("yarn.service.destroy-forcefully.enabled", "false"); + ApiServer apiServer = new ApiServer(conf); + apiServer.setServiceClient(mockServerClient); + + final Response actualForRunning = apiServer.deleteService(request, + "jenkins"); + assertEquals("Delete service is ", + Response.status(Status.BAD_REQUEST).build().getStatus(), + actualForRunning.getStatus()); + + final Response actualForStopped = apiServer.deleteService(request, + "jenkins-already-stopped"); + assertEquals("Delete service is ", + Response.status(Status.OK).build().getStatus(), + actualForStopped.getStatus()); + } + @Test public void testDecreaseContainerAndStop() { Service service = new Service(); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/conf/YarnServiceConf.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/conf/YarnServiceConf.java index 13ed1aaca97..b69222d957f 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/conf/YarnServiceConf.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/conf/YarnServiceConf.java @@ -182,6 +182,14 @@ public static final long DEFAULT_CONTAINER_HEALTH_THRESHOLD_INIT_DELAY_SEC = DEFAULT_CONTAINER_HEALTH_THRESHOLD_WINDOW_SEC; + /** + * By default destroy is allowed even if running app. + */ + public static final String DESTROY_FORCEFULLY_ENABLED = + "yarn.service.destroy-forcefully.enabled"; + public static final boolean DEFAULT_DESTROY_FORCEFULLY_ENABLED = true; + + /** * Get long value for the property. First get from the userConf, if not * present, get from systemConf.