-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
We would like to upgrade to kubernetes version 1.33 and are therefor making a conscious effort to ditch endpoints in favour of endpointslices. Since we also use Keda in our cluster to scale our pods sometimes all the way to zero not all endpoints in the endpointslices object are always set. You would expect Spring cloud to handle this properly by filtering out empty endpoint slices. Yet I get a NPE whenever all endpoint slices are being retrieved
I was able to replicate the issue with the following test
`
import io.fabric8.kubernetes.api.model.ObjectReference;
import io.fabric8.kubernetes.api.model.discovery.v1.Endpoint;
import io.fabric8.kubernetes.api.model.discovery.v1.EndpointConditions;
import io.fabric8.kubernetes.api.model.discovery.v1.EndpointHints;
import io.fabric8.kubernetes.api.model.discovery.v1.EndpointSlice;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.kubernetes.commons.discovery.EndpointNameAndNamespace;
import static java.util.Comparator.comparing;
import static java.util.Comparator.nullsLast;
public class Debugtest {
@Test
void check() {
var endpointSlice1 = new EndpointSlice("IPv4", "discovery.k8s.io/v1", null, "EndpointSlice" , null, null);
var endpointSlice2 = new EndpointSlice("IPv4", "discovery.k8s.io/v1",
List.of(new Endpoint(List.of("192.168.0.0"),
new EndpointConditions(true, true, true),
Map.of("key", "val"),
new EndpointHints(),
"hostName", "nodeName",
new ObjectReference("discovery.k8s.io/v1", "*/", "pod", "name!!!", "nameSpace!!!!", "", ""), "zone")), "EndpointSlice" , null, null);
List<EndpointSlice> list = List.of(endpointSlice1, endpointSlice2);
Stream<ObjectReference> references = list.stream()
.map(EndpointSlice::getEndpoints)
.flatMap(List::stream)
.map(Endpoint::getTargetRef);
var resList = references.filter(Objects::nonNull)
.map(x -> new EndpointNameAndNamespace(x.getName(), x.getNamespace()))
.sorted(comparing(EndpointNameAndNamespace::endpointName, nullsLast(String::compareTo)))
.toList();
}
}
`
The test consist of a slice where there is no endpoints object and one where there is.
I was able to confirm that this scenario was equals to the our scenario during runtime.
A very easy way around this would be:
Stream<ObjectReference> references = list.stream() .map(EndpointSlice::getEndpoints) .filter(Objects::nonNull) .flatMap(List::stream) .map(Endpoint::getTargetRef);
This filters out everything in the stream that has no Endpoint.
I am also wondering why this isnt handle with Optionals?
That would have prevented the issue from happening all together