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/client/ApiServiceClient.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/client/ApiServiceClient.java index ca6cc508b27..1ebe37a9430 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/client/ApiServiceClient.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/client/ApiServiceClient.java @@ -723,4 +723,31 @@ public String getInstances(String appName, List components, } return null; } + + @Override + public int actionDecommissionInstances(String appName, List componentInstances) throws IOException, YarnException { + int result = EXIT_SUCCESS; + try { + Service service = new Service(); + service.setName(appName); + for (String instance : componentInstances) { + String componentName = ServiceApiUtil.parseComponentName(instance); + Component component = service.getComponent(componentName); + if (component == null) { + component = new Component(); + component.setName(componentName); + service.addComponent(component); + } + component.addDecommissionedInstance(instance); + } + String buffer = jsonSerDeser.toJson(service); + ClientResponse response = getApiClient(getServicePath(appName)) + .put(ClientResponse.class, buffer); + result = processResponse(response); + } catch (Exception e) { + LOG.error("Fail to decommission instance: ", e); + result = EXIT_EXCEPTION_THROWN; + } + return result; + } } 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 cd6f0d79e2d..2625b3fe1d4 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 @@ -450,6 +450,12 @@ public Response updateService(@Context HttpServletRequest request, && updateServiceData.getLifetime() > 0) { return updateLifetime(appName, updateServiceData, ugi); } + + for (Component c : updateServiceData.getComponents()) { + if (c.getDecommissionedInstances().size() > 0) { + return decommissionInstances(updateServiceData, ugi); + } + } } catch (UndeclaredThrowableException e) { return formatResponse(Status.BAD_REQUEST, e.getCause().getMessage()); @@ -766,6 +772,40 @@ private int invokeContainersUpgrade(UserGroupInformation ugi, }); } + private Response decommissionInstances(Service service, UserGroupInformation + ugi) throws IOException, InterruptedException { + String appName = service.getName(); + Response response = Response.status(Status.BAD_REQUEST).build(); + + List instances = new ArrayList<>(); + for (Component c : service.getComponents()) { + instances.addAll(c.getDecommissionedInstances()); + } + Integer result = ugi.doAs(new PrivilegedExceptionAction() { + @Override + public Integer run() throws YarnException, IOException { + int result = 0; + ServiceClient sc = new ServiceClient(); + sc.init(YARN_CONFIG); + sc.start(); + result = sc + .actionDecommissionInstances(appName, instances); + sc.close(); + return Integer.valueOf(result); + } + }); + if (result == EXIT_SUCCESS) { + String message = "Service " + appName + " has successfully " + + "decommissioned instances."; + LOG.info(message); + ServiceStatus status = new ServiceStatus(); + status.setDiagnostics(message); + status.setState(ServiceState.ACCEPTED); + response = formatResponse(Status.ACCEPTED, status); + } + return response; + } + private Service getServiceFromClient(UserGroupInformation ugi, String serviceName) throws IOException, InterruptedException { diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/main/resources/definition/YARN-Simplified-V1-API-Layer-For-Services.yaml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/main/resources/definition/YARN-Simplified-V1-API-Layer-For-Services.yaml index d90ae06e04e..c983f51bddb 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/main/resources/definition/YARN-Simplified-V1-API-Layer-For-Services.yaml +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/main/resources/definition/YARN-Simplified-V1-API-Layer-For-Services.yaml @@ -405,6 +405,11 @@ definitions: type: integer format: int64 description: Number of containers for this component (optional). If not specified, the service level global number_of_containers takes effect. + decommissioned_instances: + type: array + items: + type: string + description: List of decommissioned component instances. containers: type: array description: Containers of a started component. Specifying a value for this attribute for the POST payload raises a validation error. This blob is available only in the GET response of a started 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/ClientAMProtocol.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/ClientAMProtocol.java index 652a314abef..99b86d5c792 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/ClientAMProtocol.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/ClientAMProtocol.java @@ -21,6 +21,8 @@ import org.apache.hadoop.yarn.exceptions.YarnException; import org.apache.hadoop.yarn.proto.ClientAMProtocol.CompInstancesUpgradeResponseProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.CompInstancesUpgradeRequestProto; +import org.apache.hadoop.yarn.proto.ClientAMProtocol.DecommissionCompInstancesRequestProto; +import org.apache.hadoop.yarn.proto.ClientAMProtocol.DecommissionCompInstancesResponseProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.FlexComponentsRequestProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.FlexComponentsResponseProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.GetCompInstancesRequestProto; @@ -60,4 +62,8 @@ CompInstancesUpgradeResponseProto upgrade( GetCompInstancesResponseProto getCompInstances( GetCompInstancesRequestProto request) throws IOException, YarnException; + + DecommissionCompInstancesResponseProto decommissionCompInstances( + DecommissionCompInstancesRequestProto request) throws IOException, + YarnException; } 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/ClientAMService.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/ClientAMService.java index 2ef8f7ee7b7..6c519e553c8 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/ClientAMService.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/ClientAMService.java @@ -24,6 +24,7 @@ import org.apache.hadoop.net.NetUtils; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.service.AbstractService; +import org.apache.hadoop.service.Service; import org.apache.hadoop.util.ExitUtil; import org.apache.hadoop.yarn.api.ApplicationConstants; import org.apache.hadoop.yarn.api.records.ContainerId; @@ -33,6 +34,8 @@ import org.apache.hadoop.yarn.proto.ClientAMProtocol.CompInstancesUpgradeRequestProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.CompInstancesUpgradeResponseProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.ComponentCountProto; +import org.apache.hadoop.yarn.proto.ClientAMProtocol.DecommissionCompInstancesRequestProto; +import org.apache.hadoop.yarn.proto.ClientAMProtocol.DecommissionCompInstancesResponseProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.FlexComponentsRequestProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.FlexComponentsResponseProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.GetCompInstancesRequestProto; @@ -58,6 +61,7 @@ import java.net.InetSocketAddress; import java.util.List; +import static org.apache.hadoop.yarn.service.component.ComponentEventType.DECOMMISSION_INSTANCE; import static org.apache.hadoop.yarn.service.component.ComponentEventType.FLEX; public class ClientAMService extends AbstractService @@ -208,4 +212,21 @@ public GetCompInstancesResponseProto getCompInstances( ServiceApiUtil.CONTAINER_JSON_SERDE.toJson(containers.toArray( new Container[containers.size()]))).build(); } + + @Override + public DecommissionCompInstancesResponseProto decommissionCompInstances( + DecommissionCompInstancesRequestProto request) + throws IOException, YarnException { + if (!request.getCompInstancesList().isEmpty()) { + for (String instance : request.getCompInstancesList()) { + String componentName = ServiceApiUtil.parseComponentName(instance); + ComponentEvent event = new ComponentEvent(componentName, + DECOMMISSION_INSTANCE).setInstanceName(instance); + context.scheduler.getDispatcher().getEventHandler().handle(event); + LOG.info("Decommissioning component {} instance {}", componentName, + instance); + } + } + return DecommissionCompInstancesResponseProto.newBuilder().build(); + } } 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/api/records/Component.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/api/records/Component.java index 0481123c197..9a117cd2c8e 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/api/records/Component.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/api/records/Component.java @@ -80,6 +80,10 @@ @XmlElement(name = "number_of_containers") private Long numberOfContainers = null; + @JsonProperty("decommissioned_instances") + @XmlElement(name = "decommissioned_instances") + private List decommissionedInstances = new ArrayList<>(); + @JsonProperty("run_privileged_container") @XmlElement(name = "run_privileged_container") private Boolean runPrivilegedContainer = false; @@ -296,6 +300,28 @@ public void setNumberOfContainers(Long numberOfContainers) { this.numberOfContainers = numberOfContainers; } + /** + * A list of decommissioned component instances. + **/ + public Component decommissionedInstances(List + decommissionedInstances) { + this.decommissionedInstances = decommissionedInstances; + return this; + } + + @ApiModelProperty(example = "null", value = "A list of decommissioned component instances.") + public List getDecommissionedInstances() { + return decommissionedInstances; + } + + public void setDecommissionedInstances(List decommissionedInstances) { + this.decommissionedInstances = decommissionedInstances; + } + + public void addDecommissionedInstance(String componentInstanceName) { + this.decommissionedInstances.add(componentInstanceName); + } + @ApiModelProperty(example = "null", value = "Containers of a started component. Specifying a value for this attribute for the POST payload raises a validation error. This blob is available only in the GET response of a started service.") public List getContainers() { return containers; 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/client/ServiceClient.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/client/ServiceClient.java index a27ed87aa63..a96c22b787a 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/client/ServiceClient.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/client/ServiceClient.java @@ -57,6 +57,7 @@ import org.apache.hadoop.yarn.ipc.YarnRPC; import org.apache.hadoop.yarn.proto.ClientAMProtocol.CompInstancesUpgradeRequestProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.ComponentCountProto; +import org.apache.hadoop.yarn.proto.ClientAMProtocol.DecommissionCompInstancesRequestProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.FlexComponentsRequestProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.GetCompInstancesRequestProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.GetCompInstancesResponseProto; @@ -353,6 +354,61 @@ public int actionUpgradeComponents(String appName, } @Override + public int actionDecommissionInstances(String appName, + List componentInstances) throws IOException, YarnException { + checkAppExistOnHdfs(appName); + Service persistedService = ServiceApiUtil.loadService(fs, appName); + if (StringUtils.isEmpty(persistedService.getId())) { + throw new YarnException( + persistedService.getName() + " appId is null, may be not submitted " + + "to YARN yet"); + } + cachedAppInfo.put(persistedService.getName(), new AppInfo( + ApplicationId.fromString(persistedService.getId()), persistedService + .getKerberosPrincipal().getPrincipalName())); + + for (String instance : componentInstances) { + String componentName = ServiceApiUtil.parseComponentName( + ServiceApiUtil.parseAndValidateComponentInstanceName(instance, + appName, getConfig())); + Component component = persistedService.getComponent(componentName); + if (component == null) { + throw new IllegalArgumentException(instance + " does not exist !"); + } + if (!component.getDecommissionedInstances().contains(instance)) { + component.addDecommissionedInstance(instance); + component.setNumberOfContainers(Math.max(0, component + .getNumberOfContainers() - 1)); + } + } + ServiceApiUtil.writeAppDefinition(fs, persistedService); + + ApplicationReport appReport = + yarnClient.getApplicationReport(ApplicationId.fromString( + persistedService.getId())); + if (appReport.getYarnApplicationState() != RUNNING) { + String message = + persistedService.getName() + " is at " + appReport + .getYarnApplicationState() + " state, decommission can only be " + + "invoked when service is running"; + LOG.error(message); + throw new YarnException(message); + } + + if (StringUtils.isEmpty(appReport.getHost())) { + throw new YarnException(persistedService.getName() + " AM hostname is " + + "empty"); + } + ClientAMProtocol proxy = + createAMProxy(persistedService.getName(), appReport); + DecommissionCompInstancesRequestProto.Builder requestBuilder = + DecommissionCompInstancesRequestProto.newBuilder(); + requestBuilder.addAllCompInstances(componentInstances); + proxy.decommissionCompInstances(requestBuilder.build()); + return EXIT_SUCCESS; + } + + @Override public int actionCleanUp(String appName, String userName) throws IOException, YarnException { if (cleanUpRegistry(appName, userName)) { @@ -555,9 +611,7 @@ private long parseNumberOfContainers(Component component, String newNumber) { throw new YarnException("Components " + componentCounts.keySet() + " do not exist in app definition."); } - jsonSerDeser - .save(fs.getFileSystem(), ServiceApiUtil.getServiceJsonPath(fs, serviceName), - persistedService, true); + ServiceApiUtil.writeAppDefinition(fs, persistedService); ApplicationId appId = getAppId(serviceName); if (appId == null) { 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/component/Component.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/component/Component.java index acf3404fe93..a8699e1f47b 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/component/Component.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/component/Component.java @@ -59,6 +59,7 @@ import org.apache.hadoop.yarn.service.monitor.probe.Probe; import org.apache.hadoop.yarn.service.containerlaunch.ContainerLaunchService; import org.apache.hadoop.yarn.service.provider.ProviderUtils; +import org.apache.hadoop.yarn.service.utils.ServiceApiUtil; import org.apache.hadoop.yarn.service.utils.ServiceUtils; import org.apache.hadoop.yarn.state.InvalidStateTransitionException; import org.apache.hadoop.yarn.state.MultipleArcTransition; @@ -143,6 +144,9 @@ // container recovered on AM restart .addTransition(INIT, INIT, CONTAINER_RECOVERED, new ContainerRecoveredTransition()) + // instance decommissioned + .addTransition(INIT, INIT, DECOMMISSION_INSTANCE, + new DecommissionInstanceTransition()) // container recovered in AM heartbeat .addTransition(FLEXING, FLEXING, CONTAINER_RECOVERED, @@ -160,6 +164,9 @@ // Flex while previous flex is still in progress .addTransition(FLEXING, EnumSet.of(FLEXING, STABLE), FLEX, new FlexComponentTransition()) + // instance decommissioned + .addTransition(FLEXING, FLEXING, DECOMMISSION_INSTANCE, + new DecommissionInstanceTransition()) // container failed while stable .addTransition(STABLE, FLEXING, CONTAINER_COMPLETED, @@ -172,6 +179,10 @@ // For flex down, go to STABLE state .addTransition(STABLE, EnumSet.of(STABLE, FLEXING), FLEX, new FlexComponentTransition()) + // instance decommissioned + .addTransition(STABLE, STABLE, DECOMMISSION_INSTANCE, + new DecommissionInstanceTransition()) + // upgrade component .addTransition(STABLE, UPGRADING, ComponentEventType.UPGRADE, new ComponentNeedsUpgradeTransition()) //Upgrade while previous upgrade is still in progress @@ -185,6 +196,9 @@ new CheckStableTransition()) .addTransition(UPGRADING, FLEXING, CONTAINER_COMPLETED, new ContainerCompletedTransition()) + // instance decommissioned + .addTransition(UPGRADING, UPGRADING, DECOMMISSION_INSTANCE, + new DecommissionInstanceTransition()) .installTopology(); public Component( @@ -231,6 +245,11 @@ private void createOneCompInstance() { ComponentInstanceId id = new ComponentInstanceId(instanceIdCounter.getAndIncrement(), componentSpec.getName()); + while (componentSpec.getDecommissionedInstances().contains(id + .getCompInstanceName())) { + id = new ComponentInstanceId(instanceIdCounter.getAndIncrement(), + componentSpec.getName()); + } ComponentInstance instance = new ComponentInstance(this, id); compInstances.put(instance.getCompInstanceName(), instance); pendingInstances.add(instance); @@ -367,6 +386,38 @@ public ComponentState transition(Component component, } } + private static class DecommissionInstanceTransition extends BaseTransition { + @Override + public void transition(Component component, ComponentEvent event) { + String instanceName = event.getInstanceName(); + String hostnameSuffix = component.getHostnameSuffix(); + if (instanceName.endsWith(hostnameSuffix)) { + instanceName = instanceName.substring(0, + instanceName.length() - hostnameSuffix.length()); + } + if (component.getComponentSpec().getDecommissionedInstances() + .contains(instanceName)) { + LOG.info("Instance {} already decommissioned", instanceName); + return; + } + component.getComponentSpec().addDecommissionedInstance(instanceName); + ComponentInstance instance = component.getComponentInstance(instanceName); + if (instance == null) { + LOG.info("Instance was null for decommissioned instance {}", + instanceName); + return; + } + // remove the instance + component.compInstances.remove(instance.getCompInstanceName()); + component.pendingInstances.remove(instance); + component.scheduler.getServiceMetrics().containersDesired.decr(); + component.componentMetrics.containersDesired.decr(); + component.getComponentSpec().setNumberOfContainers(component + .getComponentSpec().getNumberOfContainers() - 1); + instance.destroy(); + } + } + private static class ContainerAllocatedTransition extends BaseTransition { @Override public void transition(Component component, ComponentEvent event) { @@ -787,10 +838,8 @@ public void requestContainers(long count) { private void setDesiredContainers(int n) { int delta = n - scheduler.getServiceMetrics().containersDesired.value(); - if (delta > 0) { + if (delta != 0) { scheduler.getServiceMetrics().containersDesired.incr(delta); - } else { - scheduler.getServiceMetrics().containersDesired.decr(delta); } componentMetrics.containersDesired.set(n); } @@ -1093,4 +1142,9 @@ public ComponentRestartPolicy getRestartPolicyHandler() { RestartPolicyEnum restartPolicyEnum = getComponentSpec().getRestartPolicy(); return getRestartPolicyHandler(restartPolicyEnum); } + + public String getHostnameSuffix() { + return ServiceApiUtil.getHostnameSuffix(context.service.getName(), + scheduler.getConfig()); + } } 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/component/ComponentEvent.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/component/ComponentEvent.java index 643961d505a..8356dbb4636 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/component/ComponentEvent.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/component/ComponentEvent.java @@ -31,6 +31,7 @@ private final ComponentEventType type; private Container container; private ComponentInstance instance; + private String instanceName; private ContainerStatus status; private ContainerId containerId; private org.apache.hadoop.yarn.service.api.records.Component targetSpec; @@ -87,6 +88,15 @@ public ComponentEvent setInstance(ComponentInstance instance) { return this; } + public String getInstanceName() { + return instanceName; + } + + public ComponentEvent setInstanceName(String instanceName) { + this.instanceName = instanceName; + return this; + } + public ContainerStatus getStatus() { return status; } 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/component/ComponentEventType.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/component/ComponentEventType.java index 44d781f2257..2778b43207b 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/component/ComponentEventType.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/component/ComponentEventType.java @@ -25,5 +25,6 @@ CONTAINER_STARTED, CONTAINER_COMPLETED, UPGRADE, - CHECK_STABLE + CHECK_STABLE, + DECOMMISSION_INSTANCE } 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/component/instance/ComponentInstance.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/component/instance/ComponentInstance.java index afd8c671fa4..3b5717df8e3 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/component/instance/ComponentInstance.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/component/instance/ComponentInstance.java @@ -21,9 +21,7 @@ import com.google.common.annotations.VisibleForTesting; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; -import org.apache.hadoop.registry.client.api.RegistryConstants; import org.apache.hadoop.registry.client.binding.RegistryPathUtils; -import org.apache.hadoop.registry.client.binding.RegistryUtils; import org.apache.hadoop.registry.client.types.ServiceRecord; import org.apache.hadoop.registry.client.types.yarn.PersistencePolicies; import org.apache.hadoop.util.StringUtils; @@ -748,21 +746,7 @@ private void cancelContainerStatusRetriever() { } public String getHostname() { - String domain = getComponent().getScheduler().getConfig() - .get(RegistryConstants.KEY_DNS_DOMAIN); - String hostname; - if (domain == null || domain.isEmpty()) { - hostname = MessageFormat - .format("{0}.{1}.{2}", getCompInstanceName(), - getComponent().getContext().service.getName(), - RegistryUtils.currentUser()); - } else { - hostname = MessageFormat - .format("{0}.{1}.{2}.{3}", getCompInstanceName(), - getComponent().getContext().service.getName(), - RegistryUtils.currentUser(), domain); - } - return hostname; + return getCompInstanceName() + getComponent().getHostnameSuffix(); } @Override 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/impl/pb/client/ClientAMProtocolPBClientImpl.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/impl/pb/client/ClientAMProtocolPBClientImpl.java index 49ecd2e425f..02bfaccf40d 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/impl/pb/client/ClientAMProtocolPBClientImpl.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/impl/pb/client/ClientAMProtocolPBClientImpl.java @@ -32,6 +32,8 @@ import org.apache.hadoop.yarn.proto.ClientAMProtocol.CompInstancesUpgradeResponseProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.CompInstancesUpgradeRequestProto; +import org.apache.hadoop.yarn.proto.ClientAMProtocol.DecommissionCompInstancesRequestProto; +import org.apache.hadoop.yarn.proto.ClientAMProtocol.DecommissionCompInstancesResponseProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.FlexComponentsRequestProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.FlexComponentsResponseProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.GetCompInstancesRequestProto; @@ -141,4 +143,16 @@ public GetCompInstancesResponseProto getCompInstances( } return null; } + + @Override + public DecommissionCompInstancesResponseProto decommissionCompInstances( + DecommissionCompInstancesRequestProto request) + throws IOException, YarnException { + try { + return proxy.decommissionCompInstances(null, request); + } catch (ServiceException e) { + RPCUtil.unwrapAndThrowException(e); + } + return null; + } } 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/impl/pb/service/ClientAMProtocolPBServiceImpl.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/impl/pb/service/ClientAMProtocolPBServiceImpl.java index eab3f9fb959..566e67d49c6 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/impl/pb/service/ClientAMProtocolPBServiceImpl.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/impl/pb/service/ClientAMProtocolPBServiceImpl.java @@ -23,6 +23,8 @@ import org.apache.hadoop.yarn.exceptions.YarnException; import org.apache.hadoop.yarn.proto.ClientAMProtocol.CompInstancesUpgradeRequestProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.CompInstancesUpgradeResponseProto; +import org.apache.hadoop.yarn.proto.ClientAMProtocol.DecommissionCompInstancesRequestProto; +import org.apache.hadoop.yarn.proto.ClientAMProtocol.DecommissionCompInstancesResponseProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.FlexComponentsRequestProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.FlexComponentsResponseProto; import org.apache.hadoop.yarn.proto.ClientAMProtocol.GetCompInstancesRequestProto; @@ -116,4 +118,15 @@ public GetCompInstancesResponseProto getCompInstances( throw new ServiceException(e); } } + + @Override + public DecommissionCompInstancesResponseProto decommissionCompInstances( + RpcController controller, DecommissionCompInstancesRequestProto + request) throws ServiceException { + try { + return real.decommissionCompInstances(request); + } catch (IOException | YarnException e) { + throw new ServiceException(e); + } + } } 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/utils/ServiceApiUtil.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/utils/ServiceApiUtil.java index b588e88ae7f..ed743de976b 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/utils/ServiceApiUtil.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/utils/ServiceApiUtil.java @@ -55,6 +55,7 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -565,6 +566,13 @@ public static Path writeAppDefinition(SliderFileSystem fs, Path appDir, return appJson; } + public static Path writeAppDefinition(SliderFileSystem fs, Service service) + throws IOException { + Path appJson = getServiceJsonPath(fs, service.getName()); + jsonSerDeser.save(fs.getFileSystem(), appJson, service, true); + return appJson; + } + public static List getLiveContainers(Service service, List componentInstances) throws YarnException { @@ -664,9 +672,53 @@ public static void validateInstancesUpgrade(List return containerNeedUpgrade; } - private static String parseComponentName(String componentInstanceName) + public static String getHostnameSuffix(String serviceName, org.apache + .hadoop.conf.Configuration conf) { + String domain = conf.get(RegistryConstants.KEY_DNS_DOMAIN); + String hostnameSuffix; + if (domain == null || domain.isEmpty()) { + hostnameSuffix = MessageFormat + .format(".{0}.{1}", serviceName, RegistryUtils.currentUser()); + } else { + hostnameSuffix = MessageFormat + .format(".{0}.{1}.{2}", serviceName, + RegistryUtils.currentUser(), domain); + } + return hostnameSuffix; + } + + public static String parseAndValidateComponentInstanceName(String + instanceOrHostname, String serviceName, org.apache.hadoop.conf + .Configuration conf) throws IllegalArgumentException { + int idx = instanceOrHostname.indexOf('.'); + String hostnameSuffix = getHostnameSuffix(serviceName, conf); + if (idx != -1) { + if (!instanceOrHostname.endsWith(hostnameSuffix)) { + throw new IllegalArgumentException("Specified hostname " + + instanceOrHostname + " does not have the expected format " + + "componentInstanceName" + + hostnameSuffix); + } + instanceOrHostname = instanceOrHostname.substring(0, instanceOrHostname + .length() - hostnameSuffix.length()); + } + idx = instanceOrHostname.indexOf('.'); + if (idx != -1) { + throw new IllegalArgumentException("Specified hostname " + + instanceOrHostname + " does not have the expected format " + + "componentInstanceName" + + hostnameSuffix); + } + return instanceOrHostname; + } + + public static String parseComponentName(String componentInstanceName) throws YarnException { - int idx = componentInstanceName.lastIndexOf('-'); + int idx = componentInstanceName.indexOf('.'); + if (idx != -1) { + componentInstanceName = componentInstanceName.substring(0, idx); + } + idx = componentInstanceName.lastIndexOf('-'); if (idx == -1) { throw new YarnException("Invalid component instance (" + componentInstanceName + ") name."); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/proto/ClientAMProtocol.proto b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/proto/ClientAMProtocol.proto index 169f765b8a8..e0083f62142 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/proto/ClientAMProtocol.proto +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/proto/ClientAMProtocol.proto @@ -34,6 +34,8 @@ service ClientAMProtocolService { (CompInstancesUpgradeResponseProto); rpc getCompInstances(GetCompInstancesRequestProto) returns (GetCompInstancesResponseProto); + rpc decommissionCompInstances(DecommissionCompInstancesRequestProto) + returns (DecommissionCompInstancesResponseProto); } message FlexComponentsRequestProto { @@ -94,4 +96,11 @@ message GetCompInstancesRequestProto { message GetCompInstancesResponseProto { optional string compInstances = 1; -} \ No newline at end of file +} + +message DecommissionCompInstancesRequestProto { + repeated string compInstances = 1; +} + +message DecommissionCompInstancesResponseProto { +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/ServiceTestUtils.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/ServiceTestUtils.java index 6b49ab07c1a..8e22b30a374 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/ServiceTestUtils.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/ServiceTestUtils.java @@ -246,7 +246,7 @@ protected void setupInternal(int numNodeManager) if (yarnCluster == null) { yarnCluster = - new MiniYARNCluster(TestYarnNativeServices.class.getSimpleName(), 1, + new MiniYARNCluster(this.getClass().getSimpleName(), 1, numNodeManager, 1, 1); yarnCluster.init(conf); yarnCluster.start(); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/component/TestComponentDecommissionInstances.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/component/TestComponentDecommissionInstances.java new file mode 100644 index 00000000000..fb443fc25ae --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/component/TestComponentDecommissionInstances.java @@ -0,0 +1,144 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.yarn.service.component; + +import org.apache.commons.io.FileUtils; +import org.apache.hadoop.registry.client.binding.RegistryUtils; +import org.apache.hadoop.test.GenericTestUtils; +import org.apache.hadoop.yarn.exceptions.YarnException; +import org.apache.hadoop.yarn.service.ServiceTestUtils; +import org.apache.hadoop.yarn.service.api.records.Component; +import org.apache.hadoop.yarn.service.api.records.Container; +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.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeoutException; + +public class TestComponentDecommissionInstances extends ServiceTestUtils { + private static final Logger LOG = + LoggerFactory.getLogger(TestComponentDecommissionInstances.class); + + private static final String APP_NAME = "test-decommission"; + private static final String COMPA = "compa"; + + @Rule + public TemporaryFolder tmpFolder = new TemporaryFolder(); + + @Before + public void setup() throws Exception { + File tmpYarnDir = new File("target", "tmp"); + FileUtils.deleteQuietly(tmpYarnDir); + } + + @After + public void tearDown() throws IOException { + shutdown(); + } + + @Test + public void testDecommissionInstances() throws Exception { + setupInternal(3); + ServiceClient client = createClient(getConf()); + Service exampleApp = new Service(); + exampleApp.setName(APP_NAME); + exampleApp.setVersion("v1"); + Component comp = createComponent(COMPA, 6L, "sleep 1000"); + exampleApp.addComponent(comp); + client.actionCreate(exampleApp); + waitForServiceToBeStable(client, exampleApp); + + checkInstances(client, COMPA + "-0", COMPA + "-1", COMPA + "-2", + COMPA + "-3", COMPA + "-4", COMPA + "-5"); + client.actionDecommissionInstances(APP_NAME, Arrays.asList(COMPA + "-1", + COMPA + "-5")); + waitForNumInstances(client, 4); + checkInstances(client, COMPA + "-0", COMPA + "-2", COMPA + "-3", + COMPA + "-4"); + + // Stop and start service + client.actionStop(APP_NAME); + waitForServiceToBeInState(client, exampleApp, ServiceState.STOPPED); + client.actionStart(APP_NAME); + waitForServiceToBeStable(client, exampleApp); + checkInstances(client, COMPA + "-0", COMPA + "-2", COMPA + "-3", + COMPA + "-4"); + + Map compCounts = new HashMap<>(); + compCounts.put(COMPA, "5"); + client.actionFlex(APP_NAME, compCounts); + waitForNumInstances(client, 5); + checkInstances(client, COMPA + "-0", COMPA + "-2", COMPA + "-3", + COMPA + "-4", COMPA + "-6"); + + client.actionDecommissionInstances(APP_NAME, Arrays.asList(COMPA + "-0." + + APP_NAME + "." + RegistryUtils.currentUser())); + waitForNumInstances(client, 4); + checkInstances(client, COMPA + "-2", COMPA + "-3", + COMPA + "-4", COMPA + "-6"); + } + + private static void waitForNumInstances(ServiceClient client, int + expectedInstances) throws TimeoutException, InterruptedException { + GenericTestUtils.waitFor(() -> { + try { + Service retrievedApp = client.getStatus(APP_NAME); + return retrievedApp.getComponent(COMPA).getContainers().size() == + expectedInstances && retrievedApp.getState() == ServiceState.STABLE; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + }, 2000, 200000); + } + + private static void checkInstances(ServiceClient client, String... instances) + throws IOException, YarnException { + Service service = client.getStatus(APP_NAME); + Component component = service.getComponent(COMPA); + Assert.assertEquals("Service state should be STABLE", ServiceState.STABLE, + service.getState()); + Assert.assertEquals(instances.length + " containers are expected to be " + + "running", instances.length, component.getContainers().size()); + Set existingInstances = new HashSet<>(); + for (Container cont : component.getContainers()) { + existingInstances.add(cont.getComponentInstanceName()); + } + Assert.assertEquals(instances.length + " instances are expected to be " + + "running", instances.length, existingInstances.size()); + for (String instance : instances) { + Assert.assertTrue("Expected instance did not exist " + instance, + existingInstances.contains(instance)); + } + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/ApplicationCLI.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/ApplicationCLI.java index a0e4e02b0a4..ce753eb09ee 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/ApplicationCLI.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/ApplicationCLI.java @@ -99,6 +99,7 @@ public static final String DESTROY_CMD = "destroy"; public static final String FLEX_CMD = "flex"; public static final String COMPONENT = "component"; + public static final String DECOMMISSION = "decommission"; public static final String ENABLE_FAST_LAUNCH = "enableFastLaunch"; public static final String UPGRADE_CMD = "upgrade"; public static final String UPGRADE_EXPRESS = "express"; @@ -698,6 +699,18 @@ public int run(String[] args) throws Exception { } return client.actionStart(appName); } + } else if (cliParser.hasOption(DECOMMISSION)) { + if (!cliParser.hasOption(COMPONENT_INSTS) || + hasAnyOtherCLIOptions(cliParser, opts, DECOMMISSION, COMPONENT_INSTS, + APP_TYPE_CMD)) { + printUsage(title, opts); + return exitCode; + } + String[] instances = cliParser.getOptionValues(COMPONENT_INSTS); + String[] appNameAndType = getAppNameAndType(cliParser, DECOMMISSION); + return AppAdminClient.createAppAdminClient(appNameAndType[1], getConf()) + .actionDecommissionInstances(appNameAndType[0], + Arrays.asList(instances)); } else { syserr.println("Invalid Command Usage : "); printUsage(title, opts); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/client/api/AppAdminClient.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/client/api/AppAdminClient.java index 232666db7d6..87dec6469d4 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/client/api/AppAdminClient.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/client/api/AppAdminClient.java @@ -300,4 +300,16 @@ public abstract String getInstances(String appName, @Unstable public abstract int actionUpgradeExpress(String appName, File fileName) throws IOException, YarnException; + + /** + * Decommission component instances of a long running service. + * + * @param appName the name of the application. + * @param componentInstances the name of the component instances. + */ + @Public + @Unstable + public abstract int actionDecommissionInstances(String appName, + List componentInstances) throws IOException, YarnException; + } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/yarn-service/YarnServiceAPI.md b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/yarn-service/YarnServiceAPI.md index 4bfa742c454..16ffe51b309 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/yarn-service/YarnServiceAPI.md +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/yarn-service/YarnServiceAPI.md @@ -228,6 +228,7 @@ One or more components of the service. If the service is HBase say, then the com |launch_command|The custom launch command of this component (optional for DOCKER component, required otherwise). When specified at the component level, it overrides the value specified at the global level (if any). If docker image supports ENTRYPOINT, launch_command is delimited by comma(,) instead of space.|false|string|| |resource|Resource of this component (optional). If not specified, the service level global resource takes effect.|false|Resource|| |number_of_containers|Number of containers for this component (optional). If not specified, the service level global number_of_containers takes effect.|false|integer (int64)|| +|decommissioned_instances|List of decommissioned component instances.|false|string array|| |containers|Containers of a started component. Specifying a value for this attribute for the POST payload raises a validation error. This blob is available only in the GET response of a started service.|false|Container array|| |run_privileged_container|Run all containers of this component in privileged mode (YARN-4262).|false|boolean|| |placement_policy|Advanced scheduling and placement policies for all containers of this component.|false|PlacementPolicy||