|
| 1 | +package tech.stackable.hadoop; |
| 2 | + |
| 3 | +import java.util.Arrays; |
| 4 | +import java.util.List; |
| 5 | +import java.util.Locale; |
| 6 | +import java.util.stream.Collectors; |
| 7 | +import org.slf4j.Logger; |
| 8 | +import org.slf4j.LoggerFactory; |
| 9 | + |
| 10 | +public class TopologyLabel { |
| 11 | + private static final Logger LOG = LoggerFactory.getLogger(TopologyLabel.class); |
| 12 | + public static final String VARNAME_LABELS = "TOPOLOGY_LABELS"; |
| 13 | + public static final String VARNAME_MAX_LEVELS = "TOPOLOGY_MAX_LEVELS"; |
| 14 | + private static final int MAX_LEVELS_DEFAULT = 2; |
| 15 | + |
| 16 | + public enum Type { |
| 17 | + NODE, |
| 18 | + POD, |
| 19 | + UNDEFINED |
| 20 | + } |
| 21 | + |
| 22 | + private final Type type; |
| 23 | + private final String name; |
| 24 | + |
| 25 | + TopologyLabel(String config) { |
| 26 | + if (config == null || config.isEmpty()) { |
| 27 | + this.type = Type.UNDEFINED; |
| 28 | + this.name = null; |
| 29 | + return; |
| 30 | + } |
| 31 | + |
| 32 | + String[] parts = config.toLowerCase(Locale.ROOT).split(":", 2); |
| 33 | + |
| 34 | + if (parts.length != 2) { |
| 35 | + LOG.warn("Invalid topology label format '{}' - expected '[node|pod]:<label>'", config); |
| 36 | + this.type = Type.UNDEFINED; |
| 37 | + this.name = null; |
| 38 | + return; |
| 39 | + } |
| 40 | + |
| 41 | + this.name = parts[1]; |
| 42 | + |
| 43 | + switch (parts[0]) { |
| 44 | + case "node": |
| 45 | + this.type = Type.NODE; |
| 46 | + break; |
| 47 | + case "pod": |
| 48 | + this.type = Type.POD; |
| 49 | + break; |
| 50 | + default: |
| 51 | + LOG.warn("Unsupported label type '{}' - must be 'node' or 'pod'", parts[0]); |
| 52 | + this.type = Type.UNDEFINED; |
| 53 | + } |
| 54 | + } |
| 55 | + |
| 56 | + boolean isNodeLabel() { |
| 57 | + return type == Type.NODE; |
| 58 | + } |
| 59 | + |
| 60 | + boolean isUndefined() { |
| 61 | + return type == Type.UNDEFINED; |
| 62 | + } |
| 63 | + |
| 64 | + String getName() { |
| 65 | + return name; |
| 66 | + } |
| 67 | + |
| 68 | + Type getType() { |
| 69 | + return type; |
| 70 | + } |
| 71 | + |
| 72 | + public static List<TopologyLabel> initializeTopologyLabels() { |
| 73 | + // Read the labels to be used to build a topology from environment variables. Labels are |
| 74 | + // configured in the EnvVar "TOPOLOGY_LABELS". They should be specified in the form |
| 75 | + // "[node|pod]:<labelname>" and separated by ";". So a valid configuration that reads topology |
| 76 | + // information from the labels "kubernetes.io/zone" and "kubernetes.io/rack" on the k8s node |
| 77 | + // that is running a datanode pod would look like this: |
| 78 | + // "node:kubernetes.io/zone;node:kubernetes.io/rack" By default, there is an upper limit of 2 on |
| 79 | + // the number of labels that are processed, because this is what Hadoop traditionally allows - |
| 80 | + // this can be overridden via setting the EnvVar "MAX_TOPOLOGY_LEVELS". |
| 81 | + String topologyConfig = System.getenv(VARNAME_LABELS); |
| 82 | + |
| 83 | + if (topologyConfig == null || topologyConfig.isEmpty()) { |
| 84 | + LOG.error( |
| 85 | + "Missing env var [{}] this is required for rack awareness to work.", VARNAME_LABELS); |
| 86 | + throw new RuntimeException("TOPOLOGY_LABELS environment variable not set"); |
| 87 | + } |
| 88 | + |
| 89 | + String[] labelConfigs = topologyConfig.split(";"); |
| 90 | + |
| 91 | + if (labelConfigs.length > getMaxLabels()) { |
| 92 | + LOG.error( |
| 93 | + "Found [{}] topology labels configured, but maximum allowed number is [{}]: " |
| 94 | + + "please check your config or raise the number of allowed labels.", |
| 95 | + labelConfigs.length, |
| 96 | + getMaxLabels()); |
| 97 | + throw new RuntimeException("Too many topology labels configured"); |
| 98 | + } |
| 99 | + // Create TopologyLabels from config strings |
| 100 | + List<TopologyLabel> labels = |
| 101 | + Arrays.stream(labelConfigs).map(TopologyLabel::new).collect(Collectors.toList()); |
| 102 | + |
| 103 | + if (labels.stream().anyMatch(TopologyLabel::isUndefined)) { |
| 104 | + LOG.error( |
| 105 | + "Invalid topology label configuration - labels must be in format '[pod|node]:<label>'"); |
| 106 | + throw new RuntimeException("Invalid topology label configuration"); |
| 107 | + } |
| 108 | + |
| 109 | + return labels; |
| 110 | + } |
| 111 | + |
| 112 | + private static int getMaxLabels() { |
| 113 | + return TopologyUtils.parseIntFromEnv( |
| 114 | + VARNAME_MAX_LEVELS, MAX_LEVELS_DEFAULT, "maximum topology levels"); |
| 115 | + } |
| 116 | +} |
0 commit comments