/** * 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.tez.dag.app; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import javax.annotation.Nullable; import org.apache.hadoop.security.token.Token; import org.apache.hadoop.security.token.TokenIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.hadoop.security.Credentials; import org.apache.hadoop.yarn.api.records.LocalResource; import org.apache.hadoop.yarn.api.records.LocalResourceType; import org.apache.tez.dag.app.dag.Vertex; import com.google.common.base.Preconditions; public class ContainerContext { private static final Logger LOG = LoggerFactory.getLogger(ContainerContext.class); private final Map<String, LocalResource> localResources; private final Credentials credentials; private final Map<String, String> environment; private final String javaOpts; private final Vertex vertex; // FIXME Add support for service meta data comparisons public ContainerContext(Map<String, LocalResource> localResources, Credentials credentials, Map<String, String> environment, String javaOpts) { Preconditions.checkNotNull(localResources, "localResources should not be null"); Preconditions.checkNotNull(credentials, "credentials should not be null"); Preconditions.checkNotNull(environment, "environment should not be null"); Preconditions.checkNotNull(javaOpts, "javaOpts should not be null"); this.localResources = localResources; this.credentials = credentials; this.environment = environment; this.javaOpts = javaOpts; this.vertex = null; } public ContainerContext(Map<String, LocalResource> localResources, Credentials credentials, Map<String, String> environment, String javaOpts, @Nullable Vertex vertex) { Preconditions.checkNotNull(localResources, "localResources should not be null"); Preconditions.checkNotNull(credentials, "credentials should not be null"); Preconditions.checkNotNull(environment, "environment should not be null"); Preconditions.checkNotNull(javaOpts, "javaOpts should not be null"); this.localResources = localResources; this.credentials = credentials; this.environment = environment; this.javaOpts = javaOpts; this.vertex = vertex; } public Map<String, LocalResource> getLocalResources() { return this.localResources; } public Credentials getCredentials() { return this.credentials; } public Map<String, String> getEnvironment() { return this.environment; } public String getJavaOpts() { return this.javaOpts; } /** * @return true if this ContainerContext is a super-set of the specified * container context. */ public boolean isSuperSet(ContainerContext otherContext) { Preconditions.checkNotNull(otherContext, "otherContext should not null"); // Assumptions: // Credentials are the same for all containers belonging to a DAG. // Matching can be added if containers are used across DAGs // Match javaOpts if (!this.javaOpts.equals(otherContext.javaOpts)) { if (LOG.isDebugEnabled()) { LOG.debug("Incompatible java opts, " + ", this=" + this.javaOpts + ", other=" + otherContext.javaOpts); } return false; } return isSuperSet(this.environment, otherContext.getEnvironment(), "Environment") && localResourcesCompatible(this.localResources, otherContext.getLocalResources()); } /** * @return true if this ContainerContext is an exact match of the specified * container context. */ public boolean isExactMatch(ContainerContext otherContext) { return (this.vertex == otherContext.vertex); } // TODO Once LRs are handled via YARN, remove this check - and ensure // YarnTezDAGChild knows how to handle the additional types in terms of // classpath modification private static boolean localResourcesCompatible(Map<String, LocalResource> srcLRs, Map<String, LocalResource> reqLRs) { Map<String, LocalResource> reqLRsCopy = new HashMap<String, LocalResource>(reqLRs); for (Entry<String, LocalResource> srcLREntry : srcLRs.entrySet()) { LocalResource requestedLocalResource = reqLRsCopy.remove(srcLREntry.getKey()); if (requestedLocalResource != null && !srcLREntry.getValue().equals(requestedLocalResource)) { if (LOG.isDebugEnabled()) { LOG.debug("Cannot match container: Attempting to use same target resource name: " + srcLREntry.getKey() + ", but with different source resources. Already localized: " + srcLREntry.getValue() + ", requested: " + requestedLocalResource); } return false; } } for (Entry<String, LocalResource> additionalLREntry : reqLRsCopy.entrySet()) { LocalResource lr = additionalLREntry.getValue(); if (EnumSet.of(LocalResourceType.ARCHIVE, LocalResourceType.PATTERN).contains(lr.getType())) { if (LOG.isDebugEnabled()) { LOG.debug("Cannot match container: Additional local resource needed is not of type FILE" + ", resourceName: " + additionalLREntry.getKey() + ", resourceDetails: " + additionalLREntry); } return false; } } return true; } private static <K, V> boolean isSuperSet(Map<K, V> srcMap, Map<K, V> matchMap, String matchInfo) { for (Entry<K, V> oEntry : matchMap.entrySet()) { K oKey = oEntry.getKey(); V oVal = oEntry.getValue(); if (srcMap.containsKey(oKey)) { if (!oVal.equals(srcMap.get(oKey))) { if (LOG.isDebugEnabled()) { LOG.debug("Incompatible container context" + ", matchInfo=" + matchInfo + ", thisKey=" + oKey + ", thisVal=" + srcMap.get(oKey) + ", otherVal=" + oVal); } return false; } } else { if (LOG.isDebugEnabled()) { LOG.debug("Incompatible container context" + ", matchInfo=" + matchInfo + ", thisKey=" + oKey + ", thisVal=null" + ", otherVal=" + oVal); } return false; } } return true; } /** * Create a new ContainerContext to account for container re-use. On re-use, there is * re-localization of additional LocalResources. Also, a task from a different vertex could be * run on the given container. * * Only a merge of local resources is needed as: * * credentials are modified at run-time based on the task spec. * the environment for a container cannot be changed. A re-used container is always * expected to have a super-set. * javaOpts have to be identical for re-use. * * Vertex should be overridden to account for the new task being scheduled to run on this * container context. * * @param c1 ContainerContext 1 Original task's context * @param c2 ContainerContext 2 Newly assigned task's context * @return Merged ContainerContext */ public static ContainerContext union(ContainerContext c1, ContainerContext c2) { HashMap<String, LocalResource> mergedLR = new HashMap<String, LocalResource>(); mergedLR.putAll(c1.getLocalResources()); mergedLR.putAll(c2.getLocalResources()); ContainerContext union = new ContainerContext(mergedLR, c1.credentials, c1.environment, c1.javaOpts, c2.vertex); return union; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("LocalResources: ["); if (localResources != null) { for (Map.Entry<String, LocalResource> lr : localResources.entrySet()) { sb.append("[ name=") .append(lr.getKey()) .append(", value=") .append(lr.getValue()) .append("],"); } } sb.append("], environment: ["); if (environment != null) { for (Map.Entry<String, String> entry : environment.entrySet()) { sb.append("[ ").append(entry.getKey()).append("=").append(entry.getValue()) .append(" ],"); } } sb.append("], credentials(token kinds): ["); if (credentials != null) { for (Token<? extends TokenIdentifier> t : credentials.getAllTokens()) { sb.append(t.getKind().toString()) .append(","); } } sb.append("], javaOpts: ") .append(javaOpts) .append(", vertex: ") .append(( vertex == null ? "null" : vertex.getLogIdentifier())); return sb.toString(); } }