kubernetes client discovery cleanup 4 (#1217)

This commit is contained in:
erabii
2023-02-09 17:06:08 +02:00
committed by GitHub
parent 3ae67f667b
commit 0abbdefba0
6 changed files with 256 additions and 39 deletions

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2013-2023 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.cloud.kubernetes.client.discovery;
import java.util.Map;
import java.util.Optional;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Service;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.core.log.LogAccessor;
/**
* @author wind57
*/
final class KubernetesDiscoveryClientUtils {
private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(KubernetesDiscoveryClientUtils.class));
private KubernetesDiscoveryClientUtils() {
}
static boolean matchesServiceLabels(V1Service service, KubernetesDiscoveryProperties properties) {
Map<String, String> propertiesServiceLabels = properties.serviceLabels();
Map<String, String> serviceLabels = Optional.ofNullable(service.getMetadata()).map(V1ObjectMeta::getLabels)
.orElse(Map.of());
if (propertiesServiceLabels.isEmpty()) {
LOG.debug(() -> "service labels from properties are empty, service with name : '"
+ service.getMetadata().getName() + "' will match");
return true;
}
if (serviceLabels.isEmpty()) {
LOG.debug(() -> "service with name : '" + service.getMetadata().getName() + "' does not have labels");
return false;
}
LOG.debug(() -> "Service labels from properties : " + properties.serviceLabels());
LOG.debug(() -> "Service labels from service : " + service.getMetadata().getLabels());
return serviceLabels.keySet().containsAll(propertiesServiceLabels.keySet());
}
}

View File

@@ -46,6 +46,7 @@ import org.springframework.core.log.LogAccessor;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import static org.springframework.cloud.kubernetes.client.discovery.KubernetesDiscoveryClientUtils.matchesServiceLabels;
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.HTTP;
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.HTTPS;
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.UNSET_PORT_NAME;
@@ -105,7 +106,7 @@ public class KubernetesInformerDiscoveryClient implements DiscoveryClient {
List<V1Service> services = properties.allNamespaces()
? serviceLister.list().stream().filter(svc -> serviceId.equals(svc.getMetadata().getName())).toList()
: List.of(serviceLister.namespace(namespace).get(serviceId));
if (services.size() == 0 || !services.stream().anyMatch(this::matchServiceLabels)) {
if (services.size() == 0 || !services.stream().anyMatch(service -> matchesServiceLabels(service, properties))) {
// no such service present in the cluster
return new ArrayList<>();
}
@@ -229,8 +230,8 @@ public class KubernetesInformerDiscoveryClient implements DiscoveryClient {
public List<String> getServices() {
List<V1Service> services = properties.allNamespaces() ? serviceLister.list()
: serviceLister.namespace(namespace).list();
return services.stream().filter(this::matchServiceLabels).map(s -> s.getMetadata().getName())
.collect(Collectors.toList());
return services.stream().filter(service -> matchesServiceLabels(service, properties))
.map(s -> s.getMetadata().getName()).collect(Collectors.toList());
}
@PostConstruct
@@ -253,26 +254,4 @@ public class KubernetesInformerDiscoveryClient implements DiscoveryClient {
+ " services) , discovery client is now available");
}
private boolean matchServiceLabels(V1Service service) {
LOG.debug(() -> "Kubernetes Service Label Properties:");
if (properties.serviceLabels() != null) {
properties.serviceLabels().forEach((key, value) -> LOG.debug(() -> key + ":" + value));
}
LOG.debug(() -> "Service " + service.getMetadata().getName() + " labels:");
if (service.getMetadata() != null && service.getMetadata().getLabels() != null) {
service.getMetadata().getLabels().forEach((key, value) -> LOG.debug(() -> key + ":" + value));
}
// safeguard
if (service.getMetadata() == null) {
return false;
}
if (properties.serviceLabels() == null || properties.serviceLabels().isEmpty()) {
return true;
}
return properties.serviceLabels().keySet().stream()
.allMatch(k -> service.getMetadata().getLabels() != null
&& service.getMetadata().getLabels().containsKey(k)
&& service.getMetadata().getLabels().get(k).equals(properties.serviceLabels().get(k)));
}
}

View File

@@ -0,0 +1,170 @@
/*
* Copyright 2013-2023 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.cloud.kubernetes.client.discovery;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Service;
import io.kubernetes.client.openapi.models.V1ServiceBuilder;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import static org.springframework.cloud.kubernetes.client.discovery.KubernetesDiscoveryClientUtils.matchesServiceLabels;
/**
* @author wind57
*/
@ExtendWith(OutputCaptureExtension.class)
class KubernetesDiscoveryClientUtilsTests {
/**
* properties service labels are empty
*/
@Test
void testEmptyServiceLabelsFromProperties(CapturedOutput output) {
KubernetesDiscoveryProperties properties = KubernetesDiscoveryProperties.DEFAULT;
V1Service service = new V1ServiceBuilder().withMetadata(new V1ObjectMeta().name("my-service")).build();
boolean result = matchesServiceLabels(service, properties);
Assertions.assertTrue(result);
Assertions.assertTrue(output.getOut()
.contains("service labels from properties are empty, service with name : 'my-service' will match"));
}
/**
* labels from service are empty
*/
@Test
void testEmptyServiceLabelsFromService(CapturedOutput output) {
Map<String, String> propertiesLabels = Map.of("key", "value");
KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, true, Set.of(), true, 60L,
true, "", Set.of(), propertiesLabels, "", null, 0, false);
V1Service service = new V1ServiceBuilder().withMetadata(new V1ObjectMeta().name("my-service")).build();
boolean result = matchesServiceLabels(service, properties);
Assertions.assertFalse(result);
Assertions.assertTrue(output.getOut().contains("service with name : 'my-service' does not have labels"));
}
/**
* <pre>
* properties = [a=b]
* service = [a=b]
*
* This means the service is picked-up.
* </pre>
*/
@Test
void testOne(CapturedOutput output) {
Map<String, String> propertiesLabels = Map.of("a", "b");
Map<String, String> serviceLabels = Map.of("a", "b");
KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, true, Set.of(), true, 60L,
true, "", Set.of(), propertiesLabels, "", null, 0, false);
V1Service service = new V1ServiceBuilder()
.withMetadata(new V1ObjectMeta().labels(serviceLabels).name("my-service")).build();
boolean result = matchesServiceLabels(service, properties);
Assertions.assertTrue(result);
Assertions.assertTrue(output.getOut().contains("Service labels from properties : {a=b}"));
Assertions.assertTrue(output.getOut().contains("Service labels from service : {a=b}"));
}
/**
* <pre>
* properties = [a=b, c=d]
* service = [a=b]
*
* This means the service is not picked-up.
* </pre>
*/
@Test
void testTwo(CapturedOutput output) {
Map<String, String> propertiesLabels = ordered(Map.of("a", "b", "c", "d"));
Map<String, String> serviceLabels = Map.of("a", "b");
KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, true, Set.of(), true, 60L,
true, "", Set.of(), propertiesLabels, "", null, 0, false);
V1Service service = new V1ServiceBuilder()
.withMetadata(new V1ObjectMeta().labels(serviceLabels).name("my-service")).build();
boolean result = matchesServiceLabels(service, properties);
Assertions.assertFalse(result);
Assertions.assertTrue(output.getOut().contains("Service labels from properties : {a=b, c=d}"));
Assertions.assertTrue(output.getOut().contains("Service labels from service : {a=b}"));
}
/**
* <pre>
* properties = [a=b, c=d]
* service = [a=b, c=d]
*
* This means the service is picked-up.
* </pre>
*/
@Test
void testThree(CapturedOutput output) {
Map<String, String> propertiesLabels = ordered(Map.of("a", "b", "c", "d"));
Map<String, String> serviceLabels = ordered(Map.of("a", "b", "c", "d"));
KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, true, Set.of(), true, 60L,
true, "", Set.of(), propertiesLabels, "", null, 0, false);
V1Service service = new V1ServiceBuilder()
.withMetadata(new V1ObjectMeta().labels(serviceLabels).name("my-service")).build();
boolean result = matchesServiceLabels(service, properties);
Assertions.assertTrue(result);
Assertions.assertTrue(output.getOut().contains("Service labels from properties : {a=b, c=d}"));
Assertions.assertTrue(output.getOut().contains("Service labels from service : {a=b, c=d}"));
}
/**
* <pre>
* properties = [a=b]
* service = [a=b, c=d]
*
* This means the service is picked-up.
* </pre>
*/
@Test
void testFour(CapturedOutput output) {
Map<String, String> propertiesLabels = Map.of("a", "b");
Map<String, String> serviceLabels = ordered(Map.of("a", "b", "c", "d"));
KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, true, Set.of(), true, 60L,
true, "", Set.of(), propertiesLabels, "", null, 0, false);
V1Service service = new V1ServiceBuilder()
.withMetadata(new V1ObjectMeta().labels(serviceLabels).name("my-service")).build();
boolean result = matchesServiceLabels(service, properties);
Assertions.assertTrue(result);
Assertions.assertTrue(output.getOut().contains("Service labels from properties : {a=b}"));
Assertions.assertTrue(output.getOut().contains("Service labels from service : {a=b, c=d}"));
}
// preserve order for testing reasons
private Map<String, String> ordered(Map<String, String> input) {
return input.entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (left, right) -> left, LinkedHashMap::new));
}
}

View File

@@ -235,7 +235,7 @@ public class KubernetesInformerDiscoveryClientTests {
Lister<V1Endpoints> endpointsLister = setupEndpointsLister(testEndpoints1);
KubernetesDiscoveryProperties kubernetesDiscoveryProperties = new KubernetesDiscoveryProperties(true, true,
Set.of(), true, 60, false, null, Set.of(), null, null, null, 0, true);
Set.of(), true, 60, false, null, Set.of(), Map.of(), null, null, 0, true);
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
@@ -250,7 +250,7 @@ public class KubernetesInformerDiscoveryClientTests {
Lister<V1Endpoints> endpointsLister = setupEndpointsLister(testEndpoints1);
KubernetesDiscoveryProperties kubernetesDiscoveryProperties = new KubernetesDiscoveryProperties(true, false,
Set.of(), true, 60, false, null, Set.of(), null, null, null, 0, true);
Set.of(), true, 60, false, null, Set.of(), Map.of(), null, null, 0, true);
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
@@ -277,7 +277,7 @@ public class KubernetesInformerDiscoveryClientTests {
Lister<V1Endpoints> endpointsLister = setupEndpointsLister(testEndpointWithoutReadyAddresses);
KubernetesDiscoveryProperties kubernetesDiscoveryProperties = new KubernetesDiscoveryProperties(true, false,
Set.of(), true, 60, true, null, Set.of(), null, null, null, 0, true);
Set.of(), true, 60, true, null, Set.of(), Map.of(), null, null, 0, true);
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
@@ -318,7 +318,7 @@ public class KubernetesInformerDiscoveryClientTests {
Lister<V1Endpoints> endpointsLister = setupEndpointsLister(testEndpointWithMultiplePorts);
KubernetesDiscoveryProperties kubernetesDiscoveryProperties = new KubernetesDiscoveryProperties(true, false,
Set.of(), true, 60, false, null, Set.of(), null, null, null, 0, true);
Set.of(), true, 60, false, null, Set.of(), Map.of(), null, null, 0, true);
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
@@ -337,7 +337,7 @@ public class KubernetesInformerDiscoveryClientTests {
testEndpointWithMultiplePortsWithoutSupportedPortNames);
KubernetesDiscoveryProperties kubernetesDiscoveryProperties = new KubernetesDiscoveryProperties(true, false,
Set.of(), true, 60, false, null, Set.of(), null, null, null, 0, true);
Set.of(), true, 60, false, null, Set.of(), Map.of(), null, null, 0, true);
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
@@ -353,7 +353,7 @@ public class KubernetesInformerDiscoveryClientTests {
Lister<V1Endpoints> endpointsLister = setupEndpointsLister(testEndpointWithMultiplePorts);
KubernetesDiscoveryProperties kubernetesDiscoveryProperties = new KubernetesDiscoveryProperties(true, false,
Set.of(), true, 60, false, null, Set.of(), null, "https", null, 0, true);
Set.of(), true, 60, false, null, Set.of(), Map.of(), "https", null, 0, true);
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
@@ -369,7 +369,7 @@ public class KubernetesInformerDiscoveryClientTests {
testEndpointWithMultiplePortsWithoutSupportedPortNames);
KubernetesDiscoveryProperties kubernetesDiscoveryProperties = new KubernetesDiscoveryProperties(true, false,
Set.of(), true, 60, false, null, Set.of(), null, "oops", null, 0, true);
Set.of(), true, 60, false, null, Set.of(), Map.of(), "oops", null, 0, true);
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
@@ -384,7 +384,7 @@ public class KubernetesInformerDiscoveryClientTests {
Lister<V1Endpoints> endpointsLister = setupEndpointsLister(testEndpointWithMultiplePorts);
KubernetesDiscoveryProperties kubernetesDiscoveryProperties = new KubernetesDiscoveryProperties(true, false,
Set.of(), true, 60, false, null, Set.of(), null, null, null, 0, true);
Set.of(), true, 60, false, null, Set.of(), Map.of(), null, null, 0, true);
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
@@ -399,7 +399,7 @@ public class KubernetesInformerDiscoveryClientTests {
Lister<V1Endpoints> endpointsLister = setupEndpointsLister(testEndpointWithMultiplePortsWithoutHttps);
KubernetesDiscoveryProperties kubernetesDiscoveryProperties = new KubernetesDiscoveryProperties(true, false,
Set.of(), true, 60, false, null, Set.of(), null, null, null, 0, true);
Set.of(), true, 60, false, null, Set.of(), Map.of(), null, null, 0, true);
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
@@ -415,7 +415,7 @@ public class KubernetesInformerDiscoveryClientTests {
testEndpointWithMultiplePortsWithoutSupportedPortNames);
KubernetesDiscoveryProperties kubernetesDiscoveryProperties = new KubernetesDiscoveryProperties(true, false,
Set.of(), true, 60, false, null, Set.of(), null, null, null, 0, true);
Set.of(), true, 60, false, null, Set.of(), Map.of(), null, null, 0, true);
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
@@ -430,7 +430,7 @@ public class KubernetesInformerDiscoveryClientTests {
Lister<V1Endpoints> endpointsLister = setupEndpointsLister(testEndpoints1, testEndpoints2);
KubernetesDiscoveryProperties kubernetesDiscoveryProperties = new KubernetesDiscoveryProperties(true, true,
Set.of(), true, 60, false, null, Set.of(), null, null, null, 0, false);
Set.of(), true, 60, false, null, Set.of(), Map.of(), null, null, 0, false);
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(null,
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.kubernetes.client.discovery.reactive;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import io.kubernetes.client.informer.SharedInformerFactory;
@@ -101,7 +102,7 @@ public class KubernetesInformerReactiveDiscoveryClientTests {
Lister<V1Endpoints> endpointsLister = setupEndpointsLister(testEndpoints1);
KubernetesDiscoveryProperties kubernetesDiscoveryProperties = new KubernetesDiscoveryProperties(true, true,
Set.of(), true, 60, false, null, Set.of(), null, null, null, 0, false);
Set.of(), true, 60, false, null, Set.of(), Map.of(), null, null, 0, false);
KubernetesInformerReactiveDiscoveryClient discoveryClient = new KubernetesInformerReactiveDiscoveryClient(
new KubernetesNamespaceProvider(new MockEnvironment()), sharedInformerFactory, serviceLister,
@@ -120,7 +121,7 @@ public class KubernetesInformerReactiveDiscoveryClientTests {
Lister<V1Endpoints> endpointsLister = setupEndpointsLister(testEndpoints1);
KubernetesDiscoveryProperties kubernetesDiscoveryProperties = new KubernetesDiscoveryProperties(true, false,
Set.of(), true, 60, false, null, Set.of(), null, null, null, 0, false);
Set.of(), true, 60, false, null, Set.of(), Map.of(), null, null, 0, false);
KubernetesNamespaceProvider kubernetesNamespaceProvider = mock(KubernetesNamespaceProvider.class);
when(kubernetesNamespaceProvider.getNamespace()).thenReturn("namespace1");

View File

@@ -11,5 +11,8 @@
<logger name="org.testcontainers" level="INFO"/>
<logger name="com.github.dockerjava" level="WARN"/>
<logger name="io.fabric8.kubernetes.client" level="ERROR"/>
<!-- needed for CapturedOutput -->
<logger name="org.springframework.cloud.kubernetes.client.discovery" level="DEBUG"/>
</configuration>