|
17 | 17 | package org.springframework.cloud.kubernetes.commons.discovery; |
18 | 18 |
|
19 | 19 | import java.util.HashMap; |
| 20 | +import java.util.LinkedHashMap; |
20 | 21 | import java.util.Map; |
| 22 | +import java.util.Optional; |
| 23 | +import java.util.stream.Collectors; |
21 | 24 |
|
22 | 25 | import org.apache.commons.logging.LogFactory; |
23 | 26 |
|
24 | 27 | import org.springframework.core.log.LogAccessor; |
| 28 | +import org.springframework.util.StringUtils; |
25 | 29 |
|
26 | 30 | import static org.springframework.cloud.kubernetes.commons.config.ConfigUtils.keysWithPrefix; |
| 31 | +import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.HTTP; |
| 32 | +import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.HTTPS; |
27 | 33 | import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.NAMESPACE_METADATA_KEY; |
| 34 | +import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.PRIMARY_PORT_NAME_LABEL_KEY; |
28 | 35 | import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.SERVICE_TYPE; |
29 | 36 |
|
30 | 37 | /** |
@@ -77,4 +84,95 @@ public static Map<String, String> serviceMetadata(String serviceId, Map<String, |
77 | 84 | return serviceMetadata; |
78 | 85 | } |
79 | 86 |
|
| 87 | + public static ServicePortNameAndNumber endpointsPort(LinkedHashMap<String, Integer> endpointsPorts, |
| 88 | + String serviceId, KubernetesDiscoveryProperties properties, Map<String, String> serviceLabels) { |
| 89 | + |
| 90 | + if (endpointsPorts.size() == 0) { |
| 91 | + LOG.debug(() -> "no ports found for service : " + serviceId + ", will return zero"); |
| 92 | + return new ServicePortNameAndNumber(0, "http"); |
| 93 | + } |
| 94 | + |
| 95 | + if (endpointsPorts.size() == 1) { |
| 96 | + Map.Entry<String, Integer> single = endpointsPorts.entrySet().iterator().next(); |
| 97 | + LOG.debug(() -> "endpoint ports has a single entry, using port : " + single.getValue()); |
| 98 | + return new ServicePortNameAndNumber(single.getValue(), single.getKey()); |
| 99 | + } |
| 100 | + |
| 101 | + else { |
| 102 | + |
| 103 | + Optional<ServicePortNameAndNumber> portData; |
| 104 | + String primaryPortName = primaryPortName(properties, serviceLabels, serviceId); |
| 105 | + |
| 106 | + Map<String, Integer> existingPorts = endpointsPorts.entrySet().stream() |
| 107 | + .filter(entry -> StringUtils.hasText(entry.getKey())) |
| 108 | + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
| 109 | + |
| 110 | + portData = fromMap(existingPorts, primaryPortName, "found primary-port-name (with value: '" |
| 111 | + + primaryPortName + "') via properties or service labels to match port"); |
| 112 | + if (portData.isPresent()) { |
| 113 | + return portData.get(); |
| 114 | + } |
| 115 | + |
| 116 | + portData = fromMap(existingPorts, HTTPS, "found primary-port-name via 'https' to match port"); |
| 117 | + if (portData.isPresent()) { |
| 118 | + return portData.get(); |
| 119 | + } |
| 120 | + |
| 121 | + portData = fromMap(existingPorts, HTTP, "found primary-port-name via 'http' to match port"); |
| 122 | + if (portData.isPresent()) { |
| 123 | + return portData.get(); |
| 124 | + } |
| 125 | + |
| 126 | + logWarnings(); |
| 127 | + Map.Entry<String, Integer> first = endpointsPorts.entrySet().iterator().next(); |
| 128 | + return new ServicePortNameAndNumber(first.getValue(), first.getKey()); |
| 129 | + |
| 130 | + } |
| 131 | + } |
| 132 | + |
| 133 | + /** |
| 134 | + * take primary-port-name from service label "PRIMARY_PORT_NAME_LABEL_KEY" if it |
| 135 | + * exists, otherwise from KubernetesDiscoveryProperties if it exists, otherwise null. |
| 136 | + */ |
| 137 | + static String primaryPortName(KubernetesDiscoveryProperties properties, Map<String, String> serviceLabels, |
| 138 | + String serviceId) { |
| 139 | + String primaryPortNameFromProperties = properties.primaryPortName(); |
| 140 | + |
| 141 | + // the value from labels takes precedence over the one from properties |
| 142 | + String primaryPortName = Optional |
| 143 | + .ofNullable(Optional.ofNullable(serviceLabels).orElse(Map.of()).get(PRIMARY_PORT_NAME_LABEL_KEY)) |
| 144 | + .orElse(primaryPortNameFromProperties); |
| 145 | + |
| 146 | + if (primaryPortName == null) { |
| 147 | + LOG.debug( |
| 148 | + () -> "did not find a primary-port-name in neither properties nor service labels for service with ID : " |
| 149 | + + serviceId); |
| 150 | + return null; |
| 151 | + } |
| 152 | + |
| 153 | + LOG.debug(() -> "will use primaryPortName : " + primaryPortName + " for service with ID = " + serviceId); |
| 154 | + return primaryPortName; |
| 155 | + } |
| 156 | + |
| 157 | + private static Optional<ServicePortNameAndNumber> fromMap(Map<String, Integer> existingPorts, String key, |
| 158 | + String message) { |
| 159 | + Integer fromPrimaryPortName = existingPorts.get(key); |
| 160 | + if (fromPrimaryPortName == null) { |
| 161 | + LOG.debug(() -> "not " + message); |
| 162 | + return Optional.empty(); |
| 163 | + } |
| 164 | + else { |
| 165 | + LOG.debug(() -> message + " : " + fromPrimaryPortName); |
| 166 | + return Optional.of(new ServicePortNameAndNumber(fromPrimaryPortName, key)); |
| 167 | + } |
| 168 | + } |
| 169 | + |
| 170 | + private static void logWarnings() { |
| 171 | + LOG.warn(() -> """ |
| 172 | + Make sure that either the primary-port-name label has been added to the service, |
| 173 | + or spring.cloud.kubernetes.discovery.primary-port-name has been configured. |
| 174 | + Alternatively name the primary port 'https' or 'http' |
| 175 | + An incorrect configuration may result in non-deterministic behaviour."""); |
| 176 | + } |
| 177 | + |
80 | 178 | } |
0 commit comments