diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/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-api/src/main/resources/definition/YARN-Simplified-V1-API-Layer-For-Services.yaml index 17723bcff10..117d5dd89a4 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/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-api/src/main/resources/definition/YARN-Simplified-V1-API-Layer-For-Services.yaml @@ -365,6 +365,7 @@ definitions: type: string description: E.g. HTTP (YARN will perform a simple REST call at a regular interval and expect a 204 No content). enum: + - DEFAULT - HTTP - PORT properties: 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/ReadinessCheck.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/ReadinessCheck.java index af7c5427e7c..0665cb53fad 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/ReadinessCheck.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/ReadinessCheck.java @@ -60,6 +60,7 @@ @XmlType(name = "type") @XmlEnum public enum TypeEnum { + DEFAULT("DEFAULT"), HTTP("HTTP"), PORT("PORT"); 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 0e3e11bc72e..c57d8882720 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 @@ -20,7 +20,9 @@ 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.ExitUtil; @@ -520,6 +522,24 @@ 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; + } + @Override public int compareTo(ComponentInstance to) { long delta = containerStartedTime - to.containerStartedTime; 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/monitor/probe/DefaultProbe.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/monitor/probe/DefaultProbe.java new file mode 100644 index 00000000000..b721f887a64 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/monitor/probe/DefaultProbe.java @@ -0,0 +1,90 @@ +/** + * 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.monitor.probe; + +import org.apache.hadoop.yarn.api.records.ContainerStatus; +import org.apache.hadoop.yarn.service.component.instance.ComponentInstance; +import org.apache.hadoop.yarn.service.utils.ServiceRegistryUtils; +import org.apache.hadoop.yarn.service.utils.ServiceUtils; + +import java.io.IOException; +import java.util.Collections; +import java.util.Map; + +public class DefaultProbe extends Probe { + private final boolean dnsCheckEnabled; + private final String dnsAddress; + + public DefaultProbe(Map props) { + this("Default probe: IP presence", props); + } + + protected DefaultProbe(String name, Map props) { + this.dnsCheckEnabled = getPropertyBool(props, + DEFAULT_PROBE_DNS_CHECK_ENABLED, + DEFAULT_PROBE_DNS_CHECK_ENABLED_DEFAULT); + this.dnsAddress = props.get(DEFAULT_PROBE_DNS_ADDRESS); + String additionalName = ""; + if (dnsCheckEnabled) { + if (dnsAddress == null) { + additionalName = " with DNS checking"; + } else { + additionalName = " with DNS checking and DNS server address " + dnsAddress; + } + } + setName(name + additionalName); + } + + public static DefaultProbe create() throws IOException { + return new DefaultProbe(Collections.emptyMap()); + } + + public static DefaultProbe create(Map props) throws + IOException { + return new DefaultProbe(props); + } + + @Override + public ProbeStatus ping(ComponentInstance instance) { + ProbeStatus status = new ProbeStatus(); + + ContainerStatus containerStatus = instance.getContainerStatus(); + if (containerStatus == null || ServiceUtils.isEmpty(containerStatus + .getIPs())) { + status.fail(this, new IOException( + instance.getCompInstanceName() + ": IP is not available yet")); + return status; + } + + String hostname = instance.getHostname(); + if (dnsCheckEnabled && !ServiceRegistryUtils.registryDNSLookupExists( + dnsAddress, hostname)) { + status.fail(this, new IOException( + instance.getCompInstanceName() + ": DNS checking is enabled, but " + + "lookup for " + hostname + " is not available yet")); + return status; + } + + status.succeed(this); + return status; + } + + protected boolean isDnsCheckEnabled() { + return dnsCheckEnabled; + } +} 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/monitor/probe/HttpProbe.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/monitor/probe/HttpProbe.java index 1ed13a9c360..4af219ee9e2 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/monitor/probe/HttpProbe.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/monitor/probe/HttpProbe.java @@ -17,11 +17,7 @@ package org.apache.hadoop.yarn.service.monitor.probe; -import org.apache.commons.lang.StringUtils; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.yarn.api.records.ContainerStatus; import org.apache.hadoop.yarn.service.component.instance.ComponentInstance; -import org.apache.hadoop.yarn.service.utils.ServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,7 +26,7 @@ import java.net.URL; import java.util.Map; -public class HttpProbe extends Probe { +public class HttpProbe extends DefaultProbe { protected static final Logger log = LoggerFactory.getLogger(HttpProbe.class); private static final String HOST_TOKEN = "${THIS_HOST}"; @@ -40,9 +36,9 @@ private final int min, max; - public HttpProbe(String url, int timeout, int min, int max, Configuration - conf) { - super("Http probe of " + url + " [" + min + "-" + max + "]", conf); + public HttpProbe(String url, int timeout, int min, int max, + Map props) { + super("Http probe of " + url + " [" + min + "-" + max + "]", props); this.urlString = url; this.timeout = timeout; this.min = min; @@ -59,7 +55,7 @@ public static HttpProbe create(Map props) WEB_PROBE_MIN_SUCCESS_DEFAULT); int maxSuccess = getPropertyInt(props, WEB_PROBE_MAX_SUCCESS, WEB_PROBE_MAX_SUCCESS_DEFAULT); - return new HttpProbe(urlString, timeout, minSuccess, maxSuccess, null); + return new HttpProbe(urlString, timeout, minSuccess, maxSuccess, props); } @@ -73,15 +69,11 @@ private static HttpURLConnection getConnection(URL url, int timeout) throws @Override public ProbeStatus ping(ComponentInstance instance) { - ProbeStatus status = new ProbeStatus(); - ContainerStatus containerStatus = instance.getContainerStatus(); - if (containerStatus == null || ServiceUtils.isEmpty(containerStatus.getIPs()) - || StringUtils.isEmpty(containerStatus.getHost())) { - status.fail(this, new IOException("IP is not available yet")); + ProbeStatus status = super.ping(instance); + if (!status.isSuccess()) { return status; } - - String ip = containerStatus.getIPs().get(0); + String ip = instance.getContainerStatus().getIPs().get(0); HttpURLConnection connection = null; try { URL url = new URL(urlString.replace(HOST_TOKEN, ip)); 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/monitor/probe/MonitorKeys.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/monitor/probe/MonitorKeys.java index 55b55f68eec..97770d4d2b1 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/monitor/probe/MonitorKeys.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/monitor/probe/MonitorKeys.java @@ -23,6 +23,18 @@ public interface MonitorKeys { /** + * Default probing key : DNS check enabled {@value}. + */ + String DEFAULT_PROBE_DNS_CHECK_ENABLED = "dns.check.enabled"; + /** + * Default probing default : DNS check enabled {@value}. + */ + boolean DEFAULT_PROBE_DNS_CHECK_ENABLED_DEFAULT = false; + /** + * Default probing key : DNS checking address IP:port {@value}. + */ + String DEFAULT_PROBE_DNS_ADDRESS = "dns.address"; + /** * Port probing key : port to attempt to create a TCP connection to {@value}. */ String PORT_PROBE_PORT = "port"; 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/monitor/probe/MonitorUtils.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/monitor/probe/MonitorUtils.java index c4f63aee7ae..0b57e6c6bf5 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/monitor/probe/MonitorUtils.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/monitor/probe/MonitorUtils.java @@ -61,20 +61,20 @@ public static String millisToHumanTime(long milliseconds) { } public static Probe getProbe(ReadinessCheck readinessCheck) { - if (readinessCheck == null) { - return null; - } - if (readinessCheck.getType() == null) { - return null; - } try { + if (readinessCheck == null) { + return DefaultProbe.create(); + } + if (readinessCheck.getType() == null) { + return DefaultProbe.create(readinessCheck.getProperties()); + } switch (readinessCheck.getType()) { case HTTP: return HttpProbe.create(readinessCheck.getProperties()); case PORT: return PortProbe.create(readinessCheck.getProperties()); default: - return null; + return DefaultProbe.create(readinessCheck.getProperties()); } } catch (Throwable t) { throw new IllegalArgumentException("Error creating readiness check " + 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/monitor/probe/PortProbe.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/monitor/probe/PortProbe.java index 85569f86d4a..7289c89e92c 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/monitor/probe/PortProbe.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/monitor/probe/PortProbe.java @@ -19,7 +19,6 @@ import org.apache.hadoop.io.IOUtils; import org.apache.hadoop.yarn.service.component.instance.ComponentInstance; -import org.apache.hadoop.yarn.service.utils.ServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,13 +30,13 @@ /** * Probe for a port being open. */ -public class PortProbe extends Probe { +public class PortProbe extends DefaultProbe { protected static final Logger log = LoggerFactory.getLogger(PortProbe.class); private final int port; private final int timeout; - public PortProbe(int port, int timeout) { - super("Port probe of " + port + " for " + timeout + "ms", null); + public PortProbe(int port, int timeout, Map props) { + super("Port probe of " + port + " for " + timeout + "ms", props); this.port = port; this.timeout = timeout; } @@ -54,7 +53,7 @@ public static PortProbe create(Map props) int timeout = getPropertyInt(props, PORT_PROBE_CONNECT_TIMEOUT, PORT_PROBE_CONNECT_TIMEOUT_DEFAULT); - return new PortProbe(port, timeout); + return new PortProbe(port, timeout, props); } /** @@ -65,12 +64,8 @@ public static PortProbe create(Map props) */ @Override public ProbeStatus ping(ComponentInstance instance) { - ProbeStatus status = new ProbeStatus(); - - if (instance.getContainerStatus() == null || ServiceUtils - .isEmpty(instance.getContainerStatus().getIPs())) { - status.fail(this, new IOException( - instance.getCompInstanceName() + ": IP is not available yet")); + ProbeStatus status = super.ping(instance); + if (!status.isSuccess()) { 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/monitor/probe/Probe.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/monitor/probe/Probe.java index 3237a2bd499..341a0c8f46d 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/monitor/probe/Probe.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/monitor/probe/Probe.java @@ -18,7 +18,6 @@ package org.apache.hadoop.yarn.service.monitor.probe; import org.apache.commons.lang.StringUtils; -import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.yarn.service.component.instance.ComponentInstance; import java.io.IOException; @@ -29,18 +28,18 @@ */ public abstract class Probe implements MonitorKeys { - protected final Configuration conf; private String name; + protected Probe() { + } + /** * Create a probe of a specific name * * @param name probe name - * @param conf configuration being stored. */ - public Probe(String name, Configuration conf) { + public Probe(String name) { this.name = name; - this.conf = conf; } @@ -82,6 +81,15 @@ public static int getPropertyInt(Map props, String name, return Integer.parseInt(value); } + public static boolean getPropertyBool(Map props, String name, + boolean defaultValue) { + String value = props.get(name); + if (StringUtils.isEmpty(value)) { + return defaultValue; + } + return Boolean.parseBoolean(value); + } + /** * perform any prelaunch initialization */ 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/provider/docker/DockerProviderService.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/provider/docker/DockerProviderService.java index 6ac8de1e6b6..c3e2619245c 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/provider/docker/DockerProviderService.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/provider/docker/DockerProviderService.java @@ -17,8 +17,6 @@ */ package org.apache.hadoop.yarn.service.provider.docker; -import org.apache.hadoop.registry.client.api.RegistryConstants; -import org.apache.hadoop.registry.client.binding.RegistryUtils; import org.apache.hadoop.yarn.service.component.instance.ComponentInstance; import org.apache.hadoop.yarn.service.provider.AbstractProviderService; import org.apache.hadoop.yarn.service.api.records.Service; @@ -26,7 +24,6 @@ import org.apache.hadoop.yarn.service.containerlaunch.AbstractLauncher; import java.io.IOException; -import java.text.MessageFormat; public class DockerProviderService extends AbstractProviderService implements DockerKeys { @@ -38,19 +35,7 @@ public void processArtifact(AbstractLauncher launcher, launcher.setDockerImage(compInstance.getCompSpec().getArtifact().getId()); launcher.setDockerNetwork(compInstance.getCompSpec().getConfiguration() .getProperty(DOCKER_NETWORK)); - String domain = compInstance.getComponent().getScheduler().getConfig() - .get(RegistryConstants.KEY_DNS_DOMAIN); - String hostname; - if (domain == null || domain.isEmpty()) { - hostname = MessageFormat - .format("{0}.{1}.{2}", compInstance.getCompInstanceName(), - service.getName(), RegistryUtils.currentUser()); - } else { - hostname = MessageFormat - .format("{0}.{1}.{2}.{3}", compInstance.getCompInstanceName(), - service.getName(), RegistryUtils.currentUser(), domain); - } - launcher.setDockerHostname(hostname); + launcher.setDockerHostname(compInstance.getHostname()); launcher.setRunPrivilegedContainer( compInstance.getCompSpec().getRunPrivilegedContainer()); } 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/ServiceRegistryUtils.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/ServiceRegistryUtils.java index dfc30f75e4d..84a02e0cc3f 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/ServiceRegistryUtils.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/ServiceRegistryUtils.java @@ -20,9 +20,23 @@ import org.apache.hadoop.registry.client.binding.RegistryUtils; import org.apache.hadoop.yarn.service.conf.YarnServiceConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.naming.Context; +import javax.naming.NameNotFoundException; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Hashtable; public class ServiceRegistryUtils { + private static final Logger LOG = + LoggerFactory.getLogger(ServiceRegistryUtils.class); public static final String SVC_USERS = "/services/yarn/users"; @@ -53,4 +67,45 @@ public static String mkServiceHomePath(String username, String serviceName) { public static String mkUserHomePath(String username) { return SVC_USERS + "/" + username; } + + /** + * Determine whether a DNS lookup exists for a given name. + * + * @param addr host:port dns address, or null + * @param name name to look up + * @return true if a lookup succeeds for the specified name + */ + public static boolean registryDNSLookupExists(String addr, String + name) { + if (addr == null) { + try { + InetAddress.getByName(name); + return true; + } catch (UnknownHostException e) { + return false; + } + } + + String dnsURI = String.format("dns://%s", addr); + Hashtable env = new Hashtable<>(); + env.put(Context.INITIAL_CONTEXT_FACTORY, + "com.sun.jndi.dns.DnsContextFactory"); + env.put(Context.PROVIDER_URL, dnsURI); + + try { + DirContext ictx = new InitialDirContext(env); + Attributes attrs = ictx.getAttributes(name, new String[]{"A"}); + + if (attrs.size() > 0) { + return true; + } + } catch (NameNotFoundException e) { + // this doesn't need to be logged + } catch (NamingException e) { + LOG.error("Got exception when performing DNS lookup", e); + } + + return false; + } + } 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/monitor/probe/TestDefaultProbe.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/monitor/probe/TestDefaultProbe.java new file mode 100644 index 00000000000..8f58b3e2b6d --- /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/monitor/probe/TestDefaultProbe.java @@ -0,0 +1,152 @@ +/** + * 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.monitor.probe; + +import org.apache.hadoop.yarn.api.records.ContainerStatus; +import org.apache.hadoop.yarn.service.api.records.ReadinessCheck; +import org.apache.hadoop.yarn.service.component.instance.ComponentInstance; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(Parameterized.class) +public class TestDefaultProbe { + private final DefaultProbe probe; + + public TestDefaultProbe(Probe probe) { + this.probe = (DefaultProbe) probe; + } + + @Parameterized.Parameters + public static Collection data() { + // test run 1: Default probe checks that container has an IP + Probe p1 = MonitorUtils.getProbe(null); + + // test run 2: Default probe with DNS check for component instance hostname + ReadinessCheck rc2 = new ReadinessCheck() + .type(ReadinessCheck.TypeEnum.DEFAULT) + .properties(Collections.singletonMap( + MonitorKeys.DEFAULT_PROBE_DNS_CHECK_ENABLED, "true")); + Probe p2 = MonitorUtils.getProbe(rc2); + + // test run 3: Default probe with DNS check using specific DNS server + Map props = new HashMap<>(); + props.put(MonitorKeys.DEFAULT_PROBE_DNS_CHECK_ENABLED, "true"); + props.put(MonitorKeys.DEFAULT_PROBE_DNS_ADDRESS, "8.8.8.8"); + ReadinessCheck rc3 = new ReadinessCheck() + .type(ReadinessCheck.TypeEnum.DEFAULT).properties(props); + Probe p3 = MonitorUtils.getProbe(rc3); + + return Arrays.asList(new Object[][] {{p1}, {p2}, {p3}}); + } + + @Test + public void testDefaultProbe() { + // component instance has a good hostname, so probe will eventually succeed + // whether or not DNS checking is enabled + ComponentInstance componentInstance = + createMockComponentInstance("example.com"); + checkPingResults(probe, componentInstance, false); + + // component instance has a bad hostname, so probe will fail when DNS + // checking is enabled + componentInstance = createMockComponentInstance("bad.dns.test"); + checkPingResults(probe, componentInstance, probe.isDnsCheckEnabled()); + } + + private static void checkPingResults(Probe probe, ComponentInstance + componentInstance, boolean expectDNSCheckFailure) { + // on the first ping, null container status results in failure + ProbeStatus probeStatus = probe.ping(componentInstance); + assertFalse("Expected failure for " + probeStatus.toString(), + probeStatus.isSuccess()); + assertTrue("Expected IP failure for " + probeStatus.toString(), + probeStatus.toString().contains( + componentInstance.getCompInstanceName() + ": IP is not available yet")); + + // on the second ping, container status is retrieved but there are no + // IPs, resulting in failure + probeStatus = probe.ping(componentInstance); + assertFalse("Expected failure for " + probeStatus.toString(), + probeStatus.isSuccess()); + assertTrue("Expected IP failure for " + probeStatus.toString(), + probeStatus.toString().contains(componentInstance + .getCompInstanceName() + ": IP is not available yet")); + + // on the third ping, IPs are retrieved and success depends on whether or + // not a DNS lookup can be performed for the component instance hostname + probeStatus = probe.ping(componentInstance); + if (expectDNSCheckFailure) { + assertFalse("Expected failure for " + probeStatus.toString(), + probeStatus.isSuccess()); + assertTrue("Expected DNS failure for " + probeStatus.toString(), + probeStatus.toString().contains(componentInstance + .getCompInstanceName() + ": DNS checking is enabled, but lookup" + + " for " + componentInstance.getHostname() + " is not available " + + "yet")); + } else { + assertTrue("Expected success for " + probeStatus.toString(), + probeStatus.isSuccess()); + } + } + + private static ComponentInstance createMockComponentInstance(String + hostname) { + ComponentInstance componentInstance = mock(ComponentInstance.class); + when(componentInstance.getHostname()).thenReturn(hostname); + when(componentInstance.getCompInstanceName()).thenReturn("comp-0"); + when(componentInstance.getContainerStatus()) + .thenAnswer(new Answer() { + int count = 0; + + @Override + public ContainerStatus answer(InvocationOnMock invocationOnMock) { + count++; + if (count == 1) { + // first call to getContainerStatus returns null + return null; + } else if (count == 2) { + // second call returns a ContainerStatus with no IPs + ContainerStatus containerStatus = mock(ContainerStatus.class); + when(containerStatus.getIPs()).thenReturn(null); + return containerStatus; + } else { + // third call returns a ContainerStatus with one IP + ContainerStatus containerStatus = mock(ContainerStatus.class); + when(containerStatus.getIPs()) + .thenReturn(Collections.singletonList("1.2.3.4")); + return containerStatus; + } + } + }); + return componentInstance; + } +} 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 1e18b9969f5..1873db781ad 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 @@ -316,7 +316,7 @@ A custom command or a pluggable helper container to determine the readiness of a |Name|Description|Required|Schema|Default| |----|----|----|----|----| -|type|E.g. HTTP (YARN will perform a simple REST call at a regular interval and expect a 204 No content).|true|enum (HTTP, PORT)|| +|type|E.g. HTTP (YARN will perform a simple REST call at a regular interval and expect a 204 No content).|true|enum (DEFAULT, HTTP, PORT)|| |properties|A blob of key value pairs that will be used to configure the check.|false|object|| |artifact|Artifact of the pluggable readiness check helper container (optional). If specified, this helper container typically hosts the http uri and encapsulates the complex scripts required to perform actual container readiness check. At the end it is expected to respond a 204 No content just like the simplified use case. This pluggable framework benefits service owners who can run services without any packaging modifications. Note, artifacts of type docker only is supported for now. NOT IMPLEMENTED YET|false|Artifact||