YARN-8734. Readiness check for remote service belonging to the same user. Contributed by Eric Yang

This commit is contained in:
Billie Rinaldi 2018-09-28 09:39:59 -07:00
parent e2113500df
commit d1c1dde309
8 changed files with 109 additions and 2 deletions

View File

@ -254,6 +254,11 @@ definitions:
docker_client_config: docker_client_config:
type: string type: string
description: URI of the file containing the docker client configuration (e.g. hdfs:///tmp/config.json). description: URI of the file containing the docker client configuration (e.g. hdfs:///tmp/config.json).
dependencies:
type: array
items:
type: string
description: An array of services which should be in STABLE state, before this service can be started.
ResourceInformation: ResourceInformation:
description: description:
ResourceInformation determines unit/value of resource types in addition to memory and vcores. It will be part of Resource object. ResourceInformation determines unit/value of resource types in addition to memory and vcores. It will be part of Resource object.

View File

@ -170,7 +170,8 @@ public State transition(ServiceManager serviceManager,
} else { } else {
serviceManager.setServiceState(ServiceState.UPGRADING); serviceManager.setServiceState(ServiceState.UPGRADING);
} }
ServiceApiUtil.checkServiceDependencySatisified(serviceManager
.getServiceSpec());
return State.UPGRADING; return State.UPGRADING;
} catch (Throwable e) { } catch (Throwable e) {
LOG.error("[SERVICE]: Upgrade to version {} failed", event.getVersion(), LOG.error("[SERVICE]: Upgrade to version {} failed", event.getVersion(),

View File

@ -389,6 +389,8 @@ public void serviceStart() throws Exception {
// Since AM has been started and registered, the service is in STARTED state // Since AM has been started and registered, the service is in STARTED state
app.setState(ServiceState.STARTED); app.setState(ServiceState.STARTED);
ServiceApiUtil.checkServiceDependencySatisified(context.service);
// recover components based on containers sent from RM // recover components based on containers sent from RM
recoverComponents(response); recoverComponents(response);

View File

@ -73,6 +73,7 @@ public class Service extends BaseResource {
private String version = null; private String version = null;
private String description = null; private String description = null;
private String dockerClientConfig = null; private String dockerClientConfig = null;
private List<String> dependencies = new ArrayList<String>();
/** /**
* A unique service name. * A unique service name.
@ -352,6 +353,18 @@ public void setQueue(String queue) {
this.queue = queue; this.queue = queue;
} }
@ApiModelProperty(example = "null", value = "A list of dependent services.")
@XmlElement(name = "dependencies")
@JsonProperty("dependencies")
public List<String> getDependencies() {
return dependencies;
}
public void setDependencies(List<String>
dependencies) {
this.dependencies = dependencies;
}
public Service kerberosPrincipal(KerberosPrincipal kerberosPrincipal) { public Service kerberosPrincipal(KerberosPrincipal kerberosPrincipal) {
this.kerberosPrincipal = kerberosPrincipal; this.kerberosPrincipal = kerberosPrincipal;
return this; return this;
@ -437,6 +450,8 @@ public String toString() {
.append(toIndentedString(kerberosPrincipal)).append("\n"); .append(toIndentedString(kerberosPrincipal)).append("\n");
sb.append(" dockerClientConfig: ") sb.append(" dockerClientConfig: ")
.append(toIndentedString(dockerClientConfig)).append("\n"); .append(toIndentedString(dockerClientConfig)).append("\n");
sb.append(" dependencies: ")
.append(toIndentedString(dependencies)).append("\n");
sb.append("}"); sb.append("}");
return sb.toString(); return sb.toString();
} }

View File

@ -35,6 +35,8 @@
import org.apache.hadoop.yarn.service.api.records.Container; import org.apache.hadoop.yarn.service.api.records.Container;
import org.apache.hadoop.yarn.service.api.records.ContainerState; import org.apache.hadoop.yarn.service.api.records.ContainerState;
import org.apache.hadoop.yarn.service.api.records.Service; 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.api.records.Artifact; import org.apache.hadoop.yarn.service.api.records.Artifact;
import org.apache.hadoop.yarn.service.api.records.Component; import org.apache.hadoop.yarn.service.api.records.Component;
import org.apache.hadoop.yarn.service.api.records.Configuration; import org.apache.hadoop.yarn.service.api.records.Configuration;
@ -705,4 +707,45 @@ public static List<String> resolveCompsDependency(Service service) {
} }
return components; return components;
} }
private static boolean serviceDependencySatisfied(Service service) {
boolean result = true;
try {
List<String> dependencies = service
.getDependencies();
org.apache.hadoop.conf.Configuration conf =
new org.apache.hadoop.conf.Configuration();
if (dependencies != null && dependencies.size() > 0) {
ServiceClient sc = new ServiceClient();
sc.init(conf);
sc.start();
for (String dependent : dependencies) {
Service dependentService = sc.getStatus(dependent);
if (dependentService.getState() == null ||
!dependentService.getState().equals(ServiceState.STABLE)) {
result = false;
LOG.info("Service dependency is not satisfied for " +
"service: {} state: {}", dependent,
dependentService.getState());
}
}
sc.close();
}
} catch (IOException | YarnException e) {
LOG.warn("Caught exception: ", e);
LOG.info("Service dependency is not satisified.");
result = false;
}
return result;
}
public static void checkServiceDependencySatisified(Service service) {
while (!serviceDependencySatisfied(service)) {
try {
LOG.info("Waiting for service dependencies.");
Thread.sleep(15000L);
} catch (InterruptedException e) {
}
}
}
} }

View File

@ -438,6 +438,8 @@ public void testExpressUpgrade() throws Exception {
// wait for upgrade to complete // wait for upgrade to complete
waitForServiceToBeStable(client, service); waitForServiceToBeStable(client, service);
Service active = client.getStatus(service.getName()); Service active = client.getStatus(service.getName());
Assert.assertEquals("version mismatch", service.getVersion(),
active.getVersion());
Assert.assertEquals("component not stable", ComponentState.STABLE, Assert.assertEquals("component not stable", ComponentState.STABLE,
active.getComponent(component.getName()).getState()); active.getComponent(component.getName()).getState());
Assert.assertEquals("compa does not have new env", "val1", Assert.assertEquals("compa does not have new env", "val1",

View File

@ -30,6 +30,7 @@
import org.apache.hadoop.yarn.service.api.records.PlacementType; import org.apache.hadoop.yarn.service.api.records.PlacementType;
import org.apache.hadoop.yarn.service.api.records.Resource; 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.Service;
import org.apache.hadoop.yarn.service.api.records.ServiceState;
import org.apache.hadoop.yarn.service.exceptions.RestApiErrorMessages; import org.apache.hadoop.yarn.service.exceptions.RestApiErrorMessages;
import org.junit.Assert; import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
@ -733,6 +734,44 @@ public void testResolveNoCompsDependency() {
} }
} }
@Test(timeout = 1500)
public void testNoServiceDependencies() {
Service service = createExampleApplication();
Component compa = createComponent("compa");
Component compb = createComponent("compb");
service.addComponent(compa);
service.addComponent(compb);
List<String> dependencies = new ArrayList<String>();
service.setDependencies(dependencies);
ServiceApiUtil.checkServiceDependencySatisified(service);
}
@Test
public void testServiceDependencies() {
Thread thread = new Thread() {
@Override
public void run() {
Service service = createExampleApplication();
Component compa = createComponent("compa");
Component compb = createComponent("compb");
service.addComponent(compa);
service.addComponent(compb);
List<String> dependencies = new ArrayList<String>();
dependencies.add("abc");
service.setDependencies(dependencies);
Service dependent = createExampleApplication();
dependent.setState(ServiceState.STOPPED);
ServiceApiUtil.checkServiceDependencySatisified(service);
}
};
thread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
Assert.assertTrue(thread.isAlive());
}
public static Service createExampleApplication() { public static Service createExampleApplication() {
Service exampleApp = new Service(); Service exampleApp = new Service();

View File

@ -406,7 +406,7 @@ a service resource has the following attributes.
|queue|The YARN queue that this service should be submitted to.|false|string|| |queue|The YARN queue that this service should be submitted to.|false|string||
|kerberos_principal|The principal info of the user who launches the service|false|KerberosPrincipal|| |kerberos_principal|The principal info of the user who launches the service|false|KerberosPrincipal||
|docker_client_config|URI of the file containing the docker client configuration (e.g. hdfs:///tmp/config.json)|false|string|| |docker_client_config|URI of the file containing the docker client configuration (e.g. hdfs:///tmp/config.json)|false|string||
|dependencies|A list of service names that this service depends on.| false | string array ||
### ServiceState ### ServiceState