Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.kroxylicious.proxy.internal.PortConflictDetector Maven / Gradle / Ivy
/*
* Copyright Kroxylicious Authors.
*
* Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package io.kroxylicious.proxy.internal;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import io.kroxylicious.proxy.model.VirtualCluster;
import io.kroxylicious.proxy.service.HostPort;
/**
* Detects potential for port conflicts arising between virtual cluster configurations.
*/
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class PortConflictDetector {
private static final Optional ANY_INTERFACE = Optional.empty();
private static final String ANY_STRING = "";
private enum BindingScope {
SHARED,
EXCLUSIVE
}
/**
* Validates the configuration throwing an exception if a conflict is detected.
*
* @param virtualClusterMap map of virtual clusters.
* @param otherExclusivePort an optional exclusive port that should conflict with virtual cluster ports
*/
public void validate(Collection virtualClusterMap, Optional otherExclusivePort) {
Set seenVirtualClusters = new HashSet<>();
Map, Set> inUseExclusivePorts = new HashMap<>();
Map, Map> inUseSharedPorts = new HashMap<>();
virtualClusterMap.stream()
.sorted(Comparator.comparing(VirtualCluster::getClusterName))
.forEach(virtualCluster -> {
var name = virtualCluster.getClusterName();
var proposedSharedPorts = virtualCluster.getSharedPorts();
var proposedExclusivePorts = virtualCluster.getExclusivePorts();
if (otherExclusivePort.isPresent()) {
Optional otherInterface = otherExclusivePort.map(hostPort -> hostPort.host().equals("0.0.0.0") ? null : hostPort.host());
var checkPorts = virtualCluster.getBindAddress().isEmpty() || otherInterface.isEmpty() || virtualCluster.getBindAddress().equals(otherInterface);
if (checkPorts) {
checkForConflictsWithOtherExclusivePort(otherExclusivePort.get(), name, virtualCluster, proposedExclusivePorts, BindingScope.EXCLUSIVE);
checkForConflictsWithOtherExclusivePort(otherExclusivePort.get(), name, virtualCluster, proposedSharedPorts, BindingScope.SHARED);
}
}
// if this virtual cluster is binding to , we need to check for conflicts on the interface and *all* specific interfaces,
// otherwise we just check for conflicts on and the specified specific interface
var exclusiveCheckSet = virtualCluster.getBindAddress().isEmpty() ? inUseExclusivePorts.keySet()
: Set.of(ANY_INTERFACE, virtualCluster.getBindAddress());
// check the proposed *exclusive ports* for conflicts with the *exclusive ports* seen so far.
assertExclusivePortsAreMutuallyExclusive(seenVirtualClusters, inUseExclusivePorts, virtualCluster, name, proposedExclusivePorts, exclusiveCheckSet);
// check the proposed *shared ports* for conflicts with the *exclusive ports* seen so far.
assertSharedPortsDoNotOverlapWithExlusivePorts(seenVirtualClusters, inUseExclusivePorts, virtualCluster, name, proposedSharedPorts,
exclusiveCheckSet);
// check the proposed *exclusive ports* for conflicts with the *shared ports* seen so far.
assertExclusivePortsDoNotOverlapWithExclusivePorts(seenVirtualClusters, inUseSharedPorts, virtualCluster, name, proposedExclusivePorts,
exclusiveCheckSet);
// check the proposed *shared ports* for conflicts with the shared ports seen so far on *different* interfaces.
assertSharedPortsAreExclusiveAcrossInterfaces(seenVirtualClusters, inUseSharedPorts, virtualCluster, name, proposedSharedPorts, exclusiveCheckSet);
// check for proposed shared ports for differing TLS configuration
assertSharedPortsHaveMatchingTlsConfiguration(seenVirtualClusters, inUseSharedPorts, virtualCluster, name, proposedSharedPorts);
seenVirtualClusters.add(name);
inUseExclusivePorts.computeIfAbsent(virtualCluster.getBindAddress(), k -> new HashSet<>()).addAll(proposedExclusivePorts);
var sharedMap = inUseSharedPorts.computeIfAbsent(virtualCluster.getBindAddress(), k -> new HashMap<>());
proposedSharedPorts.forEach(p -> sharedMap.put(p, virtualCluster.isUseTls()));
});
}
private void assertSharedPortsHaveMatchingTlsConfiguration(Set seenVirtualClusters, Map, Map> inUseSharedPorts,
VirtualCluster virtualCluster, String name,
Set proposedSharedPorts) {
proposedSharedPorts.forEach(p -> {
var tls = inUseSharedPorts.getOrDefault(virtualCluster.getBindAddress(), Map.of()).get(p);
if (tls != null && !tls.equals(virtualCluster.isUseTls())) {
throw buildOverviewException(name, seenVirtualClusters, buildTlsConflictException(p, virtualCluster.getBindAddress()));
}
});
}
private void assertSharedPortsAreExclusiveAcrossInterfaces(Set seenVirtualClusters, Map, Map> inUseSharedPorts,
VirtualCluster virtualCluster, String name,
Set proposedSharedPorts, Set> exclusiveCheckSet) {
var sharedCheckSet = new HashSet<>(exclusiveCheckSet);
sharedCheckSet.remove(virtualCluster.getBindAddress());
inUseSharedPorts.entrySet().stream()
.filter(interfacePortsEntry -> sharedCheckSet.contains(interfacePortsEntry.getKey()))
.forEach(entry -> {
var bindingInterface = entry.getKey();
var ports = entry.getValue().keySet();
var conflicts = getSortedPortConflicts(ports, proposedSharedPorts);
if (!conflicts.isEmpty()) {
throw buildPortConflictException(name, seenVirtualClusters, conflicts,
virtualCluster.getBindAddress(), BindingScope.SHARED,
bindingInterface, BindingScope.SHARED);
}
});
}
private void assertExclusivePortsDoNotOverlapWithExclusivePorts(Set seenVirtualClusters, Map, Map> inUseSharedPorts,
VirtualCluster virtualCluster, String name,
Set proposedExclusivePorts, Set> exclusiveCheckSet) {
inUseSharedPorts.entrySet().stream()
.filter(interfacePortsEntry -> exclusiveCheckSet.contains(interfacePortsEntry.getKey()))
.forEach(entry -> {
var bindingInterface = entry.getKey();
var ports = entry.getValue().keySet();
var conflicts = getSortedPortConflicts(ports, proposedExclusivePorts);
if (!conflicts.isEmpty()) {
throw buildPortConflictException(name, seenVirtualClusters, conflicts,
virtualCluster.getBindAddress(), BindingScope.EXCLUSIVE,
bindingInterface, BindingScope.SHARED);
}
});
}
private void assertSharedPortsDoNotOverlapWithExlusivePorts(Set seenVirtualClusters, Map, Set> inUseExclusivePorts,
VirtualCluster virtualCluster, String name,
Set proposedSharedPorts, Set> exclusiveCheckSet) {
inUseExclusivePorts.entrySet().stream()
.filter(interfacePortsEntry -> exclusiveCheckSet.contains(interfacePortsEntry.getKey()))
.forEach(entry -> {
var bindingInterface = entry.getKey();
var ports = entry.getValue();
var conflicts = getSortedPortConflicts(ports, proposedSharedPorts);
if (!conflicts.isEmpty()) {
throw buildPortConflictException(name, seenVirtualClusters, conflicts,
virtualCluster.getBindAddress(), BindingScope.SHARED,
bindingInterface, BindingScope.EXCLUSIVE);
}
});
}
private void assertExclusivePortsAreMutuallyExclusive(Set seenVirtualClusters, Map, Set> inUseExclusivePorts,
VirtualCluster virtualCluster, String name,
Set proposedExclusivePorts, Set> exclusiveCheckSet) {
inUseExclusivePorts.entrySet().stream()
.filter(interfacePortsEntry -> exclusiveCheckSet.contains(interfacePortsEntry.getKey()))
.forEach(entry -> {
var bindingInterface = entry.getKey();
var ports = entry.getValue();
var conflicts = getSortedPortConflicts(ports, proposedExclusivePorts);
if (!conflicts.isEmpty()) {
throw buildPortConflictException(name, seenVirtualClusters, conflicts,
virtualCluster.getBindAddress(), BindingScope.EXCLUSIVE,
bindingInterface, BindingScope.EXCLUSIVE);
}
});
}
private void checkForConflictsWithOtherExclusivePort(HostPort otherHostPort, String name, VirtualCluster cluster, Set proposedExclusivePorts,
BindingScope scope) {
var ports = Set.of(otherHostPort.port());
var conflicts = getSortedPortConflicts(ports, proposedExclusivePorts);
if (!conflicts.isEmpty()) {
Optional proposedBindingInterface = cluster.getBindAddress();
var portConflicts = conflicts.stream().map(String::valueOf).collect(Collectors.joining(","));
throw new IllegalStateException("The %s bind of port(s) %s for virtual cluster '%s' to %s would conflict with another (non-cluster) port binding".formatted(
scope.name().toLowerCase(Locale.ROOT),
portConflicts,
name,
proposedBindingInterface.orElse(ANY_STRING)));
}
}
private List getSortedPortConflicts(Set ports, Set candidates) {
return ports.stream().filter(candidates::contains).sorted().toList();
}
private IllegalStateException buildPortConflictException(String virtualClusterName, Set seenVirtualClusters, List conflicts,
Optional proposedBindingInterface, BindingScope proposedScope,
Optional existingBindingInterface, BindingScope existingBindingScope) {
var portConflicts = conflicts.stream().map(String::valueOf).collect(Collectors.joining(","));
var underlying = new IllegalStateException("The %s bind of port(s) %s to %s would conflict with existing %s port bindings on %s.".formatted(
proposedScope.name().toLowerCase(Locale.ROOT),
portConflicts,
proposedBindingInterface.orElse(ANY_STRING),
existingBindingScope.name().toLowerCase(Locale.ROOT),
existingBindingInterface.orElse(ANY_STRING)));
return buildOverviewException(virtualClusterName, seenVirtualClusters, underlying);
}
private IllegalStateException buildTlsConflictException(Integer port, Optional bindingAddress) {
return new IllegalStateException(
"The shared bind of port %d to %s has conflicting TLS settings with existing port on the same interface.".formatted(port,
bindingAddress.orElse(ANY_STRING)));
}
private IllegalStateException buildOverviewException(String virtualClusterName, Set seenVirtualClusters, IllegalStateException underlying) {
var seenVirtualClustersString = seenVirtualClusters.stream().sorted().map(s -> "'" + s + "'").collect(Collectors.joining(","));
return new IllegalStateException("Configuration for virtual cluster '%s' conflicts with configuration for virtual cluster%s: %s.".formatted(virtualClusterName,
seenVirtualClusters.size() > 1 ? "s" : "", seenVirtualClustersString), underlying);
}
}